diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 00000000000..3edb5883290 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,58 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# https://cwiki.apache.org/confluence/display/INFRA/git+-+.asf.yaml+features + +github: + description: "Apache ZooKeeper" + homepage: https://zookeeper.apache.org + labels: + - java + - apache + - zookeeper + - consensus + - coordination + - zab + - database + - key-value + - distributed-database + - distributed-systems + - service-discovery + - configuration-management + - distributed-configuration + - hacktoberfest + features: + wiki: false + issues: false + projects: false + enabled_merge_buttons: + squash: true + merge: false + rebase: false + autolink_jira: + - ZOOKEEPER + dependabot_alerts: true + dependabot_updates: false + +notifications: + jira_options: link label worklog + commits: commits@zookeeper.apache.org + issues: issues@zookeeper.apache.org + pullrequests: notifications@zookeeper.apache.org + diff --git a/.gitattributes b/.gitattributes index c3cc5e082bc..3d68ac6cc74 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + # Auto detect text files and perform LF normalization * text=auto diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000000..6f09ab5c914 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,106 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This workflow will build a Java project with Maven +# See also: +# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: CI + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + mvn: + strategy: + matrix: + profile: + - name: 'full-build-jdk8' + jdk: 8 + args: '-Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check' + - name: 'full-build-jdk11' + jdk: 11 + args: '-Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check' + - name: 'full-build-java-tests' + jdk: 11 + args: '-Pfull-build verify -Dsurefire-forkcount=1 -DskipCppUnit -Dsurefire.rerunFailingTestsCount=5' + - name: 'full-build-cppunit-tests' + jdk: 11 + args: '-Pfull-build verify -Dtest=_ -DfailIfNoTests=false' + fail-fast: false + timeout-minutes: 360 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up JDK ${{ matrix.profile.jdk }} + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.profile.jdk }} + distribution: temurin + cache: 'maven' + - name: Show the first log message + run: git log -n1 + - name: Install C Dependencies + run: | + sudo apt update + sudo apt install -y libcppunit-dev libsasl2-dev + - name: Build with Maven (${{ matrix.profile.name }}) + run: mvn -B -V -e -ntp "-Dstyle.color=always" ${{ matrix.profile.args }} + env: + MAVEN_OPTS: -Djansi.force=true + - name: Upload unit test results + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: surefire-reports-${{ matrix.profile.name }} + path: ./**/target/surefire-reports/ + if-no-files-found: ignore + - name: Upload integration test results + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: failsafe-reports-${{ matrix.profile.name }} + path: ./**/target/failsafe-reports/ + if-no-files-found: ignore + - name: Upload cppunit test logs + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: cppunit-logs-${{ matrix.profile.name }} + path: ./zookeeper-client/zookeeper-client-c/target/c/TEST-*.txt + if-no-files-found: ignore + typo-check: + name: Typo Check + # only run on pull requests because of security reasons + # we shouldn't trust external actions for builds within the repository + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Check typos + uses: crate-ci/typos@v1.22.4 + # To run the typo check locally, you can follow these steps: + # 1. Install typos locally using cargo: + # cargo install typos-cli + # 2. Run the typo check with the following command: + # typos + # FP is configured in the project root directory in the `.typos.toml` file. + # You can refer to the `.typos.toml` documentation here: + # https://github.com/crate-ci/typos/blob/master/docs/reference.md diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 00000000000..d20eba55b0d --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,79 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT 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: End to End Tests + +on: + push: + pull_request: + +jobs: + compatibility: + strategy: + matrix: + jdk: [8, 11] + zk: [3.5.9, 3.6.3, 3.7.0, nightly] + fail-fast: false + timeout-minutes: 360 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up JDK ${{ matrix.jdk }} + uses: actions/setup-java@v5 + with: + java-version: ${{ matrix.jdk }} + distribution: temurin + cache: 'maven' + - name: Show the first log message + run: git log -n1 + - name: Install C Dependencies + run: | + sudo apt update + sudo apt install -y libcppunit-dev libsasl2-dev + - name: Build with Maven + run: mvn -B -V -e -ntp "-Dstyle.color=always" package -DskipTests + env: + MAVEN_OPTS: -Djansi.force=true + - name: Cache ZooKeeper ${{ matrix.zk }} + id: dist-cache + if: matrix.zk != 'nightly' + uses: actions/cache@v4 + with: + key: apache-zookeeper-${{ matrix.zk }}-bin.tar.gz + path: apache-zookeeper-${{ matrix.zk }}-bin.tar.gz + - name: Download ZooKeeper ${{ matrix.zk }} + if: matrix.zk != 'nightly' && steps.dist-cache.outputs.cache-hit != 'true' + run: | + curl -O https://archive.apache.org/dist/zookeeper/zookeeper-${{ matrix.zk }}/apache-zookeeper-${{ matrix.zk }}-bin.tar.gz + - name: Extract ZooKeeper ${{ matrix.zk }} + if: matrix.zk != 'nightly' + run: tar -xzvf apache-zookeeper-${{ matrix.zk }}-bin.tar.gz + - name: Test ZooKeeper nightly server and ${{ matrix.zk }} client + if: matrix.zk != 'nightly' + run: tools/ci/test-connectivity.py --server . --client apache-zookeeper-${{ matrix.zk }}-bin + env: + ZOOCFG: zoo_sample.cfg + - name: Test ZooKeeper ${{ matrix.zk }} server and nightly client + if: matrix.zk != 'nightly' + run: tools/ci/test-connectivity.py --server apache-zookeeper-${{ matrix.zk }}-bin --client . + env: + ZOOCFG: zoo_sample.cfg + - name: Test ZooKeeper nightly server and client + if: matrix.zk == 'nightly' + run: tools/ci/test-connectivity.py --server . --client . + env: + ZOOCFG: zoo_sample.cfg diff --git a/.github/workflows/manual.yaml b/.github/workflows/manual.yaml new file mode 100644 index 00000000000..8b50b8be361 --- /dev/null +++ b/.github/workflows/manual.yaml @@ -0,0 +1,78 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# This workflow will build a Java project with Maven +# See also: +# https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven +# https://docs.github.com/en/actions/reference/events-that-trigger-workflows#manual-events + +name: Manual Build + +on: + workflow_dispatch: + inputs: + buildRef: + description: Ref to build (commit, branch, or refs/pull/1234/head or refs/pull/1234/merge) + required: true + default: refs/pull/1234/merge + mvnOpts: + description: Maven options + required: true + default: --fail-at-end + goals: + description: Maven goals + required: true + default: -Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check javadoc:jar +jobs: + mvn: + name: mvn (triggered by ${{ github.event.sender.login }}) + timeout-minutes: 360 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.inputs.buildRef }} + - name: Set up JDK 11 + uses: actions/setup-java@v5 + with: + java-version: 11 + distribution: temurin + cache: 'maven' + - name: Show the first log message + run: git log -n1 + - name: Install C Dependencies + run: | + sudo apt update + sudo apt install -y libcppunit-dev libsasl2-dev + - name: Build with Maven + run: mvn -B -V -e -ntp "-Dstyle.color=always" ${{ github.event.inputs.mvnOpts }} ${{ github.event.inputs.goals }} + env: + MAVEN_OPTS: -Djansi.force=true + - name: Upload unit test results + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: surefire-reports + path: ./**/target/surefire-reports/ + if-no-files-found: ignore + - name: Upload integration test results + if: ${{ failure() }} + uses: actions/upload-artifact@v7 + with: + name: failsafe-reports + path: ./**/target/failsafe-reports/ + if-no-files-found: ignore diff --git a/.github/workflows/scripts.yaml b/.github/workflows/scripts.yaml new file mode 100644 index 00000000000..98217b74700 --- /dev/null +++ b/.github/workflows/scripts.yaml @@ -0,0 +1,58 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# This workflow enforces quality checks on bash scripts in the project + +name: ScriptQA + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +permissions: + contents: read + +jobs: + shfmt: + name: shfmt + timeout-minutes: 3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Show the first log message + run: git log -n1 + - name: Install shfmt + run: tools/ci/install-shfmt.sh + - name: Checking formatting of all scripts + run: tools/ci/run-shfmt.sh + + shellcheck: + name: ShellCheck + timeout-minutes: 3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Show the first log message + run: git log -n1 + - name: Install shfmt + run: tools/ci/install-shfmt.sh + - name: Running shellcheck on all scripts + run: tools/ci/run-shellcheck.sh diff --git a/.gitignore b/.gitignore index 55937cec57e..5b7d5dcac48 100644 --- a/.gitignore +++ b/.gitignore @@ -69,9 +69,6 @@ tags obj zookeeper-server/src/main/resources/lib/ant-eclipse-* zookeeper-server/src/main/resources/lib/ivy-* -zookeeper-server/src/main/java/org/apache/zookeeper/version/Info.java -zookeeper-server/src/main/java/org/apache/zookeeper/version/VersionInfoMain.java -zookeeper-server/src/test/resources/ zookeeper-client/zookeeper-client-c/Makefile.in zookeeper-client/zookeeper-client-c/aclocal.m4 zookeeper-client/zookeeper-client-c/autom4te.cache/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index fe2e952a1d9..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: java -sudo: false - -matrix: - include: - - os: linux - jdk: openjdk8 - - os: linux - jdk: openjdk11 - - os: linux - arch: s390x - jdk: openjdk11 - addons: - apt: - update: true - packages: - - maven - - libcppunit-dev - -cache: - directories: - - "$HOME/.m2" - -addons: - apt: - packages: - - libcppunit-dev - -script: mvn clean apache-rat:check install -DskipTests spotbugs:check checkstyle:check -Pfull-build - -branches: - only: - - master - - branch-3.5 - - branch-3.4 diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000000..b3cd78c734f --- /dev/null +++ b/.typos.toml @@ -0,0 +1,52 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +[default.extend-words] +# abbr +"aroun" = "aroun" +"delet" = "delet" +"dout" = "dout" +"fied" = "fied" +"fle" = "fle" +"fo" = "fo" +"hte" = "hte" +"nd" = "nd" +"nwe" = "nwe" +"pn" = "pn" +"strack" = "strack" +"thr" = "thr" +"ths" = "ths" +"wew" = "wew" +# keep for comptability +"pathes" = "pathes" +"shapshot" = "shapshot" +# keyword fp +"atend" = "atend" +"chec" = "chec" +"clos" = "clos" +"erro" = "erro" +"infor" = "infor" +"locahost" = "locahost" +"leafs" = "leafs" +"sceen" = "sceen" + +[files] +extend-exclude = [ + "zookeeper-server/src/test/java/org/apache/zookeeper/common/CertificatesToPlayWith.java", +] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000000..40fcdd5bbad --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pipeline { + agent { + label 'Hadoop' + } + + options { + disableConcurrentBuilds() + buildDiscarder(logRotator(daysToKeepStr: '14')) + timeout(time: 2, unit: 'HOURS') + timestamps() + } + + triggers { + cron('@daily') + } + + stages { + stage('Prepare') { + matrix { + agent any + axes { + axis { + name 'JAVA_VERSION' + values 'jdk_1.8_latest', 'jdk_11_latest' + } + } + + tools { + maven "maven_latest" + jdk "${JAVA_VERSION}" + } + + stages { + stage('BuildAndTest') { + steps { + sh "git clean -fxd" + sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" + } + post { + always { + junit '**/target/surefire-reports/TEST-*.xml' + archiveArtifacts '**/target/*.jar' + } + // Jenkins pipeline jobs fill slaves on PRs without this :( + cleanup() { + script { + sh label: 'Cleanup workspace', script: ''' + # See HADOOP-13951 + chmod -R u+rxw "${WORKSPACE}" + ''' + deleteDir() + } + } + } + } + } + } + } + } +} diff --git a/Jenkinsfile-PreCommit b/Jenkinsfile-PreCommit new file mode 100644 index 00000000000..8390e05b929 --- /dev/null +++ b/Jenkinsfile-PreCommit @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pipeline { + agent { + label 'Hadoop' + } + + options { + disableConcurrentBuilds() + buildDiscarder(logRotator(daysToKeepStr: '14')) + timeout(time: 2, unit: 'HOURS') + timestamps() + } + + tools { + maven "maven_latest" + jdk "jdk_1.8_latest" + } + + stages { + stage('BuildAndTest') { + steps { + sh "git clean -fxd" + sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" + } + post { + always { + junit '**/target/surefire-reports/TEST-*.xml' + } + } + } + } + + post { + // Jenkins pipeline jobs fill slaves on PRs without this :( + cleanup() { + script { + sh label: 'Cleanup workspace', script: ''' + # See HADOOP-13951 + chmod -R u+rxw "${WORKSPACE}" + ''' + deleteDir() + } + } + } +} diff --git a/Jenkinsfile-owasp b/Jenkinsfile-owasp new file mode 100644 index 00000000000..2831dd4d6df --- /dev/null +++ b/Jenkinsfile-owasp @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pipeline { + agent { + label 'Hadoop' + } + + options { + buildDiscarder(logRotator(daysToKeepStr: '14')) + timeout(time: 2, unit: 'HOURS') + timestamps() + } + + tools { + maven "maven_latest" + jdk "jdk_11_latest" + } + + stages { + stage('BuildAndTest') { + steps { + sh "git clean -fxd" + sh "mvn clean package -DskipTests dependency-check:check" + } + post { + always { + archiveArtifacts '**/target/dependency-check-*' + } + } + } + } + + post { + // Jenkins pipeline jobs fill slaves on PRs without this :( + cleanup() { + script { + sh label: 'Cleanup workspace', script: ''' + # See HADOOP-13951 + chmod -R u+rxw "${WORKSPACE}" + ''' + deleteDir() + } + } + } +} diff --git a/Jenkinsfile-s390x b/Jenkinsfile-s390x new file mode 100644 index 00000000000..bd48a1c280a --- /dev/null +++ b/Jenkinsfile-s390x @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pipeline { + agent { + label 's390x' + } + + options { + disableConcurrentBuilds() + buildDiscarder(logRotator(daysToKeepStr: '14')) + timeout(time: 2, unit: 'HOURS') + timestamps() + } + + triggers { + cron('@daily') + } + + stages { + stage('Prepare') { + + agent { + label 's390x' + } + + tools { + maven "maven_latest" + jdk "jdk_11_latest" + } + + stages { + stage('BuildAndTest') { + steps { + sh "git clean -fxd" + sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" + } + post { + always { + junit '**/target/surefire-reports/TEST-*.xml' + archiveArtifacts '**/target/*.jar' + } + // Jenkins pipeline jobs fill slaves on PRs without this :( + cleanup() { + script { + sh label: 'Cleanup workspace', script: ''' + # See HADOOP-13951 + chmod -R u+rxw "${WORKSPACE}" + ''' + deleteDir() + } + } + } + } + } + } + } +} diff --git a/NOTICE.txt b/NOTICE.txt index 4c4f8b220fe..9b3afcb4705 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Apache ZooKeeper -Copyright 2009-2020 The Apache Software Foundation +Copyright 2009-2026 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). @@ -9,3 +9,17 @@ developed for Airlift (https://github.com/airlift/airlift), licensed under the Apache 2.0 license. The licensing terms for Airlift code can be found at: https://github.com/airlift/airlift/blob/master/LICENSE + +This project also includes some files with the following licenses. + +These BSD licensed files: + ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c + ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.h + ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.c + ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.h + ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_private.h + ./zookeeper-docs/src/main/resources/markdown/skin/prototype.js + +This Apache 2.0 licensed file: +./zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java + diff --git a/README.md b/README.md index 0f9c4b2af13..cd9db2b460c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ -# Apache ZooKeeper [![Build Status](https://travis-ci.org/apache/zookeeper.svg?branch=master)](https://travis-ci.org/apache/zookeeper) [![Maven Central](https://img.shields.io/maven-central/v/org.apache.zookeeper/zookeeper)](https://zookeeper.apache.org/releases.html) [![License](https://img.shields.io/github/license/apache/zookeeper)](https://github.com/apache/zookeeper/blob/master/LICENSE.txt) -![alt text](https://zookeeper.apache.org/images/zookeeper_small.gif "ZooKeeper") +# Apache ZooKeeper [![GitHub Actions CI][ciBadge]][ciLink] [![Travis CI][trBadge]][trLink] [![Maven Central][mcBadge]][mcLink] [![License][liBadge]][liLink] + +

+ + https://zookeeper.apache.org/
+
+

For the latest information about Apache ZooKeeper, please visit our website at: - http://zookeeper.apache.org/ + https://zookeeper.apache.org and our wiki, at: https://cwiki.apache.org/confluence/display/ZOOKEEPER ---------------------------- -Packaging/release artifacts +## Packaging/release artifacts Either downloaded from https://zookeeper.apache.org/releases.html or found in zookeeper-assembly/target directory after building the project with maven. @@ -33,7 +37,7 @@ As of version 3.5.5, the parent, zookeeper and zookeeper-jute artifacts are deployed to the central repository after the release is voted on and approved by the Apache ZooKeeper PMC: - https://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper/ + https://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper ## Java 8 @@ -41,6 +45,14 @@ If you are going to compile with Java 1.8, you should use a recent release at u211 or above. # Contributing -We always welcome new contributors to the project! See [How to Contribute](https://cwiki.apache.org/confluence/display/ZOOKEEPER/HowToContribute) for details on how to submit patch through pull request and our contribution workflow. +We always welcome new contributors to the project! See [How to Contribute](https://cwiki.apache.org/confluence/display/ZOOKEEPER/HowToContribute) for details on how to submit patches as pull requests and other aspects of our contribution workflow. +[ciBadge]: https://github.com/apache/zookeeper/workflows/CI/badge.svg +[ciLink]: https://github.com/apache/zookeeper/actions +[liBadge]: https://img.shields.io/github/license/apache/zookeeper?color=282661 +[liLink]: https://github.com/apache/zookeeper/blob/master/LICENSE.txt +[mcBadge]: https://img.shields.io/maven-central/v/org.apache.zookeeper/zookeeper +[mcLink]: https://zookeeper.apache.org/releases +[trBadge]: https://travis-ci.org/apache/zookeeper.svg?branch=master +[trLink]: https://travis-ci.org/apache/zookeeper diff --git a/bin/zkCleanup.sh b/bin/zkCleanup.sh index 45532410e96..2b8b87371cb 100755 --- a/bin/zkCleanup.sh +++ b/bin/zkCleanup.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # This script cleans up old transaction logs and snapshots @@ -27,27 +30,35 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" || exit -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then - . "$ZOOBINDIR"/../libexec/zkEnv.sh +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh + . "$ZOOBINDIR"/../libexec/zkEnv.sh "$@" else - . "$ZOOBINDIR"/zkEnv.sh + # shellcheck source=bin/zkEnv.sh + . "$ZOOBINDIR"/zkEnv.sh "$@" fi -ZOODATADIR="$(grep "^[[:space:]]*dataDir=" "$ZOOCFG" | sed -e 's/.*=//')" -ZOODATALOGDIR="$(grep "^[[:space:]]*dataLogDir=" "$ZOOCFG" | sed -e 's/.*=//')" +ZOODATADIR="" +ZOODATALOGDIR="" + +# Only try to read config if ZOOCFG exists +if [[ -f $ZOOCFG ]]; then + re='^[^=]*=[[:space:]]*(.*)[[:space:]]*$' + ZOODATADIR="$(grep "^[[:space:]]*dataDir=" "$ZOOCFG" 2>/dev/null)" + [[ $ZOODATADIR =~ $re ]] && ZOODATADIR="${BASH_REMATCH[1]}" + ZOODATALOGDIR="$(grep "^[[:space:]]*dataLogDir=" "$ZOOCFG" 2>/dev/null)" + [[ $ZOODATALOGDIR =~ $re ]] && ZOODATALOGDIR="${BASH_REMATCH[1]}" +fi ZOO_LOG_FILE=zookeeper-$USER-cleanup-$HOSTNAME.log -if [ "x$ZOODATALOGDIR" = "x" ] -then -"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ - -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.PurgeTxnLog "$ZOODATADIR" $* -else -"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ - -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.PurgeTxnLog "$ZOODATALOGDIR" "$ZOODATADIR" $* -fi +# shellcheck disable=SC2206 +flags=("-Dzookeeper.log.dir=$ZOO_LOG_DIR" "-Dzookeeper.log.file=$ZOO_LOG_FILE" $JVMFLAGS) + +directories=() +[[ -n $ZOODATADIR ]] && directories=("$ZOODATADIR") +[[ -n $ZOODATALOGDIR ]] && directories=("$ZOODATALOGDIR" "${directories[@]}") +"$JAVA" "${flags[@]}" org.apache.zookeeper.server.PurgeTxnLog "${directories[@]}" "$@" diff --git a/bin/zkCli.cmd b/bin/zkCli.cmd index 807ffaa9e5c..7a4391dd9ff 100644 --- a/bin/zkCli.cmd +++ b/bin/zkCli.cmd @@ -20,7 +20,7 @@ call "%~dp0zkEnv.cmd" set ZOO_LOG_FILE=zookeeper-%USERNAME%-cli-%COMPUTERNAME%.log set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain -call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" -cp "%CLASSPATH%" %ZOOMAIN% %* +call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal diff --git a/bin/zkCli.sh b/bin/zkCli.sh index ecf9a4525a0..7ae9637766b 100755 --- a/bin/zkCli.sh +++ b/bin/zkCli.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# # # This script cleans up old transaction logs and snapshots @@ -27,17 +30,23 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi ZOO_LOG_FILE=zookeeper-$USER-cli-$HOSTNAME.log -"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ - -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \ - org.apache.zookeeper.ZooKeeperMain "$@" +# shellcheck disable=SC2206 +clientflags=($CLIENT_JVMFLAGS) +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +"$JAVA" "-Dzookeeper.log.dir=$ZOO_LOG_DIR" "-Dzookeeper.log.file=$ZOO_LOG_FILE" \ + "${clientflags[@]}" "${flags[@]}" \ + org.apache.zookeeper.ZooKeeperMain "$@" diff --git a/bin/zkEnv.cmd b/bin/zkEnv.cmd index 68e231d7084..de9fc929b61 100644 --- a/bin/zkEnv.cmd +++ b/bin/zkEnv.cmd @@ -16,7 +16,6 @@ REM limitations under the License. set ZOOCFGDIR=%~dp0%..\conf set ZOO_LOG_DIR=%~dp0%..\logs -set ZOO_LOG4J_PROP=INFO,CONSOLE REM for sanity sake assume Java 1.6 REM see: http://java.sun.com/javase/6/docs/technotes/tools/windows/java.html diff --git a/bin/zkEnv.sh b/bin/zkEnv.sh index 8d93a50c7c0..40889874818 100755 --- a/bin/zkEnv.sh +++ b/bin/zkEnv.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash # -# http://www.apache.org/licenses/LICENSE-2.0 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # This script should be sourced into other zookeeper # scripts to setup the env variables @@ -26,99 +29,88 @@ # '--config' option in the command line. ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" -ZOOKEEPER_PREFIX="${ZOOBINDIR}/.." +ZOOKEEPER_PREFIX="$ZOOBINDIR/.." #check to see if the conf dir is given as an optional argument -if [ $# -gt 1 ] -then - if [ "--config" = "$1" ] - then - shift - confdir=$1 - shift - ZOOCFGDIR=$confdir - fi +if [[ $# -gt 1 ]]; then + if [[ "--config" == "$1" ]]; then + shift + ZOOCFGDIR=$1 + shift + fi fi -if [ "x$ZOOCFGDIR" = "x" ] -then - if [ -e "${ZOOKEEPER_PREFIX}/conf" ]; then +if [[ -z $ZOOCFGDIR ]]; then + if [[ -e "$ZOOKEEPER_PREFIX/conf" ]]; then ZOOCFGDIR="$ZOOBINDIR/../conf" else ZOOCFGDIR="$ZOOBINDIR/../etc/zookeeper" fi fi -if [ -f "${ZOOCFGDIR}/zookeeper-env.sh" ]; then - . "${ZOOCFGDIR}/zookeeper-env.sh" +if [[ -f "$ZOOCFGDIR/zookeeper-env.sh" ]]; then + # shellcheck source=bin/zkEnv.sh + . "$ZOOCFGDIR/zookeeper-env.sh" fi -if [ "x$ZOOCFG" = "x" ] -then - ZOOCFG="zoo.cfg" -fi +ZOOCFG=${ZOOCFG:-zoo.cfg} ZOOCFG="$ZOOCFGDIR/$ZOOCFG" -if [ -f "$ZOOCFGDIR/java.env" ] -then - . "$ZOOCFGDIR/java.env" +if [[ -f "$ZOOCFGDIR/java.env" ]]; then + # shellcheck source=bin/zkEnv.sh + . "$ZOOCFGDIR/java.env" fi -if [ "x${ZOO_LOG_DIR}" = "x" ] -then - ZOO_LOG_DIR="$ZOOKEEPER_PREFIX/logs" -fi - -if [ "x${ZOO_LOG4J_PROP}" = "x" ] -then - ZOO_LOG4J_PROP="INFO,CONSOLE" -fi +ZOO_LOG_DIR=${ZOO_LOG_DIR:-$ZOOKEEPER_PREFIX/logs} -if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then - JAVA="$JAVA_HOME/bin/java" +if [[ -n $JAVA_HOME ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then + JAVA="$JAVA_HOME/bin/java" elif type -p java; then - JAVA=java + # ignore unused; used this file is sourced + # shellcheck disable=SC2034 + JAVA=java else - echo "Error: JAVA_HOME is not set and java could not be found in PATH." 1>&2 - exit 1 + echo "Error: JAVA_HOME is not set and java could not be found in PATH." 1>&2 + exit 1 fi #add the zoocfg dir to classpath CLASSPATH="$ZOOCFGDIR:$CLASSPATH" -for i in "$ZOOBINDIR"/../zookeeper-server/src/main/resources/lib/*.jar -do - CLASSPATH="$i:$CLASSPATH" +for i in "$ZOOBINDIR"/../zookeeper-server/src/main/resources/lib/*.jar; do + CLASSPATH="$i:$CLASSPATH" done #make it work in the binary package #(use array for LIBPATH to account for spaces within wildcard expansion) -if ls "${ZOOKEEPER_PREFIX}"/share/zookeeper/zookeeper-*.jar > /dev/null 2>&1; then - LIBPATH=("${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar) +if ls "$ZOOKEEPER_PREFIX"/share/zookeeper/zookeeper-*.jar &>/dev/null; then + LIBPATH=("$ZOOKEEPER_PREFIX"/share/zookeeper/*.jar) else #release tarball format - for i in "$ZOOBINDIR"/../zookeeper-*.jar - do + for i in "$ZOOBINDIR"/../zookeeper-*.jar; do CLASSPATH="$i:$CLASSPATH" done - LIBPATH=("${ZOOBINDIR}"/../lib/*.jar) + LIBPATH=("$ZOOBINDIR"/../lib/*.jar) fi -for i in "${LIBPATH[@]}" -do - CLASSPATH="$i:$CLASSPATH" +for i in "${LIBPATH[@]}"; do + CLASSPATH="$i:$CLASSPATH" done #make it work for developers -for d in "$ZOOBINDIR"/../build/lib/*.jar -do - CLASSPATH="$d:$CLASSPATH" +for d in "$ZOOBINDIR"/../build/lib/*.jar; do + CLASSPATH="$d:$CLASSPATH" done -for d in "$ZOOBINDIR"/../zookeeper-server/target/lib/*.jar -do - CLASSPATH="$d:$CLASSPATH" +#make it work for developers +for d in "$ZOOBINDIR"/../zookeeper-server/target/lib/*.jar; do + CLASSPATH="$d:$CLASSPATH" +done + +#make it work for developers +for d in "$ZOOBINDIR"/../zookeeper-metrics-providers/zookeeper-prometheus-metrics/target/lib/*.jar; do + CLASSPATH="$d:$CLASSPATH" done #make it work for developers @@ -127,17 +119,20 @@ CLASSPATH="$ZOOBINDIR/../build/classes:$CLASSPATH" #make it work for developers CLASSPATH="$ZOOBINDIR/../zookeeper-server/target/classes:$CLASSPATH" -case "`uname`" in - CYGWIN*|MINGW*) cygwin=true ;; - *) cygwin=false ;; +#make it work for developers +CLASSPATH="$ZOOBINDIR/../zookeeper-metrics-providers/zookeeper-prometheus-metrics/target/classes:$CLASSPATH" + +case "$(uname)" in + CYGWIN* | MINGW*) cygwin=true ;; + *) cygwin=false ;; esac -if $cygwin -then - CLASSPATH=`cygpath -wp "$CLASSPATH"` +if $cygwin; then + CLASSPATH=$(cygpath -wp "$CLASSPATH") fi #echo "CLASSPATH=$CLASSPATH" +export CLASSPATH # default heap for zookeeper server ZK_SERVER_HEAP="${ZK_SERVER_HEAP:-1000}" diff --git a/bin/zkServer-initialize.sh b/bin/zkServer-initialize.sh index 062e265915c..d41befcd470 100755 --- a/bin/zkServer-initialize.sh +++ b/bin/zkServer-initialize.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -23,12 +26,14 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi @@ -47,94 +52,93 @@ usage() { exit 1 } -if [ $? != 0 ] ; then - usage - exit 1 -fi - initialize() { - if [ ! -e "$ZOOCFG" ]; then - echo "Unable to find config file at $ZOOCFG" - exit 1 - fi - - ZOO_DATADIR="$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" - ZOO_DATALOGDIR="$(grep "^[[:space:]]*dataLogDir" "$ZOOCFG" | sed -e 's/.*=//')" - - if [ -z "$ZOO_DATADIR" ]; then - echo "Unable to determine dataDir from $ZOOCFG" - exit 1 - fi - - if [ $FORCE ]; then - echo "Force enabled, data/txnlog directories will be re-initialized" - else - # we create if version-2 exists (ie real data), not the - # parent. See comments in following section for more insight - if [ -d "$ZOO_DATADIR/version-2" ]; then - echo "ZooKeeper data directory already exists at $ZOO_DATADIR (or use --force to force re-initialization)" - exit 1 - fi - - if [ -n "$ZOO_DATALOGDIR" ] && [ -d "$ZOO_DATALOGDIR/version-2" ]; then - echo "ZooKeeper txnlog directory already exists at $ZOO_DATALOGDIR (or use --force to force re-initialization)" - exit 1 - fi - fi + if [[ ! -e $ZOOCFG ]]; then + echo "Unable to find config file at $ZOOCFG" + exit 1 + fi - # remove the child files that we're (not) interested in, not the - # parent. this allows for parent to be installed separately, and - # permissions to be set based on overarching requirements. by - # default we'll use the permissions of the user running this - # script for the files contained by the parent. note also by using - # -p the parent(s) will be created if it doesn't already exist - rm -rf "$ZOO_DATADIR/myid" 2>/dev/null >/dev/null - rm -rf "$ZOO_DATADIR/version-2" 2>/dev/null >/dev/null - mkdir -p "$ZOO_DATADIR/version-2" + ZOO_DATADIR="$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" + ZOO_DATALOGDIR="$(grep "^[[:space:]]*dataLogDir" "$ZOOCFG" | sed -e 's/.*=//')" - if [ -n "$ZOO_DATALOGDIR" ]; then - rm -rf "$ZOO_DATALOGDIR/myid" 2>/dev/null >/dev/null - rm -rf "$ZOO_DATALOGDIR/version-2" 2>/dev/null >/dev/null - mkdir -p "$ZOO_DATALOGDIR/version-2" + if [[ -z $ZOO_DATADIR ]]; then + echo "Unable to determine dataDir from $ZOOCFG" + exit 1 + fi + + if [[ $FORCE -eq 1 ]]; then + echo "Force enabled, data/txnlog directories will be re-initialized" + else + # we create if version-2 exists (ie real data), not the + # parent. See comments in following section for more insight + if [[ -d "$ZOO_DATADIR/version-2" ]]; then + echo "ZooKeeper data directory already exists at $ZOO_DATADIR (or use --force to force re-initialization)" + exit 1 fi - if [ $MYID ]; then - echo "Using myid of $MYID" - echo $MYID > "$ZOO_DATADIR/myid" - else - echo "No myid provided, be sure to specify it in $ZOO_DATADIR/myid if using non-standalone" + if [[ -n $ZOO_DATALOGDIR ]] && [[ -d "$ZOO_DATALOGDIR/version-2" ]]; then + echo "ZooKeeper txnlog directory already exists at $ZOO_DATALOGDIR (or use --force to force re-initialization)" + exit 1 fi - - touch "$ZOO_DATADIR/initialize" + fi + + # remove the child files that we're (not) interested in, not the + # parent. this allows for parent to be installed separately, and + # permissions to be set based on overarching requirements. by + # default we'll use the permissions of the user running this + # script for the files contained by the parent. note also by using + # -p the parent(s) will be created if it doesn't already exist + rm -rf "$ZOO_DATADIR/myid" 2>/dev/null >/dev/null + rm -rf "$ZOO_DATADIR/version-2" 2>/dev/null >/dev/null + mkdir -p "$ZOO_DATADIR/version-2" + + if [[ -n $ZOO_DATALOGDIR ]]; then + rm -rf "$ZOO_DATALOGDIR/myid" 2>/dev/null >/dev/null + rm -rf "$ZOO_DATALOGDIR/version-2" 2>/dev/null >/dev/null + mkdir -p "$ZOO_DATALOGDIR/version-2" + fi + + if [[ -n $MYID ]]; then + echo "Using myid of $MYID" + echo "$MYID" >"$ZOO_DATADIR/myid" + else + echo "No myid provided, be sure to specify it in $ZOO_DATADIR/myid if using non-standalone" + fi + + touch "$ZOO_DATADIR/initialize" } -while [ ! -z "$1" ]; do +while [[ -n $1 ]]; do case "$1" in --configfile) - ZOOCFG=$2; shift 2 + ZOOCFG=$2 + shift 2 ;; --configfile=?*) - ZOOCFG=${1#*=}; shift 1 + ZOOCFG=${1#*=} + shift 1 ;; --myid) - MYID=$2; shift 2 + MYID=$2 + shift 2 ;; --myid=?*) - MYID=${1#*=}; shift 1 + MYID=${1#*=} + shift 1 ;; --force) - FORCE=1; shift 1 + FORCE=1 + shift 1 ;; -h) usage - ;; + ;; --help) usage - ;; + ;; *) echo "Unknown option: $1" usage - exit 1 ;; esac done diff --git a/bin/zkServer.cmd b/bin/zkServer.cmd index b03a44386e7..07ac3a30244 100644 --- a/bin/zkServer.cmd +++ b/bin/zkServer.cmd @@ -21,6 +21,6 @@ set ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain set ZOO_LOG_FILE=zookeeper-%USERNAME%-server-%COMPUTERNAME%.log echo on -call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.root.logger=%ZOO_LOG4J_PROP%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" "-XX:+HeapDumpOnOutOfMemoryError" "-XX:OnOutOfMemoryError=cmd /c taskkill /pid %%%%p /t /f" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* +call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" "-XX:+HeapDumpOnOutOfMemoryError" "-XX:OnOutOfMemoryError=cmd /c taskkill /pid %%%%p /t /f" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* endlocal diff --git a/bin/zkServer.sh b/bin/zkServer.sh index 10ead606b2d..b2fd120544e 100755 --- a/bin/zkServer.sh +++ b/bin/zkServer.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -21,15 +24,16 @@ # relative to the canonical path of this script. # - # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi @@ -37,252 +41,272 @@ fi # up the JVM to accept JMX remote management: # http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html # by default we allow local JMX connections -if [ "x$JMXLOCALONLY" = "x" ] -then - JMXLOCALONLY=false +if [[ -z $JMXLOCALONLY ]]; then + JMXLOCALONLY=false fi -if [ "x$JMXDISABLE" = "x" ] || [ "$JMXDISABLE" = 'false' ] -then +if [[ -z $JMXDISABLE ]] || [[ $JMXDISABLE == 'false' ]]; then echo "ZooKeeper JMX enabled by default" >&2 - if [ "x$JMXPORT" = "x" ] - then + if [[ -z $JMXPORT ]]; then # for some reason these two options are necessary on jdk6 on Ubuntu # accord to the docs they are not necessary, but otw jconsole cannot # do a local attach - ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" + ZOOMAIN=("-Dcom.sun.management.jmxremote" "-Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY" "org.apache.zookeeper.server.quorum.QuorumPeerMain") else - if [ "x$JMXAUTH" = "x" ] - then + if [[ -z $JMXAUTH ]]; then JMXAUTH=false fi - if [ "x$JMXSSL" = "x" ] - then + if [[ -z $JMXSSL ]]; then JMXSSL=false fi - if [ "x$JMXLOG4J" = "x" ] - then + if [[ -z $JMXLOG4J ]]; then JMXLOG4J=true fi echo "ZooKeeper remote JMX Port set to $JMXPORT" >&2 echo "ZooKeeper remote JMX authenticate set to $JMXAUTH" >&2 echo "ZooKeeper remote JMX ssl set to $JMXSSL" >&2 echo "ZooKeeper remote JMX log4j set to $JMXLOG4J" >&2 - if [ "x$JMXHOSTNAME" = "x" ] - then - ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain" + if [[ -z $JMXHOSTNAME ]]; then + ZOOMAIN=("-Dcom.sun.management.jmxremote" "-Dcom.sun.management.jmxremote.port=$JMXPORT" "-Dcom.sun.management.jmxremote.authenticate=$JMXAUTH" "-Dcom.sun.management.jmxremote.ssl=$JMXSSL" "-Dzookeeper.jmx.log4j.disable=$JMXLOG4J" "org.apache.zookeeper.server.quorum.QuorumPeerMain") else echo "ZooKeeper remote JMX Hostname set to $JMXHOSTNAME" >&2 - ZOOMAIN="-Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=$JMXHOSTNAME -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain" + ZOOMAIN=("-Dcom.sun.management.jmxremote" "-Djava.rmi.server.hostname=$JMXHOSTNAME" "-Dcom.sun.management.jmxremote.port=$JMXPORT" "-Dcom.sun.management.jmxremote.authenticate=$JMXAUTH" "-Dcom.sun.management.jmxremote.ssl=$JMXSSL" "-Dzookeeper.jmx.log4j.disable=$JMXLOG4J" "org.apache.zookeeper.server.quorum.QuorumPeerMain") fi fi else - echo "JMX disabled by user request" >&2 - ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" + echo "JMX disabled by user request" >&2 + ZOOMAIN=("org.apache.zookeeper.server.quorum.QuorumPeerMain") fi -if [ "x$SERVER_JVMFLAGS" != "x" ] -then - JVMFLAGS="$SERVER_JVMFLAGS $JVMFLAGS" +if [[ -n $SERVER_JVMFLAGS ]]; then + JVMFLAGS="$SERVER_JVMFLAGS $JVMFLAGS" fi +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +# shellcheck disable=SC2206 +clientflags=($CLIENT_JVMFLAGS) -if [ "x$2" != "x" ] -then - ZOOCFG="$ZOOCFGDIR/$2" +if [[ -n $2 ]]; then + ZOOCFG="$ZOOCFGDIR/$2" fi # if we give a more complicated path to the config, don't screw around in $ZOOCFGDIR -if [ "x$(dirname "$ZOOCFG")" != "x$ZOOCFGDIR" ] -then - ZOOCFG="$2" +if [[ "$(dirname "$ZOOCFG")" != "$ZOOCFGDIR" ]]; then + ZOOCFG="$2" fi -if $cygwin -then - ZOOCFG=`cygpath -wp "$ZOOCFG"` - # cygwin has a "kill" in the shell itself, gets confused - KILL=/bin/kill +if $cygwin; then + ZOOCFG=$(cygpath -wp "$ZOOCFG") + # cygwin has a "kill" in the shell itself, gets confused + KILL='/bin/kill' else - KILL=kill + KILL='kill' fi echo "Using config: $ZOOCFG" >&2 case "$OSTYPE" in -*solaris*) - GREP=/usr/xpg4/bin/grep - ;; -*) - GREP=grep - ;; + *solaris*) + GREP='/usr/xpg4/bin/grep' + ;; + *) + GREP='grep' + ;; esac ZOO_DATADIR="$($GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" -ZOO_DATADIR="$(echo -e "${ZOO_DATADIR}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" +ZOO_DATADIR="$(echo -e "$ZOO_DATADIR" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" ZOO_DATALOGDIR="$($GREP "^[[:space:]]*dataLogDir" "$ZOOCFG" | sed -e 's/.*=//')" # iff autocreate is turned off and the datadirs don't exist fail # immediately as we can't create the PID file, etc..., anyway. -if [ -n "$ZOO_DATADIR_AUTOCREATE_DISABLE" ]; then - if [ ! -d "$ZOO_DATADIR/version-2" ]; then - echo "ZooKeeper data directory is missing at $ZOO_DATADIR fix the path or run initialize" - exit 1 - fi +if [[ -n $ZOO_DATADIR_AUTOCREATE_DISABLE ]]; then + if [[ ! -d "$ZOO_DATADIR/version-2" ]]; then + echo "ZooKeeper data directory is missing at $ZOO_DATADIR fix the path or run initialize" + exit 1 + fi - if [ -n "$ZOO_DATALOGDIR" ] && [ ! -d "$ZOO_DATALOGDIR/version-2" ]; then - echo "ZooKeeper txnlog directory is missing at $ZOO_DATALOGDIR fix the path or run initialize" - exit 1 - fi - ZOO_DATADIR_AUTOCREATE="-Dzookeeper.datadir.autocreate=false" + if [[ -n $ZOO_DATALOGDIR ]] && [[ ! -d "$ZOO_DATALOGDIR/version-2" ]]; then + echo "ZooKeeper txnlog directory is missing at $ZOO_DATALOGDIR fix the path or run initialize" + exit 1 + fi + flags=("-Dzookeeper.datadir.autocreate=false" "${flags[@]}") fi -if [ -z "$ZOOPIDFILE" ]; then - if [ ! -d "$ZOO_DATADIR" ]; then - mkdir -p "$ZOO_DATADIR" - fi - ZOOPIDFILE="$ZOO_DATADIR/zookeeper_server.pid" +if [[ -z $ZOOPIDFILE ]]; then + if [[ ! -d $ZOO_DATADIR ]]; then + mkdir -p "$ZOO_DATADIR" + fi + ZOOPIDFILE="$ZOO_DATADIR/zookeeper_server.pid" else - # ensure it exists, otw stop will fail - mkdir -p "$(dirname "$ZOOPIDFILE")" + # ensure it exists, otw stop will fail + mkdir -p "$(dirname "$ZOOPIDFILE")" fi -if [ ! -w "$ZOO_LOG_DIR" ] ; then -mkdir -p "$ZOO_LOG_DIR" +if [[ ! -w $ZOO_LOG_DIR ]]; then + mkdir -p "$ZOO_LOG_DIR" fi -ZOO_LOG_FILE=zookeeper-$USER-server-$HOSTNAME.log +ZOO_LOG_FILE=${ZOO_LOG_FILE:-zookeeper-$USER-server-$HOSTNAME.log} _ZOO_DAEMON_OUT="$ZOO_LOG_DIR/zookeeper-$USER-server-$HOSTNAME.out" case $1 in -start) - echo -n "Starting zookeeper ... " - if [ -f "$ZOOPIDFILE" ]; then - if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then - echo $command already running as process `cat "$ZOOPIDFILE"`. - exit 1 + start) + echo -n "Starting zookeeper ... " + if [[ -f $ZOOPIDFILE ]]; then + if kill -0 "$(cat "$ZOOPIDFILE")" &>/dev/null; then + echo "already running as process $(cat "$ZOOPIDFILE")." + exit 1 fi fi - nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \ - "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ - -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null & - if [ $? -eq 0 ] - then - case "$OSTYPE" in + nohup "$JAVA" "-Dzookeeper.log.dir=$ZOO_LOG_DIR" \ + "-Dzookeeper.log.file=$ZOO_LOG_FILE" \ + -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ + "${flags[@]}" "${ZOOMAIN[@]}" "$ZOOCFG" &>"$_ZOO_DAEMON_OUT" "$ZOOPIDFILE" + /bin/echo "${!}\\c" >"$ZOOPIDFILE" + ;; + aix*) + /bin/echo "$!\c" >"$ZOOPIDFILE" ;; *) - /bin/echo -n $! > "$ZOOPIDFILE" + echo -n $! >"$ZOOPIDFILE" ;; - esac - if [ $? -eq 0 ]; - then - sleep 1 - pid=$(cat "${ZOOPIDFILE}") - if ps -p "${pid}" > /dev/null 2>&1; then - echo STARTED - else - echo FAILED TO START - exit 1 - fi + esac + # this checks the exit code of the echo statements to verify the PID was written + # shellcheck disable=SC2320,SC2181 + if [[ $? -eq 0 ]]; then + sleep 1 + pid=$(cat "$ZOOPIDFILE") + if ps -p "$pid" &>/dev/null; then + echo STARTED else - echo FAILED TO WRITE PID + echo FAILED TO START exit 1 fi else - echo SERVER DID NOT START + echo FAILED TO WRITE PID exit 1 fi ;; -start-foreground) + start-foreground) ZOO_CMD=(exec "$JAVA") - if [ "${ZOO_NOEXEC}" != "" ]; then + if [[ -n $ZOO_NOEXEC ]]; then ZOO_CMD=("$JAVA") fi - "${ZOO_CMD[@]}" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \ - "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ - -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ - -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" + "${ZOO_CMD[@]}" "-Dzookeeper.log.dir=$ZOO_LOG_DIR" \ + "-Dzookeeper.log.file=$ZOO_LOG_FILE" \ + -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ + "${flags[@]}" "${ZOOMAIN[@]}" "$ZOOCFG" + ;; + print-classpath) + echo "CLASSPATH=$CLASSPATH" ;; -print-cmd) - echo "\"$JAVA\" $ZOO_DATADIR_AUTOCREATE -Dzookeeper.log.dir=\"${ZOO_LOG_DIR}\" \ - -Dzookeeper.log.file=\"${ZOO_LOG_FILE}\" -Dzookeeper.root.logger=\"${ZOO_LOG4J_PROP}\" \ + print-cmd) + echo "\"$JAVA\" -Dzookeeper.log.dir=\"$ZOO_LOG_DIR\" \ + -Dzookeeper.log.file=\"$ZOO_LOG_FILE\" \ -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ - -cp \"$CLASSPATH\" $JVMFLAGS $ZOOMAIN \"$ZOOCFG\" > \"$_ZOO_DAEMON_OUT\" 2>&1 < /dev/null" + $(for x in "${flags[@]}"; do echo "\"$x\""; done | paste -sd' ') \ + $(for x in "${ZOOMAIN[@]}"; do echo "\"$x\""; done | paste -sd' ') \ + \"$ZOOCFG\" &>\"$_ZOO_DAEMON_OUT\" /dev/null + version) + ZOOMAIN=("org.apache.zookeeper.version.VersionInfoMain") + $JAVA "${ZOOMAIN[@]}" 2>/dev/null ;; -restart) + restart) shift - "$0" stop ${@} + "$0" stop "$@" sleep 3 - "$0" start ${@} + "$0" start "$@" ;; -status) + status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output - clientPortAddress=`$GREP "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` - if ! [ $clientPortAddress ] - then - clientPortAddress="localhost" + isSSL="false" + clientPortAddress=$($GREP "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//') + if [[ -z $clientPortAddress ]]; then + clientPortAddress="localhost" fi - clientPort=`$GREP "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` - if ! [[ "$clientPort" =~ ^[0-9]+$ ]] - then - dataDir=`$GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//'` - myid=`cat "$dataDir/myid"` - if ! [[ "$myid" =~ ^[0-9]+$ ]] ; then - echo "clientPort not found and myid could not be determined. Terminating." - exit 1 - fi - clientPortAndAddress=`$GREP "^[[:space:]]*server.$myid=.*;.*" "$ZOOCFG" | sed -e 's/.*=//' | sed -e 's/.*;//'` - if [ ! "$clientPortAndAddress" ] ; then - echo "Client port not found in static config file. Looking in dynamic config file." - dynamicConfigFile=`$GREP "^[[:space:]]*dynamicConfigFile" "$ZOOCFG" | sed -e 's/.*=//'` - clientPortAndAddress=`$GREP "^[[:space:]]*server.$myid=.*;.*" "$dynamicConfigFile" | sed -e 's/.*=//' | sed -e 's/.*;//'` - fi - if [ ! "$clientPortAndAddress" ] ; then - echo "Client port not found. Terminating." - exit 1 - fi - if [[ "$clientPortAndAddress" =~ ^.*:[0-9]+ ]] ; then - clientPortAddress=`echo "$clientPortAndAddress" | sed -e 's/:.*//'` - fi - clientPort=`echo "$clientPortAndAddress" | sed -e 's/.*://'` - if [ ! "$clientPort" ] ; then - echo "Client port not found. Terminating." - exit 1 - fi + clientPort=$($GREP "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//') + if ! [[ $clientPort =~ ^[0-9]+$ ]]; then + dataDir=$($GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//') + myid=$(cat "$dataDir/myid" 2>/dev/null) + if ! [[ $myid =~ ^[0-9]+$ ]]; then + echo "myid could not be determined, will not able to locate clientPort in the server configs." + else + clientPortAndAddress=$($GREP "^[[:space:]]*server.$myid=.*;.*" "$ZOOCFG" | sed -e 's/.*=//' | sed -e 's/.*;//') + if [[ -z $clientPortAndAddress ]]; then + echo "Client port not found in static config file. Looking in dynamic config file." + dynamicConfigFile=$($GREP "^[[:space:]]*dynamicConfigFile" "$ZOOCFG" | sed -e 's/.*=//') + clientPortAndAddress=$($GREP "^[[:space:]]*server.$myid=.*;.*" "$dynamicConfigFile" | sed -e 's/.*=//' | sed -e 's/.*;//') + fi + if [[ -z $clientPortAndAddress ]]; then + echo "Client port not found in the server configs" + else + # Extracts address and port from address:port, even if address contains optional square braces + # For example, this extracts the address 127::1 and the port 2181 from "[127::1]:2181" + if [[ $clientPortAndAddress =~ ^\[?([^\]]*)\]?:([0-9]+) ]]; then + clientPortAddress=${BASH_REMATCH[1]} + clientPort=${BASH_REMATCH[2]} + elif [[ $clientPortAndAddress =~ ^[0-9]+$ ]]; then + clientPort=$clientPortAndAddress + fi + fi + fi fi - echo "Client port found: $clientPort. Client address: $clientPortAddress." - STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ - -cp "$CLASSPATH" $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \ - $clientPortAddress $clientPort srvr 2> /dev/null \ - | $GREP Mode` - if [ "x$STAT" = "x" ] - then - echo "Error contacting service. It is probably not running." + if [[ -z $clientPort ]]; then + echo "Client port not found. Looking for secureClientPort in the static config." + secureClientPort=$($GREP "^[[:space:]]*secureClientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//') + if [[ -n $secureClientPort ]]; then + isSSL="true" + clientPort=$secureClientPort + clientPortAddress=$($GREP "^[[:space:]]*secureClientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//') + if [[ -z $clientPortAddress ]]; then + clientPortAddress="localhost" + fi + else + echo "Unable to find either secure or unsecure client port in any configs. Terminating." exit 1 + fi + fi + echo "Client port found: $clientPort. Client address: $clientPortAddress. Client SSL: $isSSL." + STAT=$("$JAVA" "-Dzookeeper.log.dir=$ZOO_LOG_DIR" "-Dzookeeper.log.file=$ZOO_LOG_FILE" \ + "${clientflags[@]}" "${flags[@]}" org.apache.zookeeper.client.FourLetterWordMain \ + "$clientPortAddress" "$clientPort" srvr "$isSSL" 2>/dev/null | + $GREP Mode) + if [[ -z $STAT ]]; then + if [[ $isSSL == "true" ]]; then + echo " " + echo "Note: We used secureClientPort ($secureClientPort) to establish connection, but we failed. The 'status'" + echo " command establishes a client connection to the server to execute diagnostic commands. Please make sure you" + echo " provided all the Client SSL connection related parameters in the CLIENT_JVMFLAGS environment variable! E.g.:" + echo ' CLIENT_JVMFLAGS="-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty' + echo " -Dzookeeper.ssl.trustStore.location=/tmp/clienttrust.jks -Dzookeeper.ssl.trustStore.password=password" + echo " -Dzookeeper.ssl.keyStore.location=/tmp/client.jks -Dzookeeper.ssl.keyStore.password=password" + echo ' -Dzookeeper.client.secure=true" ./zkServer.sh status' + echo " " + fi + echo "Error contacting service. It is probably not running." + exit 1 else - echo $STAT - exit 0 + echo "$STAT" + exit 0 fi ;; -*) + *) echo "Usage: $0 [--config ] {start|start-foreground|stop|version|restart|status|print-cmd}" >&2 + ;; esac diff --git a/bin/zkSnapShotToolkit.sh b/bin/zkSnapShotToolkit.sh index 1664d596f37..dd256c366f3 100755 --- a/bin/zkSnapShotToolkit.sh +++ b/bin/zkSnapShotToolkit.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -23,16 +26,18 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi -"$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.SnapshotFormatter "$@" - - +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +"$JAVA" "${flags[@]}" \ + org.apache.zookeeper.server.SnapshotFormatter "$@" diff --git a/bin/zkSnapshotComparer.sh b/bin/zkSnapshotComparer.sh index b284c902d39..0167fdb116a 100755 --- a/bin/zkSnapshotComparer.sh +++ b/bin/zkSnapshotComparer.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -23,14 +26,18 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi -"$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.SnapshotComparer "$@" +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +"$JAVA" "${flags[@]}" \ + org.apache.zookeeper.server.SnapshotComparer "$@" diff --git a/bin/zkSnapshotRecursiveSummaryToolkit.cmd b/bin/zkSnapshotRecursiveSummaryToolkit.cmd new file mode 100755 index 00000000000..ee893108e6b --- /dev/null +++ b/bin/zkSnapshotRecursiveSummaryToolkit.cmd @@ -0,0 +1,24 @@ +@echo off +REM Licensed to the Apache Software Foundation (ASF) under one or more +REM contributor license agreements. See the NOTICE file distributed with +REM this work for additional information regarding copyright ownership. +REM The ASF licenses this file to You under the Apache License, Version 2.0 +REM (the "License"); you may not use this file except in compliance with +REM the License. You may obtain a copy of the License at +REM +REM http://www.apache.org/licenses/LICENSE-2.0 +REM +REM Unless required by applicable law or agreed to in writing, software +REM distributed under the License is distributed on an "AS IS" BASIS, +REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +REM See the License for the specific language governing permissions and +REM limitations under the License. + +setlocal +call "%~dp0zkEnv.cmd" + +set ZOOMAIN=org.apache.zookeeper.server.SnapshotRecursiveSummary +call %JAVA% -cp "%CLASSPATH%" %ZOOMAIN% %* + +endlocal + diff --git a/bin/zkSnapshotRecursiveSummaryToolkit.sh b/bin/zkSnapshotRecursiveSummaryToolkit.sh new file mode 100755 index 00000000000..8b5ee45aab2 --- /dev/null +++ b/bin/zkSnapshotRecursiveSummaryToolkit.sh @@ -0,0 +1,43 @@ +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# If this scripted is run out of /usr/bin or some other system bin directory +# it should be linked to and not copied. Things like java jar files are found +# relative to the canonical path of this script. +# + +# use POSIX interface, symlink is followed automatically +ZOOBIN="${BASH_SOURCE-$0}" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" + +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh + . "$ZOOBINDIR"/../libexec/zkEnv.sh +else + # shellcheck source=bin/zkEnv.sh + . "$ZOOBINDIR"/zkEnv.sh +fi + +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +"$JAVA" "${flags[@]}" \ + org.apache.zookeeper.server.SnapshotRecursiveSummary "$@" diff --git a/bin/zkTxnLogToolkit.sh b/bin/zkTxnLogToolkit.sh index 8beed20ddd7..0381ffa0227 100755 --- a/bin/zkTxnLogToolkit.sh +++ b/bin/zkTxnLogToolkit.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -23,16 +26,18 @@ # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" -ZOOBIN="$(dirname "${ZOOBIN}")" -ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" +ZOOBIN="$(dirname "$ZOOBIN")" +ZOOBINDIR="$(cd "$ZOOBIN" && pwd)" -if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then +if [[ -e "$ZOOBIN/../libexec/zkEnv.sh" ]]; then + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else + # shellcheck source=bin/zkEnv.sh . "$ZOOBINDIR"/zkEnv.sh fi -"$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ - org.apache.zookeeper.server.persistence.TxnLogToolkit "$@" - - +# shellcheck disable=SC2206 +flags=($JVMFLAGS) +"$JAVA" "${flags[@]}" \ + org.apache.zookeeper.server.persistence.TxnLogToolkit "$@" diff --git a/checkstyle-simple.xml b/checkstyle-simple.xml index 204937026a8..004cd68e417 100644 --- a/checkstyle-simple.xml +++ b/checkstyle-simple.xml @@ -16,8 +16,8 @@ limitations under the License. --> + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> +page at https://checkstyle.sourceforge.io/config.html --> @@ -140,16 +140,12 @@ page at http://checkstyle.sourceforge.net/config.html --> --> - + - - - - @@ -271,27 +267,6 @@ page at http://checkstyle.sourceforge.net/config.html --> - - - - - - - - - - - - @@ -433,4 +408,25 @@ page at http://checkstyle.sourceforge.net/config.html --> - \ No newline at end of file + + + + + + + + + + + + + diff --git a/checkstyleSuppressions.xml b/checkstyleSuppressions.xml index 53d92eb5bda..513de6b4d80 100644 --- a/checkstyleSuppressions.xml +++ b/checkstyleSuppressions.xml @@ -13,9 +13,8 @@ limitations under the License. See accompanying LICENSE file. --> - + "-//Checkstyle//DTD SuppressionFilter Configuration 1.0//EN" + "https://checkstyle.org/dtds/suppressions_1_0.dtd"> diff --git a/conf/log4j.properties b/conf/log4j.properties deleted file mode 100644 index 9e12a38d333..00000000000 --- a/conf/log4j.properties +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2012 The Apache Software Foundation -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Define some default values that can be overridden by system properties -zookeeper.root.logger=INFO, CONSOLE - -zookeeper.console.threshold=INFO - -zookeeper.log.dir=. -zookeeper.log.file=zookeeper.log -zookeeper.log.threshold=INFO -zookeeper.log.maxfilesize=256MB -zookeeper.log.maxbackupindex=20 - -zookeeper.tracelog.dir=${zookeeper.log.dir} -zookeeper.tracelog.file=zookeeper_trace.log - -log4j.rootLogger=${zookeeper.root.logger} - -# -# console -# Add "console" to rootlogger above if you want to use this -# -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n - -# -# Add ROLLINGFILE to rootLogger to get log file output -# -log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender -log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} -log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file} -log4j.appender.ROLLINGFILE.MaxFileSize=${zookeeper.log.maxfilesize} -log4j.appender.ROLLINGFILE.MaxBackupIndex=${zookeeper.log.maxbackupindex} -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n - -# -# Add TRACEFILE to rootLogger to get log file output -# Log TRACE level and above messages to a log file -# -log4j.appender.TRACEFILE=org.apache.log4j.FileAppender -log4j.appender.TRACEFILE.Threshold=TRACE -log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file} - -log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout -### Notice we are including log4j's NDC here (%x) -log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n -# -# zk audit logging -# -zookeeper.auditlog.file=zookeeper_audit.log -zookeeper.auditlog.threshold=INFO -audit.logger=INFO, RFAAUDIT -log4j.logger.org.apache.zookeeper.audit.Log4jAuditLogger=${audit.logger} -log4j.additivity.org.apache.zookeeper.audit.Log4jAuditLogger=false -log4j.appender.RFAAUDIT=org.apache.log4j.RollingFileAppender -log4j.appender.RFAAUDIT.File=${zookeeper.log.dir}/${zookeeper.auditlog.file} -log4j.appender.RFAAUDIT.layout=org.apache.log4j.PatternLayout -log4j.appender.RFAAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n -log4j.appender.RFAAUDIT.Threshold=${zookeeper.auditlog.threshold} - -# Max log file size of 10MB -log4j.appender.RFAAUDIT.MaxFileSize=10MB -log4j.appender.RFAAUDIT.MaxBackupIndex=10 diff --git a/conf/logback.xml b/conf/logback.xml new file mode 100644 index 00000000000..84a53896954 --- /dev/null +++ b/conf/logback.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n + + + ${zookeeper.console.threshold} + + + + + + + + + + + + + + + + diff --git a/conf/zoo_sample.cfg b/conf/zoo_sample.cfg index 24a97d720c8..d0db6e99a1d 100644 --- a/conf/zoo_sample.cfg +++ b/conf/zoo_sample.cfg @@ -19,7 +19,7 @@ clientPort=2181 # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # -# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance +# https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 @@ -31,6 +31,7 @@ clientPort=2181 # # https://prometheus.io Metrics Exporter #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider +#metricsProvider.httpHost=0.0.0.0 #metricsProvider.httpPort=7000 #metricsProvider.exportJvmInfo=true diff --git a/dev/docker/Dockerfile b/dev/docker/Dockerfile index 8d148ee0bef..4d8a5d08dd1 100644 --- a/dev/docker/Dockerfile +++ b/dev/docker/Dockerfile @@ -17,7 +17,18 @@ # under the License. # -FROM maven:3.6.3-jdk-8 +FROM maven:3.8.4-jdk-11 RUN apt-get update -RUN apt-get install -y g++ cmake autoconf libcppunit-dev libtool openssl libssl-dev libsasl2-modules-gssapi-mit libsasl2-modules libsasl2-dev +RUN apt-get install -y \ + g++ \ + cmake \ + autoconf \ + pkg-config \ + libcppunit-dev \ + libtool \ + openssl \ + libssl-dev \ + libsasl2-modules-gssapi-mit \ + libsasl2-modules \ + libsasl2-dev diff --git a/dev/docker/run.sh b/dev/docker/run.sh index 901131046ff..10c40c5ff8e 100755 --- a/dev/docker/run.sh +++ b/dev/docker/run.sh @@ -1,52 +1,55 @@ -#!/bin/bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. set -e -x -u -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) export IMAGE_NAME="zookeeper/dev" -pushd ${SCRIPT_DIR} +pushd "$SCRIPT_DIR" -docker build --rm=true -t ${IMAGE_NAME} . +docker build --rm=true -t "$IMAGE_NAME" . popd -if [ "$(uname -s)" == "Linux" ]; then +if [[ "$(uname -s)" == "Linux" ]]; then USER_NAME=${SUDO_USER:=$USER} - USER_ID=$(id -u "${USER_NAME}") - GROUP_ID=$(id -g "${USER_NAME}") + USER_ID=$(id -u "$USER_NAME") + GROUP_ID=$(id -g "$USER_NAME") LOCAL_HOME=$(realpath ~) else # boot2docker uid and gid USER_NAME=$USER USER_ID=1000 GROUP_ID=50 - LOCAL_HOME="/Users/${USER_NAME}" + LOCAL_HOME="/Users/$USER_NAME" fi -docker build -t "${IMAGE_NAME}-${USER_NAME}" - < + + + CVE-2024-6763 + CVE-2018-8088 + + + CVE-2021-37533 + CVE-2018-8012 @@ -34,16 +55,38 @@ CVE-2018-12056 + + CVE-2023-4586 CVE-2019-3826 + + + + + CVE-2021-29425 + CVE-2021-28164 + CVE-2021-34429 + + - - CVE-2019-17571 + + + + CVE-2023-35116 + + + CVE-2022-45688 diff --git a/pom.xml b/pom.xml old mode 100755 new mode 100644 index 041b41571f8..2811e0dfa06 --- a/pom.xml +++ b/pom.xml @@ -30,8 +30,8 @@ org.apache.zookeeper parent pom - - 3.7.0-SNAPSHOT + + 3.10.0-SNAPSHOT Apache ZooKeeper ZooKeeper is a centralized service for maintaining configuration information, naming, @@ -64,6 +64,7 @@ zookeeper-client zookeeper-recipes zookeeper-assembly + zookeeper-compatibility-tests @@ -78,7 +79,7 @@ jenkins - https://builds.apache.org/view/S-Z/view/ZooKeeper/ + https://ci-hadoop.apache.org/view/ZooKeeper/ @@ -117,7 +118,7 @@ tdunning - Ted Dunning + Ted Dunning tdunning@apache.org -8 @@ -252,6 +253,36 @@ eolivelli@apache.org +1 + + nkalmar + Norbert Kalmar + nkalmar@apache.org + +1 + + + enixon + Brian Nixon + enixon@apache.org + -8 + + + symat + Mate Szalay-Beko + symat@apache.org + +1 + + + ddiederen + Damien Diederen + ddiederen@apache.org + Europe/Berlin + + + maoling + Ling Mao + maoling@apache.org + +8 + @@ -259,13 +290,18 @@ full-build zookeeper-contrib + + zookeeper-it - java-build - - true - + + fatjar + + zookeeper-contrib + + zookeeper-it + apache-release @@ -317,6 +353,88 @@ 8 + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.codehaus.mojo + exec-maven-plugin + [0,) + + exec + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + [0,) + + run + + + + + + + + + com.ruleoftech + markdown-page-generator-plugin + [0,) + + generate + + + + + + + + + org.codehaus.mojo + javacc-maven-plugin + [0,) + + javacc + + + + + + + + + com.github.koraktor + mavanagaiata + [0,) + + commit + + + + + + + + + + + + + jdk-release-flag @@ -327,38 +445,137 @@ 8 + + clover + + false + + clover + + + + ${project.build.directory}/clover/zookeeper-coverage.db + + + + + org.openclover + clover-maven-plugin + + false + true + ${cloverDatabase} + 50% + ${project.build.directory}/clover + true + false + ${project.build.directory}/clover/zookeeper-coverage.db + ${project.build.directory}/clover/zookeeper-coverage.db + ${project.build.directory}/clover + true + true + false + true + + org/apache/zookeeper/**/* + + + org/apache/zookeeper/version/**/* + **/ReadOnlyModeTest.java + **/KeeperStateTest.java + + + + + clover-setup + process-sources + + setup + + + + clover + test + + clover + + + + + + + + + sonar + + ${project.build.directory}/clover/clover.xml + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + reuseReports + clover + ${project.build.directory}/clover/clover.xml + target/surefire-reports + clover + ${clover-maven-plugin.version} + + + + + + 1.8 1.8 + 1691130453 false + 2.22.1 8 + true - 1.7.25 - 0.5.0 + 2.0.13 + 1.3.15 + 0.12.0 1.48 - 4.12 - 1.2.17 - 2.27.0 - 1.3 - 1.2 - 4.1.45.Final - 9.4.24.v20191120 - 2.10.3 - 1.1.1 - 2.11 - 1.1.7 + 5.6.2 + 1.6.2 + 4.9.0 + 2.2 + 1.5.0 + 4.1.130.Final + 9.4.58.v20250814 + 2.18.1 + 3.25.1 + 1.1.10.5 2.0.0 - 1.60 - 3.2.2 - 2.6 - 3.2.5 - 3.1.9 - 8.17 + 1.78 + 4.4 + 4.1.12.1 + 4.0.2 + 8.39 + 3.0.0-M3 + 2.17.0 + 0.25.4 + 3.5.1 + 4.4.1 + 3.7.0.1746 yes @@ -370,19 +587,14 @@ org.hamcrest - hamcrest-all + hamcrest-library ${hamcrest.version} - commons-collections - commons-collections + org.apache.commons + commons-collections4 ${commons-collections.version} - - commons-lang - commons-lang - ${commons-lang.version} - org.apache.yetus audience-annotations @@ -432,12 +644,12 @@ org.bouncycastle - bcprov-jdk15on + bcprov-jdk18on ${bouncycastle.version} org.bouncycastle - bcpkix-jdk15on + bcpkix-jdk18on ${bouncycastle.version} @@ -446,20 +658,14 @@ ${slf4j.version} - org.slf4j - slf4j-log4j12 - ${slf4j.version} - - - * - * - - + ch.qos.logback + logback-core + ${logback-version} - log4j - log4j - ${log4j.version} + ch.qos.logback + logback-classic + ${logback-version} org.jmockit @@ -467,8 +673,30 @@ ${jmockit.version} - junit - junit + org.junit.jupiter + junit-jupiter-api + ${junit.version} + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.junit.platform + junit-platform-runner + ${junit-platform.version} + test + + + org.junit.vintage + junit-vintage-engine ${junit.version} @@ -477,14 +705,17 @@ ${mockito.version} - io.netty - netty-handler - ${netty.version} + org.mockito + mockito-inline + ${mockito.version} + test io.netty - netty-transport-native-epoll + netty-bom ${netty.version} + pom + import org.eclipse.jetty @@ -496,6 +727,11 @@ jetty-servlet ${jetty.version} + + org.eclipse.jetty + jetty-client + ${jetty.version} + io.dropwizard.metrics metrics-core @@ -513,18 +749,7 @@ ${jackson.version} - com.googlecode.json-simple - json-simple - ${json.version} - - - junit - junit - - - - - jline + org.jline jline ${jline.version} @@ -540,6 +765,21 @@ snappy-java ${snappy.version} + + commons-io + commons-io + ${commons-io.version} + + + org.burningwave + tools + ${burningwave.mockdns.version} + + + dnsjava + dnsjava + ${dnsjava.version} + @@ -549,7 +789,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 true @@ -566,7 +805,6 @@ org.apache.maven.plugins maven-jar-plugin - 3.1.0 @@ -578,25 +816,24 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.1.1 + true none org.apache.maven.plugins maven-assembly-plugin - 3.1.0 + 3.6.0 org.apache.maven.plugins maven-release-plugin - 2.5.3 org.apache.maven.plugins @@ -606,17 +843,18 @@ org.apache.maven.plugins maven-surefire-plugin - 2.22.1 + + false + ${redirectTestOutputToFile} + org.apache.maven.plugins maven-antrun-plugin - 1.8 org.apache.maven.plugins maven-dependency-plugin - 3.1.1 org.codehaus.mojo @@ -644,12 +882,12 @@ org.openclover clover-maven-plugin - 4.3.1 + ${clover-maven-plugin.version} com.github.spotbugs spotbugs-maven-plugin - 3.1.9 + 4.0.0 excludeFindBugsFilter.xml @@ -657,12 +895,12 @@ org.owasp dependency-check-maven - 5.3.0 + 12.1.6 org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 + 3.1.1 com.puppycrawl.tools @@ -680,15 +918,6 @@ false true - - - checkstyle - validate - - check - - - @@ -700,6 +929,16 @@ + + org.apache.felix + maven-bundle-plugin + 5.1.9 + + + org.cyclonedx + cyclonedx-maven-plugin + 2.7.9 + @@ -717,34 +956,12 @@ - - org.openclover - clover-maven-plugin - - true - true - - org/apache/zookeeper/**/* - - - org/apache/zookeeper/version/**/* - - - - - pre-site - - instrument - aggregate - - - - org.apache.maven.plugins maven-antrun-plugin + set-hostname-property validate run @@ -852,9 +1069,9 @@ org.apache.rat apache-rat-plugin + 0.15 - **/log4j.properties **/README.md **/findbugsExcludeFile.xml **/checkstyle-noframes-sorted.xsl @@ -868,24 +1085,24 @@ src/main/resources/markdown/skin/* src/main/resources/markdown/html/* src/main/resources/markdown/images/* + **/src/test/resources/embedded/*.conf - zookeeper-contrib-monitoring/JMX-RESOURCES - zookeeper-contrib-fatjar/src/main/resources/mainClasses - zookeeper-contrib-zkperl/Changes - zookeeper-contrib-zkperl/MANIFEST - zookeeper-contrib-zkpython/src/test/zoo.cfg - zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/* - src/main/resources/webapp/org/apache/zookeeper/graph/resources/* - src/main/java/com/nitido/utils/toaster/Toaster.java - TODO + **/JMX-RESOURCES + **/src/main/resources/mainClasses + **/Changes + **/MANIFEST + **/src/test/zoo.cfg + **/src/main/resources/webapp/org/apache/zookeeper/graph/resources/* + **/src/main/java/com/nitido/utils/toaster/Toaster.java + **/TODO **/acinclude.m4 **/aminclude.am - src/hashtable/* - include/winconfig.h - tests/wrappers.opt - tests/zoo.cfg - tests/wrappers-mt.opt + **/src/hashtable/* + **/include/winconfig.h + **/tests/wrappers.opt + **/tests/zoo.cfg + **/tests/wrappers-mt.opt **/c-doc.Doxyfile true @@ -897,8 +1114,12 @@ false - clean install -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release - clean install -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release + clean verify -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release + clean verify -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release + false + true + true + release-@{project.version} @@ -931,6 +1152,76 @@ + + org.apache.maven.plugins + maven-enforcer-plugin + ${enforcer.version} + + + banned-commons-lang + + enforce + + + + + + commons-lang:commons-lang + + false + We don't use commons-lang anymore, so do not depend on it directly. + + + + + + banned-commons-lang3 + + enforce + + + + + + org.apache.commons:commons-lang3 + + false + We don't use commons-lang3, so do not depend on it directly. + + + + + + banned-json-simple + + enforce + + + + + + com.googlecode.json-simple:json-simple + + false + We don't use json-simple anymore, so do not depend on it directly. + + + + + + + + org.cyclonedx + cyclonedx-maven-plugin + + + + makeBom + + package + + + diff --git a/tools/ci/install-shfmt.sh b/tools/ci/install-shfmt.sh new file mode 100755 index 00000000000..fd73efc3c6c --- /dev/null +++ b/tools/ci/install-shfmt.sh @@ -0,0 +1,29 @@ +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Install shfmt tool to search for and optionally format bash scripts +# This is useful for other CI tools to run ShellCheck and shfmt to format + +set -e +set -x + +shfmt_version=3.4.3 +sudo wget "https://github.com/mvdan/sh/releases/download/v${shfmt_version}/shfmt_v${shfmt_version}_linux_amd64" -O /usr/local/bin/shfmt && + sudo chmod +x /usr/local/bin/shfmt diff --git a/tools/ci/run-shellcheck.sh b/tools/ci/run-shellcheck.sh new file mode 100755 index 00000000000..5d6ca75d2d7 --- /dev/null +++ b/tools/ci/run-shellcheck.sh @@ -0,0 +1,28 @@ +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Run ShellCheck on all bash scripts + +set -e + +mapfile -t filestocheck < <(shfmt -f .) + +set -x +shellcheck -P SCRIPTDIR -x "${filestocheck[@]}" diff --git a/tools/ci/run-shfmt.sh b/tools/ci/run-shfmt.sh new file mode 100755 index 00000000000..233df9aff49 --- /dev/null +++ b/tools/ci/run-shfmt.sh @@ -0,0 +1,26 @@ +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Check formatting of all bash scripts + +set -e +set -x + +shfmt -ln bash -l -d -i 2 -ci -s . diff --git a/tools/ci/test-connectivity.py b/tools/ci/test-connectivity.py new file mode 100755 index 00000000000..2ad837571c0 --- /dev/null +++ b/tools/ci/test-connectivity.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import subprocess + +from pathlib import Path + +class Server(): + def __init__(self, binpath): + self.binpath = binpath + def __enter__(self): + subprocess.run([f'{self.binpath}', 'start'], check=True) + return self + def __exit__(self, type, value, traceback): + subprocess.run([f'{self.binpath}', 'stop'], check=True) + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--server', help="basepath to zk server", required=True) + parser.add_argument('--client', help="basepath to zk client", required=True) + + args = parser.parse_args() + + server_basepath = Path(args.server).absolute() + server_binpath = server_basepath / "bin" / "zkServer.sh" + assert server_binpath.exists(), f"server binpath not exist: {server_binpath}" + client_basepath = Path(args.client).absolute() + client_binpath = client_basepath / "bin" / "zkCli.sh" + assert client_binpath.exists(), f"client binpath not exist: {client_binpath}" + + with Server(server_binpath): + subprocess.run([f'{client_binpath}', 'sync', '/'], check=True) diff --git a/tools/sonar/code-coverage.sh b/tools/sonar/code-coverage.sh new file mode 100755 index 00000000000..4dfe4eea11a --- /dev/null +++ b/tools/sonar/code-coverage.sh @@ -0,0 +1,73 @@ +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +usage() { + echo + echo "options:" + echo "-h Display help" + echo "-u SonarQube Host URL" + echo "-l SonarQube Login Credentials" + echo "-k SonarQube Project Key" + echo "-n SonarQube Project Name" + echo + echo "Important:" + echo " The required parameters for publishing the coverage results to SonarQube:" + echo " - Host URL" + echo " - Login Credentials" + echo " - Project Key" + echo +} + +execute() { + SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" + MAIN_POM="$SCRIPT_DIR/../../pom.xml" + + mvn -B -e -Pclover -f "$MAIN_POM" clean install -DskipTests -DskipShade + + mvn -B -e -Pclover -f "$MAIN_POM" test -Dparallel-tests -DtestsThreadCount=8 -Dscale + + mvn -B -e -Pclover -f "$MAIN_POM" clover:aggregate clover:clover + + # If the required parameters are given, the code coverage results are uploaded to the SonarQube Server + if [[ -n $SONAR_LOGIN ]] && [[ -n $SONAR_PROJECT_KEY ]] && [[ -n $SONAR_URL ]]; then + mvn -B -e -Pclover -f "$MAIN_POM" sonar:sonar -Dsonar.clover.reportPath=./target/clover/clover.xml \ + -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_LOGIN" -Dsonar.projectKey="$SONAR_PROJECT_KEY" -Dsonar.projectName="$SONAR_PROJECT_NAME" + fi +} + +while getopts ":u:l:k:n:h" option; do + case $option in + u) SONAR_URL=${OPTARG:-} ;; + l) SONAR_LOGIN=${OPTARG:-} ;; + k) SONAR_PROJECT_KEY=${OPTARG:-} ;; + n) SONAR_PROJECT_NAME=${OPTARG:-} ;; + h) # Display usage + usage + exit + ;; + \?) # Invalid option + echo "Error: Invalid option" + exit + ;; + esac +done + +# Start code analysis +execute diff --git a/zk-merge-pr.py b/zk-merge-pr.py old mode 100644 new mode 100755 index 5768a94d74f..100244bfc16 --- a/zk-merge-pr.py +++ b/zk-merge-pr.py @@ -32,8 +32,9 @@ import re import subprocess import sys -import urllib2 +import urllib.request, urllib.error, urllib.parse import getpass +import requests try: import jira.client @@ -53,6 +54,11 @@ JIRA_USERNAME = os.environ.get("JIRA_USERNAME", "") # ASF JIRA password JIRA_PASSWORD = os.environ.get("JIRA_PASSWORD", "") +# ASF JIRA access token +# If it is configured, username and password are dismissed +# Go to https://issues.apache.org/jira/secure/ViewProfile.jspa -> Personal Access Tokens for +# your own token management. +JIRA_ACCESS_TOKEN = os.environ.get("JIRA_ACCESS_TOKEN") # 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 @@ -75,154 +81,148 @@ def get_json(url): try: - request = urllib2.Request(url) + request = urllib.request.Request(url) if GITHUB_OAUTH_KEY: request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY) - return json.load(urllib2.urlopen(request)) - except urllib2.HTTPError as e: + 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 " + \ + print("Exceeded the GitHub API rate limit; see the instructions in " + \ "zk-merge-pr.py to configure an OAuth token for making authenticated " + \ - "GitHub requests." + "GitHub requests.") else: - print "Unable to fetch URL, exiting: %s" % url + print("Unable to fetch URL, exiting: %s" % url) sys.exit(-1) def fail(msg): - print msg + print(msg) clean_up() sys.exit(-1) def run_cmd(cmd): - print cmd + print(cmd) if isinstance(cmd, list): - return subprocess.check_output(cmd) + return subprocess.check_output(cmd, encoding='utf8') else: - return subprocess.check_output(cmd.split(" ")) + return subprocess.check_output(cmd.split(" "), encoding='utf8') def continue_maybe(prompt): - result = raw_input("\n%s (y/n): " % 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 + 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 filter(lambda x: x.startswith(TEMP_BRANCH_PREFIX), branches): - print "Deleting local branch %s" % branch + 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 = raw_input( - "Enter primary author in the format of \"name \" [%s]: " % - distinct_authors[0]) - if primary_author == "": - primary_author = distinct_authors[0] - - reviewers = raw_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 = raw_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 +def merge_pr(pr_num, title, pr_repo_desc): + + merge_message = [] + result = input("Would you like to squash the commit messages? (y/n): ") + if result.lower().strip() == "y": + # Retrieve the commits separately. + json_commits = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/commits") + if json_commits and isinstance(json_commits, list): + for commit in json_commits: + commit_message = commit['commit']['message'] + # Remove empty lines and lines containing "Change-Id:" + filtered_lines = [line for line in commit_message.split('\n') if 'Change-Id:' not in line and line.strip()] + modified_commit_message = '\n'.join(filtered_lines) + if modified_commit_message.strip() != title.strip(): + merge_message += [modified_commit_message] + + # Check for disapproval reviews. + json_reviewers = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/reviews") + disapproval_reviews = [review['user']['login'] for review in json_reviewers if review['state'] == 'CHANGES_REQUESTED'] + if disapproval_reviews: + continue_maybe("Warning: There are requested changes. Proceed with merging pull request #%s?" % pr_num) + # Verify if there are no approved reviews. + approved_reviewers = [review['user']['login'] for review in json_reviewers if review['state'] == 'APPROVED'] + if not approved_reviewers: + continue_maybe("Warning: Pull Request does not have an approved review. Proceed with merging pull request #%s?" % pr_num) 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 + reviewers_string = ', '.join(approved_reviewers) + merge_message += [f"Reviewers: {reviewers_string}"] + # Check the author and the closing line. + json_pr = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}") + primary_author = json_pr["user"]["login"] + if primary_author != "": + merge_message += [f"Author: {primary_author}"] 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 - + merge_message += [close_line] + merged_string = '\n'.join(merge_message) + + # Get the latest commit SHA. + latest_commit_sha = json_pr["head"]["sha"] + json_status = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/commits/{latest_commit_sha}/check-runs") + # Check if all checks have passed on GitHub. + all_checks_passed = all(status["conclusion"] == "success" for status in json_status["check_runs"]) + if all_checks_passed: + print("All checks have passed on the github.") + else: + any_in_progress = any(run["status"] == "in_progress" for run in json_status["check_runs"]) + if any_in_progress: + continue_maybe("Warning: There are pending checks. Would you like to continue the merge?") + else: + continue_maybe("Warning: Not all checks have passed on GitHub. Would you like to continue the merge?") + + headers = { + "Authorization": f"token {GITHUB_OAUTH_KEY}", + "Accept": "application/vnd.github.v3+json" + } + data = { + "commit_title": title, + "commit_message": merged_string, + "merge_method": "squash" + } + + response = requests.put(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/merge", headers=headers, json=data) + + if response.status_code == 200: + merge_response_json = response.json() + merge_commit_sha = merge_response_json.get("sha") + print(f"Pull request #{pr_num} merged. Sha: #{merge_commit_sha}") + return merge_commit_sha + else: + print(f"Failed to merge pull request #{pr_num}. Status code: {response.status_code}") + print(response.text) + exit() def cherry_pick(pr_num, merge_hash, default_branch): - pick_ref = raw_input("Enter a branch name [%s]: " % 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) + run_cmd("git fetch %s" % PR_REMOTE_NAME) + run_cmd("git checkout -b %s %s/%s" % (pick_branch_name, PUSH_REMOTE_NAME, pick_ref)) + + current_attempt = 0 + max_attempts = 6 + # Check if the merge hash exists + while not run_cmd("git rev-parse --verify %s" % merge_hash): + if current_attempt >= max_attempts: + print("Error: The commit hash does not exist in the local repository.") + exit() + current_attempt += 1 + print("Waiting for the merge hash to become available...(10 sec)") + time.sleep(10) + run_cmd("git fetch %s" % PR_REMOTE_NAME) try: run_cmd("git cherry-pick -sx %s" % merge_hash) @@ -244,21 +244,21 @@ def cherry_pick(pr_num, merge_hash, default_branch): 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) + 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 = filter(lambda x: x == DEFAULT_FIX_VERSION, versions) + versions = [x for x in versions if x == DEFAULT_FIX_VERSION] if len(versions) > 0: return versions[0] else: return None else: - versions = filter(lambda x: x.startswith(branch), versions) + versions = [x for x in versions if x.startswith(branch)] if len(versions) > 0: return versions[-1] else: @@ -266,10 +266,14 @@ def fix_version_from_branch(branch, versions): 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_server = {"server": JIRA_API_BASE} - jira_id = raw_input("Enter a JIRA id [%s]: " % default_jira_id) + if JIRA_ACCESS_TOKEN is not None: + asf_jira = jira.client.JIRA(jira_server, token_auth=JIRA_ACCESS_TOKEN) + else: + asf_jira = jira.client.JIRA(jira_server, 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 @@ -288,40 +292,40 @@ def resolve_jira_issue(merge_branches, comment, default_jira_id=""): 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)) + 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 = filter(lambda x: x.raw['released'] is False, versions) + versions = [x for x in versions if x.raw['released'] is False] - version_names = map(lambda x: x.name, versions) - default_fix_versions = map(lambda x: fix_version_from_branch(x, version_names), merge_branches) - default_fix_versions = filter(lambda x: x != None, default_fix_versions) + 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 = raw_input("Enter comma-separated fix version(s) [%s]: " % 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 filter(lambda v: v.name == version_str, versions)[0].raw + return [v for v in versions if v.name == version_str][0].raw - jira_fix_versions = map(lambda v: get_version_json(v), fix_versions) + jira_fix_versions = [get_version_json(v) for v in fix_versions] - resolve = filter(lambda a: a['name'] == "Resolve Issue", asf_jira.transitions(jira_id))[0] - resolution = filter(lambda r: r.raw['name'] == "Fixed", asf_jira.resolutions())[0] + 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) + 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) + jira_ids = re.findall("%s-[0-9]+" % CAPITALIZED_PROJECT_NAME, title) if len(jira_ids) == 0: resolve_jira_issue(merge_branches, comment) @@ -401,7 +405,7 @@ def check_git_remote(): # check if all remote endpoints' URLs point to project git repo name = PROJECT_NAME + ".git" - for url in repos.values(): + 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) @@ -417,15 +421,15 @@ def check_jira_env(): global JIRA_PASSWORD if JIRA_IMPORTED: - - if JIRA_USERNAME.strip() != "" and JIRA_PASSWORD.strip() == "": - inform_pwd = raw_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) + if JIRA_ACCESS_TOKEN is None: + 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 ="Neither JIRA_ACCESS_TOKEN nor JIRA_USERNAME and/or JIRA_PASSWORD are set. Want to continue? " + continue_maybe(msg) else: msg = "JIRA lib not installed. Want to continue? " continue_maybe(msg) @@ -440,89 +444,92 @@ def main(): check_git_remote() branches = get_json("%s/branches" % GITHUB_API_BASE) - branch_names = filter(lambda x: x.startswith(RELEASE_BRANCH_PREFIX), [x['name'] for x in branches]) + 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 = raw_input("Which pull request would you like to merge? (e.g. 34): ") + 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)) + + # Check if the pull request has already been closed or merged. + pull_request_state = pr.get("state", "") + if pull_request_state == "closed": + merge_hash = pr.get("merge_commit_sha", "") + merged = pr.get("merged") + # Verify if the pull request has been merged by the GitHub API. + if merged is True: + print(f"Pull request #{pr['number']} has already been merged, assuming you want to backport") + cherry_pick(pr_num, merge_hash, latest_branch) + sys.exit(0) + # Some merged pull requests may not appear as merged in the GitHub API, + # for example, those closed by an older version of this script. + else: + pr_events = get_json("%s/issues/%s/events" % (GITHUB_API_BASE, pr_num)) + for event in pr_events: + if event.get("event") == "closed": + commit_id = event.get("commit_id") + if commit_id is not None: + print(f"Pull request #{pr['number']} has already been merged, assuming you want to backport") + cherry_pick(pr_num, merge_hash, latest_branch) + sys.exit(0) + else: + print(f"Pull request #{pr['number']} has already been closed, but not merged, exiting.") + exit() + + if not bool(pr["mergeable"]): + print(f"Pull request %s is not mergeable in its current form.\n" % pr_num) + exit() url = pr["url"] pr_title = pr["title"] - commit_title = raw_input("Commit title [%s]: " % pr_title.encode("utf-8")).decode("utf-8") + 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 = raw_input("Would you like to use the modified title? (y/n): ") + 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:" + print("Using modified title:") else: - print "Using original title:" - print commit_title + 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)) + 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) + merge_hash = merge_pr(pr_num, commit_title, pr_repo_desc) pick_prompt = "Would you like to pick %s into another branch?" % merge_hash - while raw_input("\n%s (y/n): " % pick_prompt).lower().strip() == "y": + 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: + if (JIRA_ACCESS_TOKEN is not None) or (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." + print("Neither JIRA_ACCESS_TOKEN nor JIRA_USERNAME and/or JIRA_PASSWORD are 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." + 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 diff --git a/zookeeper-assembly/pom.xml b/zookeeper-assembly/pom.xml old mode 100755 new mode 100644 index c2b97c0e6a3..317233d6f3b --- a/zookeeper-assembly/pom.xml +++ b/zookeeper-assembly/pom.xml @@ -23,8 +23,7 @@ org.apache.zookeeper parent - 3.7.0-SNAPSHOT - .. + 3.10.0-SNAPSHOT zookeeper-assembly @@ -53,6 +52,7 @@ org.apache.zookeeper zookeeper-docs ${project.version} + pom org.apache.zookeeper @@ -100,12 +100,12 @@ jackson-databind - com.googlecode.json-simple - json-simple + org.jline + jline - jline - jline + ch.qos.logback + logback-classic io.dropwizard.metrics diff --git a/zookeeper-assembly/src/main/assembly/lib-package.xml b/zookeeper-assembly/src/main/assembly/lib-package.xml index 81194f9e64d..40336cee8fd 100644 --- a/zookeeper-assembly/src/main/assembly/lib-package.xml +++ b/zookeeper-assembly/src/main/assembly/lib-package.xml @@ -54,7 +54,7 @@ LICENSE - / + . ${rw.file.permission} ${rwx.file.permission} diff --git a/zookeeper-client/pom.xml b/zookeeper-client/pom.xml old mode 100755 new mode 100644 index 307d60583fe..16f6419f6d2 --- a/zookeeper-client/pom.xml +++ b/zookeeper-client/pom.xml @@ -23,8 +23,7 @@ org.apache.zookeeper parent - 3.7.0-SNAPSHOT - .. + 3.10.0-SNAPSHOT zookeeper-client @@ -39,10 +38,6 @@ zookeeper-client-c - - java-build - - @@ -55,4 +50,4 @@ - \ No newline at end of file + diff --git a/zookeeper-client/zookeeper-client-c/CMakeLists.txt b/zookeeper-client/zookeeper-client-c/CMakeLists.txt index 8400c1094c7..da1351e20f5 100644 --- a/zookeeper-client/zookeeper-client-c/CMakeLists.txt +++ b/zookeeper-client/zookeeper-client-c/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.5) -project(zookeeper VERSION 3.7.0) +project(zookeeper VERSION 3.10.0) set(email user@zookeeper.apache.org) set(description "zookeeper C client") @@ -249,6 +249,7 @@ set(test_sources tests/ThreadingUtil.cc tests/TestZookeeperInit.cc tests/TestZookeeperClose.cc + tests/TestSASLAuth.cc tests/TestReconfig.cc tests/TestReconfigServer.cc tests/TestClientRetry.cc diff --git a/zookeeper-client/zookeeper-client-c/ChangeLog b/zookeeper-client/zookeeper-client-c/ChangeLog index c85a6c0ad86..040e1d32a44 100644 --- a/zookeeper-client/zookeeper-client-c/ChangeLog +++ b/zookeeper-client/zookeeper-client-c/ChangeLog @@ -1,13 +1,13 @@ Release 2.1.1 2008-04-30 Andrew Kornev - * changed the distributino package name to "c-client-src" + * changed the distributions package name to "c-client-src" Release 2.1.0 2008-04-30 Andrew Kornev * added the client latency diagnostics; the client prints a warning when the - reponse latency exceeds 20ms + response latency exceeds 20ms * modified logging format to report the znode path for which the zookeeper operation is called @@ -31,12 +31,12 @@ Release 1.1.3 * get_xid() is not thread-safe (xid initialization race condition in the multi-threaded mode). - * the I/O thread doesn’t automatically terminate on AUTH_FAILURE and + * the I/O thread doesn't automatically terminate on AUTH_FAILURE and SESSION_EXPIRED events. * all session events should be processed on the completion thread. - * PING operation doesn’t atomically enqueue the completion and + * PING operation doesn't atomically enqueue the completion and send buffers like other operations do. * corrected zookeeper_init() doxygen docs. diff --git a/zookeeper-client/zookeeper-client-c/LICENSE b/zookeeper-client/zookeeper-client-c/LICENSE index 7df01ad5534..c3e546da36c 100644 --- a/zookeeper-client/zookeeper-client-c/LICENSE +++ b/zookeeper-client/zookeeper-client-c/LICENSE @@ -285,7 +285,7 @@ * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions + * the following conditions are adhered to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms @@ -310,7 +310,7 @@ * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library + * The word 'cryptographic' can be left out if the routines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: @@ -328,7 +328,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * The licence and distribution terms for any publically available version or + * The licence and distribution terms for any publicly available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] diff --git a/zookeeper-client/zookeeper-client-c/Makefile.am b/zookeeper-client/zookeeper-client-c/Makefile.am index 9c794a5ef09..e42a3539b8a 100644 --- a/zookeeper-client/zookeeper-client-c/Makefile.am +++ b/zookeeper-client/zookeeper-client-c/Makefile.am @@ -60,7 +60,7 @@ libzookeeper_st_la_LDFLAGS = $(LIB_LDFLAGS) -export-symbols-regex $(EXPORT_SYMBO if WANT_SYNCAPI noinst_LTLIBRARIES += libzkmt.la libzkmt_la_SOURCES =$(COMMON_SRC) src/mt_adaptor.c -libzkmt_la_CFLAGS = -DTHREADED +libzkmt_la_CFLAGS = $(AM_CFLAGS) -DTHREADED libzkmt_la_LIBADD = -lm $(CLOCK_GETTIME_LIBS) lib_LTLIBRARIES += libzookeeper_mt.la @@ -80,11 +80,11 @@ bin_PROGRAMS += cli_mt load_gen cli_mt_SOURCES = src/cli.c cli_mt_LDADD = libzookeeper_mt.la $(SASL_LIB_LDFLAGS) -cli_mt_CFLAGS = -DTHREADED +cli_mt_CFLAGS = $(AM_CFLAGS) -DTHREADED load_gen_SOURCES = src/load_gen.c load_gen_LDADD = libzookeeper_mt.la -load_gen_CFLAGS = -DTHREADED +load_gen_CFLAGS = $(AM_CFLAGS) -DTHREADED endif @@ -134,14 +134,14 @@ TESTS_ENVIRONMENT = ZKROOT=${srcdir}/../.. \ CLASSPATH=$$CLASSPATH:$$CLOVER_HOME/lib/clover*.jar nodist_zktest_st_SOURCES = $(TEST_SOURCES) zktest_st_LDADD = libzkst.la libhashtable.la $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl -zktest_st_CXXFLAGS = -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(USEIPV6) $(SOLARIS_CPPFLAGS) +zktest_st_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(SOLARIS_CPPFLAGS) zktest_st_LDFLAGS = -shared $(SYMBOL_WRAPPERS) $(SOLARIS_LIB_LDFLAGS) if WANT_SYNCAPI check_PROGRAMS += zktest-mt nodist_zktest_mt_SOURCES = $(TEST_SOURCES) tests/PthreadMocks.cc zktest_mt_LDADD = libzkmt.la libhashtable.la -lpthread $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl - zktest_mt_CXXFLAGS = -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6) + zktest_mt_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6) if SOLARIS SHELL_SYMBOL_WRAPPERS_MT = cat ${srcdir}/tests/wrappers-mt.opt SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(SHELL_SYMBOL_WRAPPERS_MT:sh) diff --git a/zookeeper-client/zookeeper-client-c/README b/zookeeper-client/zookeeper-client-c/README index ade5ad98fa1..24869756b63 100644 --- a/zookeeper-client/zookeeper-client-c/README +++ b/zookeeper-client/zookeeper-client-c/README @@ -34,70 +34,8 @@ Sync and Async API. INSTALLATION -If you're building the client from a source checkout you need to -follow the steps outlined below. If you're building from a release -tar downloaded from Apache please skip to step 2. - -1) do a "ant compile_jute" from the zookeeper top level directory (.../trunk). - This will create a directory named "generated" under zookeeper-client/zookeeper-client-c. - Skip to step 3. -2) unzip/untar the source tarball and cd to the zookeeper-x.x.x/zookeeper-client/zookeeper-client-c directory -3) change directory to zookeeper-client/zookeeper-client-c and do a "autoreconf -if" to bootstrap - autoconf, automake and libtool. Please make sure you have autoconf - version 2.59 or greater installed. If cppunit is installed in a non-standard - directory, you need to specify where to find cppunit.m4. For example, if - cppunit is installed under /usr/local, run: - - ACLOCAL="aclocal -I /usr/local/share/aclocal" autoreconf -if - -4) do a "./configure [OPTIONS]" to generate the makefile. See INSTALL - for general information about running configure. Additionally, the - configure supports the following options: - --enable-debug enables optimization and enables debug info compiler - options, disabled by default - --without-syncapi disables Sync API support; zookeeper_mt library won't - be built, enabled by default - --disable-static do not build static libraries, enabled by default - --disable-shared do not build shared libraries, enabled by default - --without-cppunit do not build the test library, enabled by default. - -5) do a "make" or "make install" to build the libraries and install them. - Alternatively, you can also build and run a unit test suite (and - you probably should). Please make sure you have cppunit-1.10.x or - higher installed before you execute step 4. Once ./configure has - finished, do a "make check". It will build the libraries, build - the tests and run them. -6) to generate doxygen documentation do a "make doxygen-doc". All - documentations will be placed to a new subfolder named docs. By - default only HTML documentation is generated. For information on - other document formats please use "./configure --help" - -Alternatively you can use the CMake build system. On Windows, this is required. -Follow steps 1 and 2 above, and then continue here. - -1) do a "cmake [OPTIONS]" to generate the makefile or msbuild files (the correct - build system will be generated based on your platform). Some options from above - are supported: - -DCMAKE_BUILD_TYPE Debug by default, Release enables optimzation etc. - -DWANT_SYNCAPI ON by default, OFF disables the Sync API support - -DWANT_CPPUNIT ON except on Windows, OFF disables the tests - -DWITH_OPENSSL ON by default, OFF disables the SSL support. You can also - specify a custom path by -DWITH_OPENSSL=/path/to/openssl/ - -DWITH_CYRUS_SASL ON by default, OFF disables SASL support. You can also - specify a custom path by -DWITH_CYRUS_SASL=/path/to/cyrus-sasl/ - -DBUILD_SHARED_LIBS not yet supported, only static libraries are built - other CMake options see "cmake --help" for generic options, such as generator - -2) do a "cmake --build ." to build the default targets. Alternatively you can - invoke "make" or "msbuild" manually. If the tests were enabled, use "ctest -V" - to run them. - -Current limitations of the CMake build system include lack of Solaris support, -no shared library option, no explicitly exported symbols (all are exported by -default), no versions on the libraries, and no documentation generation. -Features of CMake include a single, easily consumed cross-platform build system -to generate the ZooKeeper C Client libraries for any project, with little to no -configuration. +Please refer to the "Installation" item under "C Binding" section in file +".../trunk/zookeeper-docs/src/main/resources/markdown/zookeeperProgrammers.md" EXAMPLE/SAMPLE C CLIENT SHELL diff --git a/zookeeper-client/zookeeper-client-c/acinclude.m4 b/zookeeper-client/zookeeper-client-c/acinclude.m4 index 9734f215649..55061709180 100644 --- a/zookeeper-client/zookeeper-client-c/acinclude.m4 +++ b/zookeeper-client/zookeeper-client-c/acinclude.m4 @@ -10,7 +10,7 @@ # DX_???_FEATURE(ON|OFF) - control the default setting of a Doxygen feature. # Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, # 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' -# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', +# for generating a separate .chi file by the .chm file, and 'MAN', 'RTF', # 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment # variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' # paper size. @@ -239,8 +239,8 @@ DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) -# Seperate CHI file generation. -DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], +# Separate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file], [DX_CHECK_DEPEND(chm, 1)], [DX_CLEAR_DEPEND(chm, 1)], [], diff --git a/zookeeper-client/zookeeper-client-c/configure.ac b/zookeeper-client/zookeeper-client-c/configure.ac index 8e24d651c1b..0de8427e5bb 100644 --- a/zookeeper-client/zookeeper-client-c/configure.ac +++ b/zookeeper-client/zookeeper-client-c/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ(2.59) -AC_INIT([zookeeper C client],3.7.0,[user@zookeeper.apache.org],[zookeeper]) +AC_INIT([zookeeper C client],3.10.0,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX diff --git a/zookeeper-client/zookeeper-client-c/include/zookeeper.h b/zookeeper-client/zookeeper-client-c/include/zookeeper.h index 243fac28430..ead328a94a0 100644 --- a/zookeeper-client/zookeeper-client-c/include/zookeeper.h +++ b/zookeeper-client/zookeeper-client-c/include/zookeeper.h @@ -108,7 +108,7 @@ enum ZOO_ERRORS { ZBADARGUMENTS = -8, /*!< Invalid arguments */ ZINVALIDSTATE = -9, /*!< Invliad zhandle state */ ZNEWCONFIGNOQUORUM = -13, /*!< No quorum of new config is connected and - up-to-date with the leader of last commmitted + up-to-date with the leader of last committed config - try invoking reconfiguration after new servers are connected and synced */ ZRECONFIGINPROGRESS = -14, /*!< Reconfiguration requested while another @@ -140,7 +140,10 @@ enum ZOO_ERRORS { ZEPHEMERALONLOCALSESSION = -120, /*!< Attempt to create ephemeral node on a local session */ ZNOWATCHER = -121, /*!< The watcher couldn't be found */ ZRECONFIGDISABLED = -123, /*!< Attempts to perform a reconfiguration operation when reconfiguration feature is disabled */ - ZSESSIONCLOSEDREQUIRESASLAUTH = -124 /*!< The session has been closed by server because server requires client to do SASL authentication, but client is not configured with SASL authentication or configuted with SASL but failed (i.e. wrong credential used.). */ + ZSESSIONCLOSEDREQUIRESASLAUTH = -124, /*!< The session has been closed by server because server requires client to do authentication via configured authentication scheme at server, but client is not configured with required authentication scheme or configured but failed (i.e. wrong credential used.). */ + ZTHROTTLEDOP = -127 /*!< Operation was throttled and not executed at all. please, retry! */ + + /* when adding/changing values here also update zerror(int) to return correct error message */ }; #ifdef __cplusplus @@ -534,6 +537,38 @@ ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags); #ifdef HAVE_OPENSSL_H +/** + * \brief create a handle to communicate with zookeeper using SSL. + * + * This method creates a new handle and a zookeeper session that corresponds + * to that handle. Session establishment is asynchronous, meaning that the + * session should not be considered established until (and unless) an + * event of state ZOO_CONNECTED_STATE is received. + * \param host comma separated host:port pairs, each corresponding to a zk + * server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002" + * \param cert SSL certificate string. Two formats are supported: server CA + * only e.g. "/path/to/server_ca.cer" and server CA with client certificate e.g. + * "/path/to/server_ca.cer,/path/to/client_cert.pem,/path/to/client_cert.key,client_cert_password". + * \param fn the global watcher callback function. When notifications are + * triggered this function will be invoked. + * \param recv_timeout the timeout for the zookeeper session. + * \param clientid the id of a previously established session that this + * client will be reconnecting to. Pass 0 if not reconnecting to a previous + * session. Clients can access the session id of an established, valid, + * connection by calling \ref zoo_client_id. If the session corresponding to + * the specified clientid has expired, or if the clientid is invalid for + * any reason, the returned zhandle_t will be invalid -- the zhandle_t + * state will indicate the reason for failure (typically + * ZOO_EXPIRED_SESSION_STATE). + * \param context the handback object that will be associated with this instance + * of zhandle_t. Application can access it (for example, in the watcher + * callback) using \ref zoo_get_context. The object is not used by zookeeper + * internally and can be null. + * \param flags reserved for future use. Should be set to zero. + * \return a pointer to the opaque zhandle structure. If it fails to create + * a new zhandle the function returns NULL and the errno variable + * indicates the reason. + */ ZOOAPI zhandle_t *zookeeper_init_ssl(const char *host, const char *cert, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags); #endif @@ -664,8 +699,85 @@ ZOOAPI zhandle_t *zookeeper_init_sasl(const char *host, watcher_fn fn, ZOOAPI sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, const char *realm, const char* password_file); +/** + * \brief signature of the callback function for SASL password. + * + * This callback is defined by user to decrypt the content of password file with + * context into the actual password. + * + * \param content the string read from the password file. + * \param content_len the size of the content in bytes. + * \param context the handback object that will be associated with the password + * file. The object is not used by zookeeper internally and can be null. + * \param buf the buffer where the resulting actual password is saved. + * \param buf_len the size of buf in bytes, which is also the max allowed + * password length. + * \param passwd_len as an output parameter of the callback function, passwd_len + * points to the actual length of the password stored in buf. Its size must not + * exceed buf_len; otherwise, SASL_BUFOVER will be returned. + * \return SASL_OK, or the possible errors defined by the SASL library. + */ +typedef int (*zoo_sasl_password_callback_t)(const char *content, size_t content_len, + void *context, char *buf, size_t buf_len, size_t *passwd_len); + +/** + * \brief zoo_sasl_password structure. + * + * This structure holds the parameters for getting the actual SASL password. The + * user-defined callback is a processor that decrypts the content of password_file + * with context as a handback object helping the decryption into the actual password + * as is described above for zoo_sasl_password_callback_t. + * + * All of password_file, context and callback are allowed to be null; however, once + * callback is defined, password_file should not be null since the content processed + * by callback is from password_file. If callback is null and password_file is given, + * the first line of the file is just the actual password. While both password_file + * and callback are null, the password would be read from terminal with prompt. + * + * zoo_sasl_password is defined by user and used to generate the actual password in + * response to \c SASL_CB_PASS. + */ +typedef struct zoo_sasl_password { + const char *password_file; /*!< The path of the password file */ + void *context; /*!< The handback object used by callback */ + zoo_sasl_password_callback_t callback; /*!< Callback over the content of password file */ +} zoo_sasl_password_t; + +/** + * \brief allocates and initializes a basic array of Cyrus SASL callbacks. + * + * This helper function is similar to zoo_sasl_make_basic_callbacks, except that + * the actual password is generated by zoo_sasl_password object rather than just + * read from the password file. + * + * \param user the "canned" response to \c SASL_CB_USER and \c SASL_CB_AUTHNAME, + * or NULL for none. + * \param realm the "canned" response to \c SASL_CB_GETREALM, or NULL for none. + * \param password the object defined by user to specify how the actual password + * is generated in response to \c SASL_CB_PASS, should never be NULL (otherwise + * the behaviour is undefined), see struct zoo_sasl_password for details. + * \return the freshly-malloc()ed callbacks array, or NULL if allocation + * failed. Deallocate with free(), but only after the corresponding + * ZooKeeper handle is closed. + */ +ZOOAPI sasl_callback_t *zoo_sasl_make_password_callbacks(const char *user, + const char *realm, zoo_sasl_password_t *password); + #endif /* HAVE_CYRUS_SASL_H */ + +/** + * \brief return ZOO_VERSION from loaded library as a string. + * + * This method allows a calling program to determine at runtime whether the + * version of the dynamically loaded zookeeper library (a.k.a. zoo_version_str()) + * is same as the version of the library the calling program was compiled against + * (a.k.a. ZOO_VERSION). + * + * \return a string in shape "MAJOR.MINOR.PATCH", say "3.10.0" + */ +ZOOAPI const char* zoo_version_str(); + /** * \brief update the list of servers this client will connect to. * @@ -694,10 +806,36 @@ ZOOAPI sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, * \return ZOK on success or one of the following errcodes on failure: * ZBADARGUMENTS - invalid input parameters * ZINVALIDSTATE - zhandle state is either ZOO_SESSION_EXPIRED_STATE or ZOO_AUTH_FAILED_STATE - * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details + * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details */ ZOOAPI int zoo_set_servers(zhandle_t *zh, const char *hosts); +/** + * \brief sets a minimum delay to observe between "routine" host name + * resolutions. + * + * The client performs regular resolutions of the list of servers + * passed to \ref zookeeper_init or set with \ref zoo_set_servers in + * order to detect changes at the DNS level. + * + * By default, it does so every time it checks for socket readiness. + * This results in low latency in the detection of changes, but can + * lead to heavy DNS traffic when the local cache is not effective. + * + * This method allows an application to influence the rate of polling. + * When delay_ms is set to a value greater than zero, the client skips + * most "routine" resolutions which would have happened in a window of + * that many milliseconds since the last successful one. + * + * Setting delay_ms to 0 disables this logic, reverting to the default + * behavior. Setting it to -1 disables network resolutions during + * normal operation (but not, e.g., on connection loss). + * + * \param delay_ms 0, -1, or the window size in milliseconds + * \return ZOK on success or ZBADARGUMENTS for invalid input parameters + */ +ZOOAPI int zoo_set_servers_resolution_delay(zhandle_t *zh, int delay_ms); + /** * \brief cycle to the next server on the next connection attempt. * @@ -1750,7 +1888,7 @@ typedef enum { * \param wtype the watcher type to be removed * \param watcher the watcher to be removed, if null all watches for that * path (and watcher type) will be removed - * \param watcherCtx the contex associated with the watcher to be removed + * \param watcherCtx the context associated with the watcher to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. @@ -1760,7 +1898,7 @@ typedef enum { * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured + * ZSYSTEMERROR - a system error occurred */ ZOOAPI int zoo_aremove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, @@ -1781,7 +1919,7 @@ ZOOAPI int zoo_aremove_watches(zhandle_t *zh, const char *path, * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured + * ZSYSTEMERROR - a system error occurred */ ZOOAPI int zoo_remove_all_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, int local); @@ -1801,7 +1939,7 @@ ZOOAPI int zoo_remove_all_watches(zhandle_t *zh, const char *path, * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured + * ZSYSTEMERROR - a system error occurred */ ZOOAPI int zoo_aremove_all_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, int local, void_completion_t *completion, @@ -2405,7 +2543,7 @@ ZOOAPI int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_resul * \param wtype the watcher type to be removed * \param watcher the watcher to be removed, if null all watches for that * path (and watcher type) will be removed - * \param watcherCtx the contex associated with the watcher to be removed + * \param watcherCtx the context associated with the watcher to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. @@ -2415,7 +2553,7 @@ ZOOAPI int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_resul * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory - * ZSYSTEMERROR - a system error occured + * ZSYSTEMERROR - a system error occurred */ ZOOAPI int zoo_remove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local); diff --git a/zookeeper-client/zookeeper-client-c/include/zookeeper_version.h b/zookeeper-client/zookeeper-client-c/include/zookeeper_version.h index 9fa2bae3d2f..639eaf5fe28 100644 --- a/zookeeper-client/zookeeper-client-c/include/zookeeper_version.h +++ b/zookeeper-client/zookeeper-client-c/include/zookeeper_version.h @@ -22,7 +22,7 @@ extern "C" { #endif -#define ZOO_VERSION "3.7.0" +#define ZOO_VERSION "3.10.0" #ifdef __cplusplus } diff --git a/zookeeper-client/zookeeper-client-c/pom.xml b/zookeeper-client/zookeeper-client-c/pom.xml old mode 100755 new mode 100644 index e8ea1aa7a97..91e45cff3e8 --- a/zookeeper-client/zookeeper-client-c/pom.xml +++ b/zookeeper-client/zookeeper-client-c/pom.xml @@ -23,32 +23,17 @@ org.apache.zookeeper zookeeper-client - 3.7.0-SNAPSHOT - .. + 3.10.0-SNAPSHOT zookeeper-client-c - jar + pom Apache ZooKeeper - Client - C ZooKeeper c client - - - c-test-coverage - - --enable-gcov - - - - no-c-test-coverage - - true - - - - - - + + + @@ -104,9 +89,9 @@ generate-sources generate-sources - + - + run @@ -116,7 +101,7 @@ build-c-client compile - + @@ -125,28 +110,7 @@ - - - - run - - - - test-cppunit - test - - - ${skipTests} - - - - - - - - - - + run @@ -160,7 +124,7 @@ - + -SNAPSHOT @@ -179,5 +143,54 @@ + + + c-test-coverage + + --enable-gcov + + + + + cppunit + + + + !skipCppUnit + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + test-cppunit + test + + + ${skipTests} + + + + + + + + + + + + + run + + + + + + + + diff --git a/zookeeper-client/zookeeper-client-c/src/addrvec.c b/zookeeper-client/zookeeper-client-c/src/addrvec.c index fdfb68d34fd..b7f244e7afb 100644 --- a/zookeeper-client/zookeeper-client-c/src/addrvec.c +++ b/zookeeper-client/zookeeper-client-c/src/addrvec.c @@ -126,8 +126,26 @@ int addrvec_contains(const addrvec_t *avec, const struct sockaddr_storage *addr) for (i = 0; i < avec->count; i++) { - if(memcmp(&avec->data[i], addr, INET_ADDRSTRLEN) == 0) - return 1; + if (avec->data[i].ss_family != addr->ss_family) + continue; + switch (addr->ss_family) { + case AF_INET: + if (memcmp(&((struct sockaddr_in*)&avec->data[i])->sin_addr, + &((struct sockaddr_in*)addr)->sin_addr, + sizeof(struct in_addr)) == 0) + return 1; + break; +#ifdef AF_INET6 + case AF_INET6: + if (memcmp(&((struct sockaddr_in6*)&avec->data[i])->sin6_addr, + &((struct sockaddr_in6*)addr)->sin6_addr, + sizeof(struct in6_addr)) == 0) + return 1; + break; +#endif + default: + break; + } } return 0; diff --git a/zookeeper-client/zookeeper-client-c/src/addrvec.h b/zookeeper-client/zookeeper-client-c/src/addrvec.h index a12642908bb..2a9a8ad9117 100644 --- a/zookeeper-client/zookeeper-client-c/src/addrvec.h +++ b/zookeeper-client/zookeeper-client-c/src/addrvec.h @@ -74,7 +74,7 @@ int addrvec_grow(addrvec_t *avec, uint32_t grow_amount); int addrvec_grow_default(addrvec_t *avec); /** - * Check if an addrvec contains the specificed sockaddr_storage value. + * Check if an addrvec contains the specified sockaddr_storage value. * \returns 1 if it contains the value and 0 otherwise. */ int addrvec_contains(const addrvec_t *avec, const struct sockaddr_storage *addr); diff --git a/zookeeper-client/zookeeper-client-c/src/cli.c b/zookeeper-client/zookeeper-client-c/src/cli.c index 1864e564571..c34be8d5961 100644 --- a/zookeeper-client/zookeeper-client-c/src/cli.c +++ b/zookeeper-client/zookeeper-client-c/src/cli.c @@ -924,7 +924,7 @@ int main(int argc, char **argv) { if (clientIdFile) { fh = fopen(clientIdFile, "r"); if (fh) { - if (fread(&myid, sizeof(myid), 1, fh) != sizeof(myid)) { + if (fread(&myid, sizeof(myid), 1, fh) != 1) { memset(&myid, 0, sizeof(myid)); } fclose(fh); @@ -948,6 +948,17 @@ int main(int argc, char **argv) { zoo_deterministic_conn_order(1); // enable deterministic order #ifdef HAVE_CYRUS_SASL_H + /* + * We need to disable the deprecation warnings as Apple has + * decided to deprecate all of CyrusSASL's functions with OS 10.11 + * (see MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also + * for covering clang. + */ +#ifdef __APPLE__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + if (mechlist) { zoo_sasl_params_t sasl_params = { 0 }; int sr; @@ -977,6 +988,11 @@ int main(int argc, char **argv) { return errno; } } + +#ifdef __APPLE__ +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_CYRUS_SASL_H */ if (!zh) { diff --git a/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c b/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c index caa5f6ccc03..bc94a59599b 100644 --- a/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c +++ b/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c @@ -70,7 +70,7 @@ hash(struct hashtable *h, void *k) static int hashtable_expand(struct hashtable *h) { - /* Double the size of the table to accomodate more entries */ + /* Double the size of the table to accommodate more entries */ struct entry **newtable; struct entry *e; struct entry **pE; diff --git a/zookeeper-client/zookeeper-client-c/src/load_gen.c b/zookeeper-client/zookeeper-client-c/src/load_gen.c index 886fe1b3e3c..f25edcbe928 100644 --- a/zookeeper-client/zookeeper-client-c/src/load_gen.c +++ b/zookeeper-client/zookeeper-client-c/src/load_gen.c @@ -27,8 +27,6 @@ static zhandle_t *zh; -static int shutdownThisThing=0; - // ***************************************************************************** // static pthread_cond_t cond=PTHREAD_COND_INITIALIZER; diff --git a/zookeeper-client/zookeeper-client-c/src/mt_adaptor.c b/zookeeper-client/zookeeper-client-c/src/mt_adaptor.c index 38cced425ba..174701c7358 100644 --- a/zookeeper-client/zookeeper-client-c/src/mt_adaptor.c +++ b/zookeeper-client/zookeeper-client-c/src/mt_adaptor.c @@ -121,7 +121,7 @@ int handle_error(zhandle_t* zh, SOCKET sock, char* message) return -1; } -//--create socket pair for interupting selects. +//--create socket pair for interrupting selects. int create_socket_pair(zhandle_t* zh, SOCKET fds[2]) { struct sockaddr_in inaddr; @@ -256,12 +256,13 @@ int adaptor_init(zhandle_t *zh) pthread_mutex_init(&zh->to_process.lock,0); pthread_mutex_init(&adaptor_threads->zh_lock,0); pthread_mutex_init(&adaptor_threads->reconfig_lock,0); - // to_send must be recursive mutex + pthread_mutex_init(&adaptor_threads->watchers_lock,0); + // to_send must be recursive mutex pthread_mutexattr_init(&recursive_mx_attr); pthread_mutexattr_settype(&recursive_mx_attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&zh->to_send.lock,&recursive_mx_attr); pthread_mutexattr_destroy(&recursive_mx_attr); - + pthread_mutex_init(&zh->sent_requests.lock,0); pthread_cond_init(&zh->sent_requests.cond,0); pthread_mutex_init(&zh->completions_to_process.lock,0); @@ -358,6 +359,7 @@ unsigned __stdcall do_io( void * v) void *do_io(void *v) #endif { + log_callback_fn log_fn; zhandle_t *zh = (zhandle_t*)v; #ifndef WIN32 struct pollfd fds[2]; @@ -369,13 +371,14 @@ void *do_io(void *v) fds[0].fd=adaptor_threads->self_pipe[0]; fds[0].events=POLLIN; while(!zh->close_requested) { - zh->io_count++; struct timeval tv; int fd; int interest; int timeout; int maxfd=1; + zh->io_count++; + zookeeper_interest(zh, &fd, &interest, &tv); if (fd != -1) { fds[1].fd=fd; @@ -454,8 +457,9 @@ void *do_io(void *v) if(is_unrecoverable(zh)) break; } - api_epilog(zh, 0); - LOG_DEBUG(LOGCALLBACK(zh), "IO thread terminated"); + log_fn = LOGCALLBACK(zh); + api_epilog(zh, 0); + LOG_DEBUG(log_fn, "IO thread terminated"); return 0; } @@ -466,6 +470,7 @@ void *do_completion(void *v) #endif { zhandle_t *zh = v; + log_callback_fn fn; api_prolog(zh); notify_thread_ready(zh); LOG_DEBUG(LOGCALLBACK(zh), "started completion thread"); @@ -477,8 +482,9 @@ void *do_completion(void *v) pthread_mutex_unlock(&zh->completions_to_process.lock); process_completions(zh); } - api_epilog(zh, 0); - LOG_DEBUG(LOGCALLBACK(zh), "completion thread terminated"); + fn = LOGCALLBACK(zh); + api_epilog(zh, 0); + LOG_DEBUG(fn, "completion thread terminated"); return 0; } @@ -530,6 +536,25 @@ int unlock_reconfig(struct _zhandle *zh) } } +int lock_watchers(struct _zhandle *zh) +{ + struct adaptor_threads *adaptor = zh->adaptor_priv; + if (adaptor) { + return pthread_mutex_lock(&adaptor->watchers_lock); + } else { + return 0; + } +} +int unlock_watchers(struct _zhandle *zh) +{ + struct adaptor_threads *adaptor = zh->adaptor_priv; + if (adaptor) { + return pthread_mutex_unlock(&adaptor->watchers_lock); + } else { + return 0; + } +} + int enter_critical(zhandle_t* zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; diff --git a/zookeeper-client/zookeeper-client-c/src/recordio.c b/zookeeper-client/zookeeper-client-c/src/recordio.c index 77fae28785f..52036f94e7a 100644 --- a/zookeeper-client/zookeeper-client-c/src/recordio.c +++ b/zookeeper-client/zookeeper-client-c/src/recordio.c @@ -145,7 +145,7 @@ int oa_serialize_buffer(struct oarchive *oa, const char *name, return rc; // this means a buffer of NUll // with size of -1. This is - // waht we use in java serialization for NULL + // what we use in java serialization for NULL if (b->len == -1) { return rc; } diff --git a/zookeeper-client/zookeeper-client-c/src/st_adaptor.c b/zookeeper-client/zookeeper-client-c/src/st_adaptor.c index ddc2582db34..07540b76ac1 100644 --- a/zookeeper-client/zookeeper-client-c/src/st_adaptor.c +++ b/zookeeper-client/zookeeper-client-c/src/st_adaptor.c @@ -94,6 +94,16 @@ int unlock_reconfig(struct _zhandle *zh) return 0; } +int lock_watchers(struct _zhandle *zh) +{ + return 0; +} + +int unlock_watchers(struct _zhandle *zh) +{ + return 0; +} + int enter_critical(zhandle_t* zh) { return 0; diff --git a/zookeeper-client/zookeeper-client-c/src/winport.c b/zookeeper-client/zookeeper-client-c/src/winport.c index d40614c9131..8d8698ee6c5 100644 --- a/zookeeper-client/zookeeper-client-c/src/winport.c +++ b/zookeeper-client/zookeeper-client-c/src/winport.c @@ -83,7 +83,7 @@ int pthread_detach(pthread_t _thread) } void pthread_mutexattr_init(pthread_mutexattr_t* ignore){} -void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore){} +void pthread_mutexattr_settype(pthread_mutexattr_t* ignore_attr, int ignore){} void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr){} int diff --git a/zookeeper-client/zookeeper-client-c/src/winport.h b/zookeeper-client/zookeeper-client-c/src/winport.h index d216f7fd30d..e8f10ba046b 100644 --- a/zookeeper-client/zookeeper-client-c/src/winport.h +++ b/zookeeper-client/zookeeper-client-c/src/winport.h @@ -80,7 +80,7 @@ int pthread_join(pthread_t _thread, void** ignore); int pthread_detach(pthread_t _thread); void pthread_mutexattr_init(pthread_mutexattr_t* ignore); -void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore); +void pthread_mutexattr_settype(pthread_mutexattr_t* ignore_attr, int ignore); void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr); diff --git a/zookeeper-client/zookeeper-client-c/src/zk_adaptor.h b/zookeeper-client/zookeeper-client-c/src/zk_adaptor.h index 57696d4b203..77d70f3bb6d 100644 --- a/zookeeper-client/zookeeper-client-c/src/zk_adaptor.h +++ b/zookeeper-client/zookeeper-client-c/src/zk_adaptor.h @@ -167,6 +167,7 @@ struct adaptor_threads { pthread_mutex_t lock; // ... and a lock pthread_mutex_t zh_lock; // critical section lock pthread_mutex_t reconfig_lock; // lock for reconfiguring cluster's ensemble + pthread_mutex_t watchers_lock; // lock for watcher operations #ifdef WIN32 SOCKET self_pipe[2]; #else @@ -200,6 +201,9 @@ struct _zhandle { addrvec_t addrs_old; // old list of addresses that we are no longer connected to addrvec_t addrs_new; // new list of addresses to connect to if we're reconfiguring + struct timeval last_resolve; // time of last hostname resolution + int resolve_delay_ms; // see zoo_set_servers_resolution_delay + int reconfig; // Are we in the process of reconfiguring cluster's ensemble double pOld, pNew; // Probability for selecting between 'addrs_old' and 'addrs_new' int delay; @@ -297,6 +301,10 @@ int zoo_unlock_auth(zhandle_t *zh); int lock_reconfig(struct _zhandle *zh); int unlock_reconfig(struct _zhandle *zh); +// watchers hashtable lock +int lock_watchers(struct _zhandle *zh); +int unlock_watchers(struct _zhandle *zh); + // critical section guards int enter_critical(zhandle_t* zh); int leave_critical(zhandle_t* zh); diff --git a/zookeeper-client/zookeeper-client-c/src/zk_log.c b/zookeeper-client/zookeeper-client-c/src/zk_log.c index 436485e97c4..79ebd61f18e 100644 --- a/zookeeper-client/zookeeper-client-c/src/zk_log.c +++ b/zookeeper-client/zookeeper-client-c/src/zk_log.c @@ -110,7 +110,7 @@ static const char* time_now(char* now_str){ now = tv.tv_sec; localtime_r(&now, <); - // clone the format used by log4j ISO8601DateFormat + // clone the format used by logback ISO8601DateFormat // specifically: "yyyy-MM-dd HH:mm:ss,SSS" len = strftime(now_str, TIME_NOW_BUF_SIZE, diff --git a/zookeeper-client/zookeeper-client-c/src/zk_sasl.c b/zookeeper-client/zookeeper-client-c/src/zk_sasl.c index e0ccfb31004..c741a88943a 100644 --- a/zookeeper-client/zookeeper-client-c/src/zk_sasl.c +++ b/zookeeper-client/zookeeper-client-c/src/zk_sasl.c @@ -47,6 +47,17 @@ #include "zk_adaptor.h" #include "zookeeper_log.h" +/* + * We need to disable the deprecation warnings as Apple has decided to + * deprecate all of CyrusSASL's functions with OS 10.11 (see + * MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also for + * covering clang. + */ +#ifdef __APPLE__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* * Store a duplicate of src, or NULL, into *target. Returns * ZSYSTEMERROR if no memory could be allocated, ZOK otherwise. @@ -93,6 +104,7 @@ zoo_sasl_client_t *zoo_sasl_client_create(zoo_sasl_params_t *sasl_params) if (rc != ZOK) { zoo_sasl_client_destroy(sc); + free(sc); return NULL; } @@ -431,6 +443,8 @@ getpassphrase(const char *prompt) { struct zsasl_secret_ctx { const char *password_file; + void *context; + zoo_sasl_password_callback_t callback; sasl_secret_t *secret; }; @@ -440,53 +454,111 @@ struct zsasl_secret_ctx { static int _zsasl_getsecret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { + /* Max allowed length of a password */ + const size_t MAX_PASSWORD_LEN = 1023; struct zsasl_secret_ctx *secret_ctx = (struct zsasl_secret_ctx *)context; - char buf[1024]; - char *password; - size_t len; + /* The extra 1 byte is reserved for storing the null terminator. */ + char buf[MAX_PASSWORD_LEN + 1]; + char *password = NULL; + size_t len = 0; + int res = 0; + /* The extra 1 byte is reserved for storing the null terminator. */ + char new_passwd[MAX_PASSWORD_LEN + 1]; + char *p = NULL; sasl_secret_t *x; /* paranoia check */ - if (!conn || !psecret || id != SASL_CB_PASS) + if (!conn || !psecret || id != SASL_CB_PASS) { return SASL_BADPARAM; + } if (secret_ctx->password_file) { - char *p; FILE *fh = fopen(secret_ctx->password_file, "rt"); - if (!fh) + if (!fh) { return SASL_FAIL; + } - if (!fgets(buf, sizeof(buf), fh)) { + /* + * The file's content may be the encrypted password with binary characters, + * thus use fread(). + */ + len = fread(buf, sizeof(buf[0]), MAX_PASSWORD_LEN, fh); + if (ferror(fh)) { fclose(fh); + fh = NULL; + return SASL_FAIL; } fclose(fh); + fh = NULL; + + /* + * Write the null terminator immediately after the last character of the + * content since it would be used as a null-terminated string once it is + * the actual password. + */ + buf[len] = '\0'; + password = buf; + } - p = strrchr(buf, '\n'); - if (p) - *p = '\0'; + if (secret_ctx->callback) { + if (!password) { + /* + * The callback takes effect only when password_file is provided. + */ + return SASL_BADPARAM; + } - password = buf; + res = secret_ctx->callback(password, len, secret_ctx->context, + new_passwd, MAX_PASSWORD_LEN, &len); + if (res != SASL_OK) { + return res; + } + + if (len > MAX_PASSWORD_LEN) { + return SASL_BUFOVER; + } + + /* + * Append the null terminator to the end of the password obtained from + * the callback function. + */ + new_passwd[len] = '\0'; + password = new_passwd; + } else if (secret_ctx->password_file) { + /* + * The file's content is the actual password, which must consist only of + * text characters (i.e., without null terminator). The first line would + * be read as the password once there are multiple lines in the file. + */ + p = strchr(password, '\n'); + if (p) { + *p = '\0'; + } } else { password = getpassphrase("Password: "); - - if (!password) + if (!password) { return SASL_FAIL; + } } + /* + * Any password, regardless of its source, is always null-terminated. + */ len = strlen(password); x = secret_ctx->secret = (sasl_secret_t *)realloc( secret_ctx->secret, sizeof(sasl_secret_t) + len); - if (!x) { memset(password, 0, len); return SASL_NOMEM; } x->len = len; - strcpy((char *) x->data, password); + + /* The extra 1 byte is the null terminator. */ + memcpy(x->data, password, len + 1); memset(password, 0, len); *psecret = x; @@ -495,9 +567,9 @@ static int _zsasl_getsecret(sasl_conn_t *conn, void *context, int id, typedef int (* sasl_callback_fn_t)(void); -sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, - const char *realm, - const char* password_file) +sasl_callback_t *zoo_sasl_make_password_callbacks(const char *user, + const char *realm, + zoo_sasl_password_t *password) { struct zsasl_secret_ctx *secret_ctx; const char *user_ctx = NULL; @@ -510,7 +582,9 @@ sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, rc = rc < 0 ? rc : _zsasl_strdup(&user_ctx, user); rc = rc < 0 ? rc : _zsasl_strdup(&realm_ctx, realm); - rc = rc < 0 ? rc : _zsasl_strdup(&secret_ctx->password_file, password_file); + rc = rc < 0 ? rc : _zsasl_strdup(&secret_ctx->password_file, password->password_file); + secret_ctx->context = password->context; + secret_ctx->callback = password->callback; { sasl_callback_t callbacks[] = { @@ -539,3 +613,15 @@ sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, return xcallbacks; } } + +sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, + const char *realm, + const char* password_file) +{ + zoo_sasl_password_t password = {password_file, NULL, NULL}; + return zoo_sasl_make_password_callbacks(user, realm, &password); +} + +#ifdef __APPLE__ +#pragma GCC diagnostic pop +#endif diff --git a/zookeeper-client/zookeeper-client-c/src/zookeeper.c b/zookeeper-client/zookeeper-client-c/src/zookeeper.c index 0c143e7bbe2..cdd9d74a537 100644 --- a/zookeeper-client/zookeeper-client-c/src/zookeeper.c +++ b/zookeeper-client/zookeeper-client-c/src/zookeeper.c @@ -244,6 +244,8 @@ typedef struct _completion_list { } completion_list_t; const char*err2string(int err); +static inline int calculate_interval(const struct timeval *start, + const struct timeval *end); static int queue_session_event(zhandle_t *zh, int state); static const char* format_endpoint_info(const struct sockaddr_storage* ep); @@ -277,7 +279,7 @@ static void queue_completion_nolock(completion_head_t *list, completion_list_t * int add_to_front); static void queue_completion(completion_head_t *list, completion_list_t *c, int add_to_front); -static int handle_socket_error_msg(zhandle_t *zh, int line, int rc, +static int handle_socket_error_msg(zhandle_t *zh, int line, const char *func, int rc, const char* format,...); static void cleanup_bufs(zhandle_t *zh,int callCompletion,int rc); @@ -330,6 +332,18 @@ static void zookeeper_set_sock_noblock(zhandle_t *, socket_t); static void zookeeper_set_sock_timeout(zhandle_t *, socket_t, int); static socket_t zookeeper_connect(zhandle_t *, struct sockaddr_storage *, socket_t); +/* + * return 1 if zh has a SASL client configured, 0 otherwise. + */ +static int has_sasl_client(zhandle_t* zh) +{ +#ifdef HAVE_CYRUS_SASL_H + return zh->sasl_client != NULL; +#else /* !HAVE_CYRUS_SASL_H */ + return 0; +#endif /* HAVE_CYRUS_SASL_H */ +} + /* * return 1 if zh has a SASL client performing authentication, 0 otherwise. */ @@ -342,6 +356,39 @@ static int is_sasl_auth_in_progress(zhandle_t* zh) #endif /* HAVE_CYRUS_SASL_H */ } +/* + * Extract the type field (ZOO_*_OP) of a serialized RequestHeader. + * + * (This is not the most efficient way of fetching 4 bytes, but it is + * currently only used during SASL negotiation.) + * + * \param buffer the buffer to extract the request type from. Must + * start with a serialized RequestHeader; + * \param len the buffer length. Must be positive. + * \param out_type out parameter; pointer to the location where the + * extracted type is to be stored. Cannot be NULL. + * \return ZOK on success, or < 0 if something went wrong + */ +static int extract_request_type(char *buffer, int len, int32_t *out_type) +{ + struct iarchive *ia; + struct RequestHeader h; + int rc; + + ia = create_buffer_iarchive(buffer, len); + rc = ia ? ZOK : ZSYSTEMERROR; + rc = rc < 0 ? rc : deserialize_RequestHeader(ia, "header", &h); + deallocate_RequestHeader(&h); + if (ia) { + close_buffer_iarchive(&ia); + } + + *out_type = h.type; + + return rc; +} + +#ifndef THREADED /* * abort due to the use of a sync api in a singlethreaded environment */ @@ -350,6 +397,7 @@ static void abort_singlethreaded(zhandle_t *zh) LOG_ERROR(LOGCALLBACK(zh), "Sync completion used without threads"); abort(); } +#endif /* THREADED */ static ssize_t zookeeper_send(zsock_t *fd, const void* buf, size_t len) { @@ -658,6 +706,7 @@ static void destroy(zhandle_t *zh) #ifdef HAVE_CYRUS_SASL_H if (zh->sasl_client) { zoo_sasl_client_destroy(zh->sasl_client); + free(zh->sasl_client); zh->sasl_client = NULL; } #endif /* HAVE_CYRUS_SASL_H */ @@ -681,7 +730,7 @@ static void setup_random() /* Assert we either read something or we were interrupted due to a * signal (errno == EINTR) in which case we need to retry. */ - int rc = read(fd, &seed + seed_len, sizeof(seed) - seed_len); + int rc = read(fd, (char *)&seed + seed_len, sizeof(seed) - seed_len); assert(rc > 0 || errno == EINTR); if (rc > 0) { seed_len += rc; @@ -982,10 +1031,14 @@ static int resolve_hosts(const zhandle_t *zh, const char *hosts_in, addrvec_t *a * * See zoo_cycle_next_server for the selection logic. * + * \param ref_time an optional "reference time," used to determine if + * resolution can be skipped in accordance to the delay set by \ref + * zoo_set_servers_resolution_delay. Passing NULL prevents skipping. + * * See {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1355} for the * protocol and its evaluation, */ -int update_addrs(zhandle_t *zh) +int update_addrs(zhandle_t *zh, const struct timeval *ref_time) { int rc = ZOK; char *hosts = NULL; @@ -1006,26 +1059,54 @@ int update_addrs(zhandle_t *zh) return ZSYSTEMERROR; } - // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new} + // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); + // Check if we are due for a host name resolution. (See + // zoo_set_servers_resolution_delay. The answer is always "yes" + // if no reference is provided or the file descriptor is invalid.) + if (ref_time && zh->fd->sock != -1) { + int do_resolve; + + if (zh->resolve_delay_ms <= 0) { + // -1 disables, 0 means unconditional. Fail safe. + do_resolve = zh->resolve_delay_ms != -1; + } else { + int elapsed_ms = calculate_interval(&zh->last_resolve, ref_time); + // Include < 0 in case of overflow, or if we are not + // backed by a monotonic clock. + do_resolve = elapsed_ms > zh->resolve_delay_ms || elapsed_ms < 0; + } + + if (!do_resolve) { + goto finish; + } + } + // Copy zh->hostname for local use hosts = strdup(zh->hostname); if (hosts == NULL) { rc = ZSYSTEMERROR; - goto fail; + goto finish; } rc = resolve_hosts(zh, hosts, &resolved); if (rc != ZOK) { - goto fail; + goto finish; + } + + // Unconditionally note last resolution time. + if (ref_time) { + zh->last_resolve = *ref_time; + } else { + get_system_time(&zh->last_resolve); } // If the addrvec list is identical to last time we ran don't do anything if (addrvec_eq(&zh->addrs, &resolved)) { - goto fail; + goto finish; } // Is the server we're connected to in the new resolved list? @@ -1045,14 +1126,14 @@ int update_addrs(zhandle_t *zh) rc = addrvec_append(&zh->addrs_old, resolved_address); if (rc != ZOK) { - goto fail; + goto finish; } } else { rc = addrvec_append(&zh->addrs_new, resolved_address); if (rc != ZOK) { - goto fail; + goto finish; } } } @@ -1105,13 +1186,13 @@ int update_addrs(zhandle_t *zh) zh->state = ZOO_NOTCONNECTED_STATE; } -fail: +finish: unlock_reconfig(zh); // If we short-circuited out and never assigned resolved to zh->addrs then we // need to free resolved to avoid a memleak. - if (zh->addrs.data != resolved.data) + if (resolved.data && zh->addrs.data != resolved.data) { addrvec_free(&resolved); } @@ -1214,6 +1295,14 @@ static void log_env(zhandle_t *zh) { #endif } +/** + * Return string of "MAJOR.MINOR.PATCH" + */ +const char *zoo_version_str() +{ + return ZOO_VERSION; +} + /** * Create a zookeeper handle associated with the given host and port. */ @@ -1308,7 +1397,7 @@ static zhandle_t *zookeeper_init_internal(const char *host, watcher_fn watcher, if (zh->hostname == 0) { goto abort; } - if(update_addrs(zh) != 0) { + if(update_addrs(zh, NULL) != 0) { goto abort; } @@ -1373,7 +1462,7 @@ zhandle_t *zookeeper_init_ssl(const char *host, const char *cert, watcher_fn wat { zcert_t zcert; zcert.certstr = strdup(cert); - zcert.ca = strtok(strdup(cert), ","); + zcert.ca = strtok(zcert.certstr, ","); zcert.cert = strtok(NULL, ","); zcert.key = strtok(NULL, ","); zcert.passwd = strtok(NULL, ","); @@ -1402,7 +1491,7 @@ int zoo_set_servers(zhandle_t *zh, const char *hosts) return ZBADARGUMENTS; } - // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new} + // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); // Reset hostname to new set of hosts to connect to @@ -1414,7 +1503,27 @@ int zoo_set_servers(zhandle_t *zh, const char *hosts) unlock_reconfig(zh); - return update_addrs(zh); + return update_addrs(zh, NULL); +} + +/* + * Sets a minimum delay to observe between "routine" host name + * resolutions. See prototype for full documentation. + */ +int zoo_set_servers_resolution_delay(zhandle_t *zh, int delay_ms) { + if (delay_ms < -1) { + LOG_ERROR(LOGCALLBACK(zh), "Resolution delay cannot be %d", delay_ms); + return ZBADARGUMENTS; + } + + // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} + lock_reconfig(zh); + + zh->resolve_delay_ms = delay_ms; + + unlock_reconfig(zh); + + return ZOK; } /** @@ -1470,7 +1579,7 @@ static int get_next_server_in_reconfig(zhandle_t *zh) /** * Cycle through our server list to the correct 'next' server. The 'next' server * to connect to depends upon whether we're in a 'reconfig' mode or not. Reconfig - * mode means we've upated the server list and are now trying to find a server + * mode means we've updated the server list and are now trying to find a server * to connect to. Once we get connected, we are no longer in the reconfig mode. * Similarly, if we try to connect to all the servers in the new configuration * and failed, reconfig mode is set to false. @@ -1479,7 +1588,7 @@ static int get_next_server_in_reconfig(zhandle_t *zh) */ void zoo_cycle_next_server(zhandle_t *zh) { - // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new} + // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); memset(&zh->addr_cur, 0, sizeof(zh->addr_cur)); @@ -1511,7 +1620,7 @@ const char* zoo_get_current_server(zhandle_t* zh) { const char *endpoint_info = NULL; - // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new} + // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} // Need the lock here as it is changed in update_addrs() lock_reconfig(zh); @@ -1521,7 +1630,7 @@ const char* zoo_get_current_server(zhandle_t* zh) } /** - * deallocated the free_path only its beeen allocated + * deallocated the free_path only its been allocated * and not equal to path */ void free_duplicate_path(const char *free_path, const char* path) { @@ -1765,7 +1874,24 @@ static int recv_buffer(zhandle_t *zh, buffer_list_t *buff) /* dirty hack to make new client work against old server * old server sends 40 bytes to finish connection handshake, * while we're expecting 41 (1 byte for read-only mode data) */ - if (buff == &zh->primer_buffer && rc == buff->len - 1) ++rc; + if (rc > 0 && buff == &zh->primer_buffer) { + /* primer_buffer's curr_offset starts at 4 (see prime_connection) */ + int avail = buff->curr_offset - sizeof(buff->len) + rc; + + /* exactly 40 bytes (out of 41 expected) collected? */ + if (avail == buff->len - 1) { + int32_t reply_len; + + /* extract length of ConnectResponse (+ 1-byte flag?) */ + memcpy(&reply_len, buff->buffer, sizeof(reply_len)); + reply_len = ntohl(reply_len); + + /* if 1-byte flag was not sent, fake it (value 0) */ + if ((int)(reply_len + sizeof(reply_len)) == buff->len - 1) { + ++rc; + } + } + } switch(rc) { case 0: @@ -1838,23 +1964,23 @@ void free_completions(zhandle_t *zh,int callCompletion,int reason) } } } - if (zoo_lock_auth(zh) == 0) { - a_list.completion = NULL; - a_list.next = NULL; - get_auth_completions(&zh->auth_h, &a_list); - zoo_unlock_auth(zh); + zoo_lock_auth(zh); + a_list.completion = NULL; + a_list.next = NULL; + get_auth_completions(&zh->auth_h, &a_list); + zoo_unlock_auth(zh); - a_tmp = &a_list; - // chain call user's completion function - while (a_tmp->completion != NULL) { - auth_completion = a_tmp->completion; - auth_completion(reason, a_tmp->auth_data); - a_tmp = a_tmp->next; - if (a_tmp == NULL) - break; - } + a_tmp = &a_list; + // chain call user's completion function + while (a_tmp->completion != NULL) { + auth_completion = a_tmp->completion; + auth_completion(reason, a_tmp->auth_data); + a_tmp = a_tmp->next; + if (a_tmp == NULL) + break; } + free_auth_completion(&a_list); } @@ -1911,7 +2037,7 @@ static void handle_error(zhandle_t *zh,int rc) addrvec_next(&zh->addrs, &zh->addr_cur); } -static int handle_socket_error_msg(zhandle_t *zh, int line, int rc, +static int handle_socket_error_msg(zhandle_t *zh, int line, const char *func, int rc, const char* format, ...) { if(logLevel>=ZOO_LOG_LEVEL_ERROR){ @@ -1919,7 +2045,7 @@ static int handle_socket_error_msg(zhandle_t *zh, int line, int rc, char buf[1024]; va_start(va,format); vsnprintf(buf, sizeof(buf)-1,format,va); - log_message(LOGCALLBACK(zh), ZOO_LOG_LEVEL_ERROR,line,__func__, + log_message(LOGCALLBACK(zh), ZOO_LOG_LEVEL_ERROR, line, func, "Socket %s zk retcode=%d, errno=%d(%s): %s", zoo_get_current_server(zh),rc,errno,strerror(errno),buf); va_end(va); @@ -2043,9 +2169,11 @@ static int send_set_watches(zhandle_t *zh) int rc; req.relativeZxid = zh->last_zxid; + lock_watchers(zh); req.dataWatches.data = collect_keys(zh->active_node_watchers, (int*)&req.dataWatches.count); req.existWatches.data = collect_keys(zh->active_exist_watchers, (int*)&req.existWatches.count); req.childWatches.data = collect_keys(zh->active_child_watchers, (int*)&req.childWatches.count); + unlock_watchers(zh); // return if there are no pending watches if (!req.dataWatches.count && !req.existWatches.count && @@ -2163,7 +2291,7 @@ static int prime_connection(zhandle_t *zh) serialize_prime_connect(&req, buffer_req); rc=rc<0 ? rc : zookeeper_send(zh->fd, buffer_req, len); if (rc<0) { - return handle_socket_error_msg(zh, __LINE__, ZCONNECTIONLOSS, + return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "failed to send a handshake packet: %s", strerror(errno)); } zh->state = ZOO_ASSOCIATING_STATE; @@ -2413,7 +2541,7 @@ int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, } api_prolog(zh); - rc = update_addrs(zh); + rc = update_addrs(zh, &now); if (rc != ZOK) { return api_epilog(zh, rc); } @@ -2438,8 +2566,9 @@ int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, *tv = get_timeval(zh->recv_timeout/60); zh->delay = 0; - LOG_WARN(LOGCALLBACK(zh), "Delaying connection after exhaustively trying all servers [%s]", - zh->hostname); + lock_reconfig(zh); + LOG_WARN(LOGCALLBACK(zh), "Delaying connection after exhaustively trying all servers [%s]", zh->hostname); + unlock_reconfig(zh); } else { if (addr_rw_server) { zh->addr_cur = *addr_rw_server; @@ -2452,6 +2581,7 @@ int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, if (zh->fd->sock < 0) { rc = handle_socket_error_msg(zh, __LINE__, + __func__, ZSYSTEMERROR, "socket() call failed"); return api_epilog(zh, rc); @@ -2475,6 +2605,7 @@ int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, } else { rc = handle_socket_error_msg(zh, __LINE__, + __func__, ZCONNECTIONLOSS, "connect() call failed"); return api_epilog(zh, rc); @@ -2523,7 +2654,7 @@ int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, *interest=0; *tv = get_timeval(0); return api_epilog(zh,handle_socket_error_msg(zh, - __LINE__,ZOPERATIONTIMEOUT, + __LINE__, __func__, ZOPERATIONTIMEOUT, "connection to %s timed out (exceeded timeout by %dms)", format_endpoint_info(&zh->addr_cur), -recv_to)); @@ -2629,11 +2760,6 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); method = TLS_client_method(); #endif - if (FIPS_mode() == 0) { - LOG_INFO(LOGCALLBACK(zh), "FIPS mode is OFF "); - } else { - LOG_INFO(LOGCALLBACK(zh), "FIPS mode is ON "); - } fd->ssl_ctx = SSL_CTX_new(method); ctx = &fd->ssl_ctx; @@ -2651,27 +2777,30 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { errno = EINVAL; return ZBADARGUMENTS; } - /*CLIENT CA FILE (With Certificate Chain)*/ - if (SSL_CTX_use_certificate_chain_file(*ctx, fd->cert->cert) != 1) { - SSL_CTX_free(*ctx); - LOG_ERROR(LOGCALLBACK(zh), "Failed to load client certificate chain from %s", fd->cert->cert); - errno = EINVAL; - return ZBADARGUMENTS; - } - /*CLIENT PRIVATE KEY*/ - SSL_CTX_set_default_passwd_cb_userdata(*ctx, fd->cert->passwd); - if (SSL_CTX_use_PrivateKey_file(*ctx, fd->cert->key, SSL_FILETYPE_PEM) != 1) { - SSL_CTX_free(*ctx); - LOG_ERROR(LOGCALLBACK(zh), "Failed to load client private key from %s", fd->cert->key); - errno = EINVAL; - return ZBADARGUMENTS; - } - /*CHECK*/ - if (SSL_CTX_check_private_key(*ctx) != 1) { - SSL_CTX_free(*ctx); - LOG_ERROR(LOGCALLBACK(zh), "SSL_CTX_check_private_key failed"); - errno = EINVAL; - return ZBADARGUMENTS; + if (fd->cert->cert != NULL && fd->cert->passwd != NULL && fd->cert->key != NULL) + { + /*CLIENT CA FILE (With Certificate Chain)*/ + if (SSL_CTX_use_certificate_chain_file(*ctx, fd->cert->cert) != 1) { + SSL_CTX_free(*ctx); + LOG_ERROR(LOGCALLBACK(zh), "Failed to load client certificate chain from %s", fd->cert->cert); + errno = EINVAL; + return ZBADARGUMENTS; + } + /*CLIENT PRIVATE KEY*/ + SSL_CTX_set_default_passwd_cb_userdata(*ctx, fd->cert->passwd); + if (SSL_CTX_use_PrivateKey_file(*ctx, fd->cert->key, SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(*ctx); + LOG_ERROR(LOGCALLBACK(zh), "Failed to load client private key from %s", fd->cert->key); + errno = EINVAL; + return ZBADARGUMENTS; + } + /*CHECK*/ + if (SSL_CTX_check_private_key(*ctx) != 1) { + SSL_CTX_free(*ctx); + LOG_ERROR(LOGCALLBACK(zh), "SSL_CTX_check_private_key failed"); + errno = EINVAL; + return ZBADARGUMENTS; + } } /*MULTIPLE HANDSHAKE*/ SSL_CTX_set_mode(*ctx, SSL_MODE_AUTO_RETRY); @@ -2679,7 +2808,7 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { fd->ssl_sock = SSL_new(*ctx); if (fd->ssl_sock == NULL) { if (fail_on_error) { - return handle_socket_error_msg(zh,__LINE__,ZSSLCONNECTIONERROR, "error creating ssl context"); + return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error creating ssl context"); } else { LOG_ERROR(LOGCALLBACK(zh), "error creating ssl context"); return ZSSLCONNECTIONERROR; @@ -2710,7 +2839,7 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { FD_CLR(sock, &s_rfds); } else { if (fail_on_error) { - return handle_socket_error_msg(zh,__LINE__,ZSSLCONNECTIONERROR, "error in ssl connect"); + return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error in ssl connect"); } else { LOG_ERROR(LOGCALLBACK(zh), "error in ssl connect"); return ZSSLCONNECTIONERROR; @@ -2719,7 +2848,7 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { rc = select(sock + 1, &s_rfds, &s_wfds, NULL, &tv); if (rc == -1) { if (fail_on_error) { - return handle_socket_error_msg(zh,__LINE__,ZSSLCONNECTIONERROR, "error in ssl connect (after select)"); + return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error in ssl connect (after select)"); } else { LOG_ERROR(LOGCALLBACK(zh), "error in ssl connect (after select)"); return ZSSLCONNECTIONERROR; @@ -2754,6 +2883,11 @@ static void finalize_session_establishment(zhandle_t *zh) { LOG_DEBUG(LOGCALLBACK(zh), "Calling a watcher for a ZOO_SESSION_EVENT and the state=ZOO_CONNECTED_STATE"); zh->input_buffer = 0; // just in case the watcher calls zookeeper_process() again PROCESS_SESSION_EVENT(zh, zh->state); + + if (has_sasl_client(zh)) { + /* some packets might have been delayed during SASL negotiation. */ + adaptor_send_queue(zh, 0); + } } #ifdef HAVE_CYRUS_SASL_H @@ -2800,8 +2934,10 @@ static int process_sasl_response(zhandle_t *zh, char *buffer, int len) struct SetSASLResponse res; int rc; + memset(&res, 0, sizeof(res)); rc = ia ? ZOK : ZSYSTEMERROR; rc = rc < 0 ? rc : deserialize_ReplyHeader(ia, "hdr", &hdr); + rc = rc < 0 ? rc : hdr.err; rc = rc < 0 ? rc : deserialize_SetSASLResponse(ia, "reply", &res); rc = rc < 0 ? rc : zoo_sasl_client_step(zh, res.token.buff, res.token.len); deallocate_SetSASLResponse(&res); @@ -2833,7 +2969,7 @@ static int check_events(zhandle_t *zh, int events) if (rc < 0 || error) { if (rc == 0) errno = error; - return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS, + return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "server refused to accept the client"); } // We do SSL_connect() here @@ -2853,7 +2989,7 @@ static int check_events(zhandle_t *zh, int events) if (rc < 0 || error) { if (rc == 0) errno = error; - return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS, + return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "server refused to accept the client"); } @@ -2868,7 +3004,7 @@ static int check_events(zhandle_t *zh, int events) /* make the flush call non-blocking by specifying a 0 timeout */ int rc=flush_send_queue(zh,0); if (rc < 0) - return handle_socket_error_msg(zh,__LINE__,ZCONNECTIONLOSS, + return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "failed while flushing send queue"); } if (events&ZOOKEEPER_READ) { @@ -2879,7 +3015,7 @@ static int check_events(zhandle_t *zh, int events) rc = recv_buffer(zh, zh->input_buffer); if (rc < 0) { - return handle_socket_error_msg(zh, __LINE__,ZCONNECTIONLOSS, + return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "failed while receiving a server response"); } if (rc > 0) { @@ -2891,6 +3027,7 @@ static int check_events(zhandle_t *zh, int events) } else { rc = process_sasl_response(zh, zh->input_buffer->buffer, zh->input_buffer->curr_offset); free_buffer(zh->input_buffer); + zh->input_buffer = 0; if (rc < 0) { zoo_sasl_mark_failed(zh); return rc; @@ -2915,7 +3052,7 @@ static int check_events(zhandle_t *zh, int events) if (oldid != 0 && oldid != newid) { zh->state = ZOO_EXPIRED_SESSION_STATE; errno = ESTALE; - return handle_socket_error_msg(zh,__LINE__,ZSESSIONEXPIRED, + return handle_socket_error_msg(zh, __LINE__, __func__, ZSESSIONEXPIRED, "sessionId=%#llx has expired.",oldid); } else { zh->recv_timeout = zh->primer_storage.timeOut; @@ -2999,7 +3136,9 @@ static int queue_session_event(zhandle_t *zh, int state) } /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); + lock_watchers(zh); cptr->c.watcher_result = collectWatchers(zh, ZOO_SESSION_EVENT, ""); + unlock_watchers(zh); queue_completion(&zh->completions_to_process, cptr, 0); if (process_async(zh->outstanding_sync)) { process_completions(zh); @@ -3011,10 +3150,9 @@ static int queue_session_event(zhandle_t *zh, int state) } //#endif -completion_list_t *dequeue_completion(completion_head_t *list) -{ +completion_list_t *dequeue_completion_nolock(completion_head_t *list) { + completion_list_t *cptr; - lock_completion_list(list); cptr = list->head; if (cptr) { list->head = cptr->next; @@ -3023,6 +3161,14 @@ completion_list_t *dequeue_completion(completion_head_t *list) list->last = 0; } } + return cptr; +} + +completion_list_t *dequeue_completion(completion_head_t *list) +{ + completion_list_t *cptr; + lock_completion_list(list); + cptr = dequeue_completion_nolock(list); unlock_completion_list(list); return cptr; } @@ -3031,7 +3177,7 @@ completion_list_t *dequeue_completion(completion_head_t *list) static void cleanup_failed_multi(zhandle_t *zh, int xid, int rc, completion_list_t *cptr) { completion_list_t *entry; completion_head_t *clist = &cptr->c.clist; - while ((entry = dequeue_completion(clist)) != NULL) { + while ((entry = dequeue_completion_nolock(clist)) != NULL) { // Fake failed response for all sub-requests deserialize_response(zh, entry->c.type, xid, 1, rc, entry, NULL); destroy_completion_entry(entry); @@ -3046,7 +3192,7 @@ static int deserialize_multi(zhandle_t *zh, int xid, completion_list_t *cptr, st assert(clist); deserialize_MultiHeader(ia, "multiheader", &mhdr); while (!mhdr.done) { - completion_list_t *entry = dequeue_completion(clist); + completion_list_t *entry = dequeue_completion_nolock(clist); assert(entry); if (mhdr.type == -1) { @@ -3305,7 +3451,9 @@ int zookeeper_process(zhandle_t *zh, int events) /* We are doing a notification, so there is no pending request */ c = create_completion_entry(zh, WATCHER_EVENT_XID,-1,0,0,0,0); c->buffer = bptr; + lock_watchers(zh); c->c.watcher_result = collectWatchers(zh, type, path); + unlock_watchers(zh); // We cannot free until now, otherwise path will become invalid deallocate_WatcherEvent(&evt); @@ -3351,7 +3499,7 @@ int zookeeper_process(zhandle_t *zh, int events) // signaled and deallocated) and disconnect from the server queue_completion(&zh->sent_requests,cptr,1); return api_epilog(zh, - handle_socket_error_msg(zh, __LINE__,ZRUNTIMEINCONSISTENCY, + handle_socket_error_msg(zh, __LINE__, __func__, ZRUNTIMEINCONSISTENCY, "unexpected server response: expected %#x, but received %#x", hdr.xid,cptr->xid)); } @@ -3360,8 +3508,10 @@ int zookeeper_process(zhandle_t *zh, int events) // Update last_zxid only when it is a request response zh->last_zxid = hdr.zxid; } + lock_watchers(zh); activateWatcher(zh, cptr->watcher, rc); deactivateWatcher(zh, cptr->watcher_deregistration, rc); + unlock_watchers(zh); if (cptr->c.void_result != SYNCHRONOUS_MARKER) { LOG_DEBUG(LOGCALLBACK(zh), "Queueing asynchronous response"); @@ -3492,6 +3642,7 @@ static completion_list_t* do_create_completion_entry(zhandle_t *zh, int xid, break; case COMPLETION_STRING_STAT: c->c.string_stat_result = (string_stat_completion_t)dc; + break; case COMPLETION_ACLLIST: c->c.acl_result = (acl_completion_t)dc; break; @@ -3699,7 +3850,7 @@ int zookeeper_close(zhandle_t *zh) * completions from calling zookeeper_close before we have * completed the adaptor_finish call below. */ - /* Signal any syncronous completions before joining the threads */ + /* Signal any synchronous completions before joining the threads */ enter_critical(zh); free_completions(zh,1,ZCLOSING); leave_critical(zh); @@ -3830,6 +3981,19 @@ static int Request_path_watch_init(zhandle_t *zh, int mode, /*---------------------------------------------------------------------------* * ASYNC API *---------------------------------------------------------------------------*/ + +/* make an attempt to send queued requests immediately without blocking */ +static int nonblocking_send(zhandle_t *zh, int rc) +{ + if (adaptor_send_queue(zh, 0) < 0) { + if (zh->fd->sock != -1) { + close_zsock(zh->fd); + zh->state = ZOO_NOTCONNECTED_STATE; + } + } + return (rc < 0) ? ZMARSHALLINGERROR : ZOK; +} + int zoo_aget(zhandle_t *zh, const char *path, int watch, data_completion_t dc, const void *data) { @@ -3869,9 +4033,8 @@ int zoo_awget(zhandle_t *zh, const char *path, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_agetconfig(zhandle_t *zh, int watch, data_completion_t dc, @@ -3913,9 +4076,8 @@ int zoo_awgetconfig(zhandle_t *zh, watcher_fn watcher, void* watcherCtx, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_areconfig(zhandle_t *zh, const char *joining, const char *leaving, @@ -3949,10 +4111,8 @@ int zoo_areconfig(zhandle_t *zh, const char *joining, const char *leaving, close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending Reconfig request xid=%#x to %s",h.xid, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + return nonblocking_send(zh, rc); } static int SetDataRequest_init(zhandle_t *zh, struct SetDataRequest *req, @@ -3995,9 +4155,8 @@ int zoo_aset(zhandle_t *zh, const char *path, const char *buffer, int buflen, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } static int CreateRequest_init(zhandle_t *zh, struct CreateRequest *req, @@ -4122,9 +4281,8 @@ int zoo_acreate_ttl(zhandle_t *zh, const char *path, const char *value, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_acreate2(zhandle_t *zh, const char *path, const char *value, @@ -4189,9 +4347,8 @@ int zoo_acreate2_ttl(zhandle_t *zh, const char *path, const char *value, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int DeleteRequest_init(zhandle_t *zh, struct DeleteRequest *req, @@ -4229,9 +4386,8 @@ int zoo_adelete(zhandle_t *zh, const char *path, int version, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_aexists(zhandle_t *zh, const char *path, int watch, @@ -4268,9 +4424,8 @@ int zoo_awexists(zhandle_t *zh, const char *path, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } static int zoo_awget_children_(zhandle_t *zh, const char *path, @@ -4301,9 +4456,8 @@ static int zoo_awget_children_(zhandle_t *zh, const char *path, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_aget_children(zhandle_t *zh, const char *path, int watch, @@ -4349,9 +4503,8 @@ static int zoo_awget_children2_(zhandle_t *zh, const char *path, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_aget_children2(zhandle_t *zh, const char *path, int watch, @@ -4392,9 +4545,8 @@ int zoo_async(zhandle_t *zh, const char *path, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } @@ -4422,9 +4574,8 @@ int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } int zoo_aset_acl(zhandle_t *zh, const char *path, int version, @@ -4453,9 +4604,8 @@ int zoo_aset_acl(zhandle_t *zh, const char *path, int version, LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0)?ZMARSHALLINGERROR:ZOK; + + return nonblocking_send(zh, rc); } /* Completions for multi-op results */ @@ -4597,7 +4747,7 @@ int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops, return ZUNIMPLEMENTED; } - queue_completion(&clist, entry, 0); + queue_completion_nolock(&clist, entry, 0); } rc = rc < 0 ? rc : serialize_MultiHeader(oa, "multiheader", &mh); @@ -4614,10 +4764,8 @@ int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops, LOG_DEBUG(LOGCALLBACK(zh), "Sending multi request xid=%#x with %d subrequests to %s", h.xid, index, zoo_get_current_server(zh)); - /* make a best (non-blocking) effort to send the requests asap */ - adaptor_send_queue(zh, 0); - return (rc < 0) ? ZMARSHALLINGERROR : ZOK; + return nonblocking_send(zh, rc); } typedef union WatchesRequest WatchesRequest; @@ -4652,19 +4800,23 @@ static int aremove_watches( goto done; } + lock_watchers(zh); if (!pathHasWatcher(zh, server_path, wtype, watcher, watcherCtx)) { rc = ZNOWATCHER; + unlock_watchers(zh); goto done; } if (local) { removeWatchers(zh, server_path, wtype, watcher, watcherCtx); + unlock_watchers(zh); #ifdef THREADED notify_sync_completion((struct sync_completion *)data); #endif rc = ZOK; goto done; } + unlock_watchers(zh); oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); @@ -4696,7 +4848,6 @@ static int aremove_watches( zh, h.xid, COMPLETION_VOID, completion, data, 0, wdo, 0); rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); - rc = rc < 0 ? ZMARSHALLINGERROR : ZOK; leave_critical(zh); /* We queued the buffer, so don't free it */ @@ -4705,7 +4856,7 @@ static int aremove_watches( LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s", h.xid, path, zoo_get_current_server(zh)); - adaptor_send_queue(zh, 0); + rc = nonblocking_send(zh, rc); done: free_duplicate_path(server_path, path); @@ -4787,6 +4938,20 @@ int flush_send_queue(zhandle_t*zh, int timeout) // successful lock_buffer_list(&zh->to_send); while (zh->to_send.head != 0 && (is_connected(zh) || is_sasl_auth_in_progress(zh))) { + if (is_sasl_auth_in_progress(zh)) { + // We don't let non-SASL packets escape as long as + // negotiation is not complete. (SASL packets are always + // pushed to the front of the queue.) + buffer_list_t *buff = zh->to_send.head; + int32_t type; + + rc = extract_request_type(buff->buffer, buff->len, &type); + + if (rc < 0 || type != ZOO_SASL_OP) { + break; + } + } + if(timeout!=0){ #ifndef _WIN32 struct pollfd fds; @@ -4863,7 +5028,7 @@ const char* zerror(int c) case ZINVALIDSTATE: return "invalid zhandle state"; case ZNEWCONFIGNOQUORUM: - return "no quorum of new config is connected and up-to-date with the leader of last commmitted config - try invoking reconfiguration after new servers are connected and synced"; + return "no quorum of new config is connected and up-to-date with the leader of last committed config - try invoking reconfiguration after new servers are connected and synced"; case ZRECONFIGINPROGRESS: return "Another reconfiguration is in progress -- concurrent reconfigs not supported (yet)"; case ZAPIERROR: @@ -4902,6 +5067,10 @@ const char* zerror(int c) return "the watcher couldn't be found"; case ZRECONFIGDISABLED: return "attempts to perform a reconfiguration operation when reconfiguration feature is disable"; + case ZSESSIONCLOSEDREQUIRESASLAUTH: + return "session closed by server because client is required to do SASL authentication"; + case ZTHROTTLEDOP: + return "Operation was throttled due to high load"; } if (c > 0) { return strerror(c); @@ -4948,19 +5117,29 @@ int zoo_add_auth(zhandle_t *zh,const char* scheme,const char* cert, add_last_auth(&zh->auth_h, authinfo); zoo_unlock_auth(zh); - if (is_connected(zh) || zh->state == ZOO_ASSOCIATING_STATE) + if (is_connected(zh) || + // When associating, only send info packets if no SASL + // negotiation is planned. (Such packets would be queued in + // front of SASL packets, which is forbidden, and SASL + // completion is followed by a 'send_auth_info' anyway.) + (zh->state == ZOO_ASSOCIATING_STATE && !has_sasl_client(zh))) { return send_last_auth_info(zh); + } return ZOK; } static const char* format_endpoint_info(const struct sockaddr_storage* ep) { - static char buf[134] = { 0 }; +#if defined(_MSC_VER) + static __declspec(thread) char buf[134] = { 0 }; +#else + static __thread char buf[134] = { 0 }; +#endif char addrstr[INET6_ADDRSTRLEN] = { 0 }; const char *fmtstring; void *inaddr; - char is_inet6 = 0; // poor man's boolean + char is_inet6 = 0; // poor man's boolean #ifdef _WIN32 char * addrstring; #endif diff --git a/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh b/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh index f32cf5895bd..cbe8199ea59 100755 --- a/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh +++ b/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh @@ -1,19 +1,22 @@ -#!/usr/bin/env bash - -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at +#! /usr/bin/env bash # -# http://www.apache.org/licenses/LICENSE-2.0 +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # This script cleans up old transaction logs and snapshots @@ -25,12 +28,11 @@ # relative to the canonical path of this script. # - # determining the domain name in the certificates: # - use the first commandline argument, if present # - if not, then use the fully qualified domain name # - if `hostname` command fails, fall back to zookeeper.apache.org -FQDN=`hostname -f` +FQDN=$(hostname -f) FQDN=${1:-$FQDN} FQDN=${FQDN:-"zookeeper.apache.org"} @@ -39,7 +41,7 @@ openssl genrsa -out rootkey.pem 2048 #Generate the root Cert openssl req -x509 -new -key rootkey.pem -out root.crt -config <( -cat <<-EOF + cat <<-EOF [ req ] default_bits = 2048 prompt = no @@ -61,7 +63,7 @@ openssl genrsa -out clientkey.pem 2048 #Generate Client Cert openssl req -new -key clientkey.pem -out client.csr -config <( -cat <<-EOF + cat <<-EOF [ req ] default_bits = 2048 prompt = no @@ -92,7 +94,7 @@ openssl genrsa -out serverkey.pem 2048 #Generate Server Cert openssl req -new -key serverkey.pem -out server.csr -config <( -cat <<-EOF + cat <<-EOF [ req ] default_bits = 2048 prompt = no @@ -116,7 +118,6 @@ openssl pkcs12 -export -in server.crt -inkey serverkey.pem -out server.pkcs12 -p # Import Keystore in JKS keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server.jks -srcstoretype pkcs12 -srcstorepass password -deststorepass password - keytool -importcert -keystore server.jks -file root.crt -storepass password -noprompt keytool -importcert -alias ca -file root.crt -keystore clienttrust.jks -storepass password -noprompt diff --git a/zookeeper-client/zookeeper-client-c/tests/LibCMocks.cc b/zookeeper-client/zookeeper-client-c/tests/LibCMocks.cc index 870a554ca3b..8a46ee54dcc 100644 --- a/zookeeper-client/zookeeper-client-c/tests/LibCMocks.cc +++ b/zookeeper-client/zookeeper-client-c/tests/LibCMocks.cc @@ -259,7 +259,12 @@ int setsockopt(int s,int level,int optname,const void *optval,socklen_t optlen){ return Mock_socket::mock_->callSet(s,level,optname,optval,optlen); } int connect(int s,const struct sockaddr *addr,socklen_t len){ +#ifdef AF_UNIX + /* don't mock UNIX domain sockets */ + if (!Mock_socket::mock_ || addr->sa_family == AF_UNIX) +#else if (!Mock_socket::mock_) +#endif return LIBC_SYMBOLS.connect(s,addr,len); return Mock_socket::mock_->callConnect(s,addr,len); } diff --git a/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.h b/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.h index 1b6f9db996d..08028abca8c 100644 --- a/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.h +++ b/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.h @@ -26,6 +26,7 @@ #include #include #include +#include #include // needed for _POSIX_MONOTONIC_CLOCK #ifdef THREADED diff --git a/zookeeper-client/zookeeper-client-c/tests/TestClient.cc b/zookeeper-client/zookeeper-client-c/tests/TestClient.cc index a7d055fbe47..83a7203d7b8 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestClient.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestClient.cc @@ -212,6 +212,10 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture #endif #ifdef HAVE_OPENSSL_H CPPUNIT_TEST(testSSL); + CPPUNIT_TEST(testSSLNoClientCert); + + // Order after above two tests as it changes ssl.clientAuth + CPPUNIT_TEST(testSSLServerOnly); #endif CPPUNIT_TEST(testCreate); CPPUNIT_TEST(testCreateContainer); @@ -227,6 +231,7 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testWatcherAutoResetWithLocal); CPPUNIT_TEST(testGetChildren2); CPPUNIT_TEST(testLastZxid); + CPPUNIT_TEST(testServersResolutionDelay); CPPUNIT_TEST(testRemoveWatchers); #endif CPPUNIT_TEST_SUITE_END(); @@ -313,6 +318,12 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT(system(cmd) == 0); } + void startServer(const char *envs) { + char cmd[1024]; + sprintf(cmd, "%s %s start %s", envs, ZKSERVER_CMD, getHostPorts()); + CPPUNIT_ASSERT(system(cmd) == 0); + } + void stopServer() { char cmd[1024]; sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); @@ -325,9 +336,10 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture /** have a callback in the default watcher **/ static void default_zoo_watcher(zhandle_t *zzh, int type, int state, const char *path, void *context){ - int zrc = 0; + int zrc; struct String_vector str_vec = {0, NULL}; zrc = zoo_wget_children(zzh, "/mytest", default_zoo_watcher, NULL, &str_vec); + CPPUNIT_ASSERT(zrc == ZOK || zrc == ZCLOSING); } /** ZOOKEEPER-1057 This checks that the client connects to the second server when the first is not reachable **/ @@ -353,24 +365,28 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture /** this checks for a deadlock in calling zookeeper_close and calls from a default watcher that might get triggered just when zookeeper_close() is in progress **/ void testHangingClient() { - int zrc = 0; + int zrc; char buff[10] = "testall"; char path[512]; - watchctx_t *ctx; + watchctx_t *ctx = NULL; struct String_vector str_vec = {0, NULL}; zhandle_t *zh = zookeeper_init(hostPorts, NULL, 10000, 0, ctx, 0); sleep(1); zrc = zoo_create(zh, "/mytest", buff, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); + CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); + CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_create(zh, "/mytest/test1", buff, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); + CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); + CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_delete(zh, "/mytest/test1", -1); + CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zookeeper_close(zh); } void testBadDescriptor() { - int zrc = 0; - watchctx_t *ctx; + watchctx_t *ctx = NULL; zhandle_t *zh = zookeeper_init(hostPorts, NULL, 10000, 0, ctx, 0); sleep(1); zh->io_count = 0; @@ -381,7 +397,78 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT(zh->io_count < 2); zookeeper_close(zh); } - + + /* Checks the zoo_set_servers_resolution_delay default and operation */ + void testServersResolutionDelay() { + watchctx_t ctx; + zhandle_t *zk = createClient(&ctx); + int rc; + struct timeval tv; + struct Stat stat; + + CPPUNIT_ASSERT(zk); + CPPUNIT_ASSERT(zk->resolve_delay_ms == 0); + + // a) Default/0 case: resolve at each request. + + tv = zk->last_resolve; + usleep(10000); // 10ms + + rc = zoo_exists(zk, "/", 0, &stat); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + // Must have changed because of the request. + CPPUNIT_ASSERT(zk->last_resolve.tv_sec != tv.tv_sec || + zk->last_resolve.tv_usec != tv.tv_usec); + + // b) Disabled/-1 case: never perform "routine" resolutions. + + rc = zoo_set_servers_resolution_delay(zk, -1); // Disabled + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + tv = zk->last_resolve; + usleep(10000); // 10ms + + rc = zoo_exists(zk, "/", 0, &stat); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + // Must not have changed as auto-resolution is disabled. + CPPUNIT_ASSERT(zk->last_resolve.tv_sec == tv.tv_sec && + zk->last_resolve.tv_usec == tv.tv_usec); + + // c) Invalid delay is rejected. + + rc = zoo_set_servers_resolution_delay(zk, -1000); // Bad + CPPUNIT_ASSERT_EQUAL((int)ZBADARGUMENTS, rc); + + // d) Valid delay, no resolution within window. + + rc = zoo_set_servers_resolution_delay(zk, 500); // 0.5s + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + tv = zk->last_resolve; + usleep(10000); // 10ms + + rc = zoo_exists(zk, "/", 0, &stat); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + // Must not have changed because the request (hopefully!) + // executed in less than 0.5s. + CPPUNIT_ASSERT(zk->last_resolve.tv_sec == tv.tv_sec && + zk->last_resolve.tv_usec == tv.tv_usec); + + // e) Valid delay, at least one resolution after delay. + + usleep(500 * 1000); // 0.5s + + rc = zoo_exists(zk, "/", 0, &stat); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + // Must have changed because we waited 0.5s between the + // capture and the last request. + CPPUNIT_ASSERT(zk->last_resolve.tv_sec != tv.tv_sec || + zk->last_resolve.tv_usec != tv.tv_usec); + } void testPing() { @@ -802,15 +889,28 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture } #ifdef HAVE_OPENSSL_H - void testSSL() { + int checkSSL(const char *path, const char *certs) { watchctx_t ctx; zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); - zhandle_t *zk = createSSLClient("127.0.0.1:22281", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password", &ctx); + zhandle_t *zk = createSSLClient("127.0.0.1:22281", certs, &ctx); CPPUNIT_ASSERT(zk); int rc = 0; - rc = zoo_create(zk, "/ssl", NULL, -1, - &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); - CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); + return zoo_create(zk, path, NULL, -1, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); + } + + void testSSL() { + CPPUNIT_ASSERT_EQUAL((int) ZOK, checkSSL("/ssl", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password")); + } + + void testSSLNoClientCert() { + CPPUNIT_ASSERT(ZOK != checkSSL("/ssl-no-client-cert", "/tmp/certs/server.crt")); + } + + void testSSLServerOnly() { + stopServer(); + startServer("CLIENT_AUTH=none"); + + CPPUNIT_ASSERT_EQUAL((int) ZOK, checkSSL("/ssl-server-only", "/tmp/certs/server.crt")); } #endif @@ -928,7 +1028,7 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture void testChroot() { // the c client async callbacks do // not callback with the path, so - // we dont need to test taht for now + // we dont need to test that for now // we should fix that though soon! watchctx_t ctx, ctx_ch; zhandle_t *zk, *zk_ch; @@ -1039,8 +1139,8 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT_EQUAL(zoo_get_log_callback(zk), &logMessageHandler); // Log 10 messages and ensure all go to callback - int expected = 10; - for (int i = 0; i < expected; i++) + const std::size_t expected = 10; + for (std::size_t i = 0; i < expected; i++) { LOG_INFO(LOGCALLBACK(zk), "%s #%d", __FUNCTION__, i); } @@ -1057,12 +1157,12 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture // All the connection messages should have gone to the callback -- don't // want this to be a maintenance issue so we're not asserting exact count - int numBefore = logMessages.size(); + std::size_t numBefore = logMessages.size(); CPPUNIT_ASSERT(numBefore != 0); // Log 10 messages and ensure all go to callback - int expected = 10; - for (int i = 0; i < expected; i++) + const std::size_t expected = 10; + for (std::size_t i = 0; i < expected; i++) { LOG_INFO(LOGCALLBACK(zk), "%s #%d", __FUNCTION__, i); } diff --git a/zookeeper-client/zookeeper-client-c/tests/TestMulti.cc b/zookeeper-client/zookeeper-client-c/tests/TestMulti.cc index 226e4708202..6dfeb96e0c0 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestMulti.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestMulti.cc @@ -486,8 +486,6 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; - char buf[sz]; - int blen; char p1[sz]; p1[0] = '\0'; struct Stat stat; @@ -573,7 +571,6 @@ class Zookeeper_multi : public CPPUNIT_NS::TestFixture int sz = 512; char p1[sz]; p1[0] = '\0'; - struct Stat s1; rc = zoo_create(zk, "/multi0", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); diff --git a/zookeeper-client/zookeeper-client-c/tests/TestOperations.cc b/zookeeper-client/zookeeper-client-c/tests/TestOperations.cc index ed8e9f460ac..534c7599c1f 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestOperations.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestOperations.cc @@ -36,6 +36,10 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testCloseWhileInProgressFromCompletion); CPPUNIT_TEST(testCloseWhileMultiInProgressFromMain); CPPUNIT_TEST(testCloseWhileMultiInProgressFromCompletion); + CPPUNIT_TEST(testConnectResponseFull); + CPPUNIT_TEST(testConnectResponseNoReadOnlyFlag); + CPPUNIT_TEST(testConnectResponseSplitAtReadOnlyFlag); + CPPUNIT_TEST(testConnectResponseNoReadOnlyFlagSplit); #else CPPUNIT_TEST(testAsyncWatcher1); CPPUNIT_TEST(testAsyncGetOperation); @@ -126,7 +130,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -169,7 +173,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -212,7 +216,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -263,7 +267,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -340,7 +344,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -375,7 +379,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; @@ -422,7 +426,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state - forceConnected(zh); + forceConnected(zh, &now.tv); // queue up a request; keep it pending (as if the server is busy or has died) AsyncGetOperationCompletion res1; @@ -477,7 +481,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); - forceConnected(zh); + forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // issue a request @@ -516,7 +520,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); - forceConnected(zh); + forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // will handle completion on request #1 and issue request #2 from it @@ -578,7 +582,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res2.rc_); } - // ZOOKEEPER-2891: Invalid processing of zookeeper_close for mutli-request + // ZOOKEEPER-2891: Invalid processing of zookeeper_close for multi-request // while there is a multi request waiting for being processed // call zookeeper_close() from the main event loop // assert the completion callback is called with status ZCLOSING @@ -590,7 +594,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); - forceConnected(zh); + forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // issue a multi request @@ -622,7 +626,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res1.rc_); } - // ZOOKEEPER-2891: Invalid processing of zookeeper_close for mutli-request + // ZOOKEEPER-2891: Invalid processing of zookeeper_close for multi-request // send some request #1 (not a multi request) // then, while there is a multi request #2 waiting for being processed // call zookeeper_close() from the completion callback of request #1 @@ -635,7 +639,7 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); - forceConnected(zh); + forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // these shall persist during the test @@ -710,6 +714,126 @@ class Zookeeper_operations : public CPPUNIT_NS::TestFixture CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res2.rc_); } + void testConnectResponseFull() + { + CloseFinally guard(&zh); + Mock_socket socketMock; + HandshakeResponse hsResponse; + std::string hsResponseData = hsResponse.toString(); + + CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(41)); + + zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); + CPPUNIT_ASSERT(zh!=0); + + int rc, fd, interest; + timeval tv; + + rc = zookeeper_interest(zh, &fd, &interest, &tv); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + socketMock.recvReturnBuffer = hsResponseData; + rc = zookeeper_process(zh, interest); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); + } + + void testConnectResponseNoReadOnlyFlag() + { + CloseFinally guard(&zh); + Mock_socket socketMock; + HandshakeResponse hsResponse; + + hsResponse.omitReadOnly = true; + + std::string hsResponseData = hsResponse.toString(); + + CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(40)); + + zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); + CPPUNIT_ASSERT(zh!=0); + + int rc, fd, interest; + timeval tv; + + rc = zookeeper_interest(zh, &fd, &interest, &tv); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + socketMock.recvReturnBuffer = hsResponseData; + rc = zookeeper_process(zh, interest); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); + } + + void testConnectResponseSplitAtReadOnlyFlag() + { + CloseFinally guard(&zh); + Mock_socket socketMock; + HandshakeResponse hsResponse; + std::string hsResponseData = hsResponse.toString(); + + CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(41)); + + zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); + CPPUNIT_ASSERT(zh!=0); + + int rc, fd, interest; + timeval tv; + + rc = zookeeper_interest(zh, &fd, &interest, &tv); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + socketMock.recvReturnBuffer = hsResponseData.substr(0, 40); + rc = zookeeper_process(zh, interest); + // Response not complete. + CPPUNIT_ASSERT_EQUAL(static_cast(ZNOTHING), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE, static_cast(zh->state)); + + socketMock.recvReturnBuffer = hsResponseData.substr(40); + rc = zookeeper_process(zh, interest); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); + } + + void testConnectResponseNoReadOnlyFlagSplit() + { + CloseFinally guard(&zh); + Mock_socket socketMock; + HandshakeResponse hsResponse; + + hsResponse.omitReadOnly = true; + + std::string hsResponseData = hsResponse.toString(); + + CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(40)); + + zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); + CPPUNIT_ASSERT(zh!=0); + + int rc, fd, interest; + timeval tv; + + rc = zookeeper_interest(zh, &fd, &interest, &tv); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + socketMock.recvReturnBuffer = hsResponseData.substr(0, 20); + rc = zookeeper_process(zh, interest); + // Response not complete. + CPPUNIT_ASSERT_EQUAL(static_cast(ZNOTHING), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE, static_cast(zh->state)); + + socketMock.recvReturnBuffer = hsResponseData.substr(20); + rc = zookeeper_process(zh, interest); + CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); + + CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); + } + #else class TestGetDataJob: public TestJob{ public: diff --git a/zookeeper-client/zookeeper-client-c/tests/TestReadOnlyClient.cc b/zookeeper-client/zookeeper-client-c/tests/TestReadOnlyClient.cc index 0d4d579f444..e864ef266ca 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestReadOnlyClient.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestReadOnlyClient.cc @@ -71,6 +71,24 @@ class Zookeeper_readOnly : public CPPUNIT_NS::TestFixture { void setUp() { zoo_set_log_stream(logfile); zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); + stopServer(); + } + + void tearDown() + { + startServer(); + } + + void startServer() { + char cmd[1024]; + sprintf(cmd, "%s start %s", ZKSERVER_CMD, "127.0.0.1:22181"); + CPPUNIT_ASSERT(system(cmd) == 0); + } + + void stopServer() { + char cmd[1024]; + sprintf(cmd, "%s stop %s", ZKSERVER_CMD, "127.0.0.1:22181"); + CPPUNIT_ASSERT(system(cmd) == 0); } void startReadOnly() { diff --git a/zookeeper-client/zookeeper-client-c/tests/TestReconfig.cc b/zookeeper-client/zookeeper-client-c/tests/TestReconfig.cc index 317ffcddfed..5ae9606b5f5 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestReconfig.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestReconfig.cc @@ -26,6 +26,10 @@ #include #include +extern "C" { +#include +} + #include "Util.h" #include "LibCMocks.h" #include "ZKMocks.h" @@ -218,6 +222,10 @@ class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testcycleNextServer); CPPUNIT_TEST(testMigrateOrNot); CPPUNIT_TEST(testMigrationCycle); + CPPUNIT_TEST(testAddrVecContainsIPv4); +#ifdef AF_INET6 + CPPUNIT_TEST(testAddrVecContainsIPv6); +#endif // In threaded mode each 'create' is a thread -- it's not practical to create // 10,000 threads to test load balancing. The load balancing code can easily @@ -491,7 +499,7 @@ class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture found = seen.find(next.str()); CPPUNIT_ASSERT_MESSAGE(next.str() + " in seen list", found == string::npos); - seen += found + ", "; + seen += next.str() + ", "; } // Now it should start connecting to the old servers @@ -508,7 +516,7 @@ class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture // Assert not in seen list then append found = seen.find(next.str()); CPPUNIT_ASSERT(found == string::npos); - seen += found + ", "; + seen += next.str() + ", "; } // NOW it goes back to normal as we've tried all the new and old @@ -609,6 +617,81 @@ class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture numServers = 9; updateAllClientsAndServers(numServers); } + + /** + * This tests that client can detect server's ipv4 address change. + * + * (1) We generate some address and put in addr, which saddr point to + * (2) Add all addresses that differ by one bit from the source + * (3) Add same address, but set ipv6 protocol + * (4) Ensure, that our address is not equal to any of generated, + * and that it equals to itself + */ + void testAddrVecContainsIPv4() { + addrvec_t vec; + addrvec_init(&vec); + + sockaddr_storage addr; + sockaddr_in* saddr = (sockaddr_in*)&addr; + saddr->sin_family = AF_INET; + saddr->sin_port = htons((u_short)1234); + saddr->sin_addr.s_addr = INADDR_ANY; + + CPPUNIT_ASSERT(sizeof(saddr->sin_addr.s_addr) == 4); + + for (int i = 0; i < 32; i++) { + saddr->sin_addr.s_addr ^= (1 << i); + addrvec_append(&vec, &addr); + saddr->sin_addr.s_addr ^= (1 << i); + } + + saddr->sin_family = AF_INET6; + addrvec_append(&vec, &addr); + saddr->sin_family = AF_INET; + + CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr)); + addrvec_append(&vec, &addr); + CPPUNIT_ASSERT(addrvec_contains(&vec, &addr)); + addrvec_free(&vec); + } + + /** + * This tests that client can detect server's ipv6 address change. + * + * Same logic as in previous testAddrVecContainsIPv4 method, + * but we keep in mind, that ipv6 is 128-bit long. + */ +#ifdef AF_INET6 + void testAddrVecContainsIPv6() { + addrvec_t vec; + addrvec_init(&vec); + + sockaddr_storage addr; + sockaddr_in6* saddr = (sockaddr_in6*)&addr; + saddr->sin6_family = AF_INET6; + saddr->sin6_port = htons((u_short)1234); + saddr->sin6_addr = in6addr_any; + + CPPUNIT_ASSERT(sizeof(saddr->sin6_addr.s6_addr) == 16); + + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 8; j++) { + saddr->sin6_addr.s6_addr[i] ^= (1 << j); + addrvec_append(&vec, &addr); + saddr->sin6_addr.s6_addr[i] ^= (1 << j); + } + } + + saddr->sin6_family = AF_INET; + addrvec_append(&vec, &addr); + saddr->sin6_family = AF_INET6; + + CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr)); + addrvec_append(&vec, &addr); + CPPUNIT_ASSERT(addrvec_contains(&vec, &addr)); + addrvec_free(&vec); + } +#endif }; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_reconfig); diff --git a/zookeeper-client/zookeeper-client-c/tests/TestReconfigServer.cc b/zookeeper-client/zookeeper-client-c/tests/TestReconfigServer.cc index c15774e9e30..54810b60c29 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestReconfigServer.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestReconfigServer.cc @@ -49,12 +49,12 @@ class TestReconfigServer : public CPPUNIT_NS::TestFixture { static const uint32_t NUM_SERVERS; FILE* logfile_; std::vector cluster_; - int32_t getLeader(); - std::vector getFollowers(); + std::size_t getLeader(); + std::vector getFollowers(); void parseConfig(char* buf, int len, std::vector& servers, std::string& version); bool waitForConnected(zhandle_t* zh, uint32_t timeout_sec); - zhandle_t* connectFollowers(std::vector &followers); + zhandle_t* connectFollowers(std::vector &followers); }; const uint32_t TestReconfigServer::NUM_SERVERS = 3; @@ -84,15 +84,15 @@ setUp() { void TestReconfigServer:: tearDown() { - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { delete cluster_[i]; } cluster_.clear(); } -int32_t TestReconfigServer:: +std::size_t TestReconfigServer:: getLeader() { - for (int32_t i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { if (cluster_[i]->isLeader()) { return i; } @@ -100,10 +100,10 @@ getLeader() { return -1; } -std::vector TestReconfigServer:: +std::vector TestReconfigServer:: getFollowers() { - std::vector followers; - for (int32_t i = 0; i < cluster_.size(); i++) { + std::vector followers; + for (std::size_t i = 0; i < cluster_.size(); i++) { if (cluster_[i]->isFollower()) { followers.push_back(i); } @@ -154,7 +154,7 @@ testRemoveFollower() { char buf[len]; // get config from leader. - int32_t leader = getLeader(); + std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); std::string host = cluster_[leader]->getHostPort(); zhandle_t* zk = zookeeper_init(host.c_str(), NULL, 10000, NULL, NULL, 0); @@ -167,13 +167,13 @@ testRemoveFollower() { // of the first NEWLEADER message, used as the initial version CPPUNIT_ASSERT_EQUAL(std::string("100000000"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // remove a follower. - std::vector followers = getFollowers(); + std::vector followers = getFollowers(); len = 1024; CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); @@ -185,7 +185,7 @@ testRemoveFollower() { parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(std::string("100000002"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } @@ -201,7 +201,7 @@ testRemoveFollower() { CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } @@ -222,7 +222,7 @@ testNonIncremental() { char buf[len]; // get config from leader. - int32_t leader = getLeader(); + std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); std::string host = cluster_[leader]->getHostPort(); zhandle_t* zk = zookeeper_init(host.c_str(), NULL, 10000, NULL, NULL, 0); @@ -236,18 +236,18 @@ testNonIncremental() { // of the first NEWLEADER message, used as the initial version CPPUNIT_ASSERT_EQUAL(std::string("100000000"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // remove a follower. - std::vector followers = getFollowers(); + std::vector followers = getFollowers(); len = 1024; CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); std::stringstream ss; - for (int i = 1; i < followers.size(); i++) { + for (std::size_t i = 1; i < followers.size(); i++) { ss << cluster_[followers[i]]->getServerString() << ","; } ss << cluster_[leader]->getServerString(); @@ -258,7 +258,7 @@ testNonIncremental() { parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(std::string("100000002"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } @@ -269,7 +269,7 @@ testNonIncremental() { // add the follower back. len = 1024; ss.str(""); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { ss << cluster_[i]->getServerString() << ","; } rc = zoo_reconfig(zk, NULL, NULL, ss.str().c_str(), -1, buf, &len, @@ -277,7 +277,7 @@ testNonIncremental() { CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } @@ -285,12 +285,12 @@ testNonIncremental() { } zhandle_t* TestReconfigServer:: -connectFollowers(std::vector &followers) { +connectFollowers(std::vector &followers) { std::stringstream ss; - int32_t leader = getLeader(); + std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); - for (int i = 0; i < followers.size(); i++) { + for (std::size_t i = 0; i < followers.size(); i++) { ss << cluster_[followers[i]]->getHostPort() << ","; } ss << cluster_[leader]->getHostPort(); @@ -321,7 +321,7 @@ testRemoveConnectedFollower() { // connect to a follower. std::stringstream ss; - std::vector followers = getFollowers(); + std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); @@ -333,7 +333,7 @@ testRemoveConnectedFollower() { CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } @@ -356,7 +356,7 @@ testReconfigFailureWithoutAuth() { // connect to a follower. std::stringstream ss; - std::vector followers = getFollowers(); + std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); // remove the follower. @@ -374,7 +374,7 @@ testReconfigFailureWithoutAuth() { CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); - for (int i = 0; i < cluster_.size(); i++) { + for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } @@ -400,7 +400,7 @@ testReconfigFailureWithoutServerSuperuserPasswordConfigured() { // connect to a follower. std::stringstream ss; - std::vector followers = getFollowers(); + std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); // remove the follower. diff --git a/zookeeper-client/zookeeper-client-c/tests/TestSASLAuth.cc b/zookeeper-client/zookeeper-client-c/tests/TestSASLAuth.cc index 080649d97f3..fcac2be1393 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestSASLAuth.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestSASLAuth.cc @@ -34,10 +34,15 @@ class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST(testServerRequireClientSASL); #ifdef HAVE_CYRUS_SASL_H CPPUNIT_TEST(testClientSASL); + CPPUNIT_TEST(testClientSASLWithPasswordFileNewline); + CPPUNIT_TEST(testClientSASLWithPasswordFileFirstLine); + CPPUNIT_TEST(testClientSASLWithPasswordEncryptedText); + CPPUNIT_TEST(testClientSASLWithPasswordEncryptedBinary); #ifdef ZOO_IPV6_ENABLED CPPUNIT_TEST(testClientSASLOverIPv6); #endif/* ZOO_IPV6_ENABLED */ CPPUNIT_TEST(testClientSASLReadOnly); + CPPUNIT_TEST(testClientSASLPacketOrder); #endif /* HAVE_CYRUS_SASL_H */ CPPUNIT_TEST_SUITE_END(); FILE *logfile; @@ -129,7 +134,18 @@ class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { } #ifdef HAVE_CYRUS_SASL_H - void testClientSASLHelper(const char *hostPorts, const char *path) { + + // We need to disable the deprecation warnings as Apple has + // decided to deprecate all of CyrusSASL's functions with OS 10.11 + // (see MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also + // for covering clang. +#ifdef __APPLE__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + + void testClientSASLHelper(const char *hostPorts, const char *path, + const sasl_callback_t *callbacks) { startServer(); // Initialize Cyrus SASL. @@ -141,8 +157,7 @@ class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { sasl_params.service = "zookeeper"; sasl_params.host = "zk-sasl-md5"; sasl_params.mechlist = "DIGEST-MD5"; - sasl_params.callbacks = zoo_sasl_make_basic_callbacks( - "myuser", NULL, "Zookeeper_SASLAuth.password"); + sasl_params.callbacks = callbacks; // Connect. watchctx_t ctx; @@ -177,10 +192,115 @@ class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { stopServer(); } + void testClientSASLHelper(const char *hostPorts, const char *path, + const char *password_file) { + const sasl_callback_t *callbacks = zoo_sasl_make_basic_callbacks( + "myuser", NULL, password_file); + testClientSASLHelper(hostPorts, path, callbacks); + } + + void testClientSASLHelper(const char *hostPorts, const char *path) { + testClientSASLHelper(hostPorts, path, "Zookeeper_SASLAuth.password"); + } + + void testClientSASLHelper(const char *hostPorts, const char *path, + zoo_sasl_password_t *password) { + const sasl_callback_t *callbacks = zoo_sasl_make_password_callbacks( + "myuser", NULL, password); + testClientSASLHelper(hostPorts, path, callbacks); + } + void testClientSASL() { testClientSASLHelper(hostPorts, "/clientSASL"); } + void testClientSASL(const char *password_file, const char *content, size_t content_len, + const char *path, zoo_sasl_password_callback_t callback) { + // Create password file for client. + FILE *passf = fopen(password_file, "wt"); + CPPUNIT_ASSERT(passf); + + // Write the specified content into the file. + size_t len = fwrite(content, sizeof(content[0]), content_len, passf); + CPPUNIT_ASSERT_EQUAL(len, content_len); + CPPUNIT_ASSERT_EQUAL(fclose(passf), 0); + passf = NULL; + + zoo_sasl_password_t passwd = {password_file, this, callback}; + testClientSASLHelper(hostPorts, path, &passwd); + } + + void testClientSASLWithPasswordFileNewline() { + // Insert a newline immediately after the correct password. + const char content[] = "mypassword\nabc"; + testClientSASL("Zookeeper_SASLAuth.password.newline", + content, + sizeof(content) - 1, + "/clientSASLWithPasswordFileNewline", + NULL); + } + + void testClientSASLWithPasswordFileFirstLine() { + // Insert multiple newlines and check if only the first line is accepted as the + // actual password. + const char content[] = "mypassword\nabc\nxyz"; + testClientSASL("Zookeeper_SASLAuth.password.firstline", + content, + sizeof(content) - 1, + "/clientSASLWithPasswordFileFirstLine", + NULL); + } + + int decryptPassword(const char *content, size_t content_len, + char incr, char *buf, size_t buf_len, + size_t *passwd_len) { + CPPUNIT_ASSERT(content_len <= buf_len); + + // A simple decryption that only increases each character by a fixed value. + for (size_t i = 0; i < content_len; ++i) { + buf[i] = content[i] + incr; + } + *passwd_len = content_len; + + // Since null terminator has not been appended to buf, use memcmp. + CPPUNIT_ASSERT_EQUAL(memcmp(buf, "mypassword", *passwd_len), 0); + return SASL_OK; + } + + static int textPasswordCallback(const char *content, size_t content_len, + void *context, char *buf, size_t buf_len, + size_t *passwd_len) { + Zookeeper_SASLAuth *auth = static_cast(context); + return auth->decryptPassword(content, content_len, 1, buf, buf_len, passwd_len); + } + + void testClientSASLWithPasswordEncryptedText() { + // Encrypt "mypassword" by subtracting 1 from each character in it as plain text. + const char content[] = {0x6C, 0x78, 0x6F, 0x60, 0x72, 0x72, 0x76, 0x6E, 0x71, 0x63}; + testClientSASL("Zookeeper_SASLAuth.password.encrypted.text", + content, + sizeof(content), + "/clientSASLWithPasswordEncryptedText", + textPasswordCallback); + } + + static int binaryPasswordCallback(const char *content, size_t content_len, + void *context, char *buf, size_t buf_len, + size_t *passwd_len) { + Zookeeper_SASLAuth *auth = static_cast(context); + return auth->decryptPassword(content, content_len, 'a', buf, buf_len, passwd_len); + } + + void testClientSASLWithPasswordEncryptedBinary() { + // Encrypt "mypassword" by subtracting 'a' from each character in it as binary format. + const char content[] = {0x0C, 0x18, 0x0F, 0x00, 0x12, 0x12, 0x16, 0x0E, 0x11, 0x03}; + testClientSASL("Zookeeper_SASLAuth.password.encrypted.binary", + content, + sizeof(content), + "/clientSASLWithPasswordEncryptedBinary", + binaryPasswordCallback); + } + void testClientSASLOverIPv6() { const char *ipAndPort = "::1:22181"; @@ -227,6 +347,42 @@ class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { stopServer(); } + void testClientSASLPacketOrder() { + startServer(); + + // Initialize Cyrus SASL. + CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK); + + // Initialize SASL parameters. + zoo_sasl_params_t sasl_params = { 0 }; + + sasl_params.service = "zookeeper"; + sasl_params.host = "zk-sasl-md5"; + sasl_params.mechlist = "DIGEST-MD5"; + sasl_params.callbacks = zoo_sasl_make_basic_callbacks( + "myuser", NULL, "Zookeeper_SASLAuth.password"); + + // Connect. + watchctx_t ctx; + int rc = 0; + zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL, + &ctx, /*flags*/0, /*log_callback*/NULL, &sasl_params); + ctx.zh = zk; + CPPUNIT_ASSERT(zk); + + // No wait: try and queue a packet before SASL auth is complete. + char buf[1024]; + int len = sizeof(buf); + rc = zoo_get(zk, "/", 0, buf, &len, 0); + CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); + + stopServer(); + } + +#ifdef __APPLE__ +#pragma GCC diagnostic pop +#endif + #endif /* HAVE_CYRUS_SASL_H */ }; diff --git a/zookeeper-client/zookeeper-client-c/tests/TestWatchers.cc b/zookeeper-client/zookeeper-client-c/tests/TestWatchers.cc index 0a269341c57..7947d6f1396 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestWatchers.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestWatchers.cc @@ -456,7 +456,7 @@ class Zookeeper_watchers : public CPPUNIT_NS::TestFixture zh=zookeeper_init("localhost:2121",activeWatcher,10000,TEST_CLIENT_ID, &watcher,0); CPPUNIT_ASSERT(zh!=0); - // wait till watcher proccessing has completed (the connection + // wait till watcher processing has completed (the connection // established event) CPPUNIT_ASSERT(ensureCondition( deliveryTracker.isWatcherProcessingCompleted(),1000)<1000); diff --git a/zookeeper-client/zookeeper-client-c/tests/TestZookeeperInit.cc b/zookeeper-client/zookeeper-client-c/tests/TestZookeeperInit.cc index 61da41dd547..d5fc12c5ded 100644 --- a/zookeeper-client/zookeeper-client-c/tests/TestZookeeperInit.cc +++ b/zookeeper-client/zookeeper-client-c/tests/TestZookeeperInit.cc @@ -36,6 +36,7 @@ using namespace std; class Zookeeper_init : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_init); + CPPUNIT_TEST(testVersion); CPPUNIT_TEST(testBasic); CPPUNIT_TEST(testAddressResolution); CPPUNIT_TEST(testMultipleAddressResolution); @@ -90,6 +91,11 @@ class Zookeeper_init : public CPPUNIT_NS::TestFixture #endif } + void testVersion() + { + CPPUNIT_ASSERT_EQUAL(string(ZOO_VERSION), string(zoo_version_str())); + } + void testBasic() { const string EXPECTED_HOST("127.0.0.1:2121"); diff --git a/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.h b/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.h index 9165412bc8b..45791fd2f59 100644 --- a/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.h +++ b/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.h @@ -38,7 +38,7 @@ class AtomicInt{ public: explicit AtomicInt(int32_t init=0):v_(init){} AtomicInt(const AtomicInt& other):v_(other){} - // assigment + // assignment AtomicInt& operator=(const AtomicInt& lhs){ atomic_fetch_store(&v_,lhs); return *this; diff --git a/zookeeper-client/zookeeper-client-c/tests/Util.cc b/zookeeper-client/zookeeper-client-c/tests/Util.cc index 2b9da84eb57..7b49ecac549 100644 --- a/zookeeper-client/zookeeper-client-c/tests/Util.cc +++ b/zookeeper-client/zookeeper-client-c/tests/Util.cc @@ -16,6 +16,8 @@ * limitations under the License. */ +#include + #include "Util.h" #include "string.h" diff --git a/zookeeper-client/zookeeper-client-c/tests/ZKMocks.cc b/zookeeper-client/zookeeper-client-c/tests/ZKMocks.cc index d26c295f24d..674945e043c 100644 --- a/zookeeper-client/zookeeper-client-c/tests/ZKMocks.cc +++ b/zookeeper-client/zookeeper-client-c/tests/ZKMocks.cc @@ -344,9 +344,11 @@ string HandshakeResponse::toString() const { tmp=htonl(passwd_len); buf.append((char*)&tmp,sizeof(tmp)); buf.append(passwd,sizeof(passwd)); - buf.append(&readOnly,sizeof(readOnly)); + if (!omitReadOnly) { + buf.append(&readOnly,sizeof(readOnly)); + } // finally set the buffer length - tmp=htonl(buf.size()+sizeof(tmp)); + tmp=htonl(buf.size()); buf.insert(0,(char*)&tmp, sizeof(tmp)); return buf; } @@ -523,7 +525,7 @@ void ZookeeperServer::notifyBufferSent(const std::string& buffer){ addRecvResponse(e); } -void forceConnected(zhandle_t* zh){ +void forceConnected(zhandle_t* zh, const struct timeval *last_recv_send){ // simulate connected state zh->state=ZOO_CONNECTED_STATE; @@ -534,8 +536,13 @@ void forceConnected(zhandle_t* zh){ zh->addrs.next++; zh->input_buffer=0; - gettimeofday(&zh->last_recv,0); - gettimeofday(&zh->last_send,0); + if (last_recv_send) { + zh->last_recv = *last_recv_send; + zh->last_send = *last_recv_send; + } else { + gettimeofday(&zh->last_recv,0); + gettimeofday(&zh->last_send,0); + } } void terminateZookeeperThreads(zhandle_t* zh){ diff --git a/zookeeper-client/zookeeper-client-c/tests/ZKMocks.h b/zookeeper-client/zookeeper-client-c/tests/ZKMocks.h index 2717ded3087..d53a41628e7 100644 --- a/zookeeper-client/zookeeper-client-c/tests/ZKMocks.h +++ b/zookeeper-client/zookeeper-client-c/tests/ZKMocks.h @@ -30,7 +30,7 @@ // sets internal zhandle_t members to certain values to simulate the client // connected state. This function should only be used with the single-threaded // Async API tests! -void forceConnected(zhandle_t* zh); +void forceConnected(zhandle_t* zh, const struct timeval *last_recv_send = NULL); /** * Gracefully terminates zookeeper I/O and completion threads. @@ -305,7 +305,7 @@ class HandshakeResponse: public Response public: HandshakeResponse(int64_t sessId=1): protocolVersion(1),timeOut(10000),sessionId(sessId), - passwd_len(sizeof(passwd)),readOnly(0) + passwd_len(sizeof(passwd)),readOnly(0),omitReadOnly(false) { memcpy(passwd,"1234567890123456",sizeof(passwd)); } @@ -315,6 +315,7 @@ class HandshakeResponse: public Response int32_t passwd_len; char passwd[16]; char readOnly; + bool omitReadOnly; virtual std::string toString() const ; }; @@ -442,7 +443,7 @@ class ZookeeperServer: public Mock_socket void returnSessionExpired(){ sessionExpired=true; } // this is a one shot trigger that gets reset back to false - // next recv call will return 0 length, thus simulating a connecton loss + // next recv call will return 0 length, thus simulating a connection loss volatile bool connectionLost; void setConnectionLost() {connectionLost=true;} diff --git a/zookeeper-client/zookeeper-client-c/tests/ZooKeeperQuorumServer.cc b/zookeeper-client/zookeeper-client-c/tests/ZooKeeperQuorumServer.cc index db79919c3c7..73548685712 100644 --- a/zookeeper-client/zookeeper-client-c/tests/ZooKeeperQuorumServer.cc +++ b/zookeeper-client/zookeeper-client-c/tests/ZooKeeperQuorumServer.cc @@ -197,6 +197,7 @@ getCluster(uint32_t numServers) { } } assert(!"The cluster didn't start for 10 seconds"); + return {}; } std::vector ZooKeeperQuorumServer:: diff --git a/zookeeper-client/zookeeper-client-c/tests/zkServer.sh b/zookeeper-client/zookeeper-client-c/tests/zkServer.sh index 99b716a2111..244eefc9b0c 100755 --- a/zookeeper-client/zookeeper-client-c/tests/zkServer.sh +++ b/zookeeper-client/zookeeper-client-c/tests/zkServer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env bash # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -8,13 +8,15 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # This is the port where zookeeper server runs on. ZOOPORT=${ZOOPORT:-"22181"} @@ -22,238 +24,207 @@ ZOOPORT=${ZOOPORT:-"22181"} # Some tests are setting the maxClientConnections. When it is not set, we fallback to default 100 ZKMAXCNXNS=${ZKMAXCNXNS:-"100"} +CLIENT_AUTH=${CLIENT_AUTH:-"need"} + EXTRA_JVM_ARGS=${EXTRA_JVM_ARGS:-""} -if [ "x$1" == "x" ] -then - echo "USAGE: $0 startClean|start|startCleanReadOnly|startRequireSASLAuth [jaasConf] [readOnly]|stop" - exit 2 +if [[ -z $1 ]]; then + echo "USAGE: $0 startClean|start|startCleanReadOnly|startRequireSASLAuth [jaasConf] [readOnly]|stop" + exit 2 fi - - - # ===== # ===== cleanup old executions # ===== -case "`uname`" in - CYGWIN*) cygwin=true ;; - *) cygwin=false ;; +case "$(uname)" in + CYGWIN* | MINGW*) cygwin=true ;; + *) cygwin=false ;; esac - -if $cygwin -then - # cygwin has a "kill" in the shell itself, gets confused - KILL=/bin/kill +if $cygwin; then + # cygwin has a "kill" in the shell itself, gets confused + KILL='/bin/kill' else - KILL=kill + KILL='kill' fi # Make sure nothing is left over from before -if [ -r "/tmp/zk.pid" ] -then -pid=`cat /tmp/zk.pid` -$KILL -9 $pid -rm -f /tmp/zk.pid +if [[ -r "/tmp/zk.pid" ]]; then + pid=$(cat /tmp/zk.pid) + $KILL -9 "$pid" + rm -f /tmp/zk.pid fi -if [ -r "${base_dir}/build/tmp/zk.pid" ] -then -pid=`cat "${base_dir}/build/tmp/zk.pid"` -$KILL -9 $pid -rm -f "${base_dir}/build/tmp/zk.pid" +if [[ -r "$base_dir/build/tmp/zk.pid" ]]; then + pid=$(cat "$base_dir/build/tmp/zk.pid") + $KILL -9 "$pid" + rm -f "$base_dir/build/tmp/zk.pid" fi # [ZOOKEEPER-820] If lsof command is present, look for a process listening -# on ZOOPORT and kill it. -which lsof &> /dev/null -if [ $? -eq 0 ] -then - pid=`lsof -i :$ZOOPORT | grep LISTEN | awk '{print $2}'` - if [ -n "$pid" ] - then - $KILL -9 $pid - fi +# on ZOOPORT and kill it. +if which lsof &>/dev/null; then + pid=$(lsof -i ":$ZOOPORT" | grep LISTEN | awk '{print $2}') + if [[ -n $pid ]]; then + $KILL -9 "$pid" + fi fi - - # ===== # ===== build classpath # ===== -if [ "x${base_dir}" == "x" ] -then -zk_base="../../../" +if [[ -z $base_dir ]]; then + zk_base="../../../" else -zk_base="${base_dir}" + zk_base="$base_dir" fi -CLASSPATH="$CLASSPATH:${zk_base}/build/classes" -CLASSPATH="$CLASSPATH:${zk_base}/conf" -CLASSPATH="$CLASSPATH:${zk_base}/zookeeper-server/target/classes" +CLASSPATH="$CLASSPATH:$zk_base/build/classes" +CLASSPATH="$CLASSPATH:$zk_base/conf" +CLASSPATH="$CLASSPATH:$zk_base/zookeeper-server/target/classes" -for i in "${zk_base}"/build/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$zk_base"/build/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done -for d in "${zk_base}"/zookeeper-server/target/lib/*.jar -do - CLASSPATH="$d:$CLASSPATH" +for d in "$zk_base"/zookeeper-server/target/lib/*.jar; do + CLASSPATH="$d:$CLASSPATH" done -for i in "${zk_base}"/zookeeper-server/src/main/resource/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$zk_base"/zookeeper-server/src/main/resource/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done -CLASSPATH="$CLASSPATH:${CLOVER_HOME}/lib/clover*.jar" +CLASSPATH="$CLASSPATH:$CLOVER_HOME/lib/clover*.jar" -if $cygwin -then - CLASSPATH=`cygpath -wp "$CLASSPATH"` +if $cygwin; then + CLASSPATH=$(cygpath -wp "$CLASSPATH") fi - - +export CLASSPATH # ===== # ===== initialize JVM arguments # ===== read_only= -PROPERTIES="$EXTRA_JVM_ARGS -Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100" -if [ "x$1" == "xstartRequireSASLAuth" ] -then - PROPERTIES="-Dzookeeper.sessionRequireClientSASLAuth=true $PROPERTIES" - if [ "x$2" != "x" ] - then - PROPERTIES="$PROPERTIES -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider" - PROPERTIES="$PROPERTIES -Djava.security.auth.login.config=$2" - fi - if [ "x$3" != "x" ] - then - PROPERTIES="-Dreadonlymode.enabled=true $PROPERTIES" - read_only=true - fi -fi -if [ "x$1" == "xstartCleanReadOnly" ] -then - PROPERTIES="-Dreadonlymode.enabled=true $PROPERTIES" +# shellcheck disable=SC2206 +PROPERTIES=($EXTRA_JVM_ARGS "-Dzookeeper.extendedTypesEnabled=true" "-Dznode.container.checkIntervalMs=100") +if [[ $1 == "startRequireSASLAuth" ]]; then + PROPERTIES=("-Dzookeeper.sessionRequireClientSASLAuth=true" "${PROPERTIES[@]}" + "-Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider" + "-Dzookeeper.fips-mode=false") + if [[ -n $2 ]]; then + PROPERTIES=("${PROPERTIES[@]}" "-Djava.security.auth.login.config=$2") + fi + if [[ -n $3 ]]; then + PROPERTIES=("-Dreadonlymode.enabled=true" "${PROPERTIES[@]}") read_only=true + fi +fi +if [[ $1 == "startCleanReadOnly" ]]; then + PROPERTIES=("-Dreadonlymode.enabled=true" "${PROPERTIES[@]}") + read_only=true fi - - # ===== # ===== initialize data and test directories # ===== -if [ "x${base_dir}" == "x" ] -then - tmp_dir="/tmp" - tests_dir="tests" +if [[ -z $base_dir ]]; then + tmp_dir="/tmp" + tests_dir="tests" else - tmp_dir="${base_dir}/build/tmp" - tests_dir=${base_dir}/zookeeper-client/zookeeper-client-c/tests + tmp_dir="$base_dir/build/tmp" + tests_dir=$base_dir/zookeeper-client/zookeeper-client-c/tests fi - - - # ===== # ===== start the ZooKeeper server # ===== case $1 in -start|startClean|startRequireSASLAuth|startCleanReadOnly) + start | startClean | startRequireSASLAuth | startCleanReadOnly) - if [ "x$1" == "xstartClean" ] || [ "x$1" == "xstartCleanReadOnly" ] - then - rm -rf "${tmp_dir}/zkdata" + if [[ $1 == "startClean" ]] || [[ $1 == "startCleanReadOnly" ]]; then + rm -rf "$tmp_dir/zkdata" fi - mkdir -p "${tmp_dir}/zkdata" - + mkdir -p "$tmp_dir/zkdata" # ===== initialize certificates certs_dir="/tmp/certs" - rm -rf "${certs_dir}" - mkdir -p "${certs_dir}" - cp ${tests_dir}/../ssl/gencerts.sh "${certs_dir}/" > /dev/null - cd ${certs_dir} > /dev/null - ./gencerts.sh > ./gencerts.stdout 2> ./gencerts.stderr - cd - > /dev/null - + rm -rf "$certs_dir" + mkdir -p "$certs_dir" + cp "$tests_dir"/../ssl/gencerts.sh "$certs_dir/" >/dev/null + ( + cd "$certs_dir" >/dev/null && + # GitHub is providing us hostnames with more than 64 characters now. + # And there are no cppunit tests do hostname verification currently, + # so we could set CN to arbitrary hostname for now. + ./gencerts.sh tests.zookeeper.apache.org >./gencerts.stdout 2>./gencerts.stderr + ) # ===== prepare the configs - sed "s#TMPDIR#${tmp_dir}#g;s#CERTDIR#${certs_dir}#g;s#MAXCLIENTCONNECTIONS#${ZKMAXCNXNS}#g;s#CLIENTPORT#${ZOOPORT}#g" ${tests_dir}/zoo.cfg > "${tmp_dir}/zoo.cfg" - if [ "x$read_only" != "x" ] - then - # we can put the new server to read-only mode by starting only a single instance of a three node server - echo "server.1=localhost:22881:33881" >> ${tmp_dir}/zoo.cfg - echo "server.2=localhost:22882:33882" >> ${tmp_dir}/zoo.cfg - echo "server.3=localhost:22883:33883" >> ${tmp_dir}/zoo.cfg - echo "1" > ${tmp_dir}/zkdata/myid - main_class="org.apache.zookeeper.server.quorum.QuorumPeerMain" + sed "s#TMPDIR#$tmp_dir#g;s#CERTDIR#$certs_dir#g;s#MAXCLIENTCONNECTIONS#$ZKMAXCNXNS#g;s#CLIENTPORT#$ZOOPORT#g;s#CLIENT_AUTH#$CLIENT_AUTH#g" "$tests_dir/zoo.cfg" >"$tmp_dir/zoo.cfg" + if [[ -n $read_only ]]; then + # we can put the new server to read-only mode by starting only a single instance of a three node server + { + echo "server.1=localhost:22881:33881" + echo "server.2=localhost:22882:33882" + echo "server.3=localhost:22883:33883" + } >>"$tmp_dir/zoo.cfg" + echo "1" >"$tmp_dir/zkdata/myid" + main_class="org.apache.zookeeper.server.quorum.QuorumPeerMain" else - main_class="org.apache.zookeeper.server.ZooKeeperServerMain" + main_class="org.apache.zookeeper.server.ZooKeeperServerMain" fi - # ===== start the server - java -cp "$CLASSPATH" $PROPERTIES ${main_class} ${tmp_dir}/zoo.cfg &> "${tmp_dir}/zk.log" & + java "${PROPERTIES[@]}" "$main_class" "$tmp_dir/zoo.cfg" &>"$tmp_dir/zk.log" & pid=$! - echo -n $! > /tmp/zk.pid - + echo -n $! >/tmp/zk.pid # ===== wait for the server to start - if [ "x$1" == "xstartRequireSASLAuth" ] || [ "x$1" == "xstartCleanReadOnly" ] - then - # ===== in these cases we can not connect simply with the java client, so we are just waiting... - sleep 4 - success=true + if [[ $1 == "startRequireSASLAuth" ]] || [[ $1 == "startCleanReadOnly" ]]; then + # ===== in these cases we can not connect simply with the java client, so we are just waiting... + sleep 4 + success=true else - # ===== wait max 120 seconds for server to be ready to server clients (this handles testing on slow hosts) - success=false - for i in {1..120} - do - if ps -p $pid > /dev/null - then - java -cp "$CLASSPATH" $PROPERTIES org.apache.zookeeper.ZooKeeperMain -server localhost:$ZOOPORT ls / > /dev/null 2>&1 - if [ $? -ne 0 ] - then - # server not up yet - wait - sleep 1 - else - # server is up and serving client connections - success=true - break - fi - else - # server died - exit now - echo -n " ZooKeeper server process failed" - break - fi - done + # ===== wait max 120 seconds for server to be ready to server clients (this handles testing on slow hosts) + success=false + for i in {1..120}; do + if ps -p $pid >/dev/null; then + if ! java "${PROPERTIES[@]}" org.apache.zookeeper.ZooKeeperMain -server "localhost:$ZOOPORT" ls / &>/dev/null; then + # server not up yet - wait + sleep 1 + else + # server is up and serving client connections + success=true + break + fi + else + # server died - exit now + echo -n " ZooKeeper server process failed" + break + fi + done fi - if $success - then - ## in case for debug, but generally don't use as it messes up the - ## console test output - echo -n " ZooKeeper server started" + if $success; then + ## in case for debug, but generally don't use as it messes up the + ## console test output + echo -n " ZooKeeper server started" else - echo -n " ZooKeeper server NOT started" + echo -n " ZooKeeper server NOT started" fi ;; -stop) + stop) # Already killed above ;; -*) - echo "Unknown command " + $1 + *) + echo "Unknown command $1" exit 2 + ;; esac - diff --git a/zookeeper-client/zookeeper-client-c/tests/zoo.cfg b/zookeeper-client/zookeeper-client-c/tests/zoo.cfg index b8e0b2a661f..5e3df626436 100644 --- a/zookeeper-client/zookeeper-client-c/tests/zoo.cfg +++ b/zookeeper-client/zookeeper-client-c/tests/zoo.cfg @@ -3,10 +3,12 @@ initLimit=10 syncLimit=5 dataDir=TMPDIR/zkdata maxClientCnxns=MAXCLIENTCONNECTIONS +localSessionsEnabled=true clientPort=CLIENTPORT secureClientPort=22281 serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory +ssl.clientAuth=CLIENT_AUTH ssl.keyStore.location=CERTDIR/server.jks ssl.keyStore.password=password ssl.trustStore.location=CERTDIR/servertrust.jks diff --git a/zookeeper-compatibility-tests/pom.xml b/zookeeper-compatibility-tests/pom.xml new file mode 100644 index 00000000000..2d85a16ad7d --- /dev/null +++ b/zookeeper-compatibility-tests/pom.xml @@ -0,0 +1,58 @@ + + + + + org.apache.zookeeper + parent + 3.10.0-SNAPSHOT + + pom + 4.0.0 + + zookeeper-compatibility-tests + + + zookeeper-compatibility-tests-curator + + + Apache ZooKeeper - Compatibility Tests + Module for various compatibility tests + + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + diff --git a/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/pom.xml b/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/pom.xml new file mode 100644 index 00000000000..44aa201a7e8 --- /dev/null +++ b/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/pom.xml @@ -0,0 +1,77 @@ + + + + + org.apache.zookeeper + zookeeper-compatibility-tests + 3.10.0-SNAPSHOT + + 4.0.0 + + zookeeper-compatibility-tests-curator + + Apache ZooKeeper - Compatibility Tests - Curator + Module for Apache Curator compatibility tests + + + 5.0.0 + + + + + org.apache.zookeeper + zookeeper + ${project.version} + + + + org.apache.curator + curator-recipes + ${apache-curator-version} + + + org.apache.zookeeper + zookeeper + + + test + + + + org.apache.curator + curator-test + ${apache-curator-version} + + + org.apache.zookeeper + zookeeper + + + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + \ No newline at end of file diff --git a/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/compatibility/TestApacheCuratorCompatibility.java b/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/compatibility/TestApacheCuratorCompatibility.java new file mode 100644 index 00000000000..d8111281b8f --- /dev/null +++ b/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/compatibility/TestApacheCuratorCompatibility.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zookeeper.compatibility; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.CuratorCache; +import org.apache.curator.retry.RetryOneTime; +import org.apache.curator.test.TestingCluster; +import org.apache.curator.test.TestingServer; +import org.junit.jupiter.api.Test; + +/** + * Make sure minimal Apache Curator APIs work correctly. As it's a widely used ZooKeeper + * client library we should not break it. + */ +public class TestApacheCuratorCompatibility { + private static final int TIMEOUT_MS = 5000; + + @Test + public void testBasicUsageOfApisAndRecipes() throws Exception { + try (TestingServer server = new TestingServer()) { + doTest(server.getConnectString()); + } + } + + @Test + public void testBasicUsageOfApisAndRecipesInCluster() throws Exception { + try (TestingCluster cluster = new TestingCluster(3)) { + cluster.start(); + doTest(cluster.getConnectString()); + } + } + + private void doTest(String connectionString) throws Exception { + RetryOneTime retryPolicy = new RetryOneTime(1); + try (CuratorFramework client = CuratorFrameworkFactory.newClient(connectionString, retryPolicy)) { + try (CuratorCache cache = CuratorCache.build(client, "/base/path")) { + client.start(); + cache.start(); + + BlockingQueue paths = new LinkedBlockingQueue<>(); + cache.listenable().addListener((dummy1, dummy2, data) -> paths.add(data.getPath())); + + client.create().creatingParentsIfNeeded().forPath("/base/path/1"); + client.create().creatingParentsIfNeeded().forPath("/base/path/2"); + client.create().creatingParentsIfNeeded().forPath("/base/path/1/a"); + client.create().creatingParentsIfNeeded().forPath("/base/path/2/a"); + + assertEquals("/base/path", poll(paths)); + assertEquals("/base/path/1", poll(paths)); + assertEquals("/base/path/2", poll(paths)); + assertEquals("/base/path/1/a", poll(paths)); + assertEquals("/base/path/2/a", poll(paths)); + } + } + } + + private static String poll(BlockingQueue queue) { + try { + String value = queue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS); + assertNotNull(value, "Event poll timed out"); + return value; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } +} diff --git a/zookeeper-contrib/pom.xml b/zookeeper-contrib/pom.xml old mode 100755 new mode 100644 index edc3cb351fd..66d76ebba65 --- a/zookeeper-contrib/pom.xml +++ b/zookeeper-contrib/pom.xml @@ -24,7 +24,7 @@ org.apache.zookeeper parent - 3.7.0-SNAPSHOT + 3.10.0-SNAPSHOT zookeeper-contrib @@ -34,24 +34,30 @@ Contrib projects to Apache ZooKeeper - - zookeeper-contrib-loggraph - zookeeper-contrib-rest - zookeeper-contrib-zooinspector - + + true + + + + + full-build + + zookeeper-contrib-fatjar + zookeeper-contrib-rest + zookeeper-contrib-zooinspector + + + + fatjar + + zookeeper-contrib-fatjar + + + - - com.github.spotbugs - spotbugs-maven-plugin - 3.1.9 - - - true - - org.apache.maven.plugins maven-compiler-plugin @@ -66,33 +72,9 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.0 - - - com.puppycrawl.tools - checkstyle - ${checkstyle.version} - - checkstyle-simple.xml - checkstyleSuppressions.xml - UTF-8 - true - true - false - true - false - - - checkstyle - validate - - check - - - diff --git a/zookeeper-contrib/zookeeper-contrib-fatjar/README.md b/zookeeper-contrib/zookeeper-contrib-fatjar/README.md new file mode 100644 index 00000000000..baf2f95f91f --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-fatjar/README.md @@ -0,0 +1,21 @@ +ZooKeeper Fatjar +================ + +This package contains build to create a fat zookeeper jar. Fatjar can be used to run: +- zookeeper server +- zookeeper client +- distributed load generator for testing (generateLoad) +- container that will instantiate classes as directed by an instance manager (ic) +- system test (systest) +- jmh micro benchmarks (jmh) + + +Use following command to build fatjar +``` +mvn clean install -P fatjar -DskipTests +``` + +To run the fatjar use: +``` +java -jar zoookeeper--fatjar.jar +``` diff --git a/zookeeper-contrib/zookeeper-contrib-fatjar/README.txt b/zookeeper-contrib/zookeeper-contrib-fatjar/README.txt deleted file mode 100644 index f8027ae8c9e..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-fatjar/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This package contains build to create a fat zookeeper jar. You need to run ant to create the fat jar. -To run the fatjar you can use. java -jar zoookeeper-*fatjar.jar diff --git a/zookeeper-contrib/zookeeper-contrib-fatjar/build.xml b/zookeeper-contrib/zookeeper-contrib-fatjar/build.xml deleted file mode 100644 index 3a1dd958e4e..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-fatjar/build.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zookeeper-contrib/zookeeper-contrib-fatjar/pom.xml b/zookeeper-contrib/zookeeper-contrib-fatjar/pom.xml new file mode 100644 index 00000000000..3cf8a30b607 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-fatjar/pom.xml @@ -0,0 +1,141 @@ + + + + 4.0.0 + + org.apache.zookeeper + zookeeper-contrib + 3.10.0-SNAPSHOT + + + org.apache.zookeeper + zookeeper-contrib-fatjar + jar + Apache ZooKeeper - Contrib - Fatjar + + + true + true + + + + + org.apache.zookeeper + zookeeper-jute + ${project.version} + + + org.apache.zookeeper + zookeeper + ${project.version} + + + org.apache.zookeeper + zookeeper + ${project.version} + test-jar + + + org.apache.zookeeper + zookeeper-it + ${project.version} + + + org.slf4j + slf4j-api + + + commons-cli + commons-cli + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-servlet + + + com.fasterxml.jackson.core + jackson-databind + + + org.jline + jline + + + io.dropwizard.metrics + metrics-core + + + org.xerial.snappy + snappy-java + + + ch.qos.logback + logback-core + + + + + + + ${project.basedir}/src/main/resources + + + ${project.basedir}/../../conf + + logback.xml + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + jar-with-dependencies + + + + org.apache.zookeeper.util.FatJarMain + + + zookeeper-${project.version}-fatjar + false + false + + + + make-assembly + package + + single + + + + + + + + diff --git a/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/mainClasses b/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/mainClasses index ba29e891973..c7b27a1325b 100644 --- a/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/mainClasses +++ b/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/mainClasses @@ -4,8 +4,6 @@ client:org.apache.zookeeper.ZooKeeperMain:Client shell to ZooKeeper server:org.apache.zookeeper.server.quorum.QuorumPeerMain:Start ZooKeeper server ::Test Commands generateLoad:org.apache.zookeeper.test.system.GenerateLoad:A distributed load generator for testing -quorumBench:org.apache.zookeeper.server.QuorumBenchmark:A benchmark of just the quorum protocol -abBench:org.apache.zookeeper.server.quorum.AtomicBroadcastBenchmark:A benchmark of just the atomic broadcast ic:org.apache.zookeeper.test.system.InstanceContainer:A container that will instantiate classes as directed by an instance manager systest:org.apache.zookeeper.test.system.BaseSysTest:Start system test jmh:org.apache.zookeeper.BenchMain:Run jmh micro benchmarks diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako index e046afc4fe5..9dd7fe7a2fe 100644 --- a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako @@ -96,7 +96,7 @@ ${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Z ${show_stats(leader)} Followers - ${leader.get('zk_followers', '')} + ${leader.get('zk_learners', '')} Synced Followers diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt b/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt deleted file mode 100644 index 8ccaa1ca5b0..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt +++ /dev/null @@ -1,70 +0,0 @@ -LogGraph README - -1 - About -LogGraph is an application for viewing and filtering zookeeper logs. It can handle transaction logs and message logs. - -2 - Compiling - -Run "ant jar" in src/contrib/loggraph/. This will download all dependencies and compile all the loggraph code. - -Once compilation has finished, you can run it the the loggraph.sh script in zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources. -This will start and embedded web server on your machine. -Navigate to http://localhost:8182/graph/main.html - -3 - Usage -LogGraph presents the user with 4 views, - - a) Simple log view - This view simply displays the log text. This isn't very useful without filters (see "Filtering the logs"). - - b) Server view - The server view shows the interactions between the different servers in an ensemble. The X axis represents time. - * Exceptions show up as red dots. Hovering your mouse over them will give you more details of the exception - * The colour of the line represents the election state of the server. - - orange means LOOKING for leader - - dark green means the server is the leader - - light green means the server is following a leader - - yellow means there isn't enough information to determine the state of the server. - * The gray arrows denote election messages between servers. Pink dashed arrows are messages that were sent but never delivered. - - c) Session view - The session view shows the lifetime of sessions on a server. Use the time filter to narrow down the view. Any more than about 2000 events will take a long time to view in your browser. - The X axis represents time. Each line is a session. The black dots represent events on the session. You can click on the black dots for more details of the event. - - d) Stats view - There is currently only one statistics view, Transactions/minute. Suggestions for other statistic views are very welcome. - -4 - Filtering the logs -The logs can be filtered in 2 ways, by time and by content. - -To filter by time simply move the slider to the desired start time. The time window specifies how many milliseconds after and including the start time will be displayed. - -Content filtering uses a adhoc filtering language, using prefix notation. The language looks somewhat similar to lisp. A statement in the language takes the form (op arg arg ....). A statement resolves to a boolean value. Statements can be nested. - -4.1 - Filter arguments -An argument can be a number, a string or a symbol. A number is any argument which starts with -, + or 0 to 9. If the number starts with 0x it is interpretted as hexidecimal. Otherwise it is interpretted as decimal. If the argument begins with a double-quote, (") it is interpretted as a string. Anything else is interpretted as a symbol. - -4.2 - Filter symbols -The possible filter symbols are: - -client-id : number, the session id of the client who initiated a transaction. -cxid : number, the cxid of a transaction -zxid : number, the zxid of a transaction -operation : string, the operation being performed, for example "setData", "createSession", "closeSession", "error", "create" - -4.3 - Filter operations -The possible filter operations are: - -or : logical or, takes 1 or more arguments which must be other statements. -and : logical and, takes 1 or more arguments which must be other statements. -not : logical not, takes 1 argument which must be another statement. -xor : exclusive or, takes 1 or more arguments which must be other statements. -= : equals, takes 1 or more arguments, which must all be equal to each other to return true. -> : greater than, takes 1 or more arguments, to return true the 1st argument must be greater than the 2nd argument which must be greater than the 3rd argument and so on... -< : less than, takes 1 or more arguments, to return true the 1st argument must be less than the 2nd argument which must be less than the 3rd argument and so on... - -4.3 - Filter examples -Give me all the setData operations with session id 0xdeadbeef or 0xcafeb33r but not with zxid 0x12341234 -> - -(and (= operation "setData") (or (= client-id 0xdeadbeef) (= client-id 0xcafeb33r)) (not (= zxid 0x12341234))) - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml deleted file mode 100644 index 07809ff7e93..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml deleted file mode 100644 index e3a1b486b7a..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - ZooKeeper Graphing - - - - - - - - - - - - - - - - - - - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java deleted file mode 100644 index b29c8462bf4..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.util.ArrayList; -import java.util.List; -import org.apache.zookeeper.graph.filterops.*; - -public abstract class FilterOp { - protected List subOps; - protected List args; - - public enum ArgType { - STRING, NUMBER, SYMBOL - } - - public FilterOp() { - subOps = new ArrayList(); - args = new ArrayList(); - } - - public static FilterOp newOp(String op) throws FilterException { - if (op.equals("or")) - return new OrOp(); - if (op.equals("and")) - return new AndOp(); - if (op.equals("not")) - return new NotOp(); - if (op.equals("xor")) - return new XorOp(); - if (op.equals("=")) - return new EqualsOp(); - if (op.equals("<")) - return new LessThanOp(); - if (op.equals(">")) - return new GreaterThanOp(); - - throw new FilterException("Invalid operation '"+op+"'"); - } - - public void addSubOp(FilterOp op) { - subOps.add(op); - } - - public void addArg(Arg arg) { - args.add(arg); - } - - public abstract boolean matches(LogEntry entry) throws FilterException; - - public String toString() { - String op = "(" + getClass().getName(); - for (FilterOp f : subOps) { - op += " " + f; - } - for (Arg a : args) { - op += " " + a; - } - return op + ")"; - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java deleted file mode 100644 index cf12e3a5335..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.PushbackReader; -import java.io.StringReader; -import java.io.IOException; -import java.util.ArrayList; - -import org.apache.zookeeper.graph.filterops.*; - -public class FilterParser { - private PushbackReader reader; - - public FilterParser(String s) { - reader = new PushbackReader(new StringReader(s)); - } - - private String readUntilSpace() throws IOException { - StringBuffer buffer = new StringBuffer(); - - int c = reader.read(); - while (!Character.isWhitespace(c) && c != ')' && c != '(') { - buffer.append((char)c); - c = reader.read(); - if (c == -1) { - break; - } - } - reader.unread(c); - - return buffer.toString().trim(); - } - - private StringArg readStringArg() throws IOException, FilterException { - int c = reader.read(); - int last = 0; - if (c != '"') { - throw new FilterException("Check the parser, trying to read a string that doesn't begin with quotes"); - } - StringBuffer buffer = new StringBuffer(); - while (reader.ready()) { - last = c; - c = reader.read(); - if (c == -1) { - break; - } - - if (c == '"' && last != '\\') { - return new StringArg(buffer.toString()); - } else { - buffer.append((char)c); - } - } - throw new FilterException("Unterminated string"); - } - - private NumberArg readNumberArg() throws IOException, FilterException { - String strval = readUntilSpace(); - - try { - if (strval.startsWith("0x")) { - return new NumberArg(Long.valueOf(strval.substring(2), 16)); - } else { - return new NumberArg(Long.valueOf(strval)); - } - } catch (NumberFormatException e) { - throw new FilterException("Not a number [" + strval + "]\n" + e); - } - } - - private SymbolArg readSymbolArg() throws IOException, FilterException { - return new SymbolArg(readUntilSpace()); - } - - public FilterOp parse() throws IOException, FilterException { - int c = reader.read(); - if (c != '(') { - throw new FilterException("Invalid format"); - } - - String opstr = readUntilSpace(); - FilterOp op = FilterOp.newOp(opstr); - - while (reader.ready()) { - c = reader.read(); - if (c == -1) { - break; - } - if (c == '(') { - reader.unread(c); - op.addSubOp(parse()); - } else if (c == ')') { - return op; - } else if (c == '"') { - reader.unread(c); - op.addArg(readStringArg()); - } else if (Character.isDigit(c) || c == '-' || c == '+') { - reader.unread(c); - op.addArg(readNumberArg()); - } else if (Character.isJavaIdentifierStart(c)) { - reader.unread(c); - op.addArg(readSymbolArg()); - } - } - throw new FilterException("Incomplete filter"); - } - - public static void main(String[] args) throws IOException, FilterException { - if (args.length == 1) { - System.out.println(new FilterParser(args[0]).parse()); - } else { - System.out.println(new FilterParser("(or (and (= session foobar) (= session barfoo)) (= session sdfs))").parse()); - } - } -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java deleted file mode 100644 index 8215833f298..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.io.Writer; -import java.io.OutputStreamWriter; -import java.io.IOException; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Set; - -public class JsonGenerator { - private JSONObject root; - private Set servers; - - private class Message { - private int from; - private int to; - private long zxid; - - public Message(int from, int to, long zxid) { - this.from = from; - this.to = to; - this.zxid = zxid; - } - - public boolean equals(Message m) { - return (m.from == this.from - && m.to == this.to - && m.zxid == this.zxid); - } - }; - - public JSONObject txnEntry(TransactionEntry e) { - JSONObject event = new JSONObject(); - - event.put("time", Long.toString(e.getTimestamp())); - event.put("client", Long.toHexString(e.getClientId())); - event.put("cxid", Long.toHexString(e.getCxid())); - event.put("zxid", Long.toHexString(e.getZxid())); - event.put("op", e.getOp()); - event.put("extra", e.getExtra()); - event.put("type", "transaction"); - - return event; - } - - /** - Assumes entries are sorted by timestamp. - */ - public JsonGenerator(LogIterator iter) { - servers = new HashSet(); - - Pattern stateChangeP = Pattern.compile("- (LOOKING|FOLLOWING|LEADING)"); - Pattern newElectionP = Pattern.compile("New election. My id = (\\d+), Proposed zxid = (\\d+)"); - Pattern receivedProposalP = Pattern.compile("Notification: (\\d+) \\(n.leader\\), (\\d+) \\(n.zxid\\), (\\d+) \\(n.round\\), .+ \\(n.state\\), (\\d+) \\(n.sid\\), .+ \\(my state\\)"); - Pattern exceptionP = Pattern.compile("xception"); - - root = new JSONObject(); - Matcher m = null; - JSONArray events = new JSONArray(); - root.put("events", events); - - long starttime = Long.MAX_VALUE; - long endtime = 0; - - int leader = 0; - long curEpoch = 0; - boolean newEpoch = false; - - while (iter.hasNext()) { - LogEntry ent = iter.next(); - - if (ent.getTimestamp() < starttime) { - starttime = ent.getTimestamp(); - } - if (ent.getTimestamp() > endtime) { - endtime = ent.getTimestamp(); - } - - if (ent.getType() == LogEntry.Type.TXN) { - events.add(txnEntry((TransactionEntry)ent)); - } else { - Log4JEntry e = (Log4JEntry)ent; - servers.add(e.getNode()); - - if ((m = stateChangeP.matcher(e.getEntry())).find()) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", e.getNode()); - stateChange.put("state", m.group(1)); - events.add(stateChange); - - if (m.group(1).equals("LEADING")) { - leader = e.getNode(); - } - } else if ((m = newElectionP.matcher(e.getEntry())).find()) { - Iterator iterator = servers.iterator(); - long zxid = Long.valueOf(m.group(2)); - int count = (int)zxid;// & 0xFFFFFFFFL; - int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; - - if (leader != 0 && epoch > curEpoch) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", leader); - stateChange.put("state", "INIT"); - events.add(stateChange); - leader = 0; - } - - while (iterator.hasNext()) { - int dst = iterator.next(); - if (dst != e.getNode()) { - JSONObject msg = new JSONObject(); - msg.put("type", "postmessage"); - msg.put("src", e.getNode()); - msg.put("dst", dst); - msg.put("time", e.getTimestamp()); - msg.put("zxid", m.group(2)); - msg.put("count", count); - msg.put("epoch", epoch); - - events.add(msg); - } - } - } else if ((m = receivedProposalP.matcher(e.getEntry())).find()) { - // Pattern.compile("Notification: \\d+, (\\d+), (\\d+), \\d+, [^,]*, [^,]*, (\\d+)");//, LOOKING, LOOKING, 2 - int src = Integer.valueOf(m.group(4)); - long zxid = Long.valueOf(m.group(2)); - int dst = e.getNode(); - long epoch2 = Long.valueOf(m.group(3)); - - int count = (int)zxid;// & 0xFFFFFFFFL; - int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; - - if (leader != 0 && epoch > curEpoch) { - JSONObject stateChange = new JSONObject(); - stateChange.put("type", "stateChange"); - stateChange.put("time", e.getTimestamp()); - stateChange.put("server", leader); - stateChange.put("state", "INIT"); - events.add(stateChange); - leader = 0; - } - - if (src != dst) { - JSONObject msg = new JSONObject(); - msg.put("type", "delivermessage"); - msg.put("src", src); - msg.put("dst", dst); - msg.put("time", e.getTimestamp()); - msg.put("zxid", zxid); - msg.put("epoch", epoch); - msg.put("count", count); - msg.put("epoch2", epoch2); - - events.add(msg); - } - } else if ((m = exceptionP.matcher(e.getEntry())).find()) { - JSONObject ex = new JSONObject(); - ex.put("type", "exception"); - ex.put("server", e.getNode()); - ex.put("time", e.getTimestamp()); - ex.put("text", e.getEntry()); - events.add(ex); - } - } - JSONObject ex = new JSONObject(); - ex.put("type", "text"); - ex.put("time", ent.getTimestamp()); - String txt = ent.toString(); - ex.put("text", txt); - events.add(ex); - } - // System.out.println("pending messages: "+pendingMessages.size()); - root.put("starttime", starttime); - root.put("endtime", endtime); - - JSONArray serversarray = new JSONArray(); - root.put("servers", serversarray); - - Iterator iterator = servers.iterator(); - while (iterator.hasNext()) { - serversarray.add(iterator.next()); - } - } - - public String toString() { - return JSONValue.toJSONString(root); - } - - public static void main(String[] args) throws Exception { - MergedLogSource src = new MergedLogSource(args); - LogIterator iter = src.iterator(); - System.out.println(new JsonGenerator(iter)); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JSource.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JSource.java deleted file mode 100644 index 84a9d983e78..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JSource.java +++ /dev/null @@ -1,391 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.File; -import java.io.InputStreamReader; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.ArrayList; -import java.util.Date; -import java.text.SimpleDateFormat; -import java.text.ParseException; -import java.util.Calendar; -import java.util.GregorianCalendar; - -import java.io.EOFException; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Log4JSource implements LogSource { - private static final Logger LOG = LoggerFactory.getLogger(Log4JSource.class); - - private static final int skipN = 10000; - private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss,SSS"; - - private LogSkipList skiplist = null; - - private String file = null; - private long starttime = 0; - private long endtime = 0; - private int serverid = 0; - private long size = 0; - - private Pattern timep; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - public LogSkipList getSkipList() { return skiplist; } - - private class Log4JSourceIterator implements LogIterator { - private RandomAccessFileReader in; - private LogEntry next = null; - private long starttime = 0; - private long endtime = 0; - private String buf = ""; - private Log4JSource src = null; - private long skippedAtStart = 0; - private SimpleDateFormat dateformat = null; - private FilterOp filter = null; - - public Log4JSourceIterator(Log4JSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src, starttime, endtime, null); - } - - public Log4JSourceIterator(Log4JSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - - this.dateformat = new SimpleDateFormat(DATE_FORMAT); - this.src = src; - this.starttime = starttime; - this.endtime = endtime; - - File f = new File(src.file); - try { - in = new RandomAccessFileReader(f); - } catch (FileNotFoundException e) { - throw new IllegalArgumentException("Bad file passed in (" + src.file +") cannot open:" + e); - } - - // skip to the offset of latest skip point before starttime - LogSkipList.Mark start = src.getSkipList().findMarkBefore(starttime); - try { - in.seek(start.getBytes()); - skippedAtStart = start.getEntriesSkipped(); - } catch (IOException ioe) { - // if we can't skip, we should just read from the start - } - - LogEntry e; - while ((e = readNextEntry()) != null && e.getTimestamp() < endtime) { - if (e.getTimestamp() >= starttime && (filter == null || filter.matches(e))) { - next = e; - return; - } - skippedAtStart++; - } - this.filter = filter; - } - - synchronized public long size() throws IOException { - if (LOG.isTraceEnabled()) { - LOG.trace("size() called"); - } - - if (this.endtime >= src.getEndTime()) { - return src.size() - skippedAtStart; - } - - long pos = in.getPosition(); - - if (LOG.isTraceEnabled()) { - LOG.trace("saved pos () = " + pos); - } - - LogEntry e; - - LogSkipList.Mark lastseg = src.getSkipList().findMarkBefore(this.endtime); - in.seek(lastseg.getBytes()); - buf = ""; // clear the buf so we don't get something we read before we sought - // number of entries skipped to get to the end of the iterator, less the number skipped to get to the start - long count = lastseg.getEntriesSkipped() - skippedAtStart; - - while ((e = readNextEntry()) != null) { - if (LOG.isTraceEnabled()) { - //LOG.trace(e); - } - if (e.getTimestamp() > this.endtime) { - break; - } - count++; - } - in.seek(pos); - buf = ""; - - if (LOG.isTraceEnabled()) { - LOG.trace("size() = " + count); - } - - return count; - } - - synchronized private LogEntry readNextEntry() { - try { - try { - while (true) { - String line = in.readLine(); - if (line == null) { - break; - } - - Matcher m = src.timep.matcher(line); - if (m.lookingAt()) { - if (buf.length() > 0) { - LogEntry e = new Log4JEntry(src.timestampFromText(dateformat, buf), src.getServerId(), buf); - buf = line; - return e; - } - buf = line; - } else if (buf.length() > 0) { - buf += line + "\n"; - } - } - } catch (EOFException eof) { - // ignore, we've simply come to the end of the file - } - if (buf.length() > 0) { - LogEntry e = new Log4JEntry(src.timestampFromText(dateformat, buf), src.getServerId(), buf); - buf = ""; - return e; - } - } catch (Exception e) { - LOG.error("Error reading next entry in file (" + src.file + "): " + e); - return null; - } - return null; - } - - public boolean hasNext() { - return next != null; - } - - public LogEntry next() throws NoSuchElementException { - LogEntry ret = next; - LogEntry e = readNextEntry(); - - if (filter != null) { - try { - while (e != null && !filter.matches(e)) { - e = readNextEntry(); - } - } catch (FilterException fe) { - throw new NoSuchElementException(e.toString()); - } - } - - if (e != null && e.getTimestamp() < endtime) { - next = e; - } else { - next = null; - } - return ret; - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for L4J logs"); - } - - public void close() throws IOException { - in.close(); - } - - public String toString() { - String size; - try { - size = new Long(size()).toString(); - } catch (IOException ioe) { - size = "Unable to read"; - } - return "Log4JSourceIterator(start=" + starttime + ", end=" + endtime + ", size=" + size + ")"; - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); //"This should never happen, you can't have a filter exception without a filter"); - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException{ - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new Log4JSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public Log4JSource(String file) throws IOException { - this.file=file; - - timep = Pattern.compile("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})"); - skiplist = new LogSkipList(); - init(); - } - - private static long timestampFromText(SimpleDateFormat format, String s) { - Date d = null; - try { - d = format.parse(s); - } catch (ParseException e) { - return 0; - } - Calendar c = new GregorianCalendar(); - c.setTime(d); - return c.getTimeInMillis(); - } - - private void init() throws IOException { - File f = new File(file); - RandomAccessFileReader in = new RandomAccessFileReader(f); - SimpleDateFormat dateformat = new SimpleDateFormat(DATE_FORMAT); - Pattern idp = Pattern.compile("\\[myid:(\\d+)\\]"); - - long lastFp = in.getPosition(); - String line = in.readLine(); - Matcher m = null; - - // if we have read data from the file, and it matchs the timep pattern - if ((line != null) && (m = timep.matcher(line)).lookingAt()) { - starttime = timestampFromText(dateformat, m.group(1)); - } else { - throw new IOException("Invalid log4j format. First line doesn't start with time"); - } - - /* - Count number of log entries. Any line starting with a timestamp counts as an entry - */ - String lastentry = line; - try { - while (line != null) { - m = timep.matcher(line); - if (m.lookingAt()) { - if (size % skipN == 0) { - long time = timestampFromText(dateformat, m.group(1)); - skiplist.addMark(time, lastFp, size); - } - size++; - lastentry = line; - } - if (serverid == 0 && (m = idp.matcher(line)).find()) { - serverid = Integer.valueOf(m.group(1)); - } - - lastFp = in.getPosition(); - line = in.readLine(); - } - } catch (EOFException eof) { - // ignore, simply end of file, though really (line!=null) should have caught this - } finally { - in.close(); - } - - m = timep.matcher(lastentry); - if (m.lookingAt()) { - endtime = timestampFromText(dateformat, m.group(1)); - } else { - throw new IOException("Invalid log4j format. Last line doesn't start with time"); - } - } - - public String toString() { - return "Log4JSource(file=" + file + ", size=" + size + ", start=" + starttime + ", end=" + endtime +", id=" + serverid +")"; - } - - public static void main(String[] args) throws IOException { - final Log4JSource s = new Log4JSource(args[0]); - System.out.println(s); - - LogIterator iter; - - if (args.length == 3) { - final long starttime = Long.valueOf(args[1]); - final long endtime = Long.valueOf(args[2]); - iter = s.iterator(starttime, endtime); - - Thread t1 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - try { - iter.close(); - } catch (IOException ioe) { - System.out.println(ioe.getMessage()); - } - }; }; - Thread t2 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - try { - iter.close(); - } catch (IOException ioe) { - System.out.println(ioe.getMessage()); - } - }; }; - Thread t3 = new Thread() { public void run () { - - LogIterator iter = s.iterator(starttime, endtime); - System.out.println(iter); - }; }; - t1.start(); - t2.start(); - // t3.start(); - } else { - iter = s.iterator(); - } - - /*while (iter.hasNext()) { - System.out.println(iter.next()); - }*/ - iter.close(); - } - - public int getServerId() { - return serverid; - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogServer.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogServer.java deleted file mode 100644 index 5cffcdd1d5f..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogServer.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.ServletException; - -import java.io.IOException; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; - -import org.apache.zookeeper.graph.servlets.*; - -public class LogServer extends ServletContextHandler { - public LogServer(MergedLogSource src) throws Exception { - super(ServletContextHandler.SESSIONS); - setContextPath("/"); - - addServlet(new ServletHolder(new StaticContent()),"/graph/*"); - - addServlet(new ServletHolder(new Fs()),"/fs"); - addServlet(new ServletHolder(new GraphData(src)), "/data"); - addServlet(new ServletHolder(new FileLoader(src)), "/loadfile"); - addServlet(new ServletHolder(new NumEvents(src)), "/info"); - addServlet(new ServletHolder(new Throughput(src)), "/throughput"); - } - - public static void main(String[] args) { - try { - MergedLogSource src = new MergedLogSource(args); - System.out.println(src); - - Server server = new Server(8182); - server.setHandler(new LogServer(src)); - - server.start(); - server.join(); - - } catch (Exception e) { - // Something is wrong. - e.printStackTrace(); - } - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogSkipList.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogSkipList.java deleted file mode 100644 index e74444291af..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogSkipList.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.util.List; -import java.util.LinkedList; -import java.util.NoSuchElementException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** -Generic skip list for holding a rough index of a log file. When the log file is loaded, this -index is built by adding a mark every n entries. Then when a specific time position is requested -from the file, a point at most n-1 entries before the time position can be jumped to. - -*/ -public class LogSkipList { - private static final Logger LOG = LoggerFactory.getLogger(LogSkipList.class); - - private LinkedList marks; - - public class Mark { - private long time; - private long bytes; - private long skipped; - - public Mark(long time, long bytes, long skipped) { - this.time = time; - this.bytes = bytes; - this.skipped = skipped; - } - - public long getTime() { return this.time; } - public long getBytes() { return this.bytes; } - public long getEntriesSkipped() { return this.skipped; } - - public String toString() { - return "Mark(time=" + time + ", bytes=" + bytes + ", skipped=" + skipped + ")"; - } - }; - - public LogSkipList() { - if (LOG.isTraceEnabled()) { - LOG.trace("New skip list"); - } - marks = new LinkedList(); - } - - public void addMark(long time, long bytes, long skipped) { - if (LOG.isTraceEnabled()) { - LOG.trace("addMark (time:" + time + ", bytes: " + bytes + ", skipped: " + skipped + ")"); - } - marks.add(new Mark(time, bytes, skipped)); - } - - /** - Find the last mark in the skip list before time. - */ - public Mark findMarkBefore(long time) throws NoSuchElementException { - if (LOG.isTraceEnabled()) { - LOG.trace("findMarkBefore(" + time + ")"); - } - - Mark last = marks.getFirst(); - for (Mark m: marks) { - if (m.getTime() > time) { - break; - } - last = m; - } - - if (LOG.isTraceEnabled()) { - LOG.trace("return " + last ); - } - - return last; - } - -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MeasureThroughput.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MeasureThroughput.java deleted file mode 100644 index a42354cb3fb..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MeasureThroughput.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.IOException; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.DataOutputStream; -import java.io.PrintStream; - -import java.util.HashSet; -import java.util.Set; - -public class MeasureThroughput { - private static final int MS_PER_SEC = 1000; - private static final int MS_PER_MIN = MS_PER_SEC*60; - private static final int MS_PER_HOUR = MS_PER_MIN*60; - - public static void main(String[] args) throws IOException { - MergedLogSource source = new MergedLogSource(args); - - PrintStream ps_ms = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-ms.out"))); - PrintStream ps_sec = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-sec.out"))); - PrintStream ps_min = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-min.out"))); - PrintStream ps_hour = new PrintStream(new BufferedOutputStream(new FileOutputStream("throughput-hour.out"))); - LogIterator iter; - - System.out.println(source); - iter = source.iterator(); - long currentms = 0; - long currentsec = 0; - long currentmin = 0; - long currenthour = 0; - Set zxids_ms = new HashSet(); - long zxid_sec = 0; - long zxid_min = 0; - long zxid_hour = 0; - - while (iter.hasNext()) { - LogEntry e = iter.next(); - TransactionEntry cxn = (TransactionEntry)e; - - long ms = cxn.getTimestamp(); - long sec = ms/MS_PER_SEC; - long min = ms/MS_PER_MIN; - long hour = ms/MS_PER_HOUR; - - if (currentms != ms && currentms != 0) { - ps_ms.println("" + currentms + " " + zxids_ms.size()); - - zxid_sec += zxids_ms.size(); - zxid_min += zxids_ms.size(); - zxid_hour += zxids_ms.size(); - zxids_ms.clear(); - } - - if (currentsec != sec && currentsec != 0) { - ps_sec.println("" + currentsec*MS_PER_SEC + " " + zxid_sec); - - zxid_sec = 0; - } - - if (currentmin != min && currentmin != 0) { - ps_min.println("" + currentmin*MS_PER_MIN + " " + zxid_min); - - zxid_min = 0; - } - - if (currenthour != hour && currenthour != 0) { - ps_hour.println("" + currenthour*MS_PER_HOUR + " " + zxid_hour); - - zxid_hour = 0; - } - - currentms = ms; - currentsec = sec; - currentmin = min; - currenthour = hour; - - zxids_ms.add(cxn.getZxid()); - } - - iter.close(); - ps_ms.close(); - ps_sec.close(); - ps_min.close(); - ps_hour.close(); - } -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MergedLogSource.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MergedLogSource.java deleted file mode 100644 index bb789d39596..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MergedLogSource.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.DateFormat; -import java.util.Date; -import java.util.zip.Adler32; -import java.util.zip.Checksum; -import java.util.HashMap; - -import org.apache.jute.BinaryInputArchive; -import org.apache.jute.InputArchive; -import org.apache.jute.Record; -import org.apache.zookeeper.server.TraceFormatter; -import org.apache.zookeeper.server.persistence.FileHeader; -import org.apache.zookeeper.server.persistence.FileTxnLog; -import org.apache.zookeeper.server.util.SerializeUtils; -import org.apache.zookeeper.txn.TxnHeader; - -import org.apache.zookeeper.ZooDefs.OpCode; - -import org.apache.zookeeper.txn.CreateSessionTxn; -import org.apache.zookeeper.txn.CreateTxn; -import org.apache.zookeeper.txn.DeleteTxn; -import org.apache.zookeeper.txn.ErrorTxn; -import org.apache.zookeeper.txn.SetACLTxn; -import org.apache.zookeeper.txn.SetDataTxn; -import org.apache.zookeeper.txn.TxnHeader; - -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Vector; -import java.util.Iterator; -import java.util.Collections; -import java.util.NoSuchElementException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class MergedLogSource implements LogSource { - private static final Logger LOG = LoggerFactory.getLogger(MergedLogSource.class); - private Vector sources = null; - private long starttime = 0; - private long endtime = 0; - private long size = 0; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - - private class MergedLogSourceIterator implements LogIterator { - private LogEntry next = null; - private long start = 0; - private long end = 0; - private MergedLogSource src = null; - private LogIterator[] sources = null; - private LogEntry[] nexts = null; - private FilterOp filter = null; - - public MergedLogSourceIterator(MergedLogSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - Vector iters = new Vector(); - for (LogSource s : src.sources) { - if (s.overlapsRange(starttime, endtime)) { - iters.add(s.iterator(starttime, endtime, filter)); - } - } - - sources = new LogIterator[iters.size()]; - sources = iters.toArray(sources); - nexts = new LogEntry[iters.size()]; - for (int i = 0; i < sources.length; i++) { - if (sources[i].hasNext()) - nexts[i] = sources[i].next(); - } - this.filter = filter; - } - - public MergedLogSourceIterator(MergedLogSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src, starttime, endtime, null); - } - - public long size() throws IOException { - long size = 0; - for (LogIterator i : sources) { - size += i.size(); - } - return size; - } - - public boolean hasNext() { - for (LogEntry n : nexts) { - if (n != null) return true; - } - return false; - } - - public LogEntry next() { - int min = -1; - for (int i = 0; i < nexts.length; i++) { - if (nexts[i] != null) { - if (min == -1) { - min = i; - } else if (nexts[i].getTimestamp() < nexts[min].getTimestamp()) { - min = i; - } - } - } - if (min == -1) { - return null; - } else { - LogEntry e = nexts[min]; - nexts[min] = sources[min].next(); - return e; - } - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for Merged logs"); - } - - public void close() throws IOException { - for (LogIterator i : sources) { - i.close(); - } - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); // shouldn't happen without filter - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new MergedLogSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public MergedLogSource(String[] files) throws IOException { - sources = new Vector(); - for (String f : files) { - addSource(f); - } - } - - public void addSource(String f) throws IOException { - LogSource s = null; - if (TxnLogSource.isTransactionFile(f)) { - s = new TxnLogSource(f); - } else { - s = new Log4JSource(f); - } - - size += s.size(); - endtime = s.getEndTime() > endtime ? s.getEndTime() : endtime; - starttime = s.getStartTime() < starttime || starttime == 0 ? s.getStartTime() : starttime; - sources.add(s); - } - - public String toString() { - String s = "MergedLogSource(size=" + size + ", start=" + starttime + ", end=" + endtime +")"; - for (LogSource src : sources) { - s += "\n\t- " +src; - } - return s; - } - - public static void main(String[] args) throws IOException { - System.out.println("Time: " + System.currentTimeMillis()); - MergedLogSource s = new MergedLogSource(args); - System.out.println(s); - - LogIterator iter; - - iter = s.iterator(); - System.out.println("Time: " + System.currentTimeMillis()); - System.out.println("Iterator Size: " + iter.size()); - System.out.println("Time: " + System.currentTimeMillis()); - /* while (iter.hasNext()) { - System.out.println(iter.next()); - }*/ - iter.close(); - System.out.println("Time: " + System.currentTimeMillis()); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/RandomAccessFileReader.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/RandomAccessFileReader.java deleted file mode 100644 index 13a41a5ae3a..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/RandomAccessFileReader.java +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.File; -import java.io.Reader; -import java.io.IOException; -import java.io.EOFException; -import java.io.RandomAccessFile; -import java.io.FileNotFoundException; - -import java.io.DataInputStream; -import java.io.ByteArrayInputStream; -import java.io.DataInput; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RandomAccessFileReader extends Reader implements DataInput { - private static final Logger LOG = LoggerFactory.getLogger(RandomAccessFileReader.class); - private RandomAccessFile file; - private byte[] buffer; - private int buffersize; - private int bufferoffset; - private long fileoffset; - private long fp; - - private static final int DEFAULT_BUFFER_SIZE = 512*1024; // 512k - private int point = 0; - - public RandomAccessFileReader(File f) throws FileNotFoundException { - file = new RandomAccessFile(f, "r"); - if (LOG.isDebugEnabled()) { - try { - LOG.debug("Opened file(" + f + ") with FD (" + file.getFD() + ")"); - } catch (IOException ioe) { - LOG.debug("Opened file(" + f + ") coulds get FD"); - } - } - - buffer = new byte[DEFAULT_BUFFER_SIZE]; - buffersize = 0; - bufferoffset = 0; - fileoffset = 0; - fp = 0; - } - - /** - fill the buffer from the file. - fp keeps track of the file pointer. - fileoffset is the offset into the file to where the buffer came from. - */ - private int fill() throws IOException { - fileoffset = fp; - int read = file.read(buffer, 0, buffer.length); - - if (LOG.isDebugEnabled()) { - String buf = new String(buffer, 0, 40, "UTF-8"); - LOG.debug("fill(buffer=" + buf + ")"); - } - - if (read == -1) { // eof reached - buffersize = 0; - } else { - buffersize = read; - } - fp += buffersize; - bufferoffset = 0; - - return buffersize; - } - - /** - * Reader interface - */ - public boolean markSupported() { return false; } - - /** - copy what we can from buffer. if it's not enough, fill buffer again and copy again - */ - synchronized public int read(char[] cbuf, int off, int len) throws IOException { - // This could be faster, but probably wont be used - byte[] b = new byte[2]; - int bytesread = 0; - while (len > 0) { - int read = read(b, 0, 2); - bytesread += read; - if (read < 2) { - return bytesread; - } - cbuf[off] = (char)((b[0] << 8) | (b[1] & 0xff)); - off += read; - len -= read; - } - - return bytesread; - } - - synchronized public int read(byte[] buf, int off, int len) throws IOException { - if (LOG.isTraceEnabled()) { - LOG.trace("read(buf, off=" + off + ", len=" + len); - } - - int read = 0; - while (len > 0) { - if (buffersize == 0) { - fill(); - if (buffersize == 0) { - break; - } - } - - int tocopy = Math.min(len, buffersize); - if (LOG.isTraceEnabled()) { - LOG.trace("tocopy=" + tocopy); - } - - System.arraycopy(buffer, bufferoffset, buf, off, tocopy); - buffersize -= tocopy; - bufferoffset += tocopy; - - len -= tocopy; - read += tocopy; - off += tocopy; - } - if (LOG.isTraceEnabled()) { - LOG.trace("read=" + read); - } - - return read; - } - - public void close() throws IOException { - file.close(); - } - - /** - * Seek interface - */ - public long getPosition() { - return bufferoffset + fileoffset; - } - - synchronized public void seek(long pos) throws IOException { - if (LOG.isDebugEnabled()) { - LOG.debug("seek(" + pos + ")"); - } - file.seek(pos); - fp = pos; - buffersize = 0; // force a buffer fill on next read - } - - /** - works like the usual readLine but disregards \r to make things easier - */ - synchronized public String readLine() throws IOException { - StringBuffer s = null; - - // go through buffer until i find a \n, if i reach end of buffer first, put whats in buffer into string buffer, - // repeat - buffering: - for (;;) { - if (buffersize == 0) { - fill(); - if (buffersize == 0) { - break; - } - } - - for (int i = 0; i < buffersize; i++) { - if (buffer[bufferoffset + i] == '\n') { - if (i > 0) { // if \n is first char in buffer, leave the string buffer empty - if (s == null) { s = new StringBuffer(); } - s.append(new String(buffer, bufferoffset, i, "UTF-8")); - } - bufferoffset += i+1; - buffersize -= i+1; - break buffering; - } - } - - // We didn't find \n, read the whole buffer into string buffer - if (s == null) { s = new StringBuffer(); } - s.append(new String(buffer, bufferoffset, buffersize, "UTF-8")); - buffersize = 0; - } - - if (s == null) { - return null; - } else { - return s.toString(); - } - } - - /** - DataInput interface - */ - public void readFully(byte[] b) throws IOException { - readFully(b, 0, b.length); - } - - public void readFully(byte[] b, int off, int len) throws IOException - { - while (len > 0) { - int read = read(b, off, len); - len -= read; - off += read; - - if (read == 0) { - throw new EOFException("End of file reached"); - } - } - } - - public int skipBytes(int n) throws IOException { - seek(getPosition() + n); - return n; - } - - public boolean readBoolean() throws IOException { - return (readByte() != 0); - } - - public byte readByte() throws IOException { - byte[] b = new byte[1]; - readFully(b, 0, 1); - return b[0]; - } - - public int readUnsignedByte() throws IOException { - return (int)readByte(); - } - - public short readShort() throws IOException { - byte[] b = new byte[2]; - readFully(b, 0, 2); - return (short)((b[0] << 8) | (b[1] & 0xff)); - } - - public int readUnsignedShort() throws IOException { - byte[] b = new byte[2]; - readFully(b, 0, 2); - return (((b[0] & 0xff) << 8) | (b[1] & 0xff)); - } - - public char readChar() throws IOException { - return (char)readShort(); - } - - public int readInt() throws IOException { - byte[] b = new byte[4]; - readFully(b, 0, 4); - return (((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) | ((b[2] & 0xff) << 8) | (b[3] & 0xff)); - } - - public long readLong() throws IOException { - byte[] b = new byte[8]; - readFully(b, 0, 8); - - return (((long)(b[0] & 0xff) << 56) | ((long)(b[1] & 0xff) << 48) | - ((long)(b[2] & 0xff) << 40) | ((long)(b[3] & 0xff) << 32) | - ((long)(b[4] & 0xff) << 24) | ((long)(b[5] & 0xff) << 16) | - ((long)(b[6] & 0xff) << 8) | ((long)(b[7] & 0xff))); - } - - public float readFloat() throws IOException { - return Float.intBitsToFloat(readInt()); - } - - public double readDouble() throws IOException { - return Double.longBitsToDouble(readLong()); - } - - public String readUTF() throws IOException { - int len = readUnsignedShort(); - byte[] bytes = new byte[len+2]; - bytes[0] = (byte)((len >> 8) & 0xFF); - bytes[1] = (byte)(len & 0xFF); - readFully(bytes, 2, len); - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); - return dis.readUTF(); - } - - public static void main(String[] args) throws IOException { - RandomAccessFileReader f = new RandomAccessFileReader(new File(args[0])); - - long pos0 = f.getPosition(); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - long pos1 = f.getPosition(); - System.out.println("pos: " + pos1); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - f.seek(pos1); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - System.out.println("============="); - f.seek(pos0); - for (int i = 0; i < 5; i++) { - System.out.println(f.readLine()); - } - long pos2 = f.getPosition(); - System.out.println("============="); - System.out.println(f.readLine()); - f.seek(pos2); - System.out.println(f.readLine()); - f.close(); - } -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TransactionEntry.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TransactionEntry.java deleted file mode 100644 index 33c7189c172..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TransactionEntry.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -public class TransactionEntry extends LogEntry { - public TransactionEntry(long timestamp, long clientId, long Cxid, long Zxid, String op) { - this(timestamp, clientId, Cxid, Zxid, op, ""); - } - - public TransactionEntry(long timestamp, long clientId, long Cxid, long Zxid, String op, String extra) { - super(timestamp); - setAttribute("client-id", new Long(clientId)); - setAttribute("cxid", new Long(Cxid)); - setAttribute("zxid", new Long(Zxid)); - setAttribute("operation", op); - setAttribute("extra", extra); - } - - public long getClientId() { - return (Long)getAttribute("client-id"); - } - - public long getCxid() { - return (Long)getAttribute("cxid"); - } - - public long getZxid() { - return (Long)getAttribute("zxid"); - } - - public String getOp() { - return (String)getAttribute("operation"); - } - - public String getExtra() { - return (String)getAttribute("extra"); - } - - public String toString() { - return getTimestamp() + ":::session(0x" + Long.toHexString(getClientId()) + ") cxid(0x" + Long.toHexString(getCxid()) + ") zxid(0x" + Long.toHexString(getZxid()) + ") op(" + getOp() + ") extra(" + getExtra() +")"; - } - - public Type getType() { return LogEntry.Type.TXN; } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TxnLogSource.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TxnLogSource.java deleted file mode 100644 index ad2e2589376..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TxnLogSource.java +++ /dev/null @@ -1,380 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph; - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.IOException; -import java.text.DateFormat; -import java.util.Date; -import java.util.zip.Adler32; -import java.util.zip.Checksum; -import java.util.HashMap; - -import org.apache.jute.BinaryInputArchive; -import org.apache.jute.InputArchive; -import org.apache.jute.Record; -import org.apache.zookeeper.server.TraceFormatter; -import org.apache.zookeeper.server.TxnLogEntry; -import org.apache.zookeeper.server.persistence.FileHeader; -import org.apache.zookeeper.server.persistence.FileTxnLog; -import org.apache.zookeeper.server.util.SerializeUtils; -import org.apache.zookeeper.txn.TxnHeader; - -import org.apache.zookeeper.ZooDefs.OpCode; - -import org.apache.zookeeper.txn.CreateSessionTxn; -import org.apache.zookeeper.txn.CreateTxn; -import org.apache.zookeeper.txn.DeleteTxn; -import org.apache.zookeeper.txn.ErrorTxn; -import org.apache.zookeeper.txn.SetACLTxn; -import org.apache.zookeeper.txn.SetDataTxn; -import org.apache.zookeeper.txn.TxnHeader; - -import java.io.File; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class TxnLogSource implements LogSource { - private static final Logger LOG = LoggerFactory.getLogger(TxnLogSource.class); - - private LogSkipList skiplist = null; - private static final int skipN = 10000; - - private String file = null; - private long starttime = 0; - private long endtime = 0; - private long size = 0; - - public boolean overlapsRange(long starttime, long endtime) { - return (starttime <= this.endtime && endtime >= this.starttime); - } - - public long size() { return size; } - public long getStartTime() { return starttime; } - public long getEndTime() { return endtime; } - public LogSkipList getSkipList() { return skiplist; } - - public static boolean isTransactionFile(String file) throws IOException { - RandomAccessFileReader reader = new RandomAccessFileReader(new File(file)); - BinaryInputArchive logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - reader.close(); - - return fhdr.getMagic() == FileTxnLog.TXNLOG_MAGIC; - } - - private class TxnLogSourceIterator implements LogIterator { - private LogEntry next = null; - private long starttime = 0; - private long endtime = 0; - private TxnLogSource src = null; - private RandomAccessFileReader reader = null; - private BinaryInputArchive logStream = null; - private long skippedAtStart = 0; - private FilterOp filter = null; - - public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException { - this(src,starttime,endtime,null); - } - - public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - try { - this.src = src; - this.starttime = starttime; - this.endtime = endtime; - reader = new RandomAccessFileReader(new File(src.file)); - logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot open transaction log ("+src.file+") :" + e); - } - - LogSkipList.Mark start = src.getSkipList().findMarkBefore(starttime); - try { - reader.seek(start.getBytes()); - skippedAtStart = start.getEntriesSkipped(); - } catch (IOException ioe) { - // if we can't skip, we should just read from the start - } - - this.filter = filter; - - LogEntry e; - while ((e = readNextEntry()) != null && e.getTimestamp() < endtime) { - if (e.getTimestamp() >= starttime && (filter == null || filter.matches(e)) ) { - next = e; - return; - } - skippedAtStart++; - } - - - } - - public long size() throws IOException { - if (this.endtime >= src.getEndTime()) { - return src.size() - skippedAtStart; - } - - long pos = reader.getPosition(); - LogEntry e; - - LogSkipList.Mark lastseg = src.getSkipList().findMarkBefore(this.endtime); - reader.seek(lastseg.getBytes()); - // number of entries skipped to get to the end of the iterator, less the number skipped to get to the start - long count = lastseg.getEntriesSkipped() - skippedAtStart; - - while ((e = readNextEntry()) != null) { - if (e.getTimestamp() > this.endtime) { - break; - } - count++; - } - reader.seek(pos);; - - return count; - } - - private LogEntry readNextEntry() { - LogEntry e = null; - try { - long crcValue; - byte[] bytes; - try { - crcValue = logStream.readLong("crcvalue"); - - bytes = logStream.readBuffer("txnEntry"); - } catch (EOFException ex) { - return null; - } - - if (bytes.length == 0) { - return null; - } - Checksum crc = new Adler32(); - crc.update(bytes, 0, bytes.length); - if (crcValue != crc.getValue()) { - throw new IOException("CRC doesn't match " + crcValue + - " vs " + crc.getValue()); - } - - TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); - TxnHeader hdr = logEntry.getHeader(); - Record r = logEntry.getTxn(); - - switch (hdr.getType()) { - case OpCode.createSession: { - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "createSession"); - } - break; - case OpCode.closeSession: { - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "closeSession"); - } - break; - case OpCode.create: - if (r != null) { - CreateTxn create = (CreateTxn)r; - String path = create.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "create", path); - } - break; - case OpCode.setData: - if (r != null) { - SetDataTxn set = (SetDataTxn)r; - String path = set.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setData", path); - } - break; - case OpCode.setACL: - if (r != null) { - SetACLTxn setacl = (SetACLTxn)r; - String path = setacl.getPath(); - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setACL", path); - } - break; - case OpCode.error: - if (r != null) { - ErrorTxn error = (ErrorTxn)r; - - e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "error", "Error: " + error.getErr()); - } - break; - default: - LOG.info("Unknown op: " + hdr.getType()); - break; - } - - if (logStream.readByte("EOR") != 'B') { - throw new EOFException("Last transaction was partial."); - } - } catch (Exception ex) { - LOG.error("Error reading transaction from (" + src.file + ") :" + e); - return null; - } - return e; - } - - public boolean hasNext() { - return next != null; - } - - public LogEntry next() throws NoSuchElementException { - LogEntry ret = next; - LogEntry e = readNextEntry(); - - if (filter != null) { - try { - while (e != null && !filter.matches(e)) { - e = readNextEntry(); - } - } catch (FilterException fe) { - throw new NoSuchElementException(fe.toString()); - } - } - if (e != null && e.getTimestamp() < endtime) { - next = e; - } else { - next = null; - } - return ret; - } - - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("remove not supported for Txn logs"); - } - - public void close() throws IOException { - reader.close(); - } - } - - public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { - try { - return iterator(starttime, endtime, null); - } catch (FilterException fe) { - assert(false); // should never ever happen - return null; - } - } - - public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { - // sanitise start and end times - if (endtime < starttime) { - throw new IllegalArgumentException("End time (" + endtime + ") must be greater or equal to starttime (" + starttime + ")"); - } - - return new TxnLogSourceIterator(this, starttime, endtime, filter); - } - - public LogIterator iterator() throws IllegalArgumentException { - return iterator(starttime, endtime+1); - } - - public TxnLogSource(String file) throws IOException { - this.file = file; - - skiplist = new LogSkipList(); - - RandomAccessFileReader reader = new RandomAccessFileReader(new File(file)); - try { - BinaryInputArchive logStream = new BinaryInputArchive(reader); - FileHeader fhdr = new FileHeader(); - fhdr.deserialize(logStream, "fileheader"); - - byte[] bytes = null; - while (true) { - long lastFp = reader.getPosition(); - - long crcValue; - - try { - crcValue = logStream.readLong("crcvalue"); - bytes = logStream.readBuffer("txnEntry"); - } catch (EOFException e) { - break; - } - - if (bytes.length == 0) { - break; - } - Checksum crc = new Adler32(); - crc.update(bytes, 0, bytes.length); - if (crcValue != crc.getValue()) { - throw new IOException("CRC doesn't match " + crcValue + - " vs " + crc.getValue()); - } - if (logStream.readByte("EOR") != 'B') { - throw new EOFException("Last transaction was partial."); - } - TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); - TxnHeader hdr = logEntry.getHeader(); - Record r = logEntry.getTxn(); - - if (starttime == 0) { - starttime = hdr.getTime(); - } - endtime = hdr.getTime(); - - if (size % skipN == 0) { - skiplist.addMark(hdr.getTime(), lastFp, size); - } - size++; - } - if (bytes == null) { - throw new IOException("Nothing read from ("+file+")"); - } - } finally { - reader.close(); - } - } - - public String toString() { - return "TxnLogSource(file=" + file + ", size=" + size + ", start=" + starttime + ", end=" + endtime +")"; - } - - public static void main(String[] args) throws IOException, FilterException { - TxnLogSource s = new TxnLogSource(args[0]); - System.out.println(s); - - LogIterator iter; - - if (args.length == 3) { - long starttime = Long.valueOf(args[1]); - long endtime = Long.valueOf(args[2]); - FilterOp fo = new FilterParser("(or (and (> zxid 0x2f0bd6f5e0) (< zxid 0x2f0bd6f5e9)) (= operation \"error\"))").parse(); - System.out.println("fo: " + fo); - iter = s.iterator(starttime, endtime, fo); - } else { - iter = s.iterator(); - } - System.out.println(iter); - while (iter.hasNext()) { - System.out.println(iter.next()); - } - iter.close(); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/EqualsOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/EqualsOp.java deleted file mode 100644 index 409815af212..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/EqualsOp.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class EqualsOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null - && !last.equals(v)) { - return false; - } - last = v; - } - - return true; - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java deleted file mode 100644 index 244dd3dabdb..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class GreaterThanOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - Arg first = args.get(0); - - if (first != null) { - FilterOp.ArgType type = first.getType(); - if (type == FilterOp.ArgType.SYMBOL) { - String key = (String)first.getValue(); - Object v = entry.getAttribute(key); - if (v instanceof String) { - type = FilterOp.ArgType.STRING; - } else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) { - type = FilterOp.ArgType.NUMBER; - } else { - throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number"); - } - } - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null) { - if (type == FilterOp.ArgType.STRING) { - if (((String)last).compareTo((String)v) <= 0) { - return false; - } - } else if (type == FilterOp.ArgType.NUMBER) { - // System.out.println("last[" + ((Number)last).longValue() + "] v["+ ((Number)v).longValue() + "]"); - if (((Number)last).longValue() <= ((Number)v).longValue()) { - return false; - } - } - } - last = v; - } - return true; - } else { - return true; - } - } - -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/LessThanOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/LessThanOp.java deleted file mode 100644 index b7d9e09ac89..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/LessThanOp.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class LessThanOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - Arg first = args.get(0); - - if (first != null) { - FilterOp.ArgType type = first.getType(); - if (type == FilterOp.ArgType.SYMBOL) { - String key = (String)first.getValue(); - Object v = entry.getAttribute(key); - if (v instanceof String) { - type = FilterOp.ArgType.STRING; - } else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) { - type = FilterOp.ArgType.NUMBER; - } else { - throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number"); - } - } - - Object last = null; - for (Arg a : args) { - Object v = a.getValue(); - if (a.getType() == FilterOp.ArgType.SYMBOL) { - String key = (String)a.getValue(); - v = entry.getAttribute(key); - } - - if (last != null) { - if (type == FilterOp.ArgType.STRING) { - if (((String)last).compareTo((String)v) >= 0) { - return false; - } - } else if (type == FilterOp.ArgType.NUMBER) { - if (((Number)last).doubleValue() >= ((Number)v).doubleValue()) { - return false; - } - } - } - last = v; - } - return true; - } else { - return true; - } - } - -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/OrOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/OrOp.java deleted file mode 100644 index d6815894a9a..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/OrOp.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class OrOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - for (FilterOp f : subOps) { - if (f.matches(entry)) { - return true; - } - } - return false; - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/XorOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/XorOp.java deleted file mode 100644 index 9e778b199d5..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/XorOp.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.filterops; - -import org.apache.zookeeper.graph.LogEntry; -import org.apache.zookeeper.graph.FilterOp; -import org.apache.zookeeper.graph.FilterException; - -public class XorOp extends FilterOp { - public boolean matches(LogEntry entry) throws FilterException { - int count = 0; - for (FilterOp f : subOps) { - if (f.matches(entry)) { - count++; - if (count > 1) { - return false; - } - } - } - if (count == 1) { - return true; - } - return false; - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/FileLoader.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/FileLoader.java deleted file mode 100644 index 67e89454ab5..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/FileLoader.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import org.apache.zookeeper.graph.*; - -public class FileLoader extends JsonServlet -{ - private MergedLogSource source = null; - - public FileLoader(MergedLogSource src) throws Exception { - source = src; - } - - String handleRequest(JsonRequest request) throws Exception - { - String output = ""; - - String file = request.getString("path", "/"); - JSONObject o = new JSONObject(); - try { - this.source.addSource(file); - o.put("status", "OK"); - - } catch (Exception e) { - o.put("status", "ERR"); - o.put("error", e.toString()); - } - - return JSONValue.toJSONString(o); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Fs.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Fs.java deleted file mode 100644 index e5b1a01339d..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Fs.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; -import java.util.Arrays; -import java.util.Comparator; - -public class Fs extends JsonServlet -{ - String handleRequest(JsonRequest request) throws Exception - { - String output = ""; - JSONArray filelist = new JSONArray(); - - File base = new File(request.getString("path", "/")); - if (!base.exists() || !base.isDirectory()) { - throw new FileNotFoundException("Couldn't find [" + request + "]"); - } - File[] files = base.listFiles(); - Arrays.sort(files, new Comparator() { - public int compare(File o1, File o2) { - if (o1.isDirectory() != o2.isDirectory()) { - if (o1.isDirectory()) { - return -1; - } else { - return 1; - } - } - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); - - for (File f : files) { - JSONObject o = new JSONObject(); - o.put("file", f.getName()); - o.put("type", f.isDirectory() ? "D" : "F"); - o.put("path", f.getCanonicalPath()); - filelist.add(o); - } - return JSONValue.toJSONString(filelist); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/GraphData.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/GraphData.java deleted file mode 100644 index fc10eb1a3d6..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/GraphData.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import org.apache.zookeeper.graph.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GraphData extends JsonServlet -{ - private static final Logger LOG = LoggerFactory.getLogger(GraphData.class); - private static final int DEFAULT_PERIOD = 1000; - - private LogSource source = null; - - public GraphData(LogSource src) throws Exception { - this.source = src; - } - - String handleRequest(JsonRequest request) throws Exception { - - - long starttime = 0; - long endtime = 0; - long period = 0; - FilterOp fo = null; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - String filterstr = request.getString("filter", ""); - - if (filterstr.length() > 0) { - fo = new FilterParser(filterstr).parse(); - } - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = starttime + DEFAULT_PERIOD; - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", period=" + period + ")"); - } - - LogIterator iterator = (fo != null) ? - source.iterator(starttime, endtime, fo) : source.iterator(starttime, endtime); - return new JsonGenerator(iterator).toString(); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/JsonServlet.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/JsonServlet.java deleted file mode 100644 index 910d44f4ede..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/JsonServlet.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.util.Map; - -abstract public class JsonServlet extends HttpServlet { - abstract String handleRequest(JsonRequest request) throws Exception; - - protected class JsonRequest { - private Map map; - - public JsonRequest(ServletRequest request) { - map = request.getParameterMap(); - } - - public long getNumber(String name, long defaultnum) { - String[] vals = (String[])map.get(name); - if (vals == null || vals.length == 0) { - return defaultnum; - } - - try { - return Long.valueOf(vals[0]); - } catch (NumberFormatException e) { - return defaultnum; - } - } - - public String getString(String name, String defaultstr) { - String[] vals = (String[])map.get(name); - if (vals == null || vals.length == 0) { - return defaultstr; - } else { - return vals[0]; - } - } - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - response.setContentType("text/plain;charset=utf-8"); - response.setStatus(HttpServletResponse.SC_OK); - - try { - String req = request.getRequestURI().substring(request.getServletPath().length()); - - response.getWriter().println(handleRequest(new JsonRequest(request))); - } catch (Exception e) { - JSONObject o = new JSONObject(); - o.put("error", e.toString()); - response.getWriter().println(JSONValue.toJSONString(o)); - } catch (java.lang.OutOfMemoryError oom) { - JSONObject o = new JSONObject(); - o.put("error", "Out of memory. Perhaps you've requested too many logs. Try narrowing you're filter criteria."); - response.getWriter().println(JSONValue.toJSONString(o)); - } - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/NumEvents.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/NumEvents.java deleted file mode 100644 index 5961a125832..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/NumEvents.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.File; -import java.io.IOException; -import java.io.FileNotFoundException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -import org.apache.zookeeper.graph.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class NumEvents extends JsonServlet -{ - private static final Logger LOG = LoggerFactory.getLogger(NumEvents.class); - private static final int DEFAULT_PERIOD = 1000; - - private LogSource source = null; - - public NumEvents(LogSource src) throws Exception { - this.source = src; - } - - String handleRequest(JsonRequest request) throws Exception { - String output = ""; - - long starttime = 0; - long endtime = 0; - long period = 0; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = source.getEndTime(); - } - } - - LogIterator iter = source.iterator(starttime, endtime); - JSONObject data = new JSONObject(); - data.put("startTime", starttime); - data.put("endTime", endtime); - long size = 0; - - size = iter.size(); - - data.put("numEntries", size); - if (LOG.isDebugEnabled()) { - LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", numEntries=" + size +")"); - } - iter.close(); - return JSONValue.toJSONString(data); - } -} - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/StaticContent.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/StaticContent.java deleted file mode 100644 index d91acb60096..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/StaticContent.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.BufferedReader; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class StaticContent extends HttpServlet { - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - String path = request.getRequestURI().substring(request.getServletPath().length()); - - InputStream resource = ClassLoader.getSystemResourceAsStream("org/apache/zookeeper/graph/resources" + path); - if (resource == null) { - response.getWriter().println(path + " not found!"); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return; - } - try { - while (resource.available() > 0) { - response.getWriter().write(resource.read()); - } - } finally { - resource.close(); - } - // response.setContentType("text/plain;charset=utf-8"); - response.setStatus(HttpServletResponse.SC_OK); - } - -} diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Throughput.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Throughput.java deleted file mode 100644 index 80ed1dc2fd1..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Throughput.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.graph.servlets; - -import java.io.IOException; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.DataOutputStream; -import java.io.PrintStream; - -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Set; - -import org.apache.zookeeper.graph.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - - -public class Throughput extends JsonServlet -{ - private static final int MS_PER_SEC = 1000; - private static final int MS_PER_MIN = MS_PER_SEC*60; - private static final int MS_PER_HOUR = MS_PER_MIN*60; - - private LogSource source = null; - - public Throughput(LogSource src) throws Exception { - this.source = src; - } - - public String handleRequest(JsonRequest request) throws Exception { - long starttime = 0; - long endtime = 0; - long period = 0; - long scale = 0; - - starttime = request.getNumber("start", 0); - endtime = request.getNumber("end", 0); - period = request.getNumber("period", 0); - - - if (starttime == 0) { starttime = source.getStartTime(); } - if (endtime == 0) { - if (period > 0) { - endtime = starttime + period; - } else { - endtime = source.getEndTime(); - } - } - - String scalestr = request.getString("scale", "minutes"); - if (scalestr.equals("seconds")) { - scale = MS_PER_SEC; - } else if (scalestr.equals("hours")) { - scale = MS_PER_HOUR; - } else { - scale = MS_PER_MIN; - } - - LogIterator iter = source.iterator(starttime, endtime); - - long current = 0; - long currentms = 0; - Set zxids_ms = new HashSet(); - long zxidcount = 0; - - JSONArray events = new JSONArray(); - while (iter.hasNext()) { - LogEntry e = iter.next(); - if (e.getType() != LogEntry.Type.TXN) { - continue; - } - - TransactionEntry cxn = (TransactionEntry)e; - - long ms = cxn.getTimestamp(); - long inscale = ms/scale; - - if (currentms != ms && currentms != 0) { - zxidcount += zxids_ms.size(); - zxids_ms.clear(); - } - - if (inscale != current && current != 0) { - JSONObject o = new JSONObject(); - o.put("time", current*scale); - o.put("count", zxidcount); - events.add(o); - zxidcount = 0; - } - current = inscale; - currentms = ms; - - zxids_ms.add(cxn.getZxid()); - } - JSONObject o = new JSONObject(); - o.put("time", current*scale); - o.put("count", zxidcount); - events.add(o); - - iter.close(); - - return JSONValue.toJSONString(events); - } - -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph-dev.sh b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph-dev.sh deleted file mode 100755 index e04434ec53c..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph-dev.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/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. - -make_canonical () { - cd $1; pwd; -} - -SCRIPTDIR=`dirname $0` -BUILDDIR=`make_canonical $SCRIPTDIR/../../../../../build/contrib/loggraph` -LIBDIR=`make_canonical $BUILDDIR/lib` -WEBDIR=`make_canonical $SCRIPTDIR/../web` -ZKDIR=`make_canonical $SCRIPTDIR/../../../../../build/` - -if [ ! -x $BUILDDIR ]; then - echo "\n\n*** You need to build loggraph before running it ***\n\n"; - exit; -fi - -for i in `ls $LIBDIR`; do - CLASSPATH=$LIBDIR/$i:$CLASSPATH -done - -for i in $ZKDIR/zookeeper-*.jar; do - CLASSPATH="$i:$CLASSPATH" -done - -CLASSPATH=$BUILDDIR/classes:$WEBDIR:$CLASSPATH -echo $CLASSPATH -java -Dlog4j.configuration=org/apache/zookeeper/graph/log4j.properties -Xdebug -Xrunjdwp:transport=dt_socket,address=4444,server=y,suspend=n -cp $CLASSPATH org.apache.zookeeper.graph.LogServer $* diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/log4j.properties b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/log4j.properties deleted file mode 100644 index ab8960b0e4f..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootLogger=TRACE, CONSOLE - -# Print the date in ISO 8601 format -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=TRACE -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n - -log4j.logger.org.apache.zookeeper.graph.LogSkipList=off -log4j.logger.org.apache.zookeeper.graph.RandomAccessFileReader=off -#log4j.logger.org.apache.zookeeper.graph.Log4JSource=off \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/date.format.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/date.format.js deleted file mode 100644 index 55150099217..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/date.format.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Date Format 1.2.3 - * (c) 2007-2009 Steven Levithan - * MIT license - * - * Includes enhancements by Scott Trenda - * and Kris Kowal - * - * Accepts a date, a mask, or a date and a mask. - * Returns a formatted version of the given date. - * The date defaults to the current date/time. - * The mask defaults to dateFormat.masks.default. - */ - -var dateFormat = function () { - var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, - timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, - timezoneClip = /[^-+\dA-Z]/g, - pad = function (val, len) { - val = String(val); - len = len || 2; - while (val.length < len) val = "0" + val; - return val; - }; - - // Regexes and supporting functions are cached through closure - return function (date, mask, utc) { - var dF = dateFormat; - - // You can't provide utc if you skip other args (use the "UTC:" mask prefix) - if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { - mask = date; - date = undefined; - } - - // Passing date through Date applies Date.parse, if necessary - date = date ? new Date(date) : new Date; - if (isNaN(date)) throw SyntaxError("invalid date"); - - mask = String(dF.masks[mask] || mask || dF.masks["default"]); - - // Allow setting the utc argument via the mask - if (mask.slice(0, 4) == "UTC:") { - mask = mask.slice(4); - utc = true; - } - - var _ = utc ? "getUTC" : "get", - d = date[_ + "Date"](), - D = date[_ + "Day"](), - m = date[_ + "Month"](), - y = date[_ + "FullYear"](), - H = date[_ + "Hours"](), - M = date[_ + "Minutes"](), - s = date[_ + "Seconds"](), - L = date[_ + "Milliseconds"](), - o = utc ? 0 : date.getTimezoneOffset(), - flags = { - d: d, - dd: pad(d), - ddd: dF.i18n.dayNames[D], - dddd: dF.i18n.dayNames[D + 7], - m: m + 1, - mm: pad(m + 1), - mmm: dF.i18n.monthNames[m], - mmmm: dF.i18n.monthNames[m + 12], - yy: String(y).slice(2), - yyyy: y, - h: H % 12 || 12, - hh: pad(H % 12 || 12), - H: H, - HH: pad(H), - M: M, - MM: pad(M), - s: s, - ss: pad(s), - l: pad(L, 3), - L: pad(L > 99 ? Math.round(L / 10) : L), - t: H < 12 ? "a" : "p", - tt: H < 12 ? "am" : "pm", - T: H < 12 ? "A" : "P", - TT: H < 12 ? "AM" : "PM", - Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), - o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), - S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] - }; - - return mask.replace(token, function ($0) { - return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); - }); - }; -}(); - -// Some common format strings -dateFormat.masks = { - "default": "ddd mmm dd yyyy HH:MM:ss", - shortDate: "m/d/yy", - mediumDate: "mmm d, yyyy", - longDate: "mmmm d, yyyy", - fullDate: "dddd, mmmm d, yyyy", - shortTime: "h:MM TT", - mediumTime: "h:MM:ss TT", - longTime: "h:MM:ss TT Z", - isoDate: "yyyy-mm-dd", - isoTime: "HH:MM:ss", - isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", - isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" -}; - -// Internationalization strings -dateFormat.i18n = { - dayNames: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" - ], - monthNames: [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ] -}; - -// For convenience... -Date.prototype.format = function (mask, utc) { - return dateFormat(this, mask, utc); -}; - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.bar.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.bar.js deleted file mode 100644 index 2f7212ad61b..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.bar.js +++ /dev/null @@ -1,385 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.barchart = function (x, y, width, height, values, opts) { - opts = opts || {}; - var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square", - gutter = parseFloat(opts.gutter || "20%"), - chart = this.set(), - bars = this.set(), - covers = this.set(), - covers2 = this.set(), - total = Math.max.apply(Math, values), - stacktotal = [], - paper = this, - multi = 0, - colors = opts.colors || this.g.colors, - len = values.length; - if (this.raphael.is(values[0], "array")) { - total = []; - multi = len; - len = 0; - for (var i = values.length; i--;) { - bars.push(this.set()); - total.push(Math.max.apply(Math, values[i])); - len = Math.max(len, values[i].length); - } - if (opts.stacked) { - for (var i = len; i--;) { - var tot = 0; - for (var j = values.length; j--;) { - tot +=+ values[j][i] || 0; - } - stacktotal.push(tot); - } - } - for (var i = values.length; i--;) { - if (values[i].length < len) { - for (var j = len; j--;) { - values[i].push(0); - } - } - } - total = Math.max.apply(Math, opts.stacked ? stacktotal : total); - } - - total = (opts.to) || total; - var barwidth = width / (len * (100 + gutter) + gutter) * 100, - barhgutter = barwidth * gutter / 100, - barvgutter = opts.vgutter == null ? 20 : opts.vgutter, - stack = [], - X = x + barhgutter, - Y = (height - 2 * barvgutter) / total; - if (!opts.stretch) { - barhgutter = Math.round(barhgutter); - barwidth = Math.floor(barwidth); - } - !opts.stacked && (barwidth /= multi || 1); - for (var i = 0; i < len; i++) { - stack = []; - for (var j = 0; j < (multi || 1); j++) { - var h = Math.round((multi ? values[j][i] : values[i]) * Y), - top = y + height - barvgutter - h, - bar = this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]}); - if (multi) { - bars[j].push(bar); - } else { - bars.push(bar); - } - bar.y = top; - bar.x = Math.round(X + barwidth / 2); - bar.w = barwidth; - bar.h = h; - bar.value = multi ? values[j][i] : values[i]; - if (!opts.stacked) { - X += barwidth; - } else { - stack.push(bar); - } - } - if (opts.stacked) { - var cvr; - covers2.push(cvr = this.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(this.g.shim)); - cvr.bars = this.set(); - var size = 0; - for (var s = stack.length; s--;) { - stack[s].toFront(); - } - for (var s = 0, ss = stack.length; s < ss; s++) { - var bar = stack[s], - cover, - h = (size + bar.value) * Y, - path = this.g.finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1); - cvr.bars.push(bar); - size && bar.attr({path: path}); - bar.h = h; - bar.y = y + height - barvgutter - !!size * .5 - h; - covers.push(cover = this.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(this.g.shim)); - cover.bar = bar; - cover.value = bar.value; - size += bar.value; - } - X += barwidth; - } - X += barhgutter; - } - covers2.toFront(); - X = x + barhgutter; - if (!opts.stacked) { - for (var i = 0; i < len; i++) { - for (var j = 0; j < (multi || 1); j++) { - var cover; - covers.push(cover = this.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(this.g.shim)); - cover.bar = multi ? bars[j][i] : bars[i]; - cover.value = cover.bar.value; - X += barwidth; - } - X += barhgutter; - } - } - chart.label = function (labels, isBottom) { - labels = labels || []; - this.labels = paper.set(); - var L, l = -Infinity; - if (opts.stacked) { - for (var i = 0; i < len; i++) { - var tot = 0; - for (var j = 0; j < (multi || 1); j++) { - tot += multi ? values[j][i] : values[i]; - if (j == multi - 1) { - var label = paper.g.labelise(labels[i], tot, total); - L = paper.g.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]); - var bb = L.getBBox(); - if (bb.x - 7 < l) { - L.remove(); - } else { - this.labels.push(L); - l = bb.x + bb.width; - } - } - } - } - } else { - for (var i = 0; i < len; i++) { - for (var j = 0; j < (multi || 1); j++) { - var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total); - L = paper.g.text(bars[i * (multi || 1) + j].x, isBottom ? y + height - barvgutter / 2 : bars[i * (multi || 1) + j].y - 10, label).insertBefore(covers[i * (multi || 1) + j]); - var bb = L.getBBox(); - if (bb.x - 7 < l) { - L.remove(); - } else { - this.labels.push(L); - l = bb.x + bb.width; - } - } - } - } - return this; - }; - chart.hover = function (fin, fout) { - covers2.hide(); - covers.show(); - covers.mouseover(fin).mouseout(fout); - return this; - }; - chart.hoverColumn = function (fin, fout) { - covers.hide(); - covers2.show(); - fout = fout || function () {}; - covers2.mouseover(fin).mouseout(fout); - return this; - }; - chart.click = function (f) { - covers2.hide(); - covers.show(); - covers.click(f); - return this; - }; - chart.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - chart.eachColumn = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers2.length; i--;) { - f.call(covers2[i]); - } - return this; - }; - chart.clickColumn = function (f) { - covers.hide(); - covers2.show(); - covers2.click(f); - return this; - }; - chart.push(bars, covers, covers2); - chart.bars = bars; - chart.covers = covers; - return chart; -}; -Raphael.fn.g.hbarchart = function (x, y, width, height, values, opts) { - opts = opts || {}; - var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square", - gutter = parseFloat(opts.gutter || "20%"), - chart = this.set(), - bars = this.set(), - covers = this.set(), - covers2 = this.set(), - total = Math.max.apply(Math, values), - stacktotal = [], - paper = this, - multi = 0, - colors = opts.colors || this.g.colors, - len = values.length; - if (this.raphael.is(values[0], "array")) { - total = []; - multi = len; - len = 0; - for (var i = values.length; i--;) { - bars.push(this.set()); - total.push(Math.max.apply(Math, values[i])); - len = Math.max(len, values[i].length); - } - if (opts.stacked) { - for (var i = len; i--;) { - var tot = 0; - for (var j = values.length; j--;) { - tot +=+ values[j][i] || 0; - } - stacktotal.push(tot); - } - } - for (var i = values.length; i--;) { - if (values[i].length < len) { - for (var j = len; j--;) { - values[i].push(0); - } - } - } - total = Math.max.apply(Math, opts.stacked ? stacktotal : total); - } - - total = (opts.to) || total; - var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100), - bargutter = Math.floor(barheight * gutter / 100), - stack = [], - Y = y + bargutter, - X = (width - 1) / total; - !opts.stacked && (barheight /= multi || 1); - for (var i = 0; i < len; i++) { - stack = []; - for (var j = 0; j < (multi || 1); j++) { - var val = multi ? values[j][i] : values[i], - bar = this.g.finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]}); - if (multi) { - bars[j].push(bar); - } else { - bars.push(bar); - } - bar.x = x + Math.round(val * X); - bar.y = Y + barheight / 2; - bar.w = Math.round(val * X); - bar.h = barheight; - bar.value = +val; - if (!opts.stacked) { - Y += barheight; - } else { - stack.push(bar); - } - } - if (opts.stacked) { - var cvr = this.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(this.g.shim); - covers2.push(cvr); - cvr.bars = this.set(); - var size = 0; - for (var s = stack.length; s--;) { - stack[s].toFront(); - } - for (var s = 0, ss = stack.length; s < ss; s++) { - var bar = stack[s], - cover, - val = Math.round((size + bar.value) * X), - path = this.g.finger(x, bar.y, val, barheight - 1, false, type, 1); - cvr.bars.push(bar); - size && bar.attr({path: path}); - bar.w = val; - bar.x = x + val; - covers.push(cover = this.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(this.g.shim)); - cover.bar = bar; - size += bar.value; - } - Y += barheight; - } - Y += bargutter; - } - covers2.toFront(); - Y = y + bargutter; - if (!opts.stacked) { - for (var i = 0; i < len; i++) { - for (var j = 0; j < multi; j++) { - var cover = this.rect(x, Y, width, barheight).attr(this.g.shim); - covers.push(cover); - cover.bar = bars[j][i]; - Y += barheight; - } - Y += bargutter; - } - } - chart.label = function (labels, isRight) { - labels = labels || []; - this.labels = paper.set(); - for (var i = 0; i < len; i++) { - for (var j = 0; j < multi; j++) { - var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total); - var X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5, - A = isRight ? "end" : "start", - L; - this.labels.push(L = paper.g.text(X, bars[i * (multi || 1) + j].y, label).attr({"text-anchor": A}).insertBefore(covers[0])); - if (L.getBBox().x < x + 5) { - L.attr({x: x + 5, "text-anchor": "start"}); - } else { - bars[i * (multi || 1) + j].label = L; - } - } - } - return this; - }; - chart.hover = function (fin, fout) { - covers2.hide(); - covers.show(); - fout = fout || function () {}; - covers.mouseover(fin).mouseout(fout); - return this; - }; - chart.hoverColumn = function (fin, fout) { - covers.hide(); - covers2.show(); - fout = fout || function () {}; - covers2.mouseover(fin).mouseout(fout); - return this; - }; - chart.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - chart.eachColumn = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers2.length; i--;) { - f.call(covers2[i]); - } - return this; - }; - chart.click = function (f) { - covers2.hide(); - covers.show(); - covers.click(f); - return this; - }; - chart.clickColumn = function (f) { - covers.hide(); - covers2.show(); - covers2.click(f); - return this; - }; - chart.push(bars, covers, covers2); - chart.bars = bars; - chart.covers = covers; - return chart; -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.dot.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.dot.js deleted file mode 100644 index 2821e62c730..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.dot.js +++ /dev/null @@ -1,110 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.dotchart = function (x, y, width, height, valuesx, valuesy, size, opts) { - function drawAxis(ax) { - +ax[0] && (ax[0] = paper.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlabels || null, opts.axisxtype || "t")); - +ax[1] && (ax[1] = paper.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.axisylabels || null, opts.axisytype || "t")); - +ax[2] && (ax[2] = paper.g.axis(x + gutter, y + height - gutter + maxR, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlabels || null, opts.axisxtype || "t")); - +ax[3] && (ax[3] = paper.g.axis(x + gutter - maxR, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.axisylabels || null, opts.axisytype || "t")); - } - opts = opts || {}; - var xdim = this.g.snapEnds(Math.min.apply(Math, valuesx), Math.max.apply(Math, valuesx), valuesx.length - 1), - minx = xdim.from, - maxx = xdim.to, - gutter = opts.gutter || 10, - ydim = this.g.snapEnds(Math.min.apply(Math, valuesy), Math.max.apply(Math, valuesy), valuesy.length - 1), - miny = ydim.from, - maxy = ydim.to, - len = Math.max(valuesx.length, valuesy.length, size.length), - symbol = this.g.markers[opts.symbol] || "disc", - res = this.set(), - series = this.set(), - max = opts.max || 100, - top = Math.max.apply(Math, size), - R = [], - paper = this, - k = Math.sqrt(top / Math.PI) * 2 / max; - - for (var i = 0; i < len; i++) { - R[i] = Math.min(Math.sqrt(size[i] / Math.PI) * 2 / k, max); - } - gutter = Math.max.apply(Math, R.concat(gutter)); - var axis = this.set(), - maxR = Math.max.apply(Math, R); - if (opts.axis) { - var ax = (opts.axis + "").split(/[,\s]+/); - drawAxis(ax); - var g = [], b = []; - for (var i = 0, ii = ax.length; i < ii; i++) { - var bb = ax[i].all ? ax[i].all.getBBox()[["height", "width"][i % 2]] : 0; - g[i] = bb + gutter; - b[i] = bb; - } - gutter = Math.max.apply(Math, g.concat(gutter)); - for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { - ax[i].remove(); - ax[i] = 1; - } - drawAxis(ax); - for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) { - axis.push(ax[i].all); - } - res.axis = axis; - } - var kx = (width - gutter * 2) / ((maxx - minx) || 1), - ky = (height - gutter * 2) / ((maxy - miny) || 1); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - var sym = this.raphael.is(symbol, "array") ? symbol[i] : symbol, - X = x + gutter + (valuesx[i] - minx) * kx, - Y = y + height - gutter - (valuesy[i] - miny) * ky; - sym && R[i] && series.push(this.g[sym](X, Y, R[i]).attr({fill: opts.heat ? this.g.colorValue(R[i], maxR) : Raphael.fn.g.colors[0], "fill-opacity": opts.opacity ? R[i] / max : 1, stroke: "none"})); - } - var covers = this.set(); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - var X = x + gutter + (valuesx[i] - minx) * kx, - Y = y + height - gutter - (valuesy[i] - miny) * ky; - covers.push(this.circle(X, Y, maxR).attr(this.g.shim)); - opts.href && opts.href[i] && covers[i].attr({href: opts.href[i]}); - covers[i].r = +R[i].toFixed(3); - covers[i].x = +X.toFixed(3); - covers[i].y = +Y.toFixed(3); - covers[i].X = valuesx[i]; - covers[i].Y = valuesy[i]; - covers[i].value = size[i] || 0; - covers[i].dot = series[i]; - } - res.covers = covers; - res.series = series; - res.push(series, axis, covers); - res.hover = function (fin, fout) { - covers.mouseover(fin).mouseout(fout); - return this; - }; - res.click = function (f) { - covers.click(f); - return this; - }; - res.each = function (f) { - if (!Raphael.is(f, "function")) { - return this; - } - for (var i = covers.length; i--;) { - f.call(covers[i]); - } - return this; - }; - res.href = function (map) { - var cover; - for (var i = covers.length; i--;) { - cover = covers[i]; - if (cover.X == map.x && cover.Y == map.y && cover.value == map.value) { - cover.attr({href: map.href}); - } - } - }; - return res; -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.line.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.line.js deleted file mode 100644 index eb56e5916dc..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.line.js +++ /dev/null @@ -1,230 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.linechart = function (x, y, width, height, valuesx, valuesy, opts) { - function shrink(values, dim) { - var k = values.length / dim, - j = 0, - l = k, - sum = 0, - res = []; - while (j < values.length) { - l--; - if (l < 0) { - sum += values[j] * (1 + l); - res.push(sum / k); - sum = values[j++] * -l; - l += k; - } else { - sum += values[j++]; - } - } - return res; - } - opts = opts || {}; - if (!this.raphael.is(valuesx[0], "array")) { - valuesx = [valuesx]; - } - if (!this.raphael.is(valuesy[0], "array")) { - valuesy = [valuesy]; - } - var allx = Array.prototype.concat.apply([], valuesx), - ally = Array.prototype.concat.apply([], valuesy), - xdim = this.g.snapEnds(Math.min.apply(Math, allx), Math.max.apply(Math, allx), valuesx[0].length - 1), - minx = xdim.from, - maxx = xdim.to, - gutter = opts.gutter || 10, - kx = (width - gutter * 2) / (maxx - minx), - ydim = this.g.snapEnds(Math.min.apply(Math, ally), Math.max.apply(Math, ally), valuesy[0].length - 1), - miny = ydim.from, - maxy = ydim.to, - ky = (height - gutter * 2) / (maxy - miny), - len = Math.max(valuesx[0].length, valuesy[0].length), - symbol = opts.symbol || "", - colors = opts.colors || Raphael.fn.g.colors, - that = this, - columns = null, - dots = null, - chart = this.set(), - path = []; - - for (var i = 0, ii = valuesy.length; i < ii; i++) { - len = Math.max(len, valuesy[i].length); - } - var shades = this.set(); - for (var i = 0, ii = valuesy.length; i < ii; i++) { - if (opts.shade) { - shades.push(this.path().attr({stroke: "none", fill: colors[i], opacity: opts.nostroke ? 1 : .3})); - } - if (valuesy[i].length > width - 2 * gutter) { - valuesy[i] = shrink(valuesy[i], width - 2 * gutter); - len = width - 2 * gutter; - } - if (valuesx[i] && valuesx[i].length > width - 2 * gutter) { - valuesx[i] = shrink(valuesx[i], width - 2 * gutter); - } - } - var axis = this.set(); - if (opts.axis) { - var ax = (opts.axis + "").split(/[,\s]+/); - +ax[0] && axis.push(this.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.northlabels)); - +ax[1] && axis.push(this.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.eastlabels)); - +ax[2] && axis.push(this.g.axis(x + gutter, y + height - gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.southlabels)); - +ax[3] && axis.push(this.g.axis(x + gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.westlabels)); - } - if (opts.northAxisLabel) { - this.g.text(x + gutter + width/2, gutter, opts.northAxisLabel); - } - if (opts.southAxisLabel) { - this.g.text(x + gutter + width/2, y + height + 20, opts.southAxisLabel); - } - if (opts.westAxisLabel) { - this.g.text(gutter, y + gutter + height/2, opts.westAxisLabel).attr({rotation: -90}); - } - if (opts.eastAxisLabel) { - this.g.text(x + gutter + width + 20, y + gutter + height/2, opts.eastAxisLabel).attr({rotation: 90}); - } - - var lines = this.set(), - symbols = this.set(), - line; - for (var i = 0, ii = valuesy.length; i < ii; i++) { - if (!opts.nostroke) { - lines.push(line = this.path().attr({ - stroke: colors[i], - "stroke-width": opts.width || 2, - "stroke-linejoin": "round", - "stroke-linecap": "round", - "stroke-dasharray": opts.dash || "" - })); - } - var sym = this.raphael.is(symbol, "array") ? symbol[i] : symbol, - symset = this.set(); - path = []; - for (var j = 0, jj = valuesy[i].length; j < jj; j++) { - var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx; - var Y = y + height - gutter - (valuesy[i][j] - miny) * ky; - (Raphael.is(sym, "array") ? sym[j] : sym) && symset.push(this.g[Raphael.fn.g.markers[this.raphael.is(sym, "array") ? sym[j] : sym]](X, Y, (opts.width || 2) * 3).attr({fill: colors[i], stroke: "none"})); - path = path.concat([j ? "L" : "M", X, Y]); - } - symbols.push(symset); - if (opts.shade) { - shades[i].attr({path: path.concat(["L", X, y + height - gutter, "L", x + gutter + ((valuesx[i] || valuesx[0])[0] - minx) * kx, y + height - gutter, "z"]).join(",")}); - } - !opts.nostroke && line.attr({path: path.join(",")}); - } - function createColumns(f) { - // unite Xs together - var Xs = []; - for (var i = 0, ii = valuesx.length; i < ii; i++) { - Xs = Xs.concat(valuesx[i]); - } - Xs.sort(); - // remove duplicates - var Xs2 = [], - xs = []; - for (var i = 0, ii = Xs.length; i < ii; i++) { - Xs[i] != Xs[i - 1] && Xs2.push(Xs[i]) && xs.push(x + gutter + (Xs[i] - minx) * kx); - } - Xs = Xs2; - ii = Xs.length; - var cvrs = f || that.set(); - for (var i = 0; i < ii; i++) { - var X = xs[i] - (xs[i] - (xs[i - 1] || x)) / 2, - w = ((xs[i + 1] || x + width) - xs[i]) / 2 + (xs[i] - (xs[i - 1] || x)) / 2, - C; - f ? (C = {}) : cvrs.push(C = that.rect(X - 1, y, Math.max(w + 1, 1), height).attr({stroke: "none", fill: "#000", opacity: 0})); - C.values = []; - C.symbols = that.set(); - C.y = []; - C.x = xs[i]; - C.axis = Xs[i]; - for (var j = 0, jj = valuesy.length; j < jj; j++) { - Xs2 = valuesx[j] || valuesx[0]; - for (var k = 0, kk = Xs2.length; k < kk; k++) { - if (Xs2[k] == Xs[i]) { - C.values.push(valuesy[j][k]); - C.y.push(y + height - gutter - (valuesy[j][k] - miny) * ky); - C.symbols.push(chart.symbols[j][k]); - } - } - } - f && f.call(C); - } - !f && (columns = cvrs); - } - function createDots(f) { - var cvrs = f || that.set(), - C; - for (var i = 0, ii = valuesy.length; i < ii; i++) { - for (var j = 0, jj = valuesy[i].length; j < jj; j++) { - var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx, - nearX = x + gutter + ((valuesx[i] || valuesx[0])[j ? j - 1 : 1] - minx) * kx, - Y = y + height - gutter - (valuesy[i][j] - miny) * ky; - f ? (C = {}) : cvrs.push(C = that.circle(X, Y, Math.abs(nearX - X) / 2).attr({stroke: "none", fill: "#000", opacity: 0})); - C.x = X; - C.y = Y; - C.value = valuesy[i][j]; - C.line = chart.lines[i]; - C.shade = chart.shades[i]; - C.symbol = chart.symbols[i][j]; - C.symbols = chart.symbols[i]; - C.axis = (valuesx[i] || valuesx[0])[j]; - f && f.call(C); - } - } - !f && (dots = cvrs); - } - chart.push(lines, shades, symbols, axis, columns, dots); - chart.lines = lines; - chart.shades = shades; - chart.symbols = symbols; - chart.axis = axis; - chart.hoverColumn = function (fin, fout) { - !columns && createColumns(); - columns.mouseover(fin).mouseout(fout); - return this; - }; - chart.clickColumn = function (f) { - !columns && createColumns(); - columns.click(f); - return this; - }; - chart.hrefColumn = function (cols) { - var hrefs = that.raphael.is(arguments[0], "array") ? arguments[0] : arguments; - if (!(arguments.length - 1) && typeof cols == "object") { - for (var x in cols) { - for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) { - columns[i].attr("href", cols[x]); - } - } - } - !columns && createColumns(); - for (var i = 0, ii = hrefs.length; i < ii; i++) { - columns[i] && columns[i].attr("href", hrefs[i]); - } - return this; - }; - chart.hover = function (fin, fout) { - !dots && createDots(); - dots.mouseover(fin).mouseout(fout); - return this; - }; - chart.click = function (f) { - !dots && createDots(); - dots.click(f); - return this; - }; - chart.each = function (f) { - createDots(f); - return this; - }; - chart.eachColumn = function (f) { - createColumns(f); - return this; - }; - return chart; -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.pie.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.pie.js deleted file mode 100644 index 8d203745528..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.pie.js +++ /dev/null @@ -1,205 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ -Raphael.fn.g.piechart = function (cx, cy, r, values, opts) { - opts = opts || {}; - var paper = this, - sectors = [], - covers = this.set(), - chart = this.set(), - series = this.set(), - order = [], - len = values.length, - angle = 0, - total = 0, - others = 0, - cut = 9, - defcut = true; - chart.covers = covers; - if (len == 1) { - series.push(this.circle(cx, cy, r).attr({fill: this.g.colors[0], stroke: opt.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth})); - covers.push(this.circle(cx, cy, r).attr(this.g.shim)); - total = values[0]; - values[0] = {value: values[0], order: 0, valueOf: function () { return this.value; }}; - series[0].middle = {x: cx, y: cy}; - series[0].mangle = 180; - } else { - function sector(cx, cy, r, startAngle, endAngle, fill) { - var rad = Math.PI / 180, - x1 = cx + r * Math.cos(-startAngle * rad), - x2 = cx + r * Math.cos(-endAngle * rad), - xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad), - y1 = cy + r * Math.sin(-startAngle * rad), - y2 = cy + r * Math.sin(-endAngle * rad), - ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad), - res = ["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2, "z"]; - res.middle = {x: xm, y: ym}; - return res; - } - for (var i = 0; i < len; i++) { - total += values[i]; - values[i] = {value: values[i], order: i, valueOf: function () { return this.value; }}; - } - values.sort(function (a, b) { - return b.value - a.value; - }); - for (var i = 0; i < len; i++) { - if (defcut && values[i] * 360 / total <= 1.5) { - cut = i; - defcut = false; - } - if (i > cut) { - defcut = false; - values[cut].value += values[i]; - values[cut].others = true; - others = values[cut].value; - } - } - len = Math.min(cut + 1, values.length); - others && values.splice(len) && (values[cut].others = true); - for (var i = 0; i < len; i++) { - var mangle = angle - 360 * values[i] / total / 2; - if (!i) { - angle = 90 - mangle; - mangle = angle - 360 * values[i] / total / 2; - } - if (opts.init) { - var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(","); - } - var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total); - var p = this.path(opts.init ? ipath : path).attr({fill: opts.colors && opts.colors[i] || this.g.colors[i] || "#666", stroke: opts.stroke || "#fff", "stroke-width": (opts.strokewidth == null ? 1 : opts.strokewidth), "stroke-linejoin": "round"}); - p.value = values[i]; - p.middle = path.middle; - p.mangle = mangle; - sectors.push(p); - series.push(p); - opts.init && p.animate({path: path.join(",")}, (+opts.init - 1) || 1000, ">"); - } - for (var i = 0; i < len; i++) { - var p = paper.path(sectors[i].attr("path")).attr(this.g.shim); - opts.href && opts.href[i] && p.attr({href: opts.href[i]}); - p.attr = function () {}; - covers.push(p); - series.push(p); - } - } - - chart.hover = function (fin, fout) { - fout = fout || function () {}; - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - mx: sector.middle.x, - my: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - cover.mouseover(function () { - fin.call(o); - }).mouseout(function () { - fout.call(o); - }); - })(series[i], covers[i], i); - } - return this; - }; - // x: where label could be put - // y: where label could be put - // value: value to show - // total: total number to count % - chart.each = function (f) { - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - x: sector.middle.x, - y: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - f.call(o); - })(series[i], covers[i], i); - } - return this; - }; - chart.click = function (f) { - var that = this; - for (var i = 0; i < len; i++) { - (function (sector, cover, j) { - var o = { - sector: sector, - cover: cover, - cx: cx, - cy: cy, - mx: sector.middle.x, - my: sector.middle.y, - mangle: sector.mangle, - r: r, - value: values[j], - total: total, - label: that.labels && that.labels[j] - }; - cover.click(function () { f.call(o); }); - })(series[i], covers[i], i); - } - return this; - }; - chart.inject = function (element) { - element.insertBefore(covers[0]); - }; - var legend = function (labels, otherslabel, mark, dir) { - var x = cx + r + r / 5, - y = cy, - h = y + 10; - labels = labels || []; - dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east"; - mark = paper.g.markers[mark && mark.toLowerCase()] || "disc"; - chart.labels = paper.set(); - for (var i = 0; i < len; i++) { - var clr = series[i].attr("fill"), - j = values[i].order, - txt; - values[i].others && (labels[j] = otherslabel || "Others"); - labels[j] = paper.g.labelise(labels[j], values[i], total); - chart.labels.push(paper.set()); - chart.labels[i].push(paper.g[mark](x + 5, h, 5).attr({fill: clr, stroke: "none"})); - chart.labels[i].push(txt = paper.text(x + 20, h, labels[j] || values[j]).attr(paper.g.txtattr).attr({fill: opts.legendcolor || "#000", "text-anchor": "start"})); - covers[i].label = chart.labels[i]; - h += txt.getBBox().height * 1.2; - } - var bb = chart.labels.getBBox(), - tr = { - east: [0, -bb.height / 2], - west: [-bb.width - 2 * r - 20, -bb.height / 2], - north: [-r - bb.width / 2, -r - bb.height - 10], - south: [-r - bb.width / 2, r + 10] - }[dir]; - chart.labels.translate.apply(chart.labels, tr); - chart.push(chart.labels); - }; - if (opts.legend) { - legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos); - } - chart.push(series, covers); - chart.series = series; - chart.covers = covers; - return chart; -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.raphael.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.raphael.js deleted file mode 100644 index 8e94c36cc07..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.raphael.js +++ /dev/null @@ -1,481 +0,0 @@ -/* - * g.Raphael 0.4 - Charting library, based on Raphaël - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ - - -(function () { - Raphael.fn.g = Raphael.fn.g || {}; - Raphael.fn.g.markers = { - disc: "disc", - o: "disc", - flower: "flower", - f: "flower", - diamond: "diamond", - d: "diamond", - square: "square", - s: "square", - triangle: "triangle", - t: "triangle", - star: "star", - "*": "star", - cross: "cross", - x: "cross", - plus: "plus", - "+": "plus", - arrow: "arrow", - "->": "arrow" - }; - Raphael.fn.g.shim = {stroke: "none", fill: "#000", "fill-opacity": 0}; - Raphael.fn.g.txtattr = {font: "12px Arial, sans-serif"}; - Raphael.fn.g.colors = []; - var hues = [.6, .2, .05, .1333, .75, 0]; - for (var i = 0; i < 10; i++) { - if (i < hues.length) { - Raphael.fn.g.colors.push("hsb(" + hues[i] + ", .75, .75)"); - } else { - Raphael.fn.g.colors.push("hsb(" + hues[i - hues.length] + ", 1, .5)"); - } - } - Raphael.fn.g.text = function (x, y, text) { - return this.text(x, y, text).attr(this.g.txtattr); - }; - Raphael.fn.g.labelise = function (label, val, total) { - if (label) { - return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g, function (all, value, percent) { - if (value) { - return (+val).toFixed(value.replace(/^#+\.?/g, "").length); - } - if (percent) { - return (val * 100 / total).toFixed(percent.replace(/^%+\.?/g, "").length) + "%"; - } - }); - } else { - return (+val).toFixed(0); - } - }; - - Raphael.fn.g.finger = function (x, y, width, height, dir, ending, isPath) { - // dir 0 for horisontal and 1 for vertical - if ((dir && !height) || (!dir && !width)) { - return isPath ? "" : this.path(); - } - ending = {square: "square", sharp: "sharp", soft: "soft"}[ending] || "round"; - var path; - height = Math.round(height); - width = Math.round(width); - x = Math.round(x); - y = Math.round(y); - switch (ending) { - case "round": - if (!dir) { - var r = Math.floor(height / 2); - if (width < r) { - r = width; - path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", 0, 0, "a", r, Math.floor(height / 2), 0, 0, 1, 0, height, "l", 0, 0, "z"]; - } else { - path = ["M", x + .5, y + .5 - r, "l", width - r, 0, "a", r, r, 0, 1, 1, 0, height, "l", r - width, 0, "z"]; - } - } else { - var r = Math.floor(width / 2); - if (height < r) { - r = height; - path = ["M", x - Math.floor(width / 2), y, "l", 0, 0, "a", Math.floor(width / 2), r, 0, 0, 1, width, 0, "l", 0, 0, "z"]; - } else { - path = ["M", x - r, y, "l", 0, r - height, "a", r, r, 0, 1, 1, width, 0, "l", 0, height - r, "z"]; - } - } - break; - case "sharp": - if (!dir) { - var half = Math.floor(height / 2); - path = ["M", x, y + half, "l", 0, -height, Math.max(width - half, 0), 0, Math.min(half, width), half, -Math.min(half, width), half + (half * 2 < height), "z"]; - } else { - var half = Math.floor(width / 2); - path = ["M", x + half, y, "l", -width, 0, 0, -Math.max(height - half, 0), half, -Math.min(half, height), half, Math.min(half, height), half, "z"]; - } - break; - case "square": - if (!dir) { - path = ["M", x, y + Math.floor(height / 2), "l", 0, -height, width, 0, 0, height, "z"]; - } else { - path = ["M", x + Math.floor(width / 2), y, "l", 1 - width, 0, 0, -height, width - 1, 0, "z"]; - } - break; - case "soft": - var r; - if (!dir) { - r = Math.min(width, Math.round(height / 5)); - path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", width - r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r * 2, "a", r, r, 0, 0, 1, -r, r, "l", r - width, 0, "z"]; - } else { - r = Math.min(Math.round(width / 5), height); - path = ["M", x - Math.floor(width / 2), y, "l", 0, r - height, "a", r, r, 0, 0, 1, r, -r, "l", width - 2 * r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r, "z"]; - } - } - if (isPath) { - return path.join(","); - } else { - return this.path(path); - } - }; - - // Symbols - Raphael.fn.g.disc = function (cx, cy, r) { - return this.circle(cx, cy, r); - }; - Raphael.fn.g.line = function (cx, cy, r) { - return this.rect(cx - r, cy - r / 5, 2 * r, 2 * r / 5); - }; - Raphael.fn.g.square = function (cx, cy, r) { - r = r * .7; - return this.rect(cx - r, cy - r, 2 * r, 2 * r); - }; - Raphael.fn.g.triangle = function (cx, cy, r) { - r *= 1.75; - return this.path("M".concat(cx, ",", cy, "m0-", r * .58, "l", r * .5, ",", r * .87, "-", r, ",0z")); - }; - Raphael.fn.g.diamond = function (cx, cy, r) { - return this.path(["M", cx, cy - r, "l", r, r, -r, r, -r, -r, r, -r, "z"]); - }; - Raphael.fn.g.flower = function (cx, cy, r, n) { - r = r * 1.25; - var rout = r, - rin = rout * .5; - n = +n < 3 || !n ? 5 : n; - var points = ["M", cx, cy + rin, "Q"], - R; - for (var i = 1; i < n * 2 + 1; i++) { - R = i % 2 ? rout : rin; - points = points.concat([+(cx + R * Math.sin(i * Math.PI / n)).toFixed(3), +(cy + R * Math.cos(i * Math.PI / n)).toFixed(3)]); - } - points.push("z"); - return this.path(points.join(",")); - }; - Raphael.fn.g.star = function (cx, cy, r, r2) { - r2 = r2 || r * .5; - var points = ["M", cx, cy + r2, "L"], - R; - for (var i = 1; i < 10; i++) { - R = i % 2 ? r : r2; - points = points.concat([(cx + R * Math.sin(i * Math.PI * .2)).toFixed(3), (cy + R * Math.cos(i * Math.PI * .2)).toFixed(3)]); - } - points.push("z"); - return this.path(points.join(",")); - }; - Raphael.fn.g.cross = function (cx, cy, r) { - r = r / 2.5; - return this.path("M".concat(cx - r, ",", cy, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"])); - }; - Raphael.fn.g.plus = function (cx, cy, r) { - r = r / 2; - return this.path("M".concat(cx - r / 2, ",", cy - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"])); - }; - Raphael.fn.g.arrow = function (cx, cy, r) { - return this.path("M".concat(cx - r * .7, ",", cy - r * .4, "l", [r * .6, 0, 0, -r * .4, r, r * .8, -r, r * .8, 0, -r * .4, -r * .6, 0], "z")); - }; - - // Tooltips - Raphael.fn.g.tag = function (x, y, text, angle, r) { - angle = angle || 0; - r = r == null ? 5 : r; - text = text == null ? "$9.99" : text; - var R = .5522 * r, - res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function () { - this.rotate(0, x, y); - var bb = this[1].getBBox(); - if (bb.height >= r * 2) { - this[0].attr({path: ["M", x, y + r, "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, "m", 0, -r * 2 -d, "a", r + d, r + d, 0, 1, 0, 0, (r + d) * 2, "L", x + r + d, y + bb.height / 2 + d, "l", bb.width + 2 * d, 0, 0, -bb.height - 2 * d, -bb.width - 2 * d, 0, "L", x, y - r - d].join(",")}); - } else { - var dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2)); - // ["c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r] - // "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, - this[0].attr({path: ["M", x, y + r, "c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r, "M", x + dx, y - bb.height / 2 - d, "a", r + d, r + d, 0, 1, 0, 0, bb.height + 2 * d, "l", r + d - dx + bb.width + 2 * d, 0, 0, -bb.height - 2 * d, "L", x + dx, y - bb.height / 2 - d].join(",")}); - } - this[1].attr({x: x + r + d + bb.width / 2, y: y}); - angle = (360 - angle) % 360; - this.rotate(angle, x, y); - angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]}); - return this; - }; - res.update(); - return res; - }; - Raphael.fn.g.popupit = function (x, y, set, dir, size) { - dir = dir == null ? 2 : dir; - size = size || 5; - x = Math.round(x) + .5; - y = Math.round(y) + .5; - var bb = set.getBBox(), - w = Math.round(bb.width / 2), - h = Math.round(bb.height / 2), - dx = [0, w + size * 2, 0, -w - size * 2], - dy = [-h * 2 - size * 3, -h - size, 0, -h - size], - p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, - "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size, - "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, - "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size, - "l", -Math.max(w - size, 0), 0, "z"].join(","), - xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir]; - set.translate(xy.x - w - bb.x, xy.y - h - bb.y); - return this.path(p).attr({fill: "#000", stroke: "none"}).insertBefore(set.node ? set : set[0]); - }; - Raphael.fn.g.popup = function (x, y, text, dir, size) { - dir = dir == null ? 2 : dir; - size = size || 5; - text = text || "$9.99"; - var res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function (X, Y, withAnimation) { - X = X || x; - Y = Y || y; - var bb = this[1].getBBox(), - w = bb.width / 2, - h = bb.height / 2, - dx = [0, w + size * 2, 0, -w - size * 2], - dy = [-h * 2 - size * 3, -h - size, 0, -h - size], - p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size, - "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size, - "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size, - "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size, - "l", -Math.max(w - size, 0), 0, "z"].join(","), - xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir]; - if (withAnimation) { - this[0].animate({path: p}, 500, ">"); - this[1].animate(xy, 500, ">"); - } else { - this[0].attr({path: p}); - this[1].attr(xy); - } - return this; - }; - return res.update(x, y); - }; - Raphael.fn.g.flag = function (x, y, text, angle) { - angle = angle || 0; - text = text || "$9.99"; - var res = this.set(), - d = 3; - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function (x, y) { - this.rotate(0, x, y); - var bb = this[1].getBBox(), - h = bb.height / 2; - this[0].attr({path: ["M", x, y, "l", h + d, -h - d, bb.width + 2 * d, 0, 0, bb.height + 2 * d, -bb.width - 2 * d, 0, "z"].join(",")}); - this[1].attr({x: x + h + d + bb.width / 2, y: y}); - angle = 360 - angle; - this.rotate(angle, x, y); - angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]}); - return this; - }; - return res.update(x, y); - }; - Raphael.fn.g.label = function (x, y, text) { - var res = this.set(); - res.push(this.rect(x, y, 10, 10).attr({stroke: "none", fill: "#000"})); - res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"})); - res.update = function () { - var bb = this[1].getBBox(), - r = Math.min(bb.width + 10, bb.height + 10) / 2; - this[0].attr({x: bb.x - r / 2, y: bb.y - r / 2, width: bb.width + r, height: bb.height + r, r: r}); - }; - res.update(); - return res; - }; - Raphael.fn.g.labelit = function (set) { - var bb = set.getBBox(), - r = Math.min(20, bb.width + 10, bb.height + 10) / 2; - return this.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r, r).attr({stroke: "none", fill: "#000"}).insertBefore(set[0]); - }; - Raphael.fn.g.drop = function (x, y, text, size, angle) { - size = size || 30; - angle = angle || 0; - var res = this.set(); - res.push(this.path(["M", x, y, "l", size, 0, "A", size * .4, size * .4, 0, 1, 0, x + size * .7, y - size * .7, "z"]).attr({fill: "#000", stroke: "none", rotation: [22.5 - angle, x, y]})); - angle = (angle + 90) * Math.PI / 180; - res.push(this.text(x + size * Math.sin(angle), y + size * Math.cos(angle), text).attr(this.g.txtattr).attr({"font-size": size * 12 / 30, fill: "#fff"})); - res.drop = res[0]; - res.text = res[1]; - return res; - }; - Raphael.fn.g.blob = function (x, y, text, angle, size) { - angle = (+angle + 1 ? angle : 45) + 90; - size = size || 12; - var rad = Math.PI / 180, - fontSize = size * 12 / 12; - var res = this.set(); - res.push(this.path().attr({fill: "#000", stroke: "none"})); - res.push(this.text(x + size * Math.sin((angle) * rad), y + size * Math.cos((angle) * rad) - fontSize / 2, text).attr(this.g.txtattr).attr({"font-size": fontSize, fill: "#fff"})); - res.update = function (X, Y, withAnimation) { - X = X || x; - Y = Y || y; - var bb = this[1].getBBox(), - w = Math.max(bb.width + fontSize, size * 25 / 12), - h = Math.max(bb.height + fontSize, size * 25 / 12), - x2 = X + size * Math.sin((angle - 22.5) * rad), - y2 = Y + size * Math.cos((angle - 22.5) * rad), - x1 = X + size * Math.sin((angle + 22.5) * rad), - y1 = Y + size * Math.cos((angle + 22.5) * rad), - dx = (x1 - x2) / 2, - dy = (y1 - y2) / 2, - rx = w / 2, - ry = h / 2, - k = -Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * dy * dy - ry * ry * dx * dx) / (rx * rx * dy * dy + ry * ry * dx * dx)), - cx = k * rx * dy / ry + (x1 + x2) / 2, - cy = k * -ry * dx / rx + (y1 + y2) / 2; - if (withAnimation) { - this.animate({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")}, 500, ">"); - } else { - this.attr({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")}); - } - return this; - }; - res.update(x, y); - return res; - }; - - Raphael.fn.g.colorValue = function (value, total, s, b) { - return "hsb(" + [Math.min((1 - value / total) * .4, 1), s || .75, b || .75] + ")"; - }; - - Raphael.fn.g.snapEnds = function (from, to, steps) { - var f = from, - t = to; - if (f == t) { - return {from: f, to: t, power: 0}; - } - function round(a) { - return Math.abs(a - .5) < .25 ? Math.floor(a) + .5 : Math.round(a); - } - var d = (t - f) / steps, - r = Math.floor(d), - R = r, - i = 0; - if (r) { - while (R) { - i--; - R = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i); - } - i ++; - } else { - while (!r) { - i = i || 1; - r = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i); - i++; - } - i && i--; - } - var t = round(to * Math.pow(10, i)) / Math.pow(10, i); - if (t < to) { - t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i); - } - var f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i)) / Math.pow(10, i); - return {from: f, to: t, power: i}; - }; - Raphael.fn.g.axis = function (x, y, length, from, to, steps, orientation, labels, type, dashsize) { - dashsize = dashsize == null ? 3 : dashsize; - type = type || "t"; - steps = steps || 10; - var path = type == "|" || type == " " ? ["M", x + .5, y, "l", 0, .001] : orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l", 0, -length] : ["M", x, y + .5, "l", length, 0], - ends = this.g.snapEnds(from, to, steps), - f = ends.from, - t = ends.to, - i = ends.power, - j = 0, - text = this.set(); - d = (t - f) / steps; - var label = f, - rnd = i > 0 ? i : 0; - dx = length / steps; - if (+orientation == 1 || +orientation == 3) { - var Y = y, - addon = (orientation - 1 ? 1 : -1) * (dashsize + 3 + !!(orientation - 1)); - while (Y >= y - length) { - type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), Y + .5, "l", dashsize * 2 + 1, 0])); - text.push(this.text(x + addon, Y, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"})); - label += d; - Y -= dx; - } - if (Math.round(Y + dx - (y - length))) { - type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), y - length + .5, "l", dashsize * 2 + 1, 0])); - text.push(this.text(x + addon, y - length, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"})); - } - } else { - var X = x, - label = f, - rnd = i > 0 ? i : 0, - addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation), - dx = length / steps, - txt = 0, - prev = 0; - while (X <= x + length) { - - text.push(txt = this.text(X, y + addon, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr)); - var bb = txt.getBBox(); - var ds = dashsize; - if (prev >= bb.x - 5) { - text.pop(text.length - 1).remove(); - ds = 1; - } else { - prev = bb.x + bb.width; - } - - type != "-" && type != " " && (path = path.concat(["M", X + .5, y - (type == "+" ? ds : !!orientation * ds * 2), "l", 0, ds * 2 + 1])); - - label += d; - X += dx; - } - if (Math.round(X - dx - x - length)) { - type != "-" && type != " " && (path = path.concat(["M", x + length + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1])); - text.push(this.text(x + length, y + addon, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr)); - } - } - var res = this.path(path); - res.text = text; - res.all = this.set([res, text]); - res.remove = function () { - this.text.remove(); - this.constructor.prototype.remove.call(this); - }; - return res; - }; - - Raphael.el.lighter = function (times) { - times = times || 2; - var fs = [this.attrs.fill, this.attrs.stroke]; - this.fs = this.fs || [fs[0], fs[1]]; - fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); - fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); - fs[0].b = Math.min(fs[0].b * times, 1); - fs[0].s = fs[0].s / times; - fs[1].b = Math.min(fs[1].b * times, 1); - fs[1].s = fs[1].s / times; - this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); - }; - Raphael.el.darker = function (times) { - times = times || 2; - var fs = [this.attrs.fill, this.attrs.stroke]; - this.fs = this.fs || [fs[0], fs[1]]; - fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex); - fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex); - fs[0].s = Math.min(fs[0].s * times, 1); - fs[0].b = fs[0].b / times; - fs[1].s = Math.min(fs[1].s * times, 1); - fs[1].b = fs[1].b / times; - this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"}); - }; - Raphael.el.original = function () { - if (this.fs) { - this.attr({fill: this.fs[0], stroke: this.fs[1]}); - delete this.fs; - } - }; -})(); \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load-big.gif b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load-big.gif deleted file mode 100644 index ddb7ff1aac1..00000000000 Binary files a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load-big.gif and /dev/null differ diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load.gif b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load.gif deleted file mode 100644 index d0bce154234..00000000000 Binary files a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load.gif and /dev/null differ diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.css b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.css deleted file mode 100644 index a84d90e07c4..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.css +++ /dev/null @@ -1,54 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -body { font-family: sans-serif; } - -div.fileSelector { border: solid 3px black; position: absolute; background: white; - -moz-border-radius: 10px; border-radius: 10px; - padding: 5px; font-family: sans-serif; - right: 10px; top: 10px; - } -div.fileSelector a { cursor: pointer; } -.fileSelector li.selectedFile { background: lightgreen; } - -div.selector { border: solid 3px black; position: absolute; background: white; - -moz-border-radius: 10px; border-radius: 10px; - padding: 5px; - right: 10px; top: 10px; background: #aaaaaa; opacity: 0.7; - } -div.selector a { cursor: pointer; } -.fileSelector li.selectedFile { background: lightgreen; } - -#fileLoader { -moz-border-radius: 10px; border-radius: 10px; background: #aaaaaa; opacity: 0.7; position: absolute; left: 20px; top: 20px; } -#loadingScreen { position: absolute; top: 100px; margin-left: 40%; margin-right: 40%; width: 500px; background: #aaaaaa; opacity: 0.7; -moz-border-radius: 10px; border-radius: 10px; text-align: center } -#filterinput { width: 500px; height: 100px; } -/* main interface */ -#actions { float: right; } -#views { float: left; } - -.closebutton { position: absolute; right: 5px; float: right; display: block; cursor: pointer; } - -.actionbutton { color: blue; text-decoration: none; padding: 3px; cursor: pointer; } -span:hover.actionbutton { background: lightblue; } - -#status { text-align: center; } - -#canvas { width: 100%; height: 1000px; } - -#logtable { width: 100%; } -.popUp { border: 3px solid black; -moz-border-radius: 10px; border-radius: 10px; position: absolute; background: white; padding: 10px; min-width: 300px; } - -.errorpage { position: absolute; top: 100px; margin-left: 40%; margin-right: 40%; width: 500px; background: #aaaaaa; opacity: 0.7; -moz-border-radius: 10px; border-radius: 10px; padding: 10px; } \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.js deleted file mode 100644 index 87bb7d89da2..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph = function(canvas, status) { - this.canvas = document.getElementById(canvas); - this.status = document.getElementById(status); - this.starttime = 0; - this.endtime = 0; - this.period = 0; - this.numEntries = 0; - this.currentRender = 0; - this.filter = ""; - - this.saveFilters = function () { - localStorage.starttime = this.starttime; - localStorage.endtime = this.endtime; - localStorage.period = this.period; - localStorage.filter = this.filter; - - }; - this.loadFilters = function () { - if (localStorage.starttime) { this.starttime = parseInt(localStorage.starttime); } - if (localStorage.endtime) { this.endtime = parseInt(localStorage.endtime); } - if (localStorage.period) { this.period = parseInt(localStorage.period); } - if (localStorage.filter) { this.filter = localStorage.filter; } - }; - this.loadFilters(); - var self = this; - - var updateStatus = function (starttime, period, filter, numEntries) { - self.starttime = starttime; - self.endtime = starttime + period; - self.period = period; - self.filter = filter; - self.saveFilters(); - - self.status.innerHTML = dateFormat(starttime, "HH:MM:ss,l") + " ⇒ " + dateFormat(self.endtime, "HH:MM:ss,l") + "    |    " + numEntries + " entries    |    " + (filter ? filter : "No filter"); - - if (self.currentRender) { - self.currentRender(); - } - }; - - YUI().use("io-base", function(Y) { - var uri = "/info"; - if (self.starttime) { - var uri = "/info?start=" + self.starttime + "&period=" + self.period + "&filter=" + self.filter; - } - - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); // Response data. - var period = data.endTime - data.startTime; - updateStatus(data.startTime, period, self.filter, data.numEntries); - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - - this.addLogs = function() { - new LogGraph.fileSelector(function (files) { new LogGraph.fileLoader(files); }); - }; - - this.editFilters = function() { - new LogGraph.filterSelector(this.starttime, this.period, this.filter, updateStatus); - }; - - this.getCleanCanvas = function () { - this.canvas.innerHTML = ""; - return this.canvas; - }; - - this.showLoadingScreen = function () { - this.loadingScreen = document.createElement("div"); - this.loadingScreen.id = "loadingScreen"; - this.loadingScreen.innerHTML = "

Loading...

"; - document.body.appendChild(this.loadingScreen); - }; - - this.hideLoadingScreen = function () { - document.body.removeChild(this.loadingScreen); - this.loadingScreen.style.visibility = "hidden"; - }; - - - /*** - * TODO: refactor these to load the data first, before handing to a draw funciton. - * We shouldn't pass the async q into the drawing function - */ - this.showLogs = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var loggraph = new LogGraph.LogTable(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showLogs; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.serverGraph = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var servergraph = new LogGraph.ServerGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showLogs; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.sessionGraph = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var sessiongraph = new LogGraph.SessionGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.sessionGraph; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; - - this.showStats = function() { - var self= this; - YUI().use('async-queue', function(Y) { - var q = new Y.AsyncQueue(self.showLoadingScreen, - // The second callback will pause the Queue and send an XHR for data - function () { - q.pause(); - var statgraph = new LogGraph.StatsGraph(q, self.getCleanCanvas(), self.starttime, self.endtime, self.filter); - self.currentRender = self.showStats; - }, - self.hideLoadingScreen); - q.run(); - } - ); - }; -}; - -LogGraph.error = function(description) { - var errorPage = document.createElement("div"); - errorPage.className = "errorpage"; - var p = document.createElement("p"); - p.innerHTML = description; - errorPage.appendChild(p); - - var span = document.createElement("span"); - p = document.createElement("p"); - span.className = "actionButton"; - span.innerHTML = "OK"; - span.onclick = function (evt) { - document.body.removeChild(errorPage); - delete errorPage; - } - p.appendChild(span); - errorPage.appendChild(p); - - document.body.appendChild(errorPage); -}; - -LogGraph.ticker =function(allow_dups) { - this.ticks = new Array(); - this.current_tick = 0; - this.allow_dups = allow_dups;; - - this.tick = function(time) { - if (time == this.ticks[this.ticks.length - 1] && this.allow_dups == true) - return this.current_tick; - - this.ticks.push(time); - return this.current_tick++; - }; - - this.current = function() { - return this.current_tick; - }; - - this.reset = function() { - while (this.ticks.length) { - this.ticks.pop(); - } - this.current_tick = 0; - }; -}; - - -LogGraph.timescale = function(starttime, endtime) { - this.starttime = starttime; - this.endtime = endtime; - this.millis = endtime - starttime; - - this.draw = function(paper) { - var scale = paper.set(); - scale.push(paper.path("M0 0 L" + paper.width + " 0")); - - for (var i = 0; i < paper.width; i += 100) { - scale.push(paper.path("M" + i + " 0 L" + i + " 5")); - // var time = dateFormat((this.starttime + (i*ms_per_pixel)), "h:MM:ss,l"); - // paper.text(i + 5, 10, time); - } - - scale.attr({"stroke-width": 2}); - }; -}; - -/* - Fetch data from an uri and process it, the process data func returns true if any of the data is useful -*/ -LogGraph.loadData = function (asyncq, uri, processdata) { - YUI().use("io-base", function(Y) { - function success(id, o, args) { - var data = eval("(" + o.responseText + ")"); // Response data. - if (data.error) { - LogGraph.error(data.error); - } else { - if (!processdata(data)) { - LogGraph.error("No data. Perhaps you should loosen your filter criteria."); - } - } - asyncq.run(); - }; - function failure(id, o, args) { - LogGraph.error("Error contacting server: (" + o.status + ") " + o.statusText); - asyncq.run(); - }; - - Y.on('io:success', success, Y, []); - Y.on('io:failure', failure, Y, []); - - var request = Y.io(uri); - }); -} \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.log.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.log.js deleted file mode 100644 index 551ea4b2373..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.log.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.LogTable = function (asyncq, canvas, starttime, endtime, filter) { - this.starttime = starttime; - this.endtime = endtime; - this.filter = filter; - - var table = document.createElement("table"); - table.id = "logtable"; - canvas.appendChild(table); - - this.addLogLine = function(time, text) { - var tr = document.createElement("tr"); - table.appendChild(tr); - - var td = document.createElement("td"); - td.innerHTML = dateFormat(time, "h:MM:ss,l"); - tr.appendChild(td); - - td = document.createElement("td"); - td.innerHTML = text; - tr.appendChild(td); - } - - var self = this; - var processdata = function(data) { - var events = data["events"]; - var count = 0; - for (var i in events) { - var e = events[i]; - if (e.type == "text") { - self.addLogLine(e.time, e.text); - count++; - } - } - return count != 0; - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + self.filter; - LogGraph.loadData(asyncq, uri, processdata); -}; diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.server.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.server.js deleted file mode 100644 index 0a74b5c24b8..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.server.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.ServerGraph = function(asyncq, canvas, starttime, endtime, filter) { - this.starttime = starttime; - this.endtime = endtime; - this.millis = endtime - starttime; - this.nextserverid = 0; - this.serveroffset = 100; - this.filter = filter; - - this.pixels_per_tick = 20; - this.ticker = new LogGraph.ticker(); - - - var paper = Raphael(canvas, 1, 1); - - var self = this; - - this.timescale = new LogGraph.timescale(starttime, endtime); - this.objects = new Array(); - - this.add = function(obj) { - this.objects.push(obj); - } - - this.tick_to_x = function (timestamp) { - var x = timestamp * this.pixels_per_tick; - return x; - }; - - this._drawTime = function(paper, x, time) { - var p = paper.path("M" + x + " 0 L" + x + " " + paper.height); - var t = paper.text(x, 10, dateFormat(time, "h:MM:ss,l")); - - t.hide(); - p.mouseover(function(evt) { - t.show(); - p.attr({stroke: "red"}); - }); - p.mouseout(function(evt) { - t.hide(); - p.attr({stroke: "lightgray"}); - }); - - return p; - }; - - this.draw = function(paper) { - var grid = paper.set(); - for (var i = 0; i < paper.height; i += 20) { - grid.push(paper.path("M0 " + i + " L" + paper.width + " " + i)); - } - var lasttick = this.starttime; - var scale = 500; // 500 ms - - var y = 0; - - for (var t = 0, len = this.ticker.ticks.length; t < len; t++) { - var basex = t * this.pixels_per_tick; - var thistick = this.ticker.ticks[t]; - var nexttick = t + 1 == this.ticker.ticks.length ? this.endtime : this.ticker.ticks[t+1]; - if (nexttick == thistick) { - continue; - } - var time = thistick - lasttick; - var first = scale - (lasttick % scale); - - /* for (var i = 0; (first+scale*i) < time; i++) { - - var toffset = first+scale*i; - var x = basex + LogGraph._pixels_per_tick * toffset/time; - grid.push(this._drawTime(paper, x, lasttick + toffset, grid)); - - }*/ - - - //grid.push(paper.path("M" + i + " 0 L" + i + " " + paper.height)); - lasttick = thistick; - } - grid.attr({stroke: "lightgray"}); - this.timescale.draw(paper); - - for (o in this.objects) { - this.objects[o].draw(paper); - } - }; - - - var processdata = function(data) { - var servermap = {}; - var servers = data.servers; - var count = 0; - for (s in servers) { - var server = new LogGraph.ServerGraph.server(self, "Server " + servers[s]); - servermap[servers[s]] = server; - self.add(server); - count++; - } - - var messages = {}; - var events = data.events; - for (var i in events) { - var e = events[i]; - var t = e.time; - if (e.type == "stateChange") { - servermap[e.server].addState(e.state, self.ticker.tick(e.time)); - } - if (e.type == "postmessage") { - src = servermap[e.src]; - dst = servermap[e.dst]; - var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid; - - var m = new LogGraph.ServerGraph.message(self, src, self.ticker.tick(e.time), dst, e.zxid); - messages[key] = m; - } - if (e.type == "delivermessage") { - var key = "key:s" + e.src + ",d" + e.dst + ",z" + e.zxid; - - var m = messages[key]; - if (m) { - m.dsttime = self.ticker.tick(e.time); - m.name = "Propose"; - self.add(m); - delete messages[key]; - } - } - if (e.type == "exception") { - servermap[e.server].addException(self.ticker.tick(e.time), e.text, e.time); - } - count++; - } - - for (var i in messages) { - var m = messages[i]; - m.markIncomplete(); - self.add(m); - count++; - } - - if (count != 0) { - paper.setSize(self.tick_to_x(self.ticker.current()), 1000); - - var line = paper.path("M0 0 L0 1000"); - line.attr({"stroke": "red", "stroke-dasharray": "- "}); - var base = canvas.offsetLeft;// + ((canvas.offsetWidth - paper.width)/2); - canvas.onmousemove = function (evt) { - var x = evt.screenX - base; - - line.attr({"path": "M" + x + " 0 L"+ x +" 1000"}); - - }; - - self.draw(paper); - return true; - } else { - return false; - } - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + filter; - - LogGraph.loadData(asyncq, uri, processdata); -}; - -LogGraph.ServerGraph.server = function (graph, name) { - this.graph = graph; - this.serverid = graph.nextserverid++; - this.name = name; - this.y = (this.serverid * 300 + graph.serveroffset); - this.states = new Array(); - this.exception = new Array(); - - this.addState = function(state, time) { - this.states.push([state, time]); - } - - this.addException = function(tick, exception, time) { - this.exception.push(new LogGraph.ServerGraph.exception(this.graph, tick, exception, time)); - } - - this.draw = function(paper) { - var st = paper.set(); - st.push(paper.path("M0 " + this.y + " L" + paper.width + " " + this.y)); - st.push(paper.text(20, this.y - 10, this.name)); - st.attr({stroke: "gray"}); - - var numstates = this.states.length; - - for (s = 0; s < numstates; s++) { - var style = {}; - switch (this.states[s][0]) { - case "INIT": style = {stroke: "yellow", "stroke-width":3}; break; - case "FOLLOWING": style = {stroke: "lightgreen", "stroke-width":7}; break; - case "LEADING": style = {stroke: "green", "stroke-width":10}; break; - case "LOOKING": style = {stroke: "orange", "stroke-width":5}; break; - } - var startx = this.graph.tick_to_x(this.states[s][1]); - var endx = s + 1 < numstates ? this.graph.tick_to_x(this.states[(s+1)][1]) : paper.width; - var p = paper.path("M" + startx + " " + this.y + " L" + endx + " " + this.y); - p.attr(style); - } - - for (e in this.exception) { - this.exception[e].draw(paper, this); - } - } -}; - -LogGraph.ServerGraph.message = function(graph, src, srctime, dst, zxid) { - this.graph = graph; - this.src = src; - this.srctime = srctime; - this.dst = dst; - this.dsttime = 0; //dsttime; - this.name = "Unknown"; - this.zxid = zxid; - this.moreinfo = "No extra information"; - this.incomplete = false; - - this.markIncomplete = function() { - this.incomplete = true; - this.dsttime = this.srctime; - } - - this.draw = function(paper) { - var srcx = this.graph.tick_to_x(this.srctime); - var dstx = this.graph.tick_to_x(this.dsttime); - - var arrow = paper.set(); - var p = paper.path("M" + srcx + " " + this.src.y + " L" + dstx + " " + this.dst.y); - arrow.push(p); - - var tx = (srcx + dstx)/2; - var ty = (this.src.y + this.dst.y)/2; - var t = paper.text(tx, ty, this.name); - - var gradiant = (this.dst.y - this.src.y)/(dstx - srcx); - var angle = Math.atan(gradiant) * 57.2958; - t.rotate(angle, true); - - var arrowl = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) +" " + this.dst.y); - arrowl.rotate(angle + 20, dstx, this.dst.y); - arrow.push(arrowl); - var arrowr = paper.path("M" + dstx + " " + this.dst.y + " L" + (dstx - 10) +" " + this.dst.y); - arrowr.rotate(angle - 20, dstx, this.dst.y); - arrow.push(arrowr); - - arrow.attr({"stroke-width": 2, stroke: "gray"}); - if (this.incomplete) { - arrow.attr({"stroke-dasharray": "- .", stroke: "pink", "stroke-width": 2}); - } - arrow.mouseover(function(evt) { - t.attr({"font-size": 20}); - arrow.attr({stroke: "red", "stroke-width": 3}); - }); - arrow.mouseout(function(evt) { - t.attr({"font-size": 10}); - - if (this.incomplete) { - arrow.attr({stroke: "pink", "stroke-width": 2}); - } else { - arrow.attr({stroke: "gray", "stroke-width": 2}); - } - }); - - - - arrow.click(function(evt) { - var popup = document.createElement("div"); - popup.className = "popUp"; - popup.innerHTML = "zxid: " + parseInt(this.zxid).toString(16); - - popup.style.top = evt.clientY; - popup.style.left = evt.clientX; - document.body.appendChild(popup); - - popup.onclick = function(evt) { - document.body.removeChild(popup); - }; - }); - } -}; - -LogGraph.ServerGraph.exception = function(graph, tick, exceptiontext, time) { - this.graph = graph; - this.time = time; - this.text = exceptiontext; - this.tick = tick; - - var self = this; - - this.draw = function(paper, server) { - var center = this.graph.tick_to_x(this.tick); - var p = paper.circle(center, server.y, 5); - p.attr({stroke: "orange", fill: "red"}); - - p.mouseover(function(evt) { - p.popup = document.createElement("div"); - p.popup.className = "popUp"; - p.popup.innerHTML = self.text.replace("\n", "
");; - p.popup.style.top = server.y + 50; - p.popup.style.left = center + 25; - document.body.appendChild(p.popup); - - p.animate({r: 10}, 500, "elastic"); - }); - p.mouseout(function(evt) { - document.body.removeChild(p.popup); - p.animate({r: 5}, 100); - }); - } -}; - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.session.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.session.js deleted file mode 100644 index 5a314d8a120..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.session.js +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.SessionGraph = function (asyncq, canvas, starttime, endtime, filter) { - this.sessions = new Array(); - this.counter = 0; - this.exceptions = new Array(); - - this.pix_per_ticks = 4; - this.pix_per_session = 7; - - var paper = Raphael(canvas, 1, 1); - this.ticker = new LogGraph.ticker(); - var self = this; - - this.starttime = starttime; - this.endtime = endtime; - this.filter = filter; - - this.findOrCreateSession = function(id) { - if (this.sessions[id] == undefined) { - this.sessions[id] = new LogGraph.SessionGraph.session(this, ++this.counter, id); - } - return this.sessions[id]; - } - - this.height = function () { return this.counter * this.pix_per_session + 10; }; - this.width = function () { return (self.ticker.current() * this.pix_per_ticks); }; - - this.draw = function(paper) { - - - var line = paper.path("M0 0 L0 " + this.height()); - line.attr({"stroke": "red", "stroke-dasharray": "- "}); - var base = canvas.offsetLeft; - var width = this.width(); - canvas.onmousemove = function (evt) { - var x = evt.clientX - base; - - line.attr({"path": "M" + x + " 0 L" + x + " " + self.height() }); - }; - - for (var i in this.sessions) { - var s = this.sessions[i]; - s.draw(paper); - } - }; - - var processdata = function(data) { - var count = 0; - for (var i in data.events) { - var e = data.events[i]; - if (e.type == "transaction") { - e.tick = self.ticker.tick(e.time, true); - var session = self.findOrCreateSession(e.client); - session.addEvent(e); - count++; - } - } - paper.setSize(self.width(), self.height()); - - if (count != 0) { - self.draw(paper); - return true; - } else { - return false; - } - }; - - var uri = "/data?start=" + self.starttime + "&end=" + self.endtime + "&filter=" + filter; - - LogGraph.loadData(asyncq, uri, processdata); -}; - -LogGraph.SessionGraph.sessionevent = function () { - this.time = time; - this.type = type; - this.client = client; - this.cxid = cxid; - this.zxid = zxid; - this.op = op; - this.extra = extra; -}; - -LogGraph.SessionGraph.sessionEventPopup = function (obj, e, x, y) { - obj.click(function(evt) { - var popup = document.createElement("div"); - popup.className = "popUp"; - - var closebutton = document.createElement("div"); - closebutton.className = "closebutton"; - closebutton.title = "Close popup"; - closebutton.innerHTML = "×"; - popup.appendChild(closebutton); - closebutton.onclick= function(evt) { popup.style.visibility = "hidden"; document.body.removeChild(popup) }; - var txt = document.createElement("span"); - txt.innerHTML = "session: " + e.client + "
op: " + e.op + "
zxid: " + e.zxid + "
time: " + e.time + "
extra: " + e.extra; - popup.appendChild(txt); - - popup.style.top = y; - popup.style.left = x; - document.body.appendChild(popup); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: popup - }); - }); - }); -}; - -LogGraph.SessionGraph.session = function (graph, index, id) { - this.index = index; - this.id = id; - this.graph = graph; - - this.events = new Array(); - this.starttick = 0; - this.endtick = undefined; - - this.addEvent = function(e) { - this.events.push(e); - - if (e.op == "createSession") { - // document.write("createSession for " + id.toString(16)); - this.starttick = e.tick; - } else if (e.op == "closeSession") { - this.endtick = e.tick; - } - }, - - this._attach_action = function (sess, label) { - sess.mouseover(function(evt) { - label.show(); - sess.attr({stroke: "gray"}); - }); - - sess.mouseout(function(evt) { - label.hide(); - sess.attr({stroke: "black"}); - }); - }, - - this.drawEvent = function (paper, y, e) { - var x = e.tick * this.graph.pix_per_ticks;; - var s = paper.path("M" + x + " " + (y - 3) + " L" + x + " " + (y + 3)); - s.attr({"stroke-width": 2}); - if (e.op == "error") { - s.attr({"stroke": "red"}); - } - s.mouseover(function(evt) { - s.attr({"stroke-width": 5}); - }); - - s.mouseout(function(evt) { - s.attr({"stroke-width": 2}); - }); - - LogGraph.SessionGraph.sessionEventPopup(s, e, x, y); - }, - - this.draw = function(paper) { - var y = this.index*this.graph.pix_per_session;; - var start = this.starttick * this.graph.pix_per_ticks; - var end = this.endtick * this.graph.pix_per_ticks; - - var sess = paper.set(); - - if (this.endtick == undefined) { - end = this.graph.width(); - } - - sess.push(paper.path("M" + start + " " + y + " L" + end + " " + y)); - for (var i in this.events) { - var e = this.events[i]; - this.drawEvent(paper, y, e); - } - - //sess.attr({"stroke-width": 3}); - label = paper.text(start + 100, y, this.id); - label.attr({"font-size": "14px"}); - label.hide(); - this._attach_action(sess, label); - } -}; - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.stats.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.stats.js deleted file mode 100644 index 0a8ac4fcce0..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.stats.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -LogGraph.StatsGraph = function (asyncq, canvas, starttime, endtime, filter) { - var processdata = function(data) { - var r = Raphael(canvas); - var x = data.map(function (x) { return x.time; }); - var y = data.map(function (x) { return x.count; }); - var xlabels = data.map(function (x) { return dateFormat(x.time, "HH:MM:ss,l"); } ); - var h1 = function () { - this.tags = r.set(); - for (var i = 0, ii = this.y.length; i < ii; i++) { - this.tags.push(r.g.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{fill: "#fff"}, {fill: this.symbols[i].attr("fill")}])); - } - }; - var h2 = function () { - this.tags && this.tags.remove(); - }; - r.g.linechart(40, 40, 1000, 500, x, y, {shade: true, axis: "0 0 1 1", symbol: "x", southlabels: xlabels, axisxstep: xlabels.length - 1 , westAxisLabel: "Write requests", southAxisLabel: "Time (min)"}).hoverColumn(h1, h2); - - return true; - //r.g.barchart(0, 0, 1000, 100, y, {shade: true, symbol: "x"}).hoverColumn(h1, h2); - }; - - var uri = "/throughput?scale=minutes"; - LogGraph.loadData(asyncq, uri, processdata); -}; - - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.ui.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.ui.js deleted file mode 100644 index 819765a3e4b..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.ui.js +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Opens a window to load files into the engine -LogGraph.fileSelector = function(callback) { - var self = this; - this.callback = callback; - this.selectedFiles = new Array(); - - var divTag = document.createElement("div"); - divTag.id = "fileSelector" + Math.round(Math.random()*100000); - // divTag.className = "popUp"; - divTag.className = "selector fileSelector"; - document.body.appendChild(divTag); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: '#' + divTag.id - }); - }); - - var list = document.createElement("ul"); - divTag.appendChild(list); - var selectedList = document.createElement("selectedlist"); - divTag.appendChild(selectedList); - - var clearanchor = document.createElement("span"); - clearanchor.innerHTML = "Remove All"; - clearanchor.className = "actionbutton"; - clearanchor.style.cssFloat = "right"; - clearanchor.onclick = function () { - self.selectedFiles = new Array(); - self.updateSelectedList(); - }; - divTag.appendChild(clearanchor); - - var doneanchor = document.createElement("span"); - doneanchor.innerHTML = "Process Files"; - doneanchor.className = "actionbutton"; - doneanchor.style.cssFloat = "left"; - doneanchor.onclick = function () { - self.callback(self.selectedFiles); - document.body.removeChild(divTag); - delete divTag; - }; - divTag.appendChild(doneanchor); - - var cancelanchor = document.createElement("span"); - cancelanchor.innerHTML = "Cancel"; - cancelanchor.className = "actionbutton"; - cancelanchor.style.cssFloat = "left"; - cancelanchor.onclick = function () { - document.body.removeChild(divTag); - delete divTag; - }; - divTag.appendChild(cancelanchor); - - this.createFileListItem = function (file) { - var li = document.createElement("li"); - var a = document.createElement("a"); - if (file.type == "D") { - a.innerHTML = file.file + "/"; - a.onclick = function () { self.updateList(file.path); }; - } else { - a.innerHTML = file.file; - a.onclick = function () { self.addSelectedFile(file.path); }; - } - - a.fullpath = file.path;; - li.appendChild(a); - return li; - }; - - this.addSelectedFile = function (file) { - if (this.selectedFiles.indexOf(file) == -1) { - this.selectedFiles.push(file); - this.updateSelectedList(); - } - }; - - this.removeSelectedFile = function (file) { - this.selectedFiles = this.selectedFiles.filter(function(f) { return !(file == f); }); - this.updateSelectedList(); - }; - - this.createSelectedListItem = function (file) { - var li = document.createElement("li"); - var a = document.createElement("a"); - li.className = "selectedFile"; - a.onclick = function () { self.removeSelectedFile(file); }; - a.innerHTML = file; - li.appendChild(a); - return li; - }; - - this.updateSelectedList = function () { - while (selectedList.firstChild) { selectedList.removeChild(selectedList.firstChild); } - - for (var i in this.selectedFiles) { - var f = this.selectedFiles[i]; - selectedList.appendChild(this.createSelectedListItem(f)); - } - }; - - this.updateList = function (base) { - while (list.firstChild) list.removeChild(list.firstChild); - - // Create a YUI instance using io-base module. - YUI().use("io-base", function(Y) { - var uri = "/fs?path=" + base; - - // Define a function to handle the response data. - function complete(id, o, args) { - var id = id; // Transaction ID. - var data = eval("(" + o.responseText + ")"); // Response data. - var parts = base.split("/").slice(0,-1); - var parent = "" - if (parts.length < 2) { - parent = "/"; - } else { - parent = parts.join("/"); - } - if (base != "/") { - var li = self.createFileListItem({"file": "..", type: "D", path: parent}); - list.appendChild(li); - } - for (var i in data) { - var f = data[i]; - if (f.file[0] != '.') { - var li = self.createFileListItem(f); - list.appendChild(li); - } - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - this.updateList("/"); -}; - -// Open a window which loads files into the engine -LogGraph.fileLoader = function(files) { - var div = document.createElement("div"); - div.id = "fileLoader"; - - var imgArray = new Array(); - var pArray = new Array(); - for (var index in files) { - var f = files[index]; - var p = document.createElement("p"); - var i = document.createElement("img"); - i.src = "load.gif"; - i.style.visibility = "hidden"; - imgArray.push(i); - pArray.push(p); - var span = document.createElement("span"); - span.innerHTML = f; - - p.appendChild(span); - p.appendChild(i); - - div.appendChild(p); - } - - var loadFile = function (index) { - // Create a YUI instance using io-base module. - YUI().use("io-base", function(Y) { - var file = files[index]; - var uri = "/loadfile?path=" + file; - imgArray[index].style.visibility = "visible"; - - // Define a function to handle the response data. - function complete(id, o, args) { - var id = id; // Transaction ID. - var data = eval("(" + o.responseText + ")"); // Response data. - if (data.status == "ERR") { - var err = document.createElement("div"); - err.innerHTML = data.error; - pArray[index].appendChild(err); - } else if (data.status == "OK") { - var ok = document.createElement("div"); - ok.innerHTML = "OK"; - pArray[index].appendChild(ok); - } - - imgArray[index].style.visibility = "hidden"; - if (index + 1 < files.length) { - loadFile(index + 1); - } else { - //alert("DONE"); - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - var doneanchor = document.createElement("a"); - doneanchor.className = "actionbutton"; - doneanchor.innerHTML = "Done"; - doneanchor.onclick = function () { - document.body.removeChild(div); - delete div; - }; - - document.body.appendChild(div); - if (files.length > 0) { - loadFile(0); - } else { - div.innerHTML ="No files to load"; - } - div.appendChild(doneanchor); -} - -// select a time period -LogGraph.filterSelector = function(starttime, period, filter, callback) { - var self = this; - this.callback = callback; - - // Container other widgets will be in - var container = document.createElement("div"); - container.id = "filterSelector" + Math.round(Math.random()*100000); - container.className = "selector filterSelector"; - document.body.appendChild(container); - - YUI().use('dd-drag', function(Y) { - //Selector of the node to make draggable - var dd = new Y.DD.Drag({ - node: '#' + container.id - }); - }); - - // Temporary loading screen - var loadingp = document.createElement("p"); - loadingp.innerHTML = "Loading..."; - var loadimg = document.createElement("img"); - loadimg.src = "load.gif"; - loadingp.appendChild(loadimg); - container.appendChild(loadingp); - - var addWithLabel = function (container, labeltxt, object) { - var p = document.createElement("p"); - var label = document.createElement("label"); - label.innerHTML = labeltxt + ":"; - p.appendChild(label); - p.appendChild(object); - container.appendChild(p); - }; - var draw = function(minstart, maxstart, entries) { - container.removeChild(loadingp); - var inittime = minstart > starttime ? minstart : starttime; - - var numEntries = 0; - var startspan = document.createElement("span"); - addWithLabel(container, "Start time", startspan); - var startinput = document.createElement("input"); - startinput.type = "hidden"; - startinput.value = inittime; - container.appendChild(startinput); - var sliderspan = document.createElement("span"); - container.appendChild(sliderspan); - - var countspan = document.createElement("p"); - countspan.innerHTML = entries + " entries";; - container.appendChild(countspan); - - var windowinput = document.createElement("input"); - windowinput.type = "text"; - windowinput.value = period; - addWithLabel(container, "Time window (ms)", windowinput); - - var filterinput = document.createElement("textarea"); - filterinput.id = "filterinput"; - filterinput.value = filter; - addWithLabel(container, "Filter", filterinput); - - /* done link, when clicked time is updated, */ - var doneanchor = document.createElement("a"); - doneanchor.className = "actionbutton"; - doneanchor.innerHTML = "Done"; - doneanchor.onclick = function () { - var start = parseInt(startinput.value); - var period = parseInt(windowinput.value); - var filter = filterinput.value; - document.body.removeChild(container); - delete container; - - update(start, period, filter, function() { - callback(start, period, filter, numEntries); - }); - }; - container.appendChild(doneanchor); - - var update = function(start, period, filter, thenrun) { - startspan.innerHTML = dateFormat(start, "HH:MM:ss,l"); - // get the min and max start time - YUI().use("io-base", function(Y) { - var uri = "/info?start=" + start + "&period=" + period + "&filter=" + filter; - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); - countspan.innerHTML = data.numEntries + " entries"; - numEntries = data.numEntries; - if (thenrun) { - thenrun(); - } - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); - }; - - var updatewindow = function(evt) { - var start = parseInt(startinput.value); - var period = parseInt(windowinput.value); - var filter = filterinput.value; - update(start, period, filter); - }; - windowinput.onkeyup = updatewindow; - - - YUI().use("slider", function (Y) { - var input, slider; - - function updateInput( e ) { - this.set( "value", e.newVal ); - - update(parseInt(startinput.value), parseInt(windowinput.value), filterinput.value); - } - - xSlider = new Y.Slider({min: minstart, max: maxstart, value: inittime, length: "1000px" }); - - // Link the input value to the Slider - xInput = Y.one( startinput ); - xInput.setData( { slider: xSlider } ); - - // Pass the input as the 'this' object inside updateInput - xSlider.after( "valueChange", updateInput, xInput ); - - // Render the Slider next to the input - xSlider.render(sliderspan); - }); - update(inittime, windowinput.value, filterinput); - }; - - // get the min and max start time - YUI().use("io-base", function(Y) { - var uri = "/info"; - function complete(id, o, args) { - var data = eval("(" + o.responseText + ")"); - draw(data.startTime, data.endTime, data.numEntries); - }; - - Y.on('io:complete', complete, Y, []); - var request = Y.io(uri); - }); -} \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/main.html b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/main.html deleted file mode 100644 index b9affe66585..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/main.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
- Edit Filters - Add logs -
-
- Log view - Servers view - Sessions view - Statistics -
-
-
-
-
- - diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/raphael.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/raphael.js deleted file mode 100644 index 3740d0f02ad..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/raphael.js +++ /dev/null @@ -1,3296 +0,0 @@ -/*! - * Raphael 1.3.2 - JavaScript Vector Library - * - * Copyright (c) 2009 Dmitry Baranovskiy (http://raphaeljs.com) - * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. - */ - -Raphael = (function () { - var separator = /[, ]+/, - elements = /^(circle|rect|path|ellipse|text|image)$/, - proto = "prototype", - has = "hasOwnProperty", - doc = document, - win = window, - oldRaphael = { - was: Object[proto][has].call(win, "Raphael"), - is: win.Raphael - }, - R = function () { - if (R.is(arguments[0], "array")) { - var a = arguments[0], - cnv = create[apply](R, a.splice(0, 3 + R.is(a[0], nu))), - res = cnv.set(); - for (var i = 0, ii = a[length]; i < ii; i++) { - var j = a[i] || {}; - elements.test(j.type) && res[push](cnv[j.type]().attr(j)); - } - return res; - } - return create[apply](R, arguments); - }, - Paper = function () {}, - appendChild = "appendChild", - apply = "apply", - concat = "concat", - E = "", - S = " ", - split = "split", - events = "click dblclick mousedown mousemove mouseout mouseover mouseup"[split](S), - join = "join", - length = "length", - lowerCase = String[proto].toLowerCase, - math = Math, - mmax = math.max, - mmin = math.min, - nu = "number", - toString = "toString", - objectToString = Object[proto][toString], - paper = {}, - pow = math.pow, - push = "push", - rg = /^(?=[\da-f]$)/, - ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i, //" - colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i, - round = math.round, - setAttribute = "setAttribute", - toFloat = parseFloat, - toInt = parseInt, - upperCase = String[proto].toUpperCase, - availableAttrs = {blur: 0, "clip-rect": "0 0 1e9 1e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0}, - availableAnimAttrs = {along: "along", blur: nu, "clip-rect": "csv", cx: nu, cy: nu, fill: "colour", "fill-opacity": nu, "font-size": nu, height: nu, opacity: nu, path: "path", r: nu, rotation: "csv", rx: nu, ry: nu, scale: "csv", stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, translation: "csv", width: nu, x: nu, y: nu}, - rp = "replace"; - R.version = "1.3.2"; - R.type = (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML"); - if (R.type == "VML") { - var d = doc.createElement("div"); - d.innerHTML = ''; - if (d.childNodes[length] != 2) { - return R.type = null; - } - d = null; - } - R.svg = !(R.vml = R.type == "VML"); - Paper[proto] = R[proto]; - R._id = 0; - R._oid = 0; - R.fn = {}; - R.is = function (o, type) { - type = lowerCase.call(type); - return ((type == "object" || type == "undefined") && typeof o == type) || (o == null && type == "null") || lowerCase.call(objectToString.call(o).slice(8, -1)) == type; - }; - R.setWindow = function (newwin) { - win = newwin; - doc = win.document; - }; - // colour utilities - var toHex = function (color) { - if (R.vml) { - // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/ - var trim = /^\s+|\s+$/g; - toHex = cacher(function (color) { - var bod; - color = (color + E)[rp](trim, E); - try { - var docum = new win.ActiveXObject("htmlfile"); - docum.write(""); - docum.close(); - bod = docum.body; - } catch(e) { - bod = win.createPopup().document.body; - } - var range = bod.createTextRange(); - try { - bod.style.color = color; - var value = range.queryCommandValue("ForeColor"); - value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16); - return "#" + ("000000" + value[toString](16)).slice(-6); - } catch(e) { - return "none"; - } - }); - } else { - var i = doc.createElement("i"); - i.title = "Rapha\xebl Colour Picker"; - i.style.display = "none"; - doc.body[appendChild](i); - toHex = cacher(function (color) { - i.style.color = color; - return doc.defaultView.getComputedStyle(i, E).getPropertyValue("color"); - }); - } - return toHex(color); - }; - var hsbtoString = function () { - return "hsb(" + [this.h, this.s, this.b] + ")"; - }, - rgbtoString = function () { - return this.hex; - }; - R.hsb2rgb = cacher(function (hue, saturation, brightness) { - if (R.is(hue, "object") && "h" in hue && "s" in hue && "b" in hue) { - brightness = hue.b; - saturation = hue.s; - hue = hue.h; - } - var red, - green, - blue; - if (brightness == 0) { - return {r: 0, g: 0, b: 0, hex: "#000"}; - } - if (hue > 1 || saturation > 1 || brightness > 1) { - hue /= 255; - saturation /= 255; - brightness /= 255; - } - var i = ~~(hue * 6), - f = (hue * 6) - i, - p = brightness * (1 - saturation), - q = brightness * (1 - (saturation * f)), - t = brightness * (1 - (saturation * (1 - f))); - red = [brightness, q, p, p, t, brightness, brightness][i]; - green = [t, brightness, brightness, q, p, p, t][i]; - blue = [p, p, t, brightness, brightness, q, p][i]; - red *= 255; - green *= 255; - blue *= 255; - var rgb = {r: red, g: green, b: blue, toString: rgbtoString}, - r = (~~red)[toString](16), - g = (~~green)[toString](16), - b = (~~blue)[toString](16); - r = r[rp](rg, "0"); - g = g[rp](rg, "0"); - b = b[rp](rg, "0"); - rgb.hex = "#" + r + g + b; - return rgb; - }, R); - R.rgb2hsb = cacher(function (red, green, blue) { - if (R.is(red, "object") && "r" in red && "g" in red && "b" in red) { - blue = red.b; - green = red.g; - red = red.r; - } - if (R.is(red, "string")) { - var clr = R.getRGB(red); - red = clr.r; - green = clr.g; - blue = clr.b; - } - if (red > 1 || green > 1 || blue > 1) { - red /= 255; - green /= 255; - blue /= 255; - } - var max = mmax(red, green, blue), - min = mmin(red, green, blue), - hue, - saturation, - brightness = max; - if (min == max) { - return {h: 0, s: 0, b: max}; - } else { - var delta = (max - min); - saturation = delta / max; - if (red == max) { - hue = (green - blue) / delta; - } else if (green == max) { - hue = 2 + ((blue - red) / delta); - } else { - hue = 4 + ((red - green) / delta); - } - hue /= 6; - hue < 0 && hue++; - hue > 1 && hue--; - } - return {h: hue, s: saturation, b: brightness, toString: hsbtoString}; - }, R); - var p2s = /,?([achlmqrstvxz]),?/gi; - R._path2string = function () { - return this.join(",")[rp](p2s, "$1"); - }; - function cacher(f, scope, postprocessor) { - function newf() { - var arg = Array[proto].slice.call(arguments, 0), - args = arg[join]("\u25ba"), - cache = newf.cache = newf.cache || {}, - count = newf.count = newf.count || []; - if (cache[has](args)) { - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - count[length] >= 1e3 && delete cache[count.shift()]; - count[push](args); - cache[args] = f[apply](scope, arg); - return postprocessor ? postprocessor(cache[args]) : cache[args]; - } - return newf; - } - - R.getRGB = cacher(function (colour) { - if (!colour || !!((colour = colour + E).indexOf("-") + 1)) { - return {r: -1, g: -1, b: -1, hex: "none", error: 1}; - } - if (colour == "none") { - return {r: -1, g: -1, b: -1, hex: "none"}; - } - !(({hs: 1, rg: 1})[has](colour.substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour)); - var res, - red, - green, - blue, - t, - rgb = colour.match(colourRegExp); - if (rgb) { - if (rgb[2]) { - blue = toInt(rgb[2].substring(5), 16); - green = toInt(rgb[2].substring(3, 5), 16); - red = toInt(rgb[2].substring(1, 3), 16); - } - if (rgb[3]) { - blue = toInt((t = rgb[3].charAt(3)) + t, 16); - green = toInt((t = rgb[3].charAt(2)) + t, 16); - red = toInt((t = rgb[3].charAt(1)) + t, 16); - } - if (rgb[4]) { - rgb = rgb[4][split](/\s*,\s*/); - red = toFloat(rgb[0]); - green = toFloat(rgb[1]); - blue = toFloat(rgb[2]); - } - if (rgb[5]) { - rgb = rgb[5][split](/\s*,\s*/); - red = toFloat(rgb[0]) * 2.55; - green = toFloat(rgb[1]) * 2.55; - blue = toFloat(rgb[2]) * 2.55; - } - if (rgb[6]) { - rgb = rgb[6][split](/\s*,\s*/); - red = toFloat(rgb[0]); - green = toFloat(rgb[1]); - blue = toFloat(rgb[2]); - return R.hsb2rgb(red, green, blue); - } - if (rgb[7]) { - rgb = rgb[7][split](/\s*,\s*/); - red = toFloat(rgb[0]) * 2.55; - green = toFloat(rgb[1]) * 2.55; - blue = toFloat(rgb[2]) * 2.55; - return R.hsb2rgb(red, green, blue); - } - rgb = {r: red, g: green, b: blue}; - var r = (~~red)[toString](16), - g = (~~green)[toString](16), - b = (~~blue)[toString](16); - r = r[rp](rg, "0"); - g = g[rp](rg, "0"); - b = b[rp](rg, "0"); - rgb.hex = "#" + r + g + b; - return rgb; - } - return {r: -1, g: -1, b: -1, hex: "none", error: 1}; - }, R); - R.getColor = function (value) { - var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75}, - rgb = this.hsb2rgb(start.h, start.s, start.b); - start.h += .075; - if (start.h > 1) { - start.h = 0; - start.s -= .2; - start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b}); - } - return rgb.hex; - }; - R.getColor.reset = function () { - delete this.start; - }; - // path utilities - var pathCommand = /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig, - pathValues = /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig; - R.parsePathString = cacher(function (pathString) { - if (!pathString) { - return null; - } - var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}, - data = []; - if (R.is(pathString, "array") && R.is(pathString[0], "array")) { // rough assumption - data = pathClone(pathString); - } - if (!data[length]) { - (pathString + E)[rp](pathCommand, function (a, b, c) { - var params = [], - name = lowerCase.call(b); - c[rp](pathValues, function (a, b) { - b && params[push](+b); - }); - if (name == "m" && params[length] > 2) { - data[push]([b][concat](params.splice(0, 2))); - name = "l"; - b = b == "m" ? "l" : "L"; - } - while (params[length] >= paramCounts[name]) { - data[push]([b][concat](params.splice(0, paramCounts[name]))); - if (!paramCounts[name]) { - break; - } - } - }); - } - data[toString] = R._path2string; - return data; - }); - R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t, - x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, - y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y, - mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x), - my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y), - nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x), - ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y), - ax = (1 - t) * p1x + t * c1x, - ay = (1 - t) * p1y + t * c1y, - cx = (1 - t) * c2x + t * p2x, - cy = (1 - t) * c2y + t * p2y, - alpha = (90 - math.atan((mx - nx) / (my - ny)) * 180 / math.PI); - (mx > nx || my < ny) && (alpha += 180); - return {x: x, y: y, m: {x: mx, y: my}, n: {x: nx, y: ny}, start: {x: ax, y: ay}, end: {x: cx, y: cy}, alpha: alpha}; - }; - var pathDimensions = cacher(function (path) { - if (!path) { - return {x: 0, y: 0, width: 0, height: 0}; - } - path = path2curve(path); - var x = 0, - y = 0, - X = [], - Y = [], - p; - for (var i = 0, ii = path[length]; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = p[1]; - y = p[2]; - X[push](x); - Y[push](y); - } else { - var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - X = X[concat](dim.min.x, dim.max.x); - Y = Y[concat](dim.min.y, dim.max.y); - x = p[5]; - y = p[6]; - } - } - var xmin = mmin[apply](0, X), - ymin = mmin[apply](0, Y); - return { - x: xmin, - y: ymin, - width: mmax[apply](0, X) - xmin, - height: mmax[apply](0, Y) - ymin - }; - }), - pathClone = function (pathArray) { - var res = []; - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - for (var i = 0, ii = pathArray[length]; i < ii; i++) { - res[i] = []; - for (var j = 0, jj = pathArray[i][length]; j < jj; j++) { - res[i][j] = pathArray[i][j]; - } - } - res[toString] = R._path2string; - return res; - }, - pathToRelative = cacher(function (pathArray) { - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0; - if (pathArray[0][0] == "M") { - x = pathArray[0][1]; - y = pathArray[0][2]; - mx = x; - my = y; - start++; - res[push](["M", x, y]); - } - for (var i = start, ii = pathArray[length]; i < ii; i++) { - var r = res[i] = [], - pa = pathArray[i]; - if (pa[0] != lowerCase.call(pa[0])) { - r[0] = lowerCase.call(pa[0]); - switch (r[0]) { - case "a": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +(pa[6] - x).toFixed(3); - r[7] = +(pa[7] - y).toFixed(3); - break; - case "v": - r[1] = +(pa[1] - y).toFixed(3); - break; - case "m": - mx = pa[1]; - my = pa[2]; - default: - for (var j = 1, jj = pa[length]; j < jj; j++) { - r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3); - } - } - } else { - r = res[i] = []; - if (pa[0] == "m") { - mx = pa[1] + x; - my = pa[2] + y; - } - for (var k = 0, kk = pa[length]; k < kk; k++) { - res[i][k] = pa[k]; - } - } - var len = res[i][length]; - switch (res[i][0]) { - case "z": - x = mx; - y = my; - break; - case "h": - x += +res[i][len - 1]; - break; - case "v": - y += +res[i][len - 1]; - break; - default: - x += +res[i][len - 2]; - y += +res[i][len - 1]; - } - } - res[toString] = R._path2string; - return res; - }, 0, pathClone), - pathToAbsolute = cacher(function (pathArray) { - if (!R.is(pathArray, "array") || !R.is(pathArray && pathArray[0], "array")) { // rough assumption - pathArray = R.parsePathString(pathArray); - } - var res = [], - x = 0, - y = 0, - mx = 0, - my = 0, - start = 0; - if (pathArray[0][0] == "M") { - x = +pathArray[0][1]; - y = +pathArray[0][2]; - mx = x; - my = y; - start++; - res[0] = ["M", x, y]; - } - for (var i = start, ii = pathArray[length]; i < ii; i++) { - var r = res[i] = [], - pa = pathArray[i]; - if (pa[0] != upperCase.call(pa[0])) { - r[0] = upperCase.call(pa[0]); - switch (r[0]) { - case "A": - r[1] = pa[1]; - r[2] = pa[2]; - r[3] = pa[3]; - r[4] = pa[4]; - r[5] = pa[5]; - r[6] = +(pa[6] + x); - r[7] = +(pa[7] + y); - break; - case "V": - r[1] = +pa[1] + y; - break; - case "H": - r[1] = +pa[1] + x; - break; - case "M": - mx = +pa[1] + x; - my = +pa[2] + y; - default: - for (var j = 1, jj = pa[length]; j < jj; j++) { - r[j] = +pa[j] + ((j % 2) ? x : y); - } - } - } else { - for (var k = 0, kk = pa[length]; k < kk; k++) { - res[i][k] = pa[k]; - } - } - switch (r[0]) { - case "Z": - x = mx; - y = my; - break; - case "H": - x = r[1]; - break; - case "V": - y = r[1]; - break; - default: - x = res[i][res[i][length] - 2]; - y = res[i][res[i][length] - 1]; - } - } - res[toString] = R._path2string; - return res; - }, null, pathClone), - l2c = function (x1, y1, x2, y2) { - return [x1, y1, x2, y2, x2, y2]; - }, - q2c = function (x1, y1, ax, ay, x2, y2) { - var _13 = 1 / 3, - _23 = 2 / 3; - return [ - _13 * x1 + _23 * ax, - _13 * y1 + _23 * ay, - _13 * x2 + _23 * ax, - _13 * y2 + _23 * ay, - x2, - y2 - ]; - }, - a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) { - // for more information of where this math came from visit: - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - var PI = math.PI, - _120 = PI * 120 / 180, - rad = PI / 180 * (+angle || 0), - res = [], - xy, - rotate = cacher(function (x, y, rad) { - var X = x * math.cos(rad) - y * math.sin(rad), - Y = x * math.sin(rad) + y * math.cos(rad); - return {x: X, y: Y}; - }); - if (!recursive) { - xy = rotate(x1, y1, -rad); - x1 = xy.x; - y1 = xy.y; - xy = rotate(x2, y2, -rad); - x2 = xy.x; - y2 = xy.y; - var cos = math.cos(PI / 180 * angle), - sin = math.sin(PI / 180 * angle), - x = (x1 - x2) / 2, - y = (y1 - y2) / 2; - // rx = mmax(rx, math.abs(x)); - // ry = mmax(ry, math.abs(y)); - var h = (x * x) / (rx * rx) + (y * y) / (ry * ry); - if (h > 1) { - h = math.sqrt(h); - rx = h * rx; - ry = h * ry; - } - var rx2 = rx * rx, - ry2 = ry * ry, - k = (large_arc_flag == sweep_flag ? -1 : 1) * - math.sqrt(math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))), - cx = k * rx * y / ry + (x1 + x2) / 2, - cy = k * -ry * x / rx + (y1 + y2) / 2, - f1 = math.asin(((y1 - cy) / ry).toFixed(7)), - f2 = math.asin(((y2 - cy) / ry).toFixed(7)); - - f1 = x1 < cx ? PI - f1 : f1; - f2 = x2 < cx ? PI - f2 : f2; - f1 < 0 && (f1 = PI * 2 + f1); - f2 < 0 && (f2 = PI * 2 + f2); - if (sweep_flag && f1 > f2) { - f1 = f1 - PI * 2; - } - if (!sweep_flag && f2 > f1) { - f2 = f2 - PI * 2; - } - } else { - f1 = recursive[0]; - f2 = recursive[1]; - cx = recursive[2]; - cy = recursive[3]; - } - var df = f2 - f1; - if (math.abs(df) > _120) { - var f2old = f2, - x2old = x2, - y2old = y2; - f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1); - x2 = cx + rx * math.cos(f2); - y2 = cy + ry * math.sin(f2); - res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]); - } - df = f2 - f1; - var c1 = math.cos(f1), - s1 = math.sin(f1), - c2 = math.cos(f2), - s2 = math.sin(f2), - t = math.tan(df / 4), - hx = 4 / 3 * rx * t, - hy = 4 / 3 * ry * t, - m1 = [x1, y1], - m2 = [x1 + hx * s1, y1 - hy * c1], - m3 = [x2 + hx * s2, y2 - hy * c2], - m4 = [x2, y2]; - m2[0] = 2 * m1[0] - m2[0]; - m2[1] = 2 * m1[1] - m2[1]; - if (recursive) { - return [m2, m3, m4][concat](res); - } else { - res = [m2, m3, m4][concat](res)[join]()[split](","); - var newres = []; - for (var i = 0, ii = res[length]; i < ii; i++) { - newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x; - } - // alert(newres); - return newres; - } - }, - findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { - var t1 = 1 - t; - return { - x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x, - y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y - }; - }, - curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x), - b = 2 * (c1x - p1x) - 2 * (c2x - c1x), - c = p1x - c1x, - t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a, - t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a, - y = [p1y, p2y], - x = [p1x, p2x], - dot; - math.abs(t1) > 1e12 && (t1 = .5); - math.abs(t2) > 1e12 && (t2 = .5); - if (t1 > 0 && t1 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); - x[push](dot.x); - y[push](dot.y); - } - if (t2 > 0 && t2 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); - x[push](dot.x); - y[push](dot.y); - } - a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y); - b = 2 * (c1y - p1y) - 2 * (c2y - c1y); - c = p1y - c1y; - t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a; - t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a; - math.abs(t1) > 1e12 && (t1 = .5); - math.abs(t2) > 1e12 && (t2 = .5); - if (t1 > 0 && t1 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1); - x[push](dot.x); - y[push](dot.y); - } - if (t2 > 0 && t2 < 1) { - dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2); - x[push](dot.x); - y[push](dot.y); - } - return { - min: {x: mmin[apply](0, x), y: mmin[apply](0, y)}, - max: {x: mmax[apply](0, x), y: mmax[apply](0, y)} - }; - }), - path2curve = cacher(function (path, path2) { - var p = pathToAbsolute(path), - p2 = path2 && pathToAbsolute(path2), - attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null}, - processPath = function (path, d) { - var nx, ny; - if (!path) { - return ["C", d.x, d.y, d.x, d.y, d.x, d.y]; - } - !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null); - switch (path[0]) { - case "M": - d.X = path[1]; - d.Y = path[2]; - break; - case "A": - path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1)))); - break; - case "S": - nx = d.x + (d.x - (d.bx || d.x)); - ny = d.y + (d.y - (d.by || d.y)); - path = ["C", nx, ny][concat](path.slice(1)); - break; - case "T": - d.qx = d.x + (d.x - (d.qx || d.x)); - d.qy = d.y + (d.y - (d.qy || d.y)); - path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2])); - break; - case "Q": - d.qx = path[1]; - d.qy = path[2]; - path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4])); - break; - case "L": - path = ["C"][concat](l2c(d.x, d.y, path[1], path[2])); - break; - case "H": - path = ["C"][concat](l2c(d.x, d.y, path[1], d.y)); - break; - case "V": - path = ["C"][concat](l2c(d.x, d.y, d.x, path[1])); - break; - case "Z": - path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y)); - break; - } - return path; - }, - fixArc = function (pp, i) { - if (pp[i][length] > 7) { - pp[i].shift(); - var pi = pp[i]; - while (pi[length]) { - pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6))); - } - pp.splice(i, 1); - ii = mmax(p[length], p2 && p2[length] || 0); - } - }, - fixM = function (path1, path2, a1, a2, i) { - if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") { - path2.splice(i, 0, ["M", a2.x, a2.y]); - a1.bx = 0; - a1.by = 0; - a1.x = path1[i][1]; - a1.y = path1[i][2]; - ii = mmax(p[length], p2 && p2[length] || 0); - } - }; - for (var i = 0, ii = mmax(p[length], p2 && p2[length] || 0); i < ii; i++) { - p[i] = processPath(p[i], attrs); - fixArc(p, i); - p2 && (p2[i] = processPath(p2[i], attrs2)); - p2 && fixArc(p2, i); - fixM(p, p2, attrs, attrs2, i); - fixM(p2, p, attrs2, attrs, i); - var seg = p[i], - seg2 = p2 && p2[i], - seglen = seg[length], - seg2len = p2 && seg2[length]; - attrs.x = seg[seglen - 2]; - attrs.y = seg[seglen - 1]; - attrs.bx = toFloat(seg[seglen - 4]) || attrs.x; - attrs.by = toFloat(seg[seglen - 3]) || attrs.y; - attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x); - attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y); - attrs2.x = p2 && seg2[seg2len - 2]; - attrs2.y = p2 && seg2[seg2len - 1]; - } - return p2 ? [p, p2] : p; - }, null, pathClone), - parseDots = cacher(function (gradient) { - var dots = []; - for (var i = 0, ii = gradient[length]; i < ii; i++) { - var dot = {}, - par = gradient[i].match(/^([^:]*):?([\d\.]*)/); - dot.color = R.getRGB(par[1]); - if (dot.color.error) { - return null; - } - dot.color = dot.color.hex; - par[2] && (dot.offset = par[2] + "%"); - dots[push](dot); - } - for (i = 1, ii = dots[length] - 1; i < ii; i++) { - if (!dots[i].offset) { - var start = toFloat(dots[i - 1].offset || 0), - end = 0; - for (var j = i + 1; j < ii; j++) { - if (dots[j].offset) { - end = dots[j].offset; - break; - } - } - if (!end) { - end = 100; - j = ii; - } - end = toFloat(end); - var d = (end - start) / (j - i + 1); - for (; i < j; i++) { - start += d; - dots[i].offset = start + "%"; - } - } - } - return dots; - }), - getContainer = function (x, y, w, h) { - var container; - if (R.is(x, "string") || R.is(x, "object")) { - container = R.is(x, "string") ? doc.getElementById(x) : x; - if (container.tagName) { - if (y == null) { - return { - container: container, - width: container.style.pixelWidth || container.offsetWidth, - height: container.style.pixelHeight || container.offsetHeight - }; - } else { - return {container: container, width: y, height: w}; - } - } - } else if (R.is(x, nu) && h != null) { - return {container: 1, x: x, y: y, width: w, height: h}; - } - }, - plugins = function (con, add) { - var that = this; - for (var prop in add) { - if (add[has](prop) && !(prop in con)) { - switch (typeof add[prop]) { - case "function": - (function (f) { - con[prop] = con === that ? f : function () { return f[apply](that, arguments); }; - })(add[prop]); - break; - case "object": - con[prop] = con[prop] || {}; - plugins.call(this, con[prop], add[prop]); - break; - default: - con[prop] = add[prop]; - break; - } - } - } - }, - tear = function (el, paper) { - el == paper.top && (paper.top = el.prev); - el == paper.bottom && (paper.bottom = el.next); - el.next && (el.next.prev = el.prev); - el.prev && (el.prev.next = el.next); - }, - tofront = function (el, paper) { - if (paper.top === el) { - return; - } - tear(el, paper); - el.next = null; - el.prev = paper.top; - paper.top.next = el; - paper.top = el; - }, - toback = function (el, paper) { - if (paper.bottom === el) { - return; - } - tear(el, paper); - el.next = paper.bottom; - el.prev = null; - paper.bottom.prev = el; - paper.bottom = el; - }, - insertafter = function (el, el2, paper) { - tear(el, paper); - el2 == paper.top && (paper.top = el); - el2.next && (el2.next.prev = el); - el.next = el2.next; - el.prev = el2; - el2.next = el; - }, - insertbefore = function (el, el2, paper) { - tear(el, paper); - el2 == paper.bottom && (paper.bottom = el); - el2.prev && (el2.prev.next = el); - el.prev = el2.prev; - el2.prev = el; - el.next = el2; - }, - removed = function (methodname) { - return function () { - throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object"); - }; - }, - radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/; - - // SVG - if (R.svg) { - Paper[proto].svgns = "http://www.w3.org/2000/svg"; - Paper[proto].xlink = "http://www.w3.org/1999/xlink"; - round = function (num) { - return +num + (~~num === num) * .5; - }; - var roundPath = function (path) { - for (var i = 0, ii = path[length]; i < ii; i++) { - if (lowerCase.call(path[i][0]) != "a") { - for (var j = 1, jj = path[i][length]; j < jj; j++) { - path[i][j] = round(path[i][j]); - } - } else { - path[i][6] = round(path[i][6]); - path[i][7] = round(path[i][7]); - } - } - return path; - }, - $ = function (el, attr) { - if (attr) { - for (var key in attr) { - if (attr[has](key)) { - el[setAttribute](key, attr[key] + E); - } - } - } else { - return doc.createElementNS(Paper[proto].svgns, el); - } - }; - R[toString] = function () { - return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version; - }; - var thePath = function (pathString, SVG) { - var el = $("path"); - SVG.canvas && SVG.canvas[appendChild](el); - var p = new Element(el, SVG); - p.type = "path"; - setFillAndStroke(p, {fill: "none", stroke: "#000", path: pathString}); - return p; - }; - var addGradientFill = function (o, gradient, SVG) { - var type = "linear", - fx = .5, fy = .5, - s = o.style; - gradient = (gradient + E)[rp](radial_gradient, function (all, _fx, _fy) { - type = "radial"; - if (_fx && _fy) { - fx = toFloat(_fx); - fy = toFloat(_fy); - var dir = ((fy > .5) * 2 - 1); - pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && - (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) && - fy != .5 && - (fy = fy.toFixed(5) - 1e-5 * dir); - } - return E; - }); - gradient = gradient[split](/\s*\-\s*/); - if (type == "linear") { - var angle = gradient.shift(); - angle = -toFloat(angle); - if (isNaN(angle)) { - return null; - } - var vector = [0, 0, math.cos(angle * math.PI / 180), math.sin(angle * math.PI / 180)], - max = 1 / (mmax(math.abs(vector[2]), math.abs(vector[3])) || 1); - vector[2] *= max; - vector[3] *= max; - if (vector[2] < 0) { - vector[0] = -vector[2]; - vector[2] = 0; - } - if (vector[3] < 0) { - vector[1] = -vector[3]; - vector[3] = 0; - } - } - var dots = parseDots(gradient); - if (!dots) { - return null; - } - var id = o.getAttribute("fill"); - id = id.match(/^url\(#(.*)\)$/); - id && SVG.defs.removeChild(doc.getElementById(id[1])); - - var el = $(type + "Gradient"); - el.id = "r" + (R._id++)[toString](36); - $(el, type == "radial" ? {fx: fx, fy: fy} : {x1: vector[0], y1: vector[1], x2: vector[2], y2: vector[3]}); - SVG.defs[appendChild](el); - for (var i = 0, ii = dots[length]; i < ii; i++) { - var stop = $("stop"); - $(stop, { - offset: dots[i].offset ? dots[i].offset : !i ? "0%" : "100%", - "stop-color": dots[i].color || "#fff" - }); - el[appendChild](stop); - } - $(o, { - fill: "url(#" + el.id + ")", - opacity: 1, - "fill-opacity": 1 - }); - s.fill = E; - s.opacity = 1; - s.fillOpacity = 1; - return 1; - }; - var updatePosition = function (o) { - var bbox = o.getBBox(); - $(o.pattern, {patternTransform: R.format("translate({0},{1})", bbox.x, bbox.y)}); - }; - var setFillAndStroke = function (o, params) { - var dasharray = { - "": [0], - "none": [0], - "-": [3, 1], - ".": [1, 1], - "-.": [3, 1, 1, 1], - "-..": [3, 1, 1, 1, 1, 1], - ". ": [1, 3], - "- ": [4, 3], - "--": [8, 3], - "- .": [4, 3, 1, 3], - "--.": [8, 3, 1, 3], - "--..": [8, 3, 1, 3, 1, 3] - }, - node = o.node, - attrs = o.attrs, - rot = o.rotate(), - addDashes = function (o, value) { - value = dasharray[lowerCase.call(value)]; - if (value) { - var width = o.attrs["stroke-width"] || "1", - butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0, - dashes = []; - var i = value[length]; - while (i--) { - dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt; - } - $(node, {"stroke-dasharray": dashes[join](",")}); - } - }; - params[has]("rotation") && (rot = params.rotation); - var rotxy = (rot + E)[split](separator); - if (!(rotxy.length - 1)) { - rotxy = null; - } else { - rotxy[1] = +rotxy[1]; - rotxy[2] = +rotxy[2]; - } - toFloat(rot) && o.rotate(0, true); - for (var att in params) { - if (params[has](att)) { - if (!availableAttrs[has](att)) { - continue; - } - var value = params[att]; - attrs[att] = value; - switch (att) { - case "blur": - o.blur(value); - break; - case "rotation": - o.rotate(value, true); - break; - // Hyperlink - case "href": - case "title": - case "target": - var pn = node.parentNode; - if (lowerCase.call(pn.tagName) != "a") { - var hl = $("a"); - pn.insertBefore(hl, node); - hl[appendChild](node); - pn = hl; - } - pn.setAttributeNS(o.paper.xlink, att, value); - break; - case "cursor": - node.style.cursor = value; - break; - case "clip-rect": - var rect = (value + E)[split](separator); - if (rect[length] == 4) { - o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode); - var el = $("clipPath"), - rc = $("rect"); - el.id = "r" + (R._id++)[toString](36); - $(rc, { - x: rect[0], - y: rect[1], - width: rect[2], - height: rect[3] - }); - el[appendChild](rc); - o.paper.defs[appendChild](el); - $(node, {"clip-path": "url(#" + el.id + ")"}); - o.clip = rc; - } - if (!value) { - var clip = doc.getElementById(node.getAttribute("clip-path")[rp](/(^url\(#|\)$)/g, E)); - clip && clip.parentNode.removeChild(clip); - $(node, {"clip-path": E}); - delete o.clip; - } - break; - case "path": - if (o.type == "path") { - $(node, {d: value ? attrs.path = roundPath(pathToAbsolute(value)) : "M0,0"}); - } - break; - case "width": - node[setAttribute](att, value); - if (attrs.fx) { - att = "x"; - value = attrs.x; - } else { - break; - } - case "x": - if (attrs.fx) { - value = -attrs.x - (attrs.width || 0); - } - case "rx": - if (att == "rx" && o.type == "rect") { - break; - } - case "cx": - rotxy && (att == "x" || att == "cx") && (rotxy[1] += value - attrs[att]); - node[setAttribute](att, round(value)); - o.pattern && updatePosition(o); - break; - case "height": - node[setAttribute](att, value); - if (attrs.fy) { - att = "y"; - value = attrs.y; - } else { - break; - } - case "y": - if (attrs.fy) { - value = -attrs.y - (attrs.height || 0); - } - case "ry": - if (att == "ry" && o.type == "rect") { - break; - } - case "cy": - rotxy && (att == "y" || att == "cy") && (rotxy[2] += value - attrs[att]); - node[setAttribute](att, round(value)); - o.pattern && updatePosition(o); - break; - case "r": - if (o.type == "rect") { - $(node, {rx: value, ry: value}); - } else { - node[setAttribute](att, value); - } - break; - case "src": - if (o.type == "image") { - node.setAttributeNS(o.paper.xlink, "href", value); - } - break; - case "stroke-width": - node.style.strokeWidth = value; - // Need following line for Firefox - node[setAttribute](att, value); - if (attrs["stroke-dasharray"]) { - addDashes(o, attrs["stroke-dasharray"]); - } - break; - case "stroke-dasharray": - addDashes(o, value); - break; - case "translation": - var xy = (value + E)[split](separator); - xy[0] = +xy[0] || 0; - xy[1] = +xy[1] || 0; - if (rotxy) { - rotxy[1] += xy[0]; - rotxy[2] += xy[1]; - } - translate.call(o, xy[0], xy[1]); - break; - case "scale": - xy = (value + E)[split](separator); - o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, isNaN(toFloat(xy[2])) ? null : +xy[2], isNaN(toFloat(xy[3])) ? null : +xy[3]); - break; - case "fill": - var isURL = (value + E).match(ISURL); - if (isURL) { - el = $("pattern"); - var ig = $("image"); - el.id = "r" + (R._id++)[toString](36); - $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1}); - $(ig, {x: 0, y: 0}); - ig.setAttributeNS(o.paper.xlink, "href", isURL[1]); - el[appendChild](ig); - - var img = doc.createElement("img"); - img.style.cssText = "position:absolute;left:-9999em;top-9999em"; - img.onload = function () { - $(el, {width: this.offsetWidth, height: this.offsetHeight}); - $(ig, {width: this.offsetWidth, height: this.offsetHeight}); - doc.body.removeChild(this); - o.paper.safari(); - }; - doc.body[appendChild](img); - img.src = isURL[1]; - o.paper.defs[appendChild](el); - node.style.fill = "url(#" + el.id + ")"; - $(node, {fill: "url(#" + el.id + ")"}); - o.pattern = el; - o.pattern && updatePosition(o); - break; - } - if (!R.getRGB(value).error) { - delete params.gradient; - delete attrs.gradient; - !R.is(attrs.opacity, "undefined") && - R.is(params.opacity, "undefined") && - $(node, {opacity: attrs.opacity}); - !R.is(attrs["fill-opacity"], "undefined") && - R.is(params["fill-opacity"], "undefined") && - $(node, {"fill-opacity": attrs["fill-opacity"]}); - } else if ((({circle: 1, ellipse: 1})[has](o.type) || (value + E).charAt() != "r") && addGradientFill(node, value, o.paper)) { - attrs.gradient = value; - attrs.fill = "none"; - break; - } - case "stroke": - node[setAttribute](att, R.getRGB(value).hex); - break; - case "gradient": - (({circle: 1, ellipse: 1})[has](o.type) || (value + E).charAt() != "r") && addGradientFill(node, value, o.paper); - break; - case "opacity": - case "fill-opacity": - if (attrs.gradient) { - var gradient = doc.getElementById(node.getAttribute("fill")[rp](/^url\(#|\)$/g, E)); - if (gradient) { - var stops = gradient.getElementsByTagName("stop"); - stops[stops[length] - 1][setAttribute]("stop-opacity", value); - } - break; - } - default: - att == "font-size" && (value = toInt(value, 10) + "px"); - var cssrule = att[rp](/(\-.)/g, function (w) { - return upperCase.call(w.substring(1)); - }); - node.style[cssrule] = value; - // Need following line for Firefox - node[setAttribute](att, value); - break; - } - } - } - - tuneText(o, params); - if (rotxy) { - o.rotate(rotxy.join(S)); - } else { - toFloat(rot) && o.rotate(rot, true); - } - }; - var leading = 1.2, - tuneText = function (el, params) { - if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) { - return; - } - var a = el.attrs, - node = el.node, - fontSize = node.firstChild ? toInt(doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10; - - if (params[has]("text")) { - a.text = params.text; - while (node.firstChild) { - node.removeChild(node.firstChild); - } - var texts = (params.text + E)[split]("\n"); - for (var i = 0, ii = texts[length]; i < ii; i++) if (texts[i]) { - var tspan = $("tspan"); - i && $(tspan, {dy: fontSize * leading, x: a.x}); - tspan[appendChild](doc.createTextNode(texts[i])); - node[appendChild](tspan); - } - } else { - texts = node.getElementsByTagName("tspan"); - for (i = 0, ii = texts[length]; i < ii; i++) { - i && $(texts[i], {dy: fontSize * leading, x: a.x}); - } - } - $(node, {y: a.y}); - var bb = el.getBBox(), - dif = a.y - (bb.y + bb.height / 2); - dif && isFinite(dif) && $(node, {y: a.y + dif}); - }, - Element = function (node, svg) { - var X = 0, - Y = 0; - this[0] = node; - this.id = R._oid++; - this.node = node; - node.raphael = this; - this.paper = svg; - this.attrs = this.attrs || {}; - this.transformations = []; // rotate, translate, scale - this._ = { - tx: 0, - ty: 0, - rt: {deg: 0, cx: 0, cy: 0}, - sx: 1, - sy: 1 - }; - !svg.bottom && (svg.bottom = this); - this.prev = svg.top; - svg.top && (svg.top.next = this); - svg.top = this; - this.next = null; - }; - Element[proto].rotate = function (deg, cx, cy) { - if (this.removed) { - return this; - } - if (deg == null) { - if (this._.rt.cx) { - return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S); - } - return this._.rt.deg; - } - var bbox = this.getBBox(); - deg = (deg + E)[split](separator); - if (deg[length] - 1) { - cx = toFloat(deg[1]); - cy = toFloat(deg[2]); - } - deg = toFloat(deg[0]); - if (cx != null) { - this._.rt.deg = deg; - } else { - this._.rt.deg += deg; - } - (cy == null) && (cx = null); - this._.rt.cx = cx; - this._.rt.cy = cy; - cx = cx == null ? bbox.x + bbox.width / 2 : cx; - cy = cy == null ? bbox.y + bbox.height / 2 : cy; - if (this._.rt.deg) { - this.transformations[0] = R.format("rotate({0} {1} {2})", this._.rt.deg, cx, cy); - this.clip && $(this.clip, {transform: R.format("rotate({0} {1} {2})", -this._.rt.deg, cx, cy)}); - } else { - this.transformations[0] = E; - this.clip && $(this.clip, {transform: E}); - } - $(this.node, {transform: this.transformations[join](S)}); - return this; - }; - Element[proto].hide = function () { - !this.removed && (this.node.style.display = "none"); - return this; - }; - Element[proto].show = function () { - !this.removed && (this.node.style.display = ""); - return this; - }; - Element[proto].remove = function () { - if (this.removed) { - return; - } - tear(this, this.paper); - this.node.parentNode.removeChild(this.node); - for (var i in this) { - delete this[i]; - } - this.removed = true; - }; - Element[proto].getBBox = function () { - if (this.removed) { - return this; - } - if (this.type == "path") { - return pathDimensions(this.attrs.path); - } - if (this.node.style.display == "none") { - this.show(); - var hide = true; - } - var bbox = {}; - try { - bbox = this.node.getBBox(); - } catch(e) { - // Firefox 3.0.x plays badly here - } finally { - bbox = bbox || {}; - } - if (this.type == "text") { - bbox = {x: bbox.x, y: Infinity, width: 0, height: 0}; - for (var i = 0, ii = this.node.getNumberOfChars(); i < ii; i++) { - var bb = this.node.getExtentOfChar(i); - (bb.y < bbox.y) && (bbox.y = bb.y); - (bb.y + bb.height - bbox.y > bbox.height) && (bbox.height = bb.y + bb.height - bbox.y); - (bb.x + bb.width - bbox.x > bbox.width) && (bbox.width = bb.x + bb.width - bbox.x); - } - } - hide && this.hide(); - return bbox; - }; - Element[proto].attr = function (name, value) { - if (this.removed) { - return this; - } - if (name == null) { - var res = {}; - for (var i in this.attrs) if (this.attrs[has](i)) { - res[i] = this.attrs[i]; - } - this._.rt.deg && (res.rotation = this.rotate()); - (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale()); - res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; - return res; - } - if (value == null && R.is(name, "string")) { - if (name == "translation") { - return translate.call(this); - } - if (name == "rotation") { - return this.rotate(); - } - if (name == "scale") { - return this.scale(); - } - if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { - return this.attrs.gradient; - } - return this.attrs[name]; - } - if (value == null && R.is(name, "array")) { - var values = {}; - for (var j = 0, jj = name.length; j < jj; j++) { - values[name[j]] = this.attr(name[j]); - } - return values; - } - if (value != null) { - var params = {}; - params[name] = value; - setFillAndStroke(this, params); - } else if (name != null && R.is(name, "object")) { - setFillAndStroke(this, name); - } - return this; - }; - Element[proto].toFront = function () { - if (this.removed) { - return this; - } - this.node.parentNode[appendChild](this.node); - var svg = this.paper; - svg.top != this && tofront(this, svg); - return this; - }; - Element[proto].toBack = function () { - if (this.removed) { - return this; - } - if (this.node.parentNode.firstChild != this.node) { - this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild); - toback(this, this.paper); - var svg = this.paper; - } - return this; - }; - Element[proto].insertAfter = function (element) { - if (this.removed) { - return this; - } - var node = element.node; - if (node.nextSibling) { - node.parentNode.insertBefore(this.node, node.nextSibling); - } else { - node.parentNode[appendChild](this.node); - } - insertafter(this, element, this.paper); - return this; - }; - Element[proto].insertBefore = function (element) { - if (this.removed) { - return this; - } - var node = element.node; - node.parentNode.insertBefore(this.node, node); - insertbefore(this, element, this.paper); - return this; - }; - Element[proto].blur = function (size) { - // Experimental. No Safari support. Use it on your own risk. - var t = this; - if (+size !== 0) { - var fltr = $("filter"), - blur = $("feGaussianBlur"); - t.attrs.blur = size; - fltr.id = "r" + (R._id++)[toString](36); - $(blur, {stdDeviation: +size || 1.5}); - fltr.appendChild(blur); - t.paper.defs.appendChild(fltr); - t._blur = fltr; - $(t.node, {filter: "url(#" + fltr.id + ")"}); - } else { - if (t._blur) { - t._blur.parentNode.removeChild(t._blur); - delete t._blur; - delete t.attrs.blur; - } - t.node.removeAttribute("filter"); - } - }; - var theCircle = function (svg, x, y, r) { - x = round(x); - y = round(y); - var el = $("circle"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"}; - res.type = "circle"; - $(el, res.attrs); - return res; - }; - var theRect = function (svg, x, y, w, h, r) { - x = round(x); - y = round(y); - var el = $("rect"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"}; - res.type = "rect"; - $(el, res.attrs); - return res; - }; - var theEllipse = function (svg, x, y, rx, ry) { - x = round(x); - y = round(y); - var el = $("ellipse"); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"}; - res.type = "ellipse"; - $(el, res.attrs); - return res; - }; - var theImage = function (svg, src, x, y, w, h) { - var el = $("image"); - $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"}); - el.setAttributeNS(svg.xlink, "href", src); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, width: w, height: h, src: src}; - res.type = "image"; - return res; - }; - var theText = function (svg, x, y, text) { - var el = $("text"); - $(el, {x: x, y: y, "text-anchor": "middle"}); - svg.canvas && svg.canvas[appendChild](el); - var res = new Element(el, svg); - res.attrs = {x: x, y: y, "text-anchor": "middle", text: text, font: availableAttrs.font, stroke: "none", fill: "#000"}; - res.type = "text"; - setFillAndStroke(res, res.attrs); - return res; - }; - var setSize = function (width, height) { - this.width = width || this.width; - this.height = height || this.height; - this.canvas[setAttribute]("width", this.width); - this.canvas[setAttribute]("height", this.height); - return this; - }; - var create = function () { - var con = getContainer[apply](0, arguments), - container = con && con.container, - x = con.x, - y = con.y, - width = con.width, - height = con.height; - if (!container) { - throw new Error("SVG container not found."); - } - var cnvs = $("svg"); - width = width || 512; - height = height || 342; - $(cnvs, { - xmlns: "http://www.w3.org/2000/svg", - version: 1.1, - width: width, - height: height - }); - if (container == 1) { - cnvs.style.cssText = "position:absolute;left:" + x + "px;top:" + y + "px"; - doc.body[appendChild](cnvs); - } else { - if (container.firstChild) { - container.insertBefore(cnvs, container.firstChild); - } else { - container[appendChild](cnvs); - } - } - container = new Paper; - container.width = width; - container.height = height; - container.canvas = cnvs; - plugins.call(container, container, R.fn); - container.clear(); - return container; - }; - Paper[proto].clear = function () { - var c = this.canvas; - while (c.firstChild) { - c.removeChild(c.firstChild); - } - this.bottom = this.top = null; - (this.desc = $("desc"))[appendChild](doc.createTextNode("Created with Rapha\xebl")); - c[appendChild](this.desc); - c[appendChild](this.defs = $("defs")); - }; - Paper[proto].remove = function () { - this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas); - for (var i in this) { - this[i] = removed(i); - } - }; - } - - // VML - if (R.vml) { - var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"}, - bites = /([clmz]),?([^clmz]*)/gi, - val = /-?[^,\s-]+/g, - coordsize = 1e3 + S + 1e3, - zoom = 10, - path2vml = function (path) { - var total = /[ahqstv]/ig, - command = pathToAbsolute; - (path + E).match(total) && (command = path2curve); - total = /[clmz]/g; - if (command == pathToAbsolute && !(path + E).match(total)) { - var res = (path + E)[rp](bites, function (all, command, args) { - var vals = [], - isMove = lowerCase.call(command) == "m", - res = map[command]; - args[rp](val, function (value) { - if (isMove && vals[length] == 2) { - res += vals + map[command == "m" ? "l" : "L"]; - vals = []; - } - vals[push](round(value * zoom)); - }); - return res + vals; - }); - return res; - } - var pa = command(path), p, r; - res = []; - for (var i = 0, ii = pa[length]; i < ii; i++) { - p = pa[i]; - r = lowerCase.call(pa[i][0]); - r == "z" && (r = "x"); - for (var j = 1, jj = p[length]; j < jj; j++) { - r += round(p[j] * zoom) + (j != jj - 1 ? "," : E); - } - res[push](r); - } - return res[join](S); - }; - - R[toString] = function () { - return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version; - }; - thePath = function (pathString, vml) { - var g = createNode("group"); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = vml.coordsize; - g.coordorigin = vml.coordorigin; - var el = createNode("shape"), ol = el.style; - ol.width = vml.width + "px"; - ol.height = vml.height + "px"; - el.coordsize = coordsize; - el.coordorigin = vml.coordorigin; - g[appendChild](el); - var p = new Element(el, g, vml), - attr = {fill: "none", stroke: "#000"}; - pathString && (attr.path = pathString); - p.isAbsolute = true; - p.type = "path"; - p.path = []; - p.Path = E; - setFillAndStroke(p, attr); - vml.canvas[appendChild](g); - return p; - }; - setFillAndStroke = function (o, params) { - o.attrs = o.attrs || {}; - var node = o.node, - a = o.attrs, - s = node.style, - xy, - res = o; - for (var par in params) if (params[has](par)) { - a[par] = params[par]; - } - params.href && (node.href = params.href); - params.title && (node.title = params.title); - params.target && (node.target = params.target); - params.cursor && (s.cursor = params.cursor); - "blur" in params && o.blur(params.blur); - if (params.path && o.type == "path") { - a.path = params.path; - node.path = path2vml(a.path); - } - if (params.rotation != null) { - o.rotate(params.rotation, true); - } - if (params.translation) { - xy = (params.translation + E)[split](separator); - translate.call(o, xy[0], xy[1]); - if (o._.rt.cx != null) { - o._.rt.cx +=+ xy[0]; - o._.rt.cy +=+ xy[1]; - o.setBox(o.attrs, xy[0], xy[1]); - } - } - if (params.scale) { - xy = (params.scale + E)[split](separator); - o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null); - } - if ("clip-rect" in params) { - var rect = (params["clip-rect"] + E)[split](separator); - if (rect[length] == 4) { - rect[2] = +rect[2] + (+rect[0]); - rect[3] = +rect[3] + (+rect[1]); - var div = node.clipRect || doc.createElement("div"), - dstyle = div.style, - group = node.parentNode; - dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect); - if (!node.clipRect) { - dstyle.position = "absolute"; - dstyle.top = 0; - dstyle.left = 0; - dstyle.width = o.paper.width + "px"; - dstyle.height = o.paper.height + "px"; - group.parentNode.insertBefore(div, group); - div[appendChild](group); - node.clipRect = div; - } - } - if (!params["clip-rect"]) { - node.clipRect && (node.clipRect.style.clip = E); - } - } - if (o.type == "image" && params.src) { - node.src = params.src; - } - if (o.type == "image" && params.opacity) { - node.filterOpacity = " progid:DXImageTransform.Microsoft.Alpha(opacity=" + (params.opacity * 100) + ")"; - s.filter = (node.filterMatrix || E) + (node.filterOpacity || E); - } - params.font && (s.font = params.font); - params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"'); //' - params["font-size"] && (s.fontSize = params["font-size"]); - params["font-weight"] && (s.fontWeight = params["font-weight"]); - params["font-style"] && (s.fontStyle = params["font-style"]); - if (params.opacity != null || - params["stroke-width"] != null || - params.fill != null || - params.stroke != null || - params["stroke-width"] != null || - params["stroke-opacity"] != null || - params["fill-opacity"] != null || - params["stroke-dasharray"] != null || - params["stroke-miterlimit"] != null || - params["stroke-linejoin"] != null || - params["stroke-linecap"] != null) { - node = o.shape || node; - var fill = (node.getElementsByTagName("fill") && node.getElementsByTagName("fill")[0]), - newfill = false; - !fill && (newfill = fill = createNode("fill")); - if ("fill-opacity" in params || "opacity" in params) { - var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1); - opacity < 0 && (opacity = 0); - opacity > 1 && (opacity = 1); - fill.opacity = opacity; - } - params.fill && (fill.on = true); - if (fill.on == null || params.fill == "none") { - fill.on = false; - } - if (fill.on && params.fill) { - var isURL = params.fill.match(ISURL); - if (isURL) { - fill.src = isURL[1]; - fill.type = "tile"; - } else { - fill.color = R.getRGB(params.fill).hex; - fill.src = E; - fill.type = "solid"; - if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || (params.fill + E).charAt() != "r") && addGradientFill(res, params.fill)) { - a.fill = "none"; - a.gradient = params.fill; - } - } - } - newfill && node[appendChild](fill); - var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]), - newstroke = false; - !stroke && (newstroke = stroke = createNode("stroke")); - if ((params.stroke && params.stroke != "none") || - params["stroke-width"] || - params["stroke-opacity"] != null || - params["stroke-dasharray"] || - params["stroke-miterlimit"] || - params["stroke-linejoin"] || - params["stroke-linecap"]) { - stroke.on = true; - } - (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false); - stroke.on && params.stroke && (stroke.color = R.getRGB(params.stroke).hex); - opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1); - var width = (toFloat(params["stroke-width"]) || 1) * .75; - opacity < 0 && (opacity = 0); - opacity > 1 && (opacity = 1); - params["stroke-width"] == null && (width = a["stroke-width"]); - params["stroke-width"] && (stroke.weight = width); - width && width < 1 && (opacity *= width) && (stroke.weight = 1); - stroke.opacity = opacity; - - params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter"); - stroke.miterlimit = params["stroke-miterlimit"] || 8; - params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round"); - if (params["stroke-dasharray"]) { - var dasharray = { - "-": "shortdash", - ".": "shortdot", - "-.": "shortdashdot", - "-..": "shortdashdotdot", - ". ": "dot", - "- ": "dash", - "--": "longdash", - "- .": "dashdot", - "--.": "longdashdot", - "--..": "longdashdotdot" - }; - stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E; - } - newstroke && node[appendChild](stroke); - } - if (res.type == "text") { - s = res.paper.span.style; - a.font && (s.font = a.font); - a["font-family"] && (s.fontFamily = a["font-family"]); - a["font-size"] && (s.fontSize = a["font-size"]); - a["font-weight"] && (s.fontWeight = a["font-weight"]); - a["font-style"] && (s.fontStyle = a["font-style"]); - res.node.string && (res.paper.span.innerHTML = (res.node.string + E)[rp](/")); - res.W = a.w = res.paper.span.offsetWidth; - res.H = a.h = res.paper.span.offsetHeight; - res.X = a.x; - res.Y = a.y + round(res.H / 2); - - // text-anchor emulationm - switch (a["text-anchor"]) { - case "start": - res.node.style["v-text-align"] = "left"; - res.bbx = round(res.W / 2); - break; - case "end": - res.node.style["v-text-align"] = "right"; - res.bbx = -round(res.W / 2); - break; - default: - res.node.style["v-text-align"] = "center"; - break; - } - } - }; - addGradientFill = function (o, gradient) { - o.attrs = o.attrs || {}; - var attrs = o.attrs, - fill = o.node.getElementsByTagName("fill"), - type = "linear", - fxfy = ".5 .5"; - o.attrs.gradient = gradient; - gradient = (gradient + E)[rp](radial_gradient, function (all, fx, fy) { - type = "radial"; - if (fx && fy) { - fx = toFloat(fx); - fy = toFloat(fy); - pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5); - fxfy = fx + S + fy; - } - return E; - }); - gradient = gradient[split](/\s*\-\s*/); - if (type == "linear") { - var angle = gradient.shift(); - angle = -toFloat(angle); - if (isNaN(angle)) { - return null; - } - } - var dots = parseDots(gradient); - if (!dots) { - return null; - } - o = o.shape || o.node; - fill = fill[0] || createNode("fill"); - if (dots[length]) { - fill.on = true; - fill.method = "none"; - fill.type = (type == "radial") ? "gradientradial" : "gradient"; - fill.color = dots[0].color; - fill.color2 = dots[dots[length] - 1].color; - var clrs = []; - for (var i = 0, ii = dots[length]; i < ii; i++) { - dots[i].offset && clrs[push](dots[i].offset + S + dots[i].color); - } - fill.colors && (fill.colors.value = clrs[length] ? clrs[join](",") : "0% " + fill.color); - if (type == "radial") { - fill.focus = "100%"; - fill.focussize = fxfy; - fill.focusposition = fxfy; - } else { - fill.angle = (270 - angle) % 360; - } - } - return 1; - }; - Element = function (node, group, vml) { - var Rotation = 0, - RotX = 0, - RotY = 0, - Scale = 1; - this[0] = node; - this.id = R._oid++; - this.node = node; - node.raphael = this; - this.X = 0; - this.Y = 0; - this.attrs = {}; - this.Group = group; - this.paper = vml; - this._ = { - tx: 0, - ty: 0, - rt: {deg:0}, - sx: 1, - sy: 1 - }; - !vml.bottom && (vml.bottom = this); - this.prev = vml.top; - vml.top && (vml.top.next = this); - vml.top = this; - this.next = null; - }; - Element[proto].rotate = function (deg, cx, cy) { - if (this.removed) { - return this; - } - if (deg == null) { - if (this._.rt.cx) { - return [this._.rt.deg, this._.rt.cx, this._.rt.cy][join](S); - } - return this._.rt.deg; - } - deg = (deg + E)[split](separator); - if (deg[length] - 1) { - cx = toFloat(deg[1]); - cy = toFloat(deg[2]); - } - deg = toFloat(deg[0]); - if (cx != null) { - this._.rt.deg = deg; - } else { - this._.rt.deg += deg; - } - cy == null && (cx = null); - this._.rt.cx = cx; - this._.rt.cy = cy; - this.setBox(this.attrs, cx, cy); - this.Group.style.rotation = this._.rt.deg; - // gradient fix for rotation. TODO - // var fill = (this.shape || this.node).getElementsByTagName("fill"); - // fill = fill[0] || {}; - // var b = ((360 - this._.rt.deg) - 270) % 360; - // !R.is(fill.angle, "undefined") && (fill.angle = b); - return this; - }; - Element[proto].setBox = function (params, cx, cy) { - if (this.removed) { - return this; - } - var gs = this.Group.style, - os = (this.shape && this.shape.style) || this.node.style; - params = params || {}; - for (var i in params) if (params[has](i)) { - this.attrs[i] = params[i]; - } - cx = cx || this._.rt.cx; - cy = cy || this._.rt.cy; - var attr = this.attrs, - x, - y, - w, - h; - switch (this.type) { - case "circle": - x = attr.cx - attr.r; - y = attr.cy - attr.r; - w = h = attr.r * 2; - break; - case "ellipse": - x = attr.cx - attr.rx; - y = attr.cy - attr.ry; - w = attr.rx * 2; - h = attr.ry * 2; - break; - case "rect": - case "image": - x = +attr.x; - y = +attr.y; - w = attr.width || 0; - h = attr.height || 0; - break; - case "text": - this.textpath.v = ["m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ", round(attr.y - 2)][join](E); - x = attr.x - round(this.W / 2); - y = attr.y - this.H / 2; - w = this.W; - h = this.H; - break; - case "path": - if (!this.attrs.path) { - x = 0; - y = 0; - w = this.paper.width; - h = this.paper.height; - } else { - var dim = pathDimensions(this.attrs.path); - x = dim.x; - y = dim.y; - w = dim.width; - h = dim.height; - } - break; - default: - x = 0; - y = 0; - w = this.paper.width; - h = this.paper.height; - break; - } - cx = (cx == null) ? x + w / 2 : cx; - cy = (cy == null) ? y + h / 2 : cy; - var left = cx - this.paper.width / 2, - top = cy - this.paper.height / 2, t; - gs.left != (t = left + "px") && (gs.left = t); - gs.top != (t = top + "px") && (gs.top = t); - this.X = this.type == "path" ? -left : x; - this.Y = this.type == "path" ? -top : y; - this.W = w; - this.H = h; - if (this.type == "path") { - os.left != (t = -left * zoom + "px") && (os.left = t); - os.top != (t = -top * zoom + "px") && (os.top = t); - } else if (this.type == "text") { - os.left != (t = -left + "px") && (os.left = t); - os.top != (t = -top + "px") && (os.top = t); - } else { - gs.width != (t = this.paper.width + "px") && (gs.width = t); - gs.height != (t = this.paper.height + "px") && (gs.height = t); - os.left != (t = x - left + "px") && (os.left = t); - os.top != (t = y - top + "px") && (os.top = t); - os.width != (t = w + "px") && (os.width = t); - os.height != (t = h + "px") && (os.height = t); - var arcsize = (+params.r || 0) / mmin(w, h); - if (this.type == "rect" && this.arcsize.toFixed(4) != arcsize.toFixed(4) && (arcsize || this.arcsize)) { - // We should replace element with the new one - var o = createNode("roundrect"), - a = {}, - ii = this.events && this.events[length]; - i = 0; - o.arcsize = arcsize; - o.raphael = this; - this.Group[appendChild](o); - this.Group.removeChild(this.node); - this[0] = this.node = o; - this.arcsize = arcsize; - for (i in attr) { - a[i] = attr[i]; - } - delete a.scale; - this.attr(a); - if (this.events) for (; i < ii; i++) { - this.events[i].unbind = addEvent(this.node, this.events[i].name, this.events[i].f, this); - } - } - } - }; - Element[proto].hide = function () { - !this.removed && (this.Group.style.display = "none"); - return this; - }; - Element[proto].show = function () { - !this.removed && (this.Group.style.display = "block"); - return this; - }; - Element[proto].getBBox = function () { - if (this.removed) { - return this; - } - if (this.type == "path") { - return pathDimensions(this.attrs.path); - } - return { - x: this.X + (this.bbx || 0), - y: this.Y, - width: this.W, - height: this.H - }; - }; - Element[proto].remove = function () { - if (this.removed) { - return; - } - tear(this, this.paper); - this.node.parentNode.removeChild(this.node); - this.Group.parentNode.removeChild(this.Group); - this.shape && this.shape.parentNode.removeChild(this.shape); - for (var i in this) { - delete this[i]; - } - this.removed = true; - }; - Element[proto].attr = function (name, value) { - if (this.removed) { - return this; - } - if (name == null) { - var res = {}; - for (var i in this.attrs) if (this.attrs[has](i)) { - res[i] = this.attrs[i]; - } - this._.rt.deg && (res.rotation = this.rotate()); - (this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale()); - res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient; - return res; - } - if (value == null && R.is(name, "string")) { - if (name == "translation") { - return translate.call(this); - } - if (name == "rotation") { - return this.rotate(); - } - if (name == "scale") { - return this.scale(); - } - if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) { - return this.attrs.gradient; - } - return this.attrs[name]; - } - if (this.attrs && value == null && R.is(name, "array")) { - var ii, values = {}; - for (i = 0, ii = name[length]; i < ii; i++) { - values[name[i]] = this.attr(name[i]); - } - return values; - } - var params; - if (value != null) { - params = {}; - params[name] = value; - } - value == null && R.is(name, "object") && (params = name); - if (params) { - if (params.text && this.type == "text") { - this.node.string = params.text; - } - setFillAndStroke(this, params); - if (params.gradient && (({circle: 1, ellipse: 1})[has](this.type) || (params.gradient + E).charAt() != "r")) { - addGradientFill(this, params.gradient); - } - (this.type != "path" || this._.rt.deg) && this.setBox(this.attrs); - } - return this; - }; - Element[proto].toFront = function () { - !this.removed && this.Group.parentNode[appendChild](this.Group); - this.paper.top != this && tofront(this, this.paper); - return this; - }; - Element[proto].toBack = function () { - if (this.removed) { - return this; - } - if (this.Group.parentNode.firstChild != this.Group) { - this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild); - toback(this, this.paper); - } - return this; - }; - Element[proto].insertAfter = function (element) { - if (this.removed) { - return this; - } - if (element.Group.nextSibling) { - element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling); - } else { - element.Group.parentNode[appendChild](this.Group); - } - insertafter(this, element, this.paper); - return this; - }; - Element[proto].insertBefore = function (element) { - if (this.removed) { - return this; - } - element.Group.parentNode.insertBefore(this.Group, element.Group); - insertbefore(this, element, this.paper); - return this; - }; - var blurregexp = / progid:\S+Blur\([^\)]+\)/g; - Element[proto].blur = function (size) { - var s = this.node.style, - f = s.filter; - f = f.replace(blurregexp, ""); - if (+size !== 0) { - this.attrs.blur = size; - s.filter = f + " progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (+size || 1.5) + ")"; - s.margin = Raphael.format("-{0}px 0 0 -{0}px", Math.round(+size || 1.5)); - } else { - s.filter = f; - s.margin = 0; - delete this.attrs.blur; - } - }; - - theCircle = function (vml, x, y, r) { - var g = createNode("group"), - o = createNode("oval"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "circle"; - setFillAndStroke(res, {stroke: "#000", fill: "none"}); - res.attrs.cx = x; - res.attrs.cy = y; - res.attrs.r = r; - res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2}); - vml.canvas[appendChild](g); - return res; - }; - theRect = function (vml, x, y, w, h, r) { - var g = createNode("group"), - o = createNode("roundrect"), - arcsize = (+r || 0) / (mmin(w, h)); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - o.arcsize = arcsize; - var res = new Element(o, g, vml); - res.type = "rect"; - setFillAndStroke(res, {stroke: "#000"}); - res.arcsize = arcsize; - res.setBox({x: x, y: y, width: w, height: h, r: r}); - vml.canvas[appendChild](g); - return res; - }; - theEllipse = function (vml, x, y, rx, ry) { - var g = createNode("group"), - o = createNode("oval"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "ellipse"; - setFillAndStroke(res, {stroke: "#000"}); - res.attrs.cx = x; - res.attrs.cy = y; - res.attrs.rx = rx; - res.attrs.ry = ry; - res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2}); - vml.canvas[appendChild](g); - return res; - }; - theImage = function (vml, src, x, y, w, h) { - var g = createNode("group"), - o = createNode("image"), - ol = o.style; - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - o.src = src; - g[appendChild](o); - var res = new Element(o, g, vml); - res.type = "image"; - res.attrs.src = src; - res.attrs.x = x; - res.attrs.y = y; - res.attrs.w = w; - res.attrs.h = h; - res.setBox({x: x, y: y, width: w, height: h}); - vml.canvas[appendChild](g); - return res; - }; - theText = function (vml, x, y, text) { - var g = createNode("group"), - el = createNode("shape"), - ol = el.style, - path = createNode("path"), - ps = path.style, - o = createNode("textpath"); - g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px"; - g.coordsize = coordsize; - g.coordorigin = vml.coordorigin; - path.v = R.format("m{0},{1}l{2},{1}", round(x * 10), round(y * 10), round(x * 10) + 1); - path.textpathok = true; - ol.width = vml.width; - ol.height = vml.height; - o.string = text + E; - o.on = true; - el[appendChild](o); - el[appendChild](path); - g[appendChild](el); - var res = new Element(o, g, vml); - res.shape = el; - res.textpath = path; - res.type = "text"; - res.attrs.text = text; - res.attrs.x = x; - res.attrs.y = y; - res.attrs.w = 1; - res.attrs.h = 1; - setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"}); - res.setBox(); - vml.canvas[appendChild](g); - return res; - }; - setSize = function (width, height) { - var cs = this.canvas.style; - width == +width && (width += "px"); - height == +height && (height += "px"); - cs.width = width; - cs.height = height; - cs.clip = "rect(0 " + width + " " + height + " 0)"; - return this; - }; - var createNode; - doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); - try { - !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml"); - createNode = function (tagName) { - return doc.createElement(''); - }; - } catch (e) { - createNode = function (tagName) { - return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">'); - }; - } - create = function () { - var con = getContainer[apply](0, arguments), - container = con.container, - height = con.height, - s, - width = con.width, - x = con.x, - y = con.y; - if (!container) { - throw new Error("VML container not found."); - } - var res = new Paper, - c = res.canvas = doc.createElement("div"), - cs = c.style; - width = width || 512; - height = height || 342; - width == +width && (width += "px"); - height == +height && (height += "px"); - res.width = 1e3; - res.height = 1e3; - res.coordsize = zoom * 1e3 + S + zoom * 1e3; - res.coordorigin = "0 0"; - res.span = doc.createElement("span"); - res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; - c[appendChild](res.span); - cs.cssText = R.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden", width, height); - if (container == 1) { - doc.body[appendChild](c); - cs.left = x + "px"; - cs.top = y + "px"; - } else { - container.style.width = width; - container.style.height = height; - if (container.firstChild) { - container.insertBefore(c, container.firstChild); - } else { - container[appendChild](c); - } - } - plugins.call(res, res, R.fn); - return res; - }; - Paper[proto].clear = function () { - this.canvas.innerHTML = E; - this.span = doc.createElement("span"); - this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;"; - this.canvas[appendChild](this.span); - this.bottom = this.top = null; - }; - Paper[proto].remove = function () { - this.canvas.parentNode.removeChild(this.canvas); - for (var i in this) { - this[i] = removed(i); - } - return true; - }; - } - - // rest - // Safari or Chrome (WebKit) rendering bug workaround method - if ((/^Apple|^Google/).test(win.navigator.vendor) && !(win.navigator.userAgent.indexOf("Version/4.0") + 1)) { - Paper[proto].safari = function () { - var rect = this.rect(-99, -99, this.width + 99, this.height + 99); - win.setTimeout(function () {rect.remove();}); - }; - } else { - Paper[proto].safari = function () {}; - } - - // Events - var addEvent = (function () { - if (doc.addEventListener) { - return function (obj, type, fn, element) { - var f = function (e) { - return fn.call(element, e); - }; - obj.addEventListener(type, f, false); - return function () { - obj.removeEventListener(type, f, false); - return true; - }; - }; - } else if (doc.attachEvent) { - return function (obj, type, fn, element) { - var f = function (e) { - return fn.call(element, e || win.event); - }; - obj.attachEvent("on" + type, f); - var detacher = function () { - obj.detachEvent("on" + type, f); - return true; - }; - return detacher; - }; - } - })(); - for (var i = events[length]; i--;) { - (function (eventName) { - Element[proto][eventName] = function (fn) { - if (R.is(fn, "function")) { - this.events = this.events || []; - this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node, eventName, fn, this)}); - } - return this; - }; - Element[proto]["un" + eventName] = function (fn) { - var events = this.events, - l = events[length]; - while (l--) if (events[l].name == eventName && events[l].f == fn) { - events[l].unbind(); - events.splice(l, 1); - !events.length && delete this.events; - return this; - } - return this; - }; - })(events[i]); - } - Element[proto].hover = function (f_in, f_out) { - return this.mouseover(f_in).mouseout(f_out); - }; - Element[proto].unhover = function (f_in, f_out) { - return this.unmouseover(f_in).unmouseout(f_out); - }; - Paper[proto].circle = function (x, y, r) { - return theCircle(this, x || 0, y || 0, r || 0); - }; - Paper[proto].rect = function (x, y, w, h, r) { - return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0); - }; - Paper[proto].ellipse = function (x, y, rx, ry) { - return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0); - }; - Paper[proto].path = function (pathString) { - pathString && !R.is(pathString, "string") && !R.is(pathString[0], "array") && (pathString += E); - return thePath(R.format[apply](R, arguments), this); - }; - Paper[proto].image = function (src, x, y, w, h) { - return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0); - }; - Paper[proto].text = function (x, y, text) { - return theText(this, x || 0, y || 0, text || E); - }; - Paper[proto].set = function (itemsArray) { - arguments[length] > 1 && (itemsArray = Array[proto].splice.call(arguments, 0, arguments[length])); - return new Set(itemsArray); - }; - Paper[proto].setSize = setSize; - Paper[proto].top = Paper[proto].bottom = null; - Paper[proto].raphael = R; - function x_y() { - return this.x + S + this.y; - } - Element[proto].scale = function (x, y, cx, cy) { - if (x == null && y == null) { - return { - x: this._.sx, - y: this._.sy, - toString: x_y - }; - } - y = y || x; - !+y && (y = x); - var dx, - dy, - dcx, - dcy, - a = this.attrs; - if (x != 0) { - var bb = this.getBBox(), - rcx = bb.x + bb.width / 2, - rcy = bb.y + bb.height / 2, - kx = x / this._.sx, - ky = y / this._.sy; - cx = (+cx || cx == 0) ? cx : rcx; - cy = (+cy || cy == 0) ? cy : rcy; - var dirx = ~~(x / math.abs(x)), - diry = ~~(y / math.abs(y)), - s = this.node.style, - ncx = cx + (rcx - cx) * kx, - ncy = cy + (rcy - cy) * ky; - switch (this.type) { - case "rect": - case "image": - var neww = a.width * dirx * kx, - newh = a.height * diry * ky; - this.attr({ - height: newh, - r: a.r * mmin(dirx * kx, diry * ky), - width: neww, - x: ncx - neww / 2, - y: ncy - newh / 2 - }); - break; - case "circle": - case "ellipse": - this.attr({ - rx: a.rx * dirx * kx, - ry: a.ry * diry * ky, - r: a.r * mmin(dirx * kx, diry * ky), - cx: ncx, - cy: ncy - }); - break; - case "path": - var path = pathToRelative(a.path), - skip = true; - for (var i = 0, ii = path[length]; i < ii; i++) { - var p = path[i], - P0 = upperCase.call(p[0]); - if (P0 == "M" && skip) { - continue; - } else { - skip = false; - } - if (P0 == "A") { - p[path[i][length] - 2] *= kx; - p[path[i][length] - 1] *= ky; - p[1] *= dirx * kx; - p[2] *= diry * ky; - p[5] = +!(dirx + diry ? !+p[5] : +p[5]); - } else if (P0 == "H") { - for (var j = 1, jj = p[length]; j < jj; j++) { - p[j] *= kx; - } - } else if (P0 == "V") { - for (j = 1, jj = p[length]; j < jj; j++) { - p[j] *= ky; - } - } else { - for (j = 1, jj = p[length]; j < jj; j++) { - p[j] *= (j % 2) ? kx : ky; - } - } - } - var dim2 = pathDimensions(path); - dx = ncx - dim2.x - dim2.width / 2; - dy = ncy - dim2.y - dim2.height / 2; - path[0][1] += dx; - path[0][2] += dy; - this.attr({path: path}); - break; - } - if (this.type in {text: 1, image:1} && (dirx != 1 || diry != 1)) { - if (this.transformations) { - this.transformations[2] = "scale("[concat](dirx, ",", diry, ")"); - this.node[setAttribute]("transform", this.transformations[join](S)); - dx = (dirx == -1) ? -a.x - (neww || 0) : a.x; - dy = (diry == -1) ? -a.y - (newh || 0) : a.y; - this.attr({x: dx, y: dy}); - a.fx = dirx - 1; - a.fy = diry - 1; - } else { - this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11="[concat](dirx, - ", M12=0, M21=0, M22=", diry, - ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')"); - s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E); - } - } else { - if (this.transformations) { - this.transformations[2] = E; - this.node[setAttribute]("transform", this.transformations[join](S)); - a.fx = 0; - a.fy = 0; - } else { - this.node.filterMatrix = E; - s.filter = (this.node.filterMatrix || E) + (this.node.filterOpacity || E); - } - } - a.scale = [x, y, cx, cy][join](S); - this._.sx = x; - this._.sy = y; - } - return this; - }; - Element[proto].clone = function () { - var attr = this.attr(); - delete attr.scale; - delete attr.translation; - return this.paper[this.type]().attr(attr); - }; - var getPointAtSegmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) { - var len = 0, - old; - for (var i = 0; i < 1.001; i+=.001) { - var dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i); - i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); - if (len >= length) { - return dot; - } - old = dot; - } - }), - getLengthFactory = function (istotal, subpath) { - return function (path, length, onlystart) { - path = path2curve(path); - var x, y, p, l, sp = "", subpaths = {}, point, - len = 0; - for (var i = 0, ii = path.length; i < ii; i++) { - p = path[i]; - if (p[0] == "M") { - x = +p[1]; - y = +p[2]; - } else { - l = segmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]); - if (len + l > length) { - if (subpath && !subpaths.start) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - sp += ["C", point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y]; - if (onlystart) {return sp;} - subpaths.start = sp; - sp = ["M", point.x, point.y + "C", point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]][join](); - len += l; - x = +p[5]; - y = +p[6]; - continue; - } - if (!istotal && !subpath) { - point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len); - return {x: point.x, y: point.y, alpha: point.alpha}; - } - } - len += l; - x = +p[5]; - y = +p[6]; - } - sp += p; - } - subpaths.end = sp; - point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], 1); - point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha}); - return point; - }; - }, - segmentLength = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) { - var old = {x: 0, y: 0}, - len = 0; - for (var i = 0; i < 1.01; i+=.01) { - var dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i); - i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5)); - old = dot; - } - return len; - }); - var getTotalLength = getLengthFactory(1), - getPointAtLength = getLengthFactory(), - getSubpathsAtLength = getLengthFactory(0, 1); - Element[proto].getTotalLength = function () { - if (this.type != "path") {return;} - return getTotalLength(this.attrs.path); - }; - Element[proto].getPointAtLength = function (length) { - if (this.type != "path") {return;} - return getPointAtLength(this.attrs.path, length); - }; - Element[proto].getSubpath = function (from, to) { - if (this.type != "path") {return;} - if (math.abs(this.getTotalLength() - to) < 1e-6) { - return getSubpathsAtLength(this.attrs.path, from).end; - } - var a = getSubpathsAtLength(this.attrs.path, to, 1); - return from ? getSubpathsAtLength(a, from).end : a; - }; - - // animation easing formulas - R.easing_formulas = { - linear: function (n) { - return n; - }, - "<": function (n) { - return pow(n, 3); - }, - ">": function (n) { - return pow(n - 1, 3) + 1; - }, - "<>": function (n) { - n = n * 2; - if (n < 1) { - return pow(n, 3) / 2; - } - n -= 2; - return (pow(n, 3) + 2) / 2; - }, - backIn: function (n) { - var s = 1.70158; - return n * n * ((s + 1) * n - s); - }, - backOut: function (n) { - n = n - 1; - var s = 1.70158; - return n * n * ((s + 1) * n + s) + 1; - }, - elastic: function (n) { - if (n == 0 || n == 1) { - return n; - } - var p = .3, - s = p / 4; - return pow(2, -10 * n) * math.sin((n - s) * (2 * math.PI) / p) + 1; - }, - bounce: function (n) { - var s = 7.5625, - p = 2.75, - l; - if (n < (1 / p)) { - l = s * n * n; - } else { - if (n < (2 / p)) { - n -= (1.5 / p); - l = s * n * n + .75; - } else { - if (n < (2.5 / p)) { - n -= (2.25 / p); - l = s * n * n + .9375; - } else { - n -= (2.625 / p); - l = s * n * n + .984375; - } - } - } - return l; - } - }; - - var animationElements = {length : 0}, - animation = function () { - var Now = +new Date; - for (var l in animationElements) if (l != "length" && animationElements[has](l)) { - var e = animationElements[l]; - if (e.stop || e.el.removed) { - delete animationElements[l]; - animationElements[length]--; - continue; - } - var time = Now - e.start, - ms = e.ms, - easing = e.easing, - from = e.from, - diff = e.diff, - to = e.to, - t = e.t, - prev = e.prev || 0, - that = e.el, - callback = e.callback, - set = {}, - now; - if (time < ms) { - var pos = R.easing_formulas[easing] ? R.easing_formulas[easing](time / ms) : time / ms; - for (var attr in from) if (from[has](attr)) { - switch (availableAnimAttrs[attr]) { - case "along": - now = pos * ms * diff[attr]; - to.back && (now = to.len - now); - var point = getPointAtLength(to[attr], now); - that.translate(diff.sx - diff.x || 0, diff.sy - diff.y || 0); - diff.x = point.x; - diff.y = point.y; - that.translate(point.x - diff.sx, point.y - diff.sy); - to.rot && that.rotate(diff.r + point.alpha, point.x, point.y); - break; - case "number": - now = +from[attr] + pos * ms * diff[attr]; - break; - case "colour": - now = "rgb(" + [ - upto255(round(from[attr].r + pos * ms * diff[attr].r)), - upto255(round(from[attr].g + pos * ms * diff[attr].g)), - upto255(round(from[attr].b + pos * ms * diff[attr].b)) - ][join](",") + ")"; - break; - case "path": - now = []; - for (var i = 0, ii = from[attr][length]; i < ii; i++) { - now[i] = [from[attr][i][0]]; - for (var j = 1, jj = from[attr][i][length]; j < jj; j++) { - now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j]; - } - now[i] = now[i][join](S); - } - now = now[join](S); - break; - case "csv": - switch (attr) { - case "translation": - var x = diff[attr][0] * (time - prev), - y = diff[attr][1] * (time - prev); - t.x += x; - t.y += y; - now = x + S + y; - break; - case "rotation": - now = +from[attr][0] + pos * ms * diff[attr][0]; - from[attr][1] && (now += "," + from[attr][1] + "," + from[attr][2]); - break; - case "scale": - now = [+from[attr][0] + pos * ms * diff[attr][0], +from[attr][1] + pos * ms * diff[attr][1], (2 in to[attr] ? to[attr][2] : E), (3 in to[attr] ? to[attr][3] : E)][join](S); - break; - case "clip-rect": - now = []; - i = 4; - while (i--) { - now[i] = +from[attr][i] + pos * ms * diff[attr][i]; - } - break; - } - break; - } - set[attr] = now; - } - that.attr(set); - that._run && that._run.call(that); - } else { - if (to.along) { - point = getPointAtLength(to.along, to.len * !to.back); - that.translate(diff.sx - (diff.x || 0) + point.x - diff.sx, diff.sy - (diff.y || 0) + point.y - diff.sy); - to.rot && that.rotate(diff.r + point.alpha, point.x, point.y); - } - (t.x || t.y) && that.translate(-t.x, -t.y); - to.scale && (to.scale = to.scale + E); - that.attr(to); - delete animationElements[l]; - animationElements[length]--; - that.in_animation = null; - R.is(callback, "function") && callback.call(that); - } - e.prev = time; - } - R.svg && that && that.paper.safari(); - animationElements[length] && win.setTimeout(animation); - }, - upto255 = function (color) { - return color > 255 ? 255 : (color < 0 ? 0 : color); - }, - translate = function (x, y) { - if (x == null) { - return {x: this._.tx, y: this._.ty, toString: x_y}; - } - this._.tx += +x; - this._.ty += +y; - switch (this.type) { - case "circle": - case "ellipse": - this.attr({cx: +x + this.attrs.cx, cy: +y + this.attrs.cy}); - break; - case "rect": - case "image": - case "text": - this.attr({x: +x + this.attrs.x, y: +y + this.attrs.y}); - break; - case "path": - var path = pathToRelative(this.attrs.path); - path[0][1] += +x; - path[0][2] += +y; - this.attr({path: path}); - break; - } - return this; - }; - Element[proto].animateWith = function (element, params, ms, easing, callback) { - animationElements[element.id] && (params.start = animationElements[element.id].start); - return this.animate(params, ms, easing, callback); - }; - Element[proto].animateAlong = along(); - Element[proto].animateAlongBack = along(1); - function along(isBack) { - return function (path, ms, rotate, callback) { - var params = {back: isBack}; - R.is(rotate, "function") ? (callback = rotate) : (params.rot = rotate); - path && path.constructor == Element && (path = path.attrs.path); - path && (params.along = path); - return this.animate(params, ms, callback); - }; - } - Element[proto].onAnimation = function (f) { - this._run = f || 0; - return this; - }; - Element[proto].animate = function (params, ms, easing, callback) { - if (R.is(easing, "function") || !easing) { - callback = easing || null; - } - var from = {}, - to = {}, - diff = {}; - for (var attr in params) if (params[has](attr)) { - if (availableAnimAttrs[has](attr)) { - from[attr] = this.attr(attr); - (from[attr] == null) && (from[attr] = availableAttrs[attr]); - to[attr] = params[attr]; - switch (availableAnimAttrs[attr]) { - case "along": - var len = getTotalLength(params[attr]), - point = getPointAtLength(params[attr], len * !!params.back), - bb = this.getBBox(); - diff[attr] = len / ms; - diff.tx = bb.x; - diff.ty = bb.y; - diff.sx = point.x; - diff.sy = point.y; - to.rot = params.rot; - to.back = params.back; - to.len = len; - params.rot && (diff.r = toFloat(this.rotate()) || 0); - break; - case "number": - diff[attr] = (to[attr] - from[attr]) / ms; - break; - case "colour": - from[attr] = R.getRGB(from[attr]); - var toColour = R.getRGB(to[attr]); - diff[attr] = { - r: (toColour.r - from[attr].r) / ms, - g: (toColour.g - from[attr].g) / ms, - b: (toColour.b - from[attr].b) / ms - }; - break; - case "path": - var pathes = path2curve(from[attr], to[attr]); - from[attr] = pathes[0]; - var toPath = pathes[1]; - diff[attr] = []; - for (var i = 0, ii = from[attr][length]; i < ii; i++) { - diff[attr][i] = [0]; - for (var j = 1, jj = from[attr][i][length]; j < jj; j++) { - diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms; - } - } - break; - case "csv": - var values = (params[attr] + E)[split](separator), - from2 = (from[attr] + E)[split](separator); - switch (attr) { - case "translation": - from[attr] = [0, 0]; - diff[attr] = [values[0] / ms, values[1] / ms]; - break; - case "rotation": - from[attr] = (from2[1] == values[1] && from2[2] == values[2]) ? from2 : [0, values[1], values[2]]; - diff[attr] = [(values[0] - from[attr][0]) / ms, 0, 0]; - break; - case "scale": - params[attr] = values; - from[attr] = (from[attr] + E)[split](separator); - diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][1]) / ms, 0, 0]; - break; - case "clip-rect": - from[attr] = (from[attr] + E)[split](separator); - diff[attr] = []; - i = 4; - while (i--) { - diff[attr][i] = (values[i] - from[attr][i]) / ms; - } - break; - } - to[attr] = values; - } - } - } - this.stop(); - this.in_animation = 1; - animationElements[this.id] = { - start: params.start || +new Date, - ms: ms, - easing: easing, - from: from, - diff: diff, - to: to, - el: this, - callback: callback, - t: {x: 0, y: 0} - }; - ++animationElements[length] == 1 && animation(); - return this; - }; - Element[proto].stop = function () { - animationElements[this.id] && animationElements[length]--; - delete animationElements[this.id]; - return this; - }; - Element[proto].translate = function (x, y) { - return this.attr({translation: x + " " + y}); - }; - Element[proto][toString] = function () { - return "Rapha\xebl\u2019s object"; - }; - R.ae = animationElements; - - // Set - var Set = function (items) { - this.items = []; - this[length] = 0; - if (items) { - for (var i = 0, ii = items[length]; i < ii; i++) { - if (items[i] && (items[i].constructor == Element || items[i].constructor == Set)) { - this[this.items[length]] = this.items[this.items[length]] = items[i]; - this[length]++; - } - } - } - }; - Set[proto][push] = function () { - var item, - len; - for (var i = 0, ii = arguments[length]; i < ii; i++) { - item = arguments[i]; - if (item && (item.constructor == Element || item.constructor == Set)) { - len = this.items[length]; - this[len] = this.items[len] = item; - this[length]++; - } - } - return this; - }; - Set[proto].pop = function () { - delete this[this[length]--]; - return this.items.pop(); - }; - for (var method in Element[proto]) if (Element[proto][has](method)) { - Set[proto][method] = (function (methodname) { - return function () { - for (var i = 0, ii = this.items[length]; i < ii; i++) { - this.items[i][methodname][apply](this.items[i], arguments); - } - return this; - }; - })(method); - } - Set[proto].attr = function (name, value) { - if (name && R.is(name, "array") && R.is(name[0], "object")) { - for (var j = 0, jj = name[length]; j < jj; j++) { - this.items[j].attr(name[j]); - } - } else { - for (var i = 0, ii = this.items[length]; i < ii; i++) { - this.items[i].attr(name, value); - } - } - return this; - }; - Set[proto].animate = function (params, ms, easing, callback) { - (R.is(easing, "function") || !easing) && (callback = easing || null); - var len = this.items[length], - i = len, - set = this, - collector; - callback && (collector = function () { - !--len && callback.call(set); - }); - this.items[--i].animate(params, ms, easing || collector, collector); - while (i--) { - this.items[i].animateWith(this.items[len - 1], params, ms, easing || collector, collector); - } - return this; - }; - Set[proto].insertAfter = function (el) { - var i = this.items[length]; - while (i--) { - this.items[i].insertAfter(el); - } - return this; - }; - Set[proto].getBBox = function () { - var x = [], - y = [], - w = [], - h = []; - for (var i = this.items[length]; i--;) { - var box = this.items[i].getBBox(); - x[push](box.x); - y[push](box.y); - w[push](box.x + box.width); - h[push](box.y + box.height); - } - x = mmin[apply](0, x); - y = mmin[apply](0, y); - return { - x: x, - y: y, - width: mmax[apply](0, w) - x, - height: mmax[apply](0, h) - y - }; - }; - Set[proto].clone = function (s) { - s = new Set; - for (var i = 0, ii = this.items[length]; i < ii; i++) { - s[push](this.items[i].clone()); - } - return s; - }; - - R.registerFont = function (font) { - if (!font.face) { - return font; - } - this.fonts = this.fonts || {}; - var fontcopy = { - w: font.w, - face: {}, - glyphs: {} - }, - family = font.face["font-family"]; - for (var prop in font.face) if (font.face[has](prop)) { - fontcopy.face[prop] = font.face[prop]; - } - if (this.fonts[family]) { - this.fonts[family][push](fontcopy); - } else { - this.fonts[family] = [fontcopy]; - } - if (!font.svg) { - fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10); - for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) { - var path = font.glyphs[glyph]; - fontcopy.glyphs[glyph] = { - w: path.w, - k: {}, - d: path.d && "M" + path.d[rp](/[mlcxtrv]/g, function (command) { - return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M"; - }) + "z" - }; - if (path.k) { - for (var k in path.k) if (path[has](k)) { - fontcopy.glyphs[glyph].k[k] = path.k[k]; - } - } - } - } - return font; - }; - Paper[proto].getFont = function (family, weight, style, stretch) { - stretch = stretch || "normal"; - style = style || "normal"; - weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400; - var font = R.fonts[family]; - if (!font) { - var name = new RegExp("(^|\\s)" + family[rp](/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i"); - for (var fontName in R.fonts) if (R.fonts[has](fontName)) { - if (name.test(fontName)) { - font = R.fonts[fontName]; - break; - } - } - } - var thefont; - if (font) { - for (var i = 0, ii = font[length]; i < ii; i++) { - thefont = font[i]; - if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) { - break; - } - } - } - return thefont; - }; - Paper[proto].print = function (x, y, string, font, size, origin) { - origin = origin || "middle"; // baseline|middle - var out = this.set(), - letters = (string + E)[split](E), - shift = 0, - path = E, - scale; - R.is(font, "string") && (font = this.getFont(font)); - if (font) { - scale = (size || 16) / font.face["units-per-em"]; - var bb = font.face.bbox.split(separator), - top = +bb[0], - height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2); - for (var i = 0, ii = letters[length]; i < ii; i++) { - var prev = i && font.glyphs[letters[i - 1]] || {}, - curr = font.glyphs[letters[i]]; - shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) : 0; - curr && curr.d && out[push](this.path(curr.d).attr({fill: "#000", stroke: "none", translation: [shift, 0]})); - } - out.scale(scale, scale, top, height).translate(x - top, y - height); - } - return out; - }; - - var formatrg = /\{(\d+)\}/g; - R.format = function (token, array) { - var args = R.is(array, "array") ? [0][concat](array) : arguments; - token && R.is(token, "string") && args[length] - 1 && (token = token[rp](formatrg, function (str, i) { - return args[++i] == null ? E : args[i]; - })); - return token || E; - }; - R.ninja = function () { - oldRaphael.was ? (Raphael = oldRaphael.is) : delete Raphael; - return R; - }; - R.el = Element[proto]; - return R; -})(); \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/yui-min.js b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/yui-min.js deleted file mode 100644 index 55f017d9b7f..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/yui-min.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -Copyright (c) 2010, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.com/yui/license.html -version: 3.1.0 -build: 2026 -*/ -if(typeof YUI==="undefined"){var YUI=function(F,E,D,C,A){var B=this,J=arguments,I,G=J.length,H=(typeof YUI_config!=="undefined")&&YUI_config;if(!(B instanceof YUI)){return new YUI(F,E,D,C,A);}else{B._init();if(H){B._config(H);}for(I=0;I-1){M="3.0.0";}YUI.prototype={_config:function(Y){Y=Y||{};var T,V,W,U=this.config,X=U.modules,S=U.groups;for(V in Y){T=Y[V];if(X&&V=="modules"){for(W in T){X[W]=T[W];}}else{if(S&&V=="groups"){for(W in T){S[W]=T[W];}}else{if(V=="win"){U[V]=T.contentWindow||T;U.doc=U[V].document;}else{U[V]=T;}}}}},_init:function(){var U,V=this,S=YUI.Env,T=V.Env;V.version=M;if(!T){V.Env={mods:{},base:K,cdn:K+M+"/build/",bootstrapped:false,_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_guidp:"y",_loaded:{},getBase:function(c,a){var W,X,Z,d,Y;X=(R&&R.getElementsByTagName("script"))||[];for(Z=0;ZJ)?H[J]:true;}}return L;};F.indexOf=(D.indexOf)?function(G,H){return D.indexOf.call(G,H);}:function(G,I){for(var H=0;H-1);};E.owns=F;E.each=function(K,J,L,I){var H=L||B,G;for(G in K){if(I||F(K,G)){J.call(H,K[G],G,K);}}return B;};E.some=function(K,J,L,I){var H=L||B,G;for(G in K){if(I||F(K,G)){if(J.call(H,K[G],G,K)){return true;}}}return false;};E.getValue=function(K,J){if(!B.Lang.isObject(K)){return D;}var H,I=B.Array(J),G=I.length;for(H=0;K!==D&&H=0){for(G=0;H!==D&&G0){C=D(I);if(C){return C;}else{E=I.lastIndexOf("-");if(E>=0){I=I.substring(0,E);if(E>=2&&I.charAt(E-2)==="-"){I=I.substring(0,E-2);}}else{break;}}}}return"";}});},"3.1.0",{requires:["yui-base"]});YUI.add("yui-log",function(A){(function(){var E,D=A,F="yui:log",B="undefined",C={debug:1,info:1,warn:1,error:1};D.log=function(I,Q,G,O){var K,N,L,J,M,H=D,P=H.config;if(P.debug){if(G){N=P.logExclude;L=P.logInclude;if(L&&!(G in L)){K=1;}else{if(N&&(G in N)){K=1;}}}if(!K){if(P.useBrowserConsole){J=(G)?G+": "+I:I;if(H.Lang.isFunction(P.logFn)){P.logFn(I,Q,G);}else{if(typeof console!=B&&console.log){M=(Q&&console[Q]&&(Q in C))?Q:"log";console[M](J);}else{if(typeof opera!=B){opera.postError(J);}}}}if(H.fire&&!O){if(!E){H.publish(F,{broadcast:2});E=1;}H.fire(F,{msg:I,cat:Q,src:G});}}}return H;};D.message=function(){return D.log.apply(D,arguments);};})();},"3.1.0",{requires:["yui-base"]});YUI.add("yui-later",function(A){(function(){var B=A.Lang,C=function(K,E,L,G,H){K=K||0;E=E||{};var F=L,J=A.Array(G),I,D;if(B.isString(L)){F=E[L];}if(!F){}I=function(){F.apply(E,J);};D=(H)?setInterval(I,K):setTimeout(I,K);return{id:D,interval:H,cancel:function(){if(this.interval){clearInterval(D);}else{clearTimeout(D);}}};};A.later=C;B.later=C;})();},"3.1.0",{requires:["yui-base"]});YUI.add("yui-throttle",function(Y){ -/* Based on work by Simon Willison: http://gist.github.com/292562 */ -var throttle=function(fn,ms){ms=(ms)?ms:(Y.config.throttleTime||150);if(ms===-1){return(function(){fn.apply(null,arguments);});}var last=(new Date()).getTime();return(function(){var now=(new Date()).getTime();if(now-last>ms){last=now;fn.apply(null,arguments);}});};Y.throttle=throttle;},"3.1.0",{requires:["yui-base"]});YUI.add("yui",function(A){},"3.1.0",{use:["yui-base","get","intl-base","yui-log","yui-later","yui-throttle"]}); \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/README b/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/README index 8188723e5e2..3beddb36e1a 100644 --- a/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/README +++ b/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/README @@ -29,7 +29,7 @@ WARNING: I have wrote these instructions while installing and configuring the pl WARNING: I'm going to make the assumption that you know how to work with Cacti and how to setup Data Input Methods for custom scripts. I'm also going to assume that you have already installed Cacti and everything works as expected. -You can extend the Cacti's data gathering functionality through external scripts. Cacti comes with a number of scripts out of the box wich are localted in the scripts/ directory. +You can extend the Cacti's data gathering functionality through external scripts. Cacti comes with a number of scripts out of the box which are located in the scripts/ directory. The check_zookeeper.py script can be used a custom data input method for Cacti. diff --git a/zookeeper-contrib/zookeeper-contrib-monitoring/check_zookeeper.py b/zookeeper-contrib/zookeeper-contrib-monitoring/check_zookeeper.py index cff6a1f2618..6badf190c11 100755 --- a/zookeeper-contrib/zookeeper-contrib-monitoring/check_zookeeper.py +++ b/zookeeper-contrib/zookeeper-contrib-monitoring/check_zookeeper.py @@ -19,7 +19,7 @@ Generic monitoring script that could be used with multiple platforms (Ganglia, Nagios, Cacti). It requires ZooKeeper 3.4.0 or greater. The script needs the 'mntr' 4letter word -command (patch ZOOKEEPER-744) that was now commited to the trunk. +command (patch ZOOKEEPER-744) that was now committed to the trunk. The script also works with ZooKeeper 3.3.x but in a limited way. """ @@ -311,10 +311,12 @@ def _parse_line(self, line): if not key: raise ValueError('The key is mandatory and should not be empty') - try: - value = int(value) - except (TypeError, ValueError): - pass + for typ in [int, float]: + try: + value = typ(value) + break + except (TypeError, ValueError): + pass return key, value diff --git a/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper.pyconf b/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper.pyconf index 44acd93e162..861028010a9 100644 --- a/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper.pyconf +++ b/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper.pyconf @@ -43,7 +43,7 @@ collection_group { metric { name = "zk_approximate_data_size" } metric { name = "zk_open_file_descriptor_count" } metric { name = "zk_max_file_descriptor_count" } - metric { name = "zk_followers" } + metric { name = "zk_learners" } metric { name = "zk_synced_followers" } metric { name = "zk_pending_syncs" } metric { name = "zk_last_proposal_size" } diff --git a/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper_ganglia.py b/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper_ganglia.py index bbb7a8e68e0..eb0e92052bb 100644 --- a/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper_ganglia.py +++ b/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/zookeeper_ganglia.py @@ -212,7 +212,7 @@ def metric_init(params=None): 'zk_approximate_data_size': {'units': 'bytes'}, 'zk_open_file_descriptor_count': {'units': 'descriptors'}, 'zk_max_file_descriptor_count': {'units': 'descriptors'}, - 'zk_followers': {'units': 'nodes'}, + 'zk_learners': {'units': 'nodes'}, 'zk_synced_followers': {'units': 'nodes'}, 'zk_pending_syncs': {'units': 'syncs'}, 'zk_last_proposal_size': {'units': 'bytes'}, diff --git a/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/README.txt b/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/README.txt index 317ae14869b..27a12566ffa 100644 --- a/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/README.txt +++ b/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/README.txt @@ -39,7 +39,7 @@ $ cp zookeeper.cfg /etc/nagios-plugins/config 4. Create a virtual host in Nagios used for monitoring the cluster as a whole -OR- Create a hostgroup named 'zookeeper-servers' and add all the zookeeper cluster nodes. -5. Define service checks like I have ilustrated bellow or just use the provided definitions. +5. Define service checks like I have illustrated bellow or just use the provided definitions. define service { use generic-service diff --git a/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt b/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt index 8c5f70175a6..3b25bb2df9a 100644 --- a/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt +++ b/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt @@ -39,7 +39,7 @@ that are easy and familiar to program against, and deployed as part of the language runtime. Thus, for simple CRUD clients, an HTTP gateway would be a less cumbersome interface than the ZooKeeper library. -This document describes a gatway for using HTTP to interact with a +This document describes a gateway for using HTTP to interact with a ZooKeeper repository. Binding ZooKeeper to HTTP @@ -72,7 +72,7 @@ best practice. Root ---- -The following examples assume that the ZooKeeper znode heirarchy is +The following examples assume that the ZooKeeper znode hierarchy is bound to the root of the HTTP servers namespace. This may not be the case in practice however, the gateway may bind to some prefix, for example the URL for accessing /a/b/c may be: @@ -128,14 +128,14 @@ a new session. If the session creation is successful, then a 201 code will be returned. -A session is just an UUID that you can pass around as a parameter and -the REST server will foward your request on the attached persistent +A session is just a UUID that you can pass around as a parameter and +the REST server will forward your request on the attached persistent connection. Keeping a session alive ----------------------- -To keep a session alive you must send hearbeat requests: +To keep a session alive you must send heartbeat requests: PUT /sessions/v1/ HTTP/1.1 diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties b/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties deleted file mode 100644 index 21ba7e4851b..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-rest/conf/log4j.properties +++ /dev/null @@ -1,68 +0,0 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# - -# -# ZooKeeper Logging Configuration -# - -# Format is " (, )+ - -# DEFAULT: console appender only -log4j.rootLogger=INFO, CONSOLE - -# Example with rolling log file -#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE - -# Example with rolling log file and tracing -#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE - -# -# Log INFO level and above messages to the console -# -log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender -log4j.appender.CONSOLE.Threshold=INFO -log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout -log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# -# Add ROLLINGFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.ROLLINGFILE=org.apache.log4j.ConsoleAppender -log4j.appender.ROLLINGFILE.Threshold=DEBUG -log4j.appender.ROLLINGFILE.File=bookkeeper.log -log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout -log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n - -# Max log file size of 10MB -log4j.appender.ROLLINGFILE.MaxFileSize=10MB -# uncomment the next line to limit number of backup files -#log4j.appender.ROLLINGFILE.MaxBackupIndex=10 - -# -# Add TRACEFILE to rootLogger to get log file output -# Log DEBUG level and above messages to a log file -log4j.appender.TRACEFILE=org.apache.log4j.FileAppender -log4j.appender.TRACEFILE.Threshold=TRACE -log4j.appender.TRACEFILE.File=bookkeeper_trace.log - -log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout -### Notice we are including log4j's NDC here (%x) -log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n diff --git a/zookeeper-contrib/zookeeper-contrib-rest/conf/logback.xml b/zookeeper-contrib/zookeeper-contrib-rest/conf/logback.xml new file mode 100644 index 00000000000..84a53896954 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-rest/conf/logback.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n + + + ${zookeeper.console.threshold} + + + + + + + + + + + + + + + + diff --git a/zookeeper-contrib/zookeeper-contrib-rest/pom.xml b/zookeeper-contrib/zookeeper-contrib-rest/pom.xml old mode 100755 new mode 100644 index 99e4f9f6121..94dc093c12b --- a/zookeeper-contrib/zookeeper-contrib-rest/pom.xml +++ b/zookeeper-contrib/zookeeper-contrib-rest/pom.xml @@ -23,7 +23,7 @@ org.apache.zookeeper zookeeper-contrib - 3.7.0-SNAPSHOT + 3.10.0-SNAPSHOT zookeeper-contrib-rest @@ -69,18 +69,8 @@ slf4j-api - org.slf4j - slf4j-log4j12 - - - * - * - - - - - log4j - log4j + ch.qos.logback + logback-core * @@ -115,8 +105,8 @@ test - junit - junit + org.junit.vintage + junit-vintage-engine test diff --git a/zookeeper-contrib/zookeeper-contrib-rest/rest.sh b/zookeeper-contrib/zookeeper-contrib-rest/rest.sh old mode 100644 new mode 100755 index daa819836e2..efbf0126ef1 --- a/zookeeper-contrib/zookeeper-contrib-rest/rest.sh +++ b/zookeeper-contrib/zookeeper-contrib-rest/rest.sh @@ -1,19 +1,22 @@ -#!/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 +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 # -# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. # # If this scripted is run out of /usr/bin or some other system bin directory @@ -22,24 +25,27 @@ # # Only follow symlinks if readlink supports it -if readlink -f "$0" > /dev/null 2>&1 -then - ZKREST=`readlink -f "$0"` +if readlink -f "$0" &>/dev/null; then + ZKREST=$(readlink -f "$0") else ZKREST="$0" fi -ZKREST_HOME=`dirname "$ZKREST"` +ZKREST_HOME=$(dirname "$ZKREST") -if $cygwin -then - # cygwin has a "kill" in the shell itself, gets confused - KILL=/bin/kill +case "$(uname)" in + CYGWIN* | MINGW*) cygwin=true ;; + *) cygwin=false ;; +esac + +if $cygwin; then + # cygwin has a "kill" in the shell itself, gets confused + KILL='/bin/kill' else - KILL=kill + KILL='kill' fi -if [ -z $ZKREST_PIDFILE ] - then ZKREST_PIDFILE=$ZKREST_HOME/server.pid +if [[ -z $ZKREST_PIDFILE ]]; then + ZKREST_PIDFILE=$ZKREST_HOME/server.pid fi ZKREST_MAIN=org.apache.zookeeper.server.jersey.RestMain @@ -49,42 +55,43 @@ ZKREST_LOG=$ZKREST_HOME/zkrest.log CLASSPATH="$ZKREST_CONF:$CLASSPATH" -for i in "$ZKREST_HOME"/lib/*.jar -do - CLASSPATH="$i:$CLASSPATH" +for i in "$ZKREST_HOME"/lib/*.jar; do + CLASSPATH="$i:$CLASSPATH" done -for i in "$ZKREST_HOME"/zookeeper-*.jar -do - CLASSPATH="$i:$CLASSPATH" +for i in "$ZKREST_HOME"/zookeeper-*.jar; do + CLASSPATH="$i:$CLASSPATH" done +export CLASSPATH case $1 in -start) - echo "Starting ZooKeeper REST Gateway ... " - java -cp "$CLASSPATH" $JVMFLAGS $ZKREST_MAIN >$ZKREST_LOG 2>&1 & - /bin/echo -n $! > "$ZKREST_PIDFILE" + start) + echo "Starting ZooKeeper REST Gateway ... " + # shellcheck disable=SC2206 + flags=($JVMFLAGS) + java "${flags[@]}" "$ZKREST_MAIN" &>"$ZKREST_LOG" & + echo -n $! >"$ZKREST_PIDFILE" echo STARTED ;; -stop) + stop) echo "Stopping ZooKeeper REST Gateway ... " - if [ ! -f "$ZKREST_PIDFILE" ] - then - echo "error: could not find file $ZKREST_PIDFILE" - exit 1 + if [[ ! -f $ZKREST_PIDFILE ]]; then + echo "error: could not find file $ZKREST_PIDFILE" + exit 1 else - $KILL -9 $(cat "$ZKREST_PIDFILE") - rm "$ZKREST_PIDFILE" - echo STOPPED + $KILL -9 "$(cat "$ZKREST_PIDFILE")" + rm "$ZKREST_PIDFILE" + echo STOPPED fi ;; -restart) + restart) shift - "$0" stop ${@} + "$0" stop "$@" sleep 3 - "$0" start ${@} + "$0" start "$@" ;; -*) + *) echo "Usage: $0 {start|stop|restart}" >&2 + ;; esac diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java index 954ad045b1c..209207f6b14 100644 --- a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java +++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,7 +54,8 @@ public void start() throws IOException { System.out.println("Starting grizzly ..."); boolean useSSL = cfg.useSSL(); - gws = new GrizzlyWebServer(cfg.getPort(), "/tmp/23cxv45345/2131xc2/", useSSL); + String zkRestResourcesTempPath = Files.createTempDirectory("zkRestResourcesTempPath").toFile().getCanonicalPath(); + gws = new GrizzlyWebServer(cfg.getPort(), zkRestResourcesTempPath, useSSL); // BUG: Grizzly needs a doc root if you are going to register multiple adapters for (Endpoint e : cfg.getEndpoints()) { diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java index 77371eab53d..84eaca819e2 100644 --- a/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java +++ b/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java @@ -307,7 +307,7 @@ public Response createZNode( throw new WebApplicationException(Response.status( Response.Status.BAD_REQUEST).entity( new ZError(ui.getRequestUri().toString(), path - + " bad operaton " + op)).build()); + + " bad operation " + op)).build()); } if (setNull.equals("true")) { @@ -358,7 +358,7 @@ public Response createZNodeAsOctet(@PathParam("path") String path, throw new WebApplicationException(Response.status( Response.Status.BAD_REQUEST).entity( new ZError(ui.getRequestUri().toString(), path - + " bad operaton " + op)).build()); + + " bad operation " + op)).build()); } if (setNull.equals("true")) { diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java b/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java similarity index 100% rename from zookeeper-server/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java rename to zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java diff --git a/zookeeper-contrib/zookeeper-contrib-rest/src/test/zkServer.sh b/zookeeper-contrib/zookeeper-contrib-rest/src/test/zkServer.sh index 4279d34265f..4272130173a 100755 --- a/zookeeper-contrib/zookeeper-contrib-rest/src/test/zkServer.sh +++ b/zookeeper-contrib/zookeeper-contrib-rest/src/test/zkServer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env bash # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -8,84 +8,78 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -if [ "x$1" == "x" ] -then - echo "USAGE: $0 startClean|start|stop hostPorts" - exit 2 +if [[ -z $1 ]]; then + echo "USAGE: $0 startClean|start|stop hostPorts" + exit 2 fi -if [ "x$1" == "xstartClean" ] -then - if [ "x${base_dir}" == "x" ] - then +if [[ $1 == "startClean" ]]; then + if [[ -z $base_dir ]]; then rm -rf /tmp/zkdata - else - rm -rf ${base_dir}/build/tmp - fi + else + rm -rf "$base_dir/build/tmp" + fi fi # Make sure nothing is left over from before -if [ -r "/tmp/zk.pid" ] -then -pid=`cat /tmp/zk.pid` -kill -9 $pid -rm -f /tmp/zk.pid +if [[ -r "/tmp/zk.pid" ]]; then + pid=$(cat /tmp/zk.pid) + kill -9 "$pid" + rm -f /tmp/zk.pid fi -if [ -r "${base_dir}/build/tmp/zk.pid" ] -then -pid=`cat ${base_dir}/build/tmp/zk.pid` -kill -9 $pid -rm -f ${base_dir}/build/tmp/zk.pid +if [[ -r "$base_dir/build/tmp/zk.pid" ]]; then + pid=$(cat "$base_dir/build/tmp/zk.pid") + kill -9 "$pid" + rm -f "$base_dir/build/tmp/zk.pid" fi -if [ "x${base_dir}" == "x" ] -then -zk_base="../../../" +if [[ -z $base_dir ]]; then + zk_base="../../../" else -zk_base="${base_dir}" + zk_base="$base_dir" fi -CLASSPATH="$CLASSPATH:${zk_base}/build/classes" -CLASSPATH="$CLASSPATH:${zk_base}/conf" +CLASSPATH="$CLASSPATH:$zk_base/build/classes" +CLASSPATH="$CLASSPATH:$zk_base/conf" -for i in "${zk_base}"/build/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$zk_base"/build/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done -for i in "${zk_base}"/zookeeper-server/src/main/resource/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$zk_base"/zookeeper-server/src/main/resource/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done +export CLASSPATH case $1 in -start|startClean) - if [ "x${base_dir}" == "x" ] - then - mkdir -p /tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 /tmp/zkdata &> /tmp/zk.log & - echo $! > /tmp/zk.pid - else - mkdir -p ${base_dir}/build/tmp/zkdata - java -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain 22182 ${base_dir}/build/tmp/zkdata &> ${base_dir}/build/tmp/zk.log & - echo $! > ${base_dir}/build/tmp/zk.pid + start | startClean) + if [[ -z $base_dir ]]; then + mkdir -p /tmp/zkdata + java org.apache.zookeeper.server.ZooKeeperServerMain 22182 /tmp/zkdata &>/tmp/zk.log & + echo $! >/tmp/zk.pid + else + mkdir -p "$base_dir/build/tmp/zkdata" + java org.apache.zookeeper.server.ZooKeeperServerMain 22182 "$base_dir/build/tmp/zkdata" &>"$base_dir/build/tmp/zk.log" & + echo $! >"$base_dir/build/tmp/zk.pid" fi - sleep 5 + sleep 5 ;; -stop) + stop) # Already killed above ;; -*) - echo "Unknown command " + $1 + *) + echo "Unknown command $1" exit 2 + ;; esac - diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/blockingqueue.h b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/blockingqueue.h index 4677290d7b6..aad10c29839 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/blockingqueue.h +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/blockingqueue.h @@ -50,7 +50,7 @@ class BlockingQueue { * \brief Retrieves and removes the head of this queue, waiting if * \brief no elements are present in this queue. * - * @param timeout how long to wait until an element becomes availabe, + * @param timeout how long to wait until an element becomes available, * in milliseconds; if 0 then wait forever * @param timedOut if not NULL then set to true whether this function timed out * @return the element from the queue diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/doxygen.cfg b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/doxygen.cfg index 308b09450a4..da384f1b33a 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/doxygen.cfg +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/doxygen.cfg @@ -401,7 +401,7 @@ SHOW_DIRECTORIES = YES # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the progam writes to standard output +# provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.h b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.h index 936ecc60453..f218be6e7b6 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.h +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.h @@ -183,7 +183,7 @@ class GenericEvent { * \brief Constructor. * * @param type the type of this event - * @param eventWarpper the wrapper around event's data + * @param eventWrapper the wrapper around event's data */ GenericEvent(int type, AbstractEventWrapper *eventWrapper) : m_type(type), m_eventWrapper(eventWrapper) { diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.h b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.h index 0ed12d7f645..91321cd6873 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.h +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.h @@ -41,7 +41,7 @@ class Thread { void Create(void* ctx, ThreadFunc func); void Join() { - //avoid SEGFAULT because of unitialized mThread + //avoid SEGFAULT because of uninitialized mThread //in case Create(...) was never called if (_func != NULL) { pthread_join(mThread, 0); diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.cc b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.cc index 7f02fa33b72..55f72396829 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.cc +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.cc @@ -505,7 +505,7 @@ ZooKeeperAdapter::verifyConnection() throw(ZooKeeperException) if (m_state != AS_CONNECTING) { LOG_TRACE( LOG, "yes. Checking if allowed to auto-reconnect..." ); - //...not in progres, so check if we can reconnect + //...not in progress, so check if we can reconnect if (!m_zkConfig.getAutoReconnect()) { //...too bad, disallowed :( LOG_TRACE( LOG, "no. Sorry." ); diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.h b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.h index 8d4d1d57f0a..66312d5c4fd 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.h +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.h @@ -160,7 +160,7 @@ class ZooKeeperConfig const int m_leaseTimeout; /** - * True if this adapater should attempt to autoreconnect in case + * True if this adapter should attempt to autoreconnect in case * the current session has been dropped. */ const bool m_autoReconnect; @@ -616,7 +616,7 @@ class ZooKeeperAdapter /** * Returns the remaining connect timeout. The timeout resets - * to {@link #m_connectTimeout} on a successfull connection to the ZK. + * to {@link #m_connectTimeout} on a successful connection to the ZK. * * @return the remaining connect timeout, in milliseconds */ diff --git a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkfuse.cc b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkfuse.cc index 6a8216885fa..cfe552c349c 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkfuse.cc +++ b/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkfuse.cc @@ -320,7 +320,7 @@ enum ZkFuseNameType { */ ZkFuseNameRegType = 1, /** - ZkFuse path is synthesized and identifies the chidlren part of a + ZkFuse path is synthesized and identifies the children part of a ZooKeeper node, i.e. Posix directory semantics is expected. */ ZkFuseNameDirType = 2 @@ -707,7 +707,7 @@ class ZkFuseHandleManager : boost::noncopyable ZkFuseHandleManagerFactory - factory for ZkFuseHandleManager. This is the only way to create a ZkFuseHandleManager instance. - to make sure that _thisWeakPtr of the instance is intialized + to make sure that _thisWeakPtr of the instance is initialized after the instance is created. */ class ZkFuseHandleManagerFactory @@ -1354,7 +1354,7 @@ class ZkFuseFile : boost::noncopyable */ uint32_t version; /** - Acces time in milliseconds. + Access time in milliseconds. */ uint64_t atime; /** @@ -1535,7 +1535,7 @@ class ZkFuseFile : boost::noncopyable /** Remove a ZkFuse directory. - If force is true, then the ZooKeeper node and its decendants + If force is true, then the ZooKeeper node and its descendants will be deleted. If force is false, then this method implements the semantics @@ -1614,7 +1614,7 @@ class ZkFuseFile : boost::noncopyable the ZooKeeper node has no data. - If the ZkFuse regular file represents the data part of the ZooKeeper node which is presented as a ZkFuse regular file, - the ZooKeeper node and its decendants are deleted. + the ZooKeeper node and its descendants are deleted. Returns -EISDIR if the ZkFuse regular file cannot be deleted because ZkFuse consider it to be a directory. @@ -1711,7 +1711,7 @@ class ZkFuseFile : boost::noncopyable Get attributes without accessing metadata. The atime and mtime returned does not take into consideration - overrides present in a matadata file. + overrides present in a metadata file. \return 0 if successful, otherwise negative errno. \param stbuf return attributes here. @@ -1836,7 +1836,7 @@ class ZkFuseFile : boost::noncopyable a client is trying to open the path. \see _refCount. Sets deleted to true. \see _deleted. Sets number of currently directory opens to zero. \see _openDirCount. - Invalidate cach for children information and data. + Invalidate cache for children information and data. \param manager the ZkFuseHandleManager instance who is creating this ZkFuseFile instance. @@ -2909,7 +2909,7 @@ class ZkFuseFile : boost::noncopyable /** Remove a ZkFuse directory. - If force is true, then the ZooKeeper node and its decendants + If force is true, then the ZooKeeper node and its descendants will be deleted. If force is false, then this method implements the semantics @@ -2956,7 +2956,7 @@ class ZkFuseFile : boost::noncopyable the ZooKeeper node has no data. - If the ZkFuse regular file represents the data part of the ZooKeeper node which is presented as a ZkFuse regular file, - the ZooKeeper node and its decendants are deleted. + the ZooKeeper node and its descendants are deleted. Returns -EISDIR if the ZkFuse regular file cannot be deleted because ZkFuse consider it to be a directory. @@ -3043,7 +3043,7 @@ ZkFuseHandleManager::allocate(const std::string & path, bool & newFile) /* Not really supposed to invoke the new ZkFuseFile instance * because this method is not supposed to invoke ZkFuseFile * methods that while holding _mutex. However, it is safe - * to do without casuing deadlock because these methods + * to do without causing deadlock because these methods * are known not to invoke other methods, especially one * that invoke this ZkFuseHandleManager instance. */ diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/Makefile.PL b/zookeeper-contrib/zookeeper-contrib-zkperl/Makefile.PL index 9a0996dddcf..0c7486f9e29 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/Makefile.PL +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/Makefile.PL @@ -28,21 +28,25 @@ my $ZOO_REQUIRED_VERSION = qr{^$ZOO_MAJOR_VERSION\.\d+.\d+$}ismx; my @zk_inc_paths; my @zk_lib_paths; +my $with_sasl2 = 0; +my @sasl2_inc_paths; +my @sasl2_lib_paths; + GetOptions( 'zookeeper-include=s' => \@zk_inc_paths, - 'zookeeper-lib=s' => \@zk_lib_paths + 'zookeeper-lib=s' => \@zk_lib_paths, + 'with-sasl2!' => \$with_sasl2, + 'sasl2-include=s' => \@sasl2_inc_paths, + 'sasl2-lib=s' => \@sasl2_lib_paths ); -my $zk_inc_paths = join(' ', map("-I$_", @zk_inc_paths)); -my $zk_lib_paths = join(' ', map("-L$_", @zk_lib_paths)); - -$zk_inc_paths .= ' ' unless ($zk_inc_paths eq ''); -$zk_lib_paths .= ' ' unless ($zk_lib_paths eq ''); +my $zk_inc = (join(' ', map("-I$_", @zk_inc_paths)) . ' -I.'); +my $zk_libs = (join(' ', map("-L$_", @zk_lib_paths)) . ' -lzookeeper_mt'); my $cc = $Config{'cc'}; my $check_file = 'build/check_zk_version'; -my $check_out = qx($cc $zk_inc_paths $zk_lib_paths -I. -o $check_file $check_file.c 2>&1); +my $check_out = qx($cc $zk_inc -o $check_file $check_file.c $zk_libs 2>&1); if ($?) { if ($check_out =~ /zookeeper_version\.h/) { @@ -63,11 +67,21 @@ elsif ($zk_ver !~ $ZOO_REQUIRED_VERSION) { warn "Net::ZooKeeper requires ZooKeeper 3.x, found $zk_ver!"; } +my @inc = ($zk_inc); +my @libs = ($zk_libs); +my %mmopt = (); + +if ($with_sasl2) { + push(@inc, join(' ', map("-I$_", @sasl2_inc_paths))); + push(@libs, join(' ', map("-L$_", @sasl2_lib_paths)) . ' -lsasl2'); + $mmopt{DEFINE} = '-DHAVE_CYRUS_SASL_H'; +} + WriteMakefile( - 'INC' => "$zk_inc_paths-I.", - 'LIBS' => [ "$zk_lib_paths-lzookeeper_mt" ], + 'INC' => join(' ', @inc), + 'LIBS' => \@libs, 'NAME' => 'Net::ZooKeeper', 'VERSION_FROM' => 'ZooKeeper.pm', - 'clean' => { 'FILES' => 'build/check_zk_version.o' } + 'clean' => { 'FILES' => 'build/check_zk_version.o' }, + %mmopt, ); - diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/README b/zookeeper-contrib/zookeeper-contrib-zkperl/README index bbe2a0d8f3b..ac13481529c 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/README +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/README @@ -32,6 +32,15 @@ ZooKeeper C include files. The path supplied to the --zookeeper-lib option should identify the directory that contains the libzookeeper_mt library. +If the C client supports Cyrus SASL (ZOOKEEPER-1112), it can also be +enabled in the Perl binding by passing a --with-sasl2 flag (and, +optionally, non-standard locations): + + perl Makefile.PL \ + --with-sasl2 \ + --sasl2-include=/path/to/sasl2/include \ + --sasl2-lib=/path/to/sasl2/lib + When running "make test", if no ZK_TEST_HOSTS environment variable is set, many tests will be skipped because no connection to a ZooKeeper server is available. To execute these tests, @@ -44,6 +53,18 @@ The tests expect to have full read/write/create/delete/admin ZooKeeper permissions under this path. If no ZK_TEST_PATH variable is defined, the root ZooKeeper path ("/") is used. +The ZK_TEST_SASL_OPTIONS environment variable, if defined, provides a +JSON-encoded map of SASL authentication options, enabling SASL tests. +E.g., + + { + "host": "zk-sasl-md5", + "mechlist": "DIGEST-MD5", + "service": "zookeeper", + "user": "bob", + "password_file": "bob.secret" + } + DEPENDENCIES Version 3.1.1 of ZooKeeper is required at a minimum. diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.pm b/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.pm index 507f0298d16..b17c9704c17 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.pm +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.pm @@ -688,7 +688,8 @@ The following methods are defined for the Net::ZooKeeper class. $zkh = Net::ZooKeeper->new('host1:7000,host2:7000'); $zkh = Net::ZooKeeper->new('host1:7000,host2:7000', 'session_timeout' => $session_timeout, - 'session_id' => $session_id); + 'session_id' => $session_id, + 'sasl_options' => $sasl_options); Creates a new Net::ZooKeeper handle object and attempts to connect to the one of the servers of the given ZooKeeper @@ -725,6 +726,33 @@ initial connection request; again, the actual timeout period to which the server agrees will be available subsequently as the value of the C attribute. +If a C<'sasl_options'> option is provided, it is used to automatically +SASL-authenticate with the server during connections (including +reconnects). Here is a brief description of the recognized keys; +please refer to the C client documentation for details: + +=over 5 + +=item service => VALUE + +=item host => VALUE + +=item mechlist => VALUE + +These map to the corresponding fields of C from the +library. + +=item user => VALUE + +=item realm => VALUE + +=item password_file => VALUE + +These map to the corresponding parameters of +C from the library. + +=back + Upon successful connection (i.e., after the success of a method which requires communication with the server), the C attribute will hold a short binary string which represents the diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.xs b/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.xs index 4b6067b1024..1fd178aba60 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.xs +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.xs @@ -28,7 +28,9 @@ #include /* CHAR_BIT */ #include /* gettimeofday() */ +#define THREADED #include +#undef THREADED #include "build/check_zk_version.h" @@ -758,6 +760,13 @@ zk_new(package, hosts, ...) char *hosts PREINIT: int recv_timeout = DEFAULT_RECV_TIMEOUT_MSEC; +#ifdef HAVE_CYRUS_SASL_H + zoo_sasl_params_t sasl_params = { 0 }; + const char *sasl_user = NULL; + const char *sasl_realm = NULL; + const char *sasl_password_file = NULL; + int use_sasl = 0; +#endif /* HAVE_CYRUS_SASL_H */ const clientid_t *client_id = NULL; zk_t *zk; zk_handle_t *handle; @@ -792,12 +801,61 @@ zk_new(package, hosts, ...) Perl_croak(aTHX_ "invalid session ID"); } } +#ifdef HAVE_CYRUS_SASL_H + else if (strcaseEQ(key, "sasl_options")) { + SV *hash_sv = ST(i + 1); + HV *hash; + char *key; + I32 key_length; + SV *value; + + if (!SvROK(hash_sv) || SvTYPE(SvRV(hash_sv)) != SVt_PVHV) { + Perl_croak(aTHX_ "sasl_options requires a hash reference"); + } + + hash = (HV *)SvRV(hash_sv); + hv_iterinit(hash); + while ((value = hv_iternextsv(hash, &key, &key_length))) { + if (strcaseEQ(key, "service")) { + sasl_params.service = SvPV_nolen(value); + } + else if (strcaseEQ(key, "host")) { + sasl_params.host = SvPV_nolen(value); + } + else if (strcaseEQ(key, "mechlist")) { + sasl_params.mechlist = SvPV_nolen(value); + } + else if (strcaseEQ(key, "user")) { + sasl_user = SvPV_nolen(value); + } + else if (strcaseEQ(key, "realm")) { + sasl_realm = SvPV_nolen(value); + } + else if (strcaseEQ(key, "password_file")) { + sasl_password_file = SvPV_nolen(value); + } + } + use_sasl = 1; + } +#endif /* HAVE_CYRUS_SASL_H */ } Newxz(zk, 1, zk_t); +#ifdef HAVE_CYRUS_SASL_H + if (use_sasl) { + /* KLUDGE: Leaks a reference count. Authen::SASL::XS does + the same, though. TODO(ddiederen): Fix. */ + sasl_client_init(NULL); + sasl_params.callbacks = zoo_sasl_make_basic_callbacks(sasl_user, + sasl_realm, sasl_password_file); + } + zk->handle = zookeeper_init_sasl(hosts, NULL, recv_timeout, + client_id, NULL, 0, NULL, use_sasl ? &sasl_params : NULL); +#else zk->handle = zookeeper_init(hosts, NULL, recv_timeout, - client_id, NULL, 0); + client_id, NULL, 0); +#endif /* HAVE_CYRUS_SASL_H */ if (!zk->handle) { Safefree(zk); @@ -1203,7 +1261,7 @@ zk_add_auth(zkh, scheme, cert) zk->last_errno = 0; if (cert_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid certificate length: %u", cert_len); + Perl_croak(aTHX_ "invalid certificate length: %zu", cert_len); } watch = _zk_create_watch(aTHX); @@ -1283,7 +1341,7 @@ zk_create(zkh, path, buf, ...) } if (buf_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid data length: %u", buf_len); + Perl_croak(aTHX_ "invalid data length: %zu", buf_len); } path_buf_len = zk->path_buf_len; @@ -1318,7 +1376,7 @@ zk_create(zkh, path, buf, ...) err = _zk_fill_acl(aTHX_ acl_arr, &acl); if (err) { - Perl_croak(aTHX_ err); + Perl_croak(aTHX_ "%s", err); } } } @@ -1757,7 +1815,7 @@ zk_set(zkh, path, buf, ...) } if (buf_len > PERL_INT_MAX) { - Perl_croak(aTHX_ "invalid data length: %u", buf_len); + Perl_croak(aTHX_ "invalid data length: %zu", buf_len); } for (i = 3; i < items; i += 2) { @@ -1920,7 +1978,7 @@ zk_set_acl(zkh, path, acl_arr, ...) err = _zk_fill_acl(aTHX_ acl_arr, &acl); if (err) { - Perl_croak(aTHX_ err); + Perl_croak(aTHX_ "%s", err); } for (i = 3; i < items; i += 2) { diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/t/10_invalid.t b/zookeeper-contrib/zookeeper-contrib-zkperl/t/10_invalid.t index 5e080b64c7d..c1a118e008c 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/t/10_invalid.t +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/t/10_invalid.t @@ -527,13 +527,13 @@ like($@, qr/Usage: Net::ZooKeeper::set_acl\(zkh, path, acl_arr, \.\.\.\)/, eval { $zkh->set_acl($node_path, 'foo'); }; -like($@, qr/acl_arr is not an array reference/, +like($@, qr/acl_arr is not an array reference/i, 'set_acl(): invalid ACL array reference'); eval { $zkh->set_acl($node_path, {}); }; -like($@, qr/acl_arr is not an array reference/, +like($@, qr/acl_arr is not an array reference/i, 'set_acl(): invalid ACL array reference to hash'); eval { diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/t/30_connect.t b/zookeeper-contrib/zookeeper-contrib-zkperl/t/30_connect.t index c2b68bb4e5f..4745e427d14 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/t/30_connect.t +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/t/30_connect.t @@ -176,6 +176,13 @@ SKIP: { ## NOTE: to test re-connections with saved session IDs we create a second ## connection with the same ID while the first is still active; ## this is bad practice in normal usage + ## + ## Test disabled because it breaks with current ZooKeeper servers: + ## $zkh1's connection gets closed as soon as $zkh2 connects, which + ## causes it to reconnect, which kills $zkh2's connection, etc. + ## TODO: figure out a way to test this. + SKIP: { + skip 'does not work with current ZK servers', 4; my $zkh2 = Net::ZooKeeper->new($hosts, 'session_id' => $session_id1, @@ -198,5 +205,6 @@ SKIP: { and $session_id2 eq $session_id1), 'FETCH(): reconnect with session ID'); } + } } diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/t/35_log.t b/zookeeper-contrib/zookeeper-contrib-zkperl/t/35_log.t index 92821afc1f3..cb7f7dbc538 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/t/35_log.t +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/t/35_log.t @@ -31,7 +31,7 @@ my($hosts, $root_path, $node_path) = zk_test_setup(0); my $zkh = Net::ZooKeeper->new($hosts); -Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_INFO); +Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_DEBUG); SKIP: { skip 'no valid handle', 2 unless (defined($zkh)); @@ -45,6 +45,7 @@ SKIP: { my $old_select = select(STDERR); $| = 1; + $/ = undef; # slurp mode. select($old_select); } else { @@ -63,7 +64,7 @@ SKIP: { skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0)); my $log = ; - like($log, qr/ZOO_/, + like($log, qr/ZOO_.*exists/, 'exists(): generated log message'); } @@ -75,7 +76,7 @@ SKIP: { skip 'no seek on stderr', 1 unless (seek(STDERR, 0, 0)); my $log = ; - like($log, qr/ZOO_/, + like($log, qr/ZOO_.*close/, 'DESTROY(): generated log message'); } diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/t/50_access.t b/zookeeper-contrib/zookeeper-contrib-zkperl/t/50_access.t index ef61ed6688f..71eb17c883b 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkperl/t/50_access.t +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/t/50_access.t @@ -18,6 +18,7 @@ use File::Spec; use Test::More tests => 40; +use Storable qw(dclone); BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; @@ -163,9 +164,16 @@ SKIP: { $! eq ''), 'get_acl(): undef returned for non-extant node'); + # The test is not running as ADMIN, which means that the server + # returns "redacted" ACLs (see ZOOKEEPER-1392 and OpCode.getACL in + # FinalRequestProcessor). We must do the same for the comparison + # to succeed. + my $redacted_digest_acl = dclone($digest_acl); + $redacted_digest_acl->[1]->{id} =~ s/:.*/:x/; + @acl = ('abc'); @acl = $zkh->get_acl($acl_node_path); - is_deeply(\@acl, $digest_acl, + is_deeply(\@acl, $redacted_digest_acl, 'get_acl(): retrieved digest ACL'); my $stat = $zkh->stat(); diff --git a/zookeeper-contrib/zookeeper-contrib-zkperl/t/70_sasl.t b/zookeeper-contrib/zookeeper-contrib-zkperl/t/70_sasl.t new file mode 100644 index 00000000000..9de379a7a4d --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zkperl/t/70_sasl.t @@ -0,0 +1,110 @@ +# Net::ZooKeeper - Perl extension for Apache ZooKeeper +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +use File::Spec; +use Test::More tests => 7; +use JSON::PP qw(decode_json); + +BEGIN { use_ok('Net::ZooKeeper', qw(:all)) }; + + +my $test_dir; +(undef, $test_dir, undef) = File::Spec->splitpath($0); +require File::Spec->catfile($test_dir, 'util.pl'); + +my($hosts, $root_path, $node_path) = zk_test_setup(0); + +my $sasl_options = $ENV{'ZK_TEST_SASL_OPTIONS'}; +if (defined($sasl_options)) { + $sasl_options = decode_json($sasl_options); +} + +SKIP: { + skip 'no sasl_options', 6 unless defined($sasl_options); + + my $zkh = Net::ZooKeeper->new($hosts, + 'sasl_options' => $sasl_options); + + my $path = $zkh->create($node_path, 'foo', + 'acl' => ZOO_OPEN_ACL_UNSAFE) if (defined($zkh)); + + skip 'no connection to ZooKeeper', 36 unless + (defined($path) and $path eq $node_path); + + ## _zk_acl_constant() + + my $acl_node_path = "$node_path/a1"; + + my $sasl_acl = [ + { + 'perms' => ZOO_PERM_READ, + 'scheme' => 'world', + 'id' => 'anyone' + }, + { + 'perms' => ZOO_PERM_ALL, + 'scheme' => 'sasl', + 'id' => $sasl_options->{user} + } + ]; + + $path = $zkh->create($acl_node_path, 'foo', 'acl' => $sasl_acl); + is($path, $acl_node_path, + 'create(): created node with SASL ACL'); + + + ## get_acl() + + @acl = ('abc'); + @acl = $zkh->get_acl($acl_node_path); + is_deeply(\@acl, $sasl_acl, + 'get_acl(): retrieved SASL ACL'); + + SKIP: { + my $zkh2 = Net::ZooKeeper->new($hosts); + + my $ret = $zkh->exists($root_path) if (defined($zkh)); + + skip 'no connection to ZooKeeper', 1 unless + (defined($ret) and $ret); + + my $node = $zkh2->get($acl_node_path); + is($node, 'foo', + 'get(): retrieved node value with world ACL'); + + $ret = $zkh2->set($acl_node_path, 'bar'); + ok((!$ret and $zkh2->get_error() == ZNOAUTH and $! eq ''), + 'set(): node value unchanged if no auth'); + } + + my $ret = $zkh->set($acl_node_path, 'bar'); + ok($ret, + 'set(): set node with SASL ACL'); + + my $node = $zkh->get($acl_node_path); + is($node, 'bar', + 'get(): retrieved new node value with SASL ACL'); + + $ret = $zkh->delete($acl_node_path); + diag(sprintf('unable to delete node %s: %d, %s', + $acl_node_path, $zkh->get_error(), $!)) unless ($ret); + + $ret = $zkh->delete($node_path); + diag(sprintf('unable to delete node %s: %d, %s', + $node_path, $zkh->get_error(), $!)) unless ($ret); +} diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/README b/zookeeper-contrib/zookeeper-contrib-zkpython/README index 56154618356..ffad255f06f 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/README +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/README @@ -5,13 +5,15 @@ Please do not rely on APIs staying constant in the short term. The handling of e DEPENDENCIES: ------------- -This has only been tested against SVN (i.e. 3.2.0 in development) but should work against 3.1.1. +This has only been tested against SVN/Git (i.e. 3.2.0 in development) but should work against 3.1.1. You will need the Python development headers installed to build the module - on many package-management systems, these can be found in python-devel. (On ubuntu 18.4, install python2.7 and python2.7-dev.) -Python >= 2.6 is required. We have tested against 2.6. We have not tested against 3.x. +Python >= 2.6 is required. We have tested against 2.6 and 3.5+. -E.g. setting up tpyhon and python devel on ubuntu 18.4: +By default, the extension assumes that the C client library was compiled with OpenSSL enabled (--with-openssl). You can disable OpenSSL support in the Python binding by setting the ZKPYTHON_NO_SSL environment variable to a non-empty string before executing Ant or setup.py. + +E.g. setting up python and python devel on ubuntu 18.4: sudo apt-get install python2.7 python2.7-dev sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 @@ -22,7 +24,7 @@ To install, make sure that the C client has been built (use `mvn clean install - ant install -from zookeeper/src/contrib/zkpython/. +from zookeeper-contrib/zookeeper-contrib-zkpython/. To test, run ant test from the same directory. diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c b/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c index e84c2b70700..f534749ced1 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c @@ -16,6 +16,7 @@ * limitations under the License. */ +#define PY_SSIZE_T_CLEAN #include #include #include @@ -600,7 +601,7 @@ void acl_completion_dispatch(int rc, struct ACL_vector *acl, struct Stat *stat, /* -------------------------------------------------------------------------- */ -static PyObject *pyzookeeper_init_optional_ssl(PyObject *self, PyObject *args, int ssl) { +static PyObject *pyzookeeper_init_common(PyObject *self, PyObject *args, int ssl) { const char *host; const char *cert_str; PyObject *watcherfn = Py_None; @@ -643,8 +644,13 @@ static PyObject *pyzookeeper_init_optional_ssl(PyObject *self, PyObject *args, i watchers[handle] = pyw; if (ssl) { +#ifdef HAVE_OPENSSL_H zh = zookeeper_init_ssl( host, cert_str, watcherfn != Py_None ? watcher_dispatch : NULL, recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 ); +#else + fprintf(stderr, "SSL support not compiled in (called with ssl=%d).\n", ssl); + abort(); +#endif } else { zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL, recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 ); @@ -652,7 +658,7 @@ static PyObject *pyzookeeper_init_optional_ssl(PyObject *self, PyObject *args, i if (zh == NULL) { - PyErr_SetString( ZooKeeperException, "Could not internally obtain SSL zookeeper handle" ); + PyErr_Format( ZooKeeperException, "Could not internally obtain%s zookeeper handle", ssl ? " SSL" : "" ); return NULL; } @@ -662,14 +668,16 @@ static PyObject *pyzookeeper_init_optional_ssl(PyObject *self, PyObject *args, i static PyObject *pyzookeeper_init(PyObject *self, PyObject *args) { - return pyzookeeper_init_optional_ssl(self, args, 0); + return pyzookeeper_init_common(self, args, /*ssl*/0); } +#ifdef HAVE_OPENSSL_H static PyObject *pyzookeeper_init_ssl(PyObject *self, PyObject *args) { - return pyzookeeper_init_optional_ssl(self, args, 1); + return pyzookeeper_init_common(self, args, /*ssl*/1); } +#endif /* -------------------------------------------------------------------------- */ @@ -1459,6 +1467,19 @@ PyObject *pyzoo_set_debug_level(PyObject *self, PyObject *args) } static PyObject *log_stream = NULL; +#if PY_MAJOR_VERSION >= 3 +static PyObject *PyIOBase_TypeObj; +static int init_file_emulator(void) +{ + PyObject *io = PyImport_ImportModule("_io"); + if (io == NULL) + return -1; + PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase"); + if (PyIOBase_TypeObj == NULL) + return -1; + return 0; +} +#endif /* Set the output file-like object for logging output. Returns Py_None */ PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) @@ -1470,8 +1491,11 @@ PyObject *pyzoo_set_log_stream(PyObject *self, PyObject *args) } #if PY_MAJOR_VERSION >= 3 - extern PyTypeObject PyIOBase_Type; - if (!PyObject_IsInstance(pystream, (PyObject *)&PyIOBase_Type)) { + if (init_file_emulator() < 0) { + return NULL; + } + + if (!PyObject_IsInstance(pystream, PyIOBase_TypeObj)) { #else if(!PyFile_Check(pystream)) { #endif @@ -1518,7 +1542,9 @@ PyObject *pyzoo_deterministic_conn_order(PyObject *self, PyObject *args) static PyMethodDef ZooKeeperMethods[] = { {"init", pyzookeeper_init, METH_VARARGS, pyzk_init_doc }, +#ifdef HAVE_OPENSSL_H {"init_ssl", pyzookeeper_init_ssl, METH_VARARGS, pyzk_init_ssl_doc }, +#endif {"create",pyzoo_create, METH_VARARGS, pyzk_create_doc }, {"delete",pyzoo_delete, METH_VARARGS, pyzk_delete_doc }, {"get_children", pyzoo_get_children, METH_VARARGS, pyzk_get_children_doc }, @@ -1589,8 +1615,14 @@ PyMODINIT_FUNC initzookeeper(void) { #else PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods); #endif + if (init_zhandles(32) == 0) { - return; // TODO: Is there any way to raise an exception here? +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(module); + return PyErr_NoMemory(); +#else + return; +#endif } ZooKeeperException = PyErr_NewException("zookeeper.ZooKeeperException", diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py index 313c020569c..b225a317400 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py @@ -15,11 +15,20 @@ # limitations under the License. from distutils.core import setup, Extension +import os zookeeper_basedir = "../../" +zookeeper_macros = [("THREADED", None)] + +# Assume the C extension includes OpenSSL support unless told +# otherwise. +if not os.environ.get("ZKPYTHON_NO_SSL"): + zookeeper_macros.append(("HAVE_OPENSSL_H", True)) + zookeepermodule = Extension("zookeeper", sources=["src/c/zookeeper.c"], + define_macros=zookeeper_macros, include_dirs=[zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/include", zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/target/c", zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/generated"], diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/async_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/async_test.py index e81343570ea..61740ae433f 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/async_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/async_test.py @@ -26,7 +26,7 @@ def setUp( self ): def test_async(self): self.assertEqual(self.connected, True) - ret = zookeeper.async(self.handle, "/") + ret = getattr(zookeeper, 'async')(self.handle, "/") self.assertEqual(ret, zookeeper.OK, "async failed") if __name__ == '__main__': diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/callback_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/callback_test.py index 55d7fe17866..95e20b4dea1 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/callback_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/callback_test.py @@ -91,9 +91,9 @@ def dispatch_callback(*args, **kwargs): self.create_callback( dispatch_callback )), lambda: self.assertEqual(True, self.callback_flag, "Strings dispatch not fired")) - self.callback_harness( lambda: zookeeper.async(self.handle, - "/", - self.create_callback( dispatch_callback )), + self.callback_harness( lambda: getattr(zookeeper, 'async')(self.handle, + "/", + self.create_callback( dispatch_callback )), lambda: self.assertEqual(True, self.callback_flag, "String dispatch not fired")) self.callback_harness( lambda: zookeeper.aget_acl(self.handle, diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/close_deadlock_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/close_deadlock_test.py index 921d2cc4b05..b56d44eecce 100644 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/close_deadlock_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/close_deadlock_test.py @@ -26,7 +26,7 @@ class CloseDeadlockTest(zktestbase.TestBase): https://issues.apache.org/jira/browse/ZOOKEEPER-763 zookeeper.close blocks on waiting for all completions to - finish. Previously it was doing so while holding teh GIL, stopping + finish. Previously it was doing so while holding the GIL, stopping any completions from actually continuing. This test is a failure if it does not exit within a few seconds. diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py index 2661e6ecaf8..3fbbd4bf85b 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py @@ -58,7 +58,8 @@ def connection_watcher(handle, type, state, path): self.handle, "/") - + @unittest.skipUnless(hasattr(zookeeper, 'init_ssl'), + "SSL support not compiled in.") def testsslconnection(self): cv = threading.Condition() self.connected = False diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/create_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/create_test.py index 8ab80f9565a..29f63a9ffb4 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/create_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/create_test.py @@ -59,7 +59,7 @@ def test_sync_create_existing(self): def test_exception_paths(self): """ - Make sure common exceptions due to API misuse are correctly propogated + Make sure common exceptions due to API misuse are correctly propagated """ self.assertRaises(zookeeper.BadArgumentsException, zookeeper.create, diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/get_set_test.py b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/get_set_test.py index b77b3b22165..226e8c183ff 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/get_set_test.py +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/get_set_test.py @@ -70,7 +70,7 @@ def test_sync_getset(self): def test_stat_deleted_node(self): """ Test for a bug that surfaced when trying to build a - stat object from a non-existant node. + stat object from a non-existent node. """ self.ensureDeleted("/zk-python-test-deleteme") diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh index a3cf4d87969..c7c0fdbdb04 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh @@ -1,41 +1,47 @@ -#!/bin/sh +#! /usr/bin/env bash # -# Licensed to the Apache Software Foundation (ASF) under one +# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# # Usage: run_tests.sh testdir [logdir] # logdir is optional, defaults to cwd +set -e + # get the number of command-line arguments given ARGC=$# # check to make sure enough arguments were given or exit -if [ $ARGC -lt 2 ]; then - export ZKPY_LOG_DIR="." +if [[ $ARGC -eq 1 ]]; then + export ZKPY_LOG_DIR="." +elif [[ $ARGC -eq 2 ]]; then + export ZKPY_LOG_DIR=$2 else - export ZKPY_LOG_DIR=$2 + echo "Usage: $0 testdir [logdir]" 1>&2 + exit 1 fi # Find the build directory containing zookeeper.so -SO_PATH=`find ./target/ -name "zookeeper.so" | head -1` -PYTHONPATH=`dirname $SO_PATH` +SO_PATH=$(find ./target/ -name 'zookeeper*.so' | head -1) +PYTHONPATH=$(dirname "$SO_PATH") LIB_PATH=../../zookeeper-client/zookeeper-client-c/target/c/.libs -for test in `ls $1/*_test.py`; -do - echo "Running $test" - echo "Running LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test" - LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test +for test in "$1"/*_test.py; do + echo "Running $test" + echo "Running LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test" + LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python "$test" done diff --git a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zkServer.sh b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zkServer.sh index 3b6ed37fc12..a8049fd6794 100755 --- a/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zkServer.sh +++ b/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zkServer.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#! /usr/bin/env bash # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -8,95 +8,82 @@ # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -if [ "x$1" == "x" ] -then - echo "USAGE: $0 startClean|start|stop" - exit 2 +if [[ -z $1 ]]; then + echo "USAGE: $0 startClean|start|stop" + exit 2 fi -if [ "x${base_dir}" == "x" ] -then +if [[ -n $base_dir ]]; then PROJECT_ROOT="../../" else - PROJECT_ROOT=${base_dir} + PROJECT_ROOT=$base_dir fi -WORK_DIR=${PROJECT_ROOT}/zookeeper-contrib/zookeeper-contrib-zkpython/target/zkpython_tests -TEST_DIR=${PROJECT_ROOT}/zookeeper-contrib/zookeeper-contrib-zkpython/src/test - +WORK_DIR=$PROJECT_ROOT/zookeeper-contrib/zookeeper-contrib-zkpython/target/zkpython_tests +TEST_DIR=$PROJECT_ROOT/zookeeper-contrib/zookeeper-contrib-zkpython/src/test -if [ -r "${WORK_DIR}/../zk.pid" ] -then - pid=`cat "${WORK_DIR}/../zk.pid"` - kill -9 $pid - rm -f "${WORK_DIR}/../zk.pid" +if [[ -r "$WORK_DIR/../zk.pid" ]]; then + pid=$(cat "$WORK_DIR/../zk.pid") + kill -9 "$pid" + rm -f "$WORK_DIR/../zk.pid" fi -which lsof &> /dev/null -if [ $? -eq 0 ] -then - pid=`lsof -i :22182 | grep LISTEN | awk '{print $2}'` - if [ -n "$pid" ] - then - kill -9 $pid - fi +if which lsof &>/dev/null; then + pid=$(lsof -i :22182 | grep LISTEN | awk '{print $2}') + if [[ -n $pid ]]; then + kill -9 "$pid" + fi fi - - - -if [ "x$1" == "xstartClean" ] -then - rm -rf ${WORK_DIR} +if [[ $1 == "startClean" ]]; then + rm -rf "$WORK_DIR" fi +CLASSPATH="$CLASSPATH:$PROJECT_ROOT/zookeeper-server/target/classes" +CLASSPATH="$CLASSPATH:$PROJECT_ROOT/conf" - -CLASSPATH="$CLASSPATH:${PROJECT_ROOT}/zookeeper-server/target/classes" -CLASSPATH="$CLASSPATH:${zk_base}/conf" - -for i in "${PROJECT_ROOT}"/zookeeper-server/target/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$PROJECT_ROOT"/zookeeper-server/target/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done -for i in "${PROJECT_ROOT}"/zookeeper-server/src/main/resource/lib/*.jar -do - CLASSPATH="$CLASSPATH:$i" +for i in "$PROJECT_ROOT"/zookeeper-server/src/main/resource/lib/*.jar; do + CLASSPATH="$CLASSPATH:$i" done +export CLASSPATH # Make sure nothing is left over from before #fuser -skn tcp 22182/tcp case $1 in -start|startClean) - mkdir -p ${WORK_DIR}/zkdata + start | startClean) + mkdir -p "$WORK_DIR/zkdata" - rm -rf ${WORK_DIR}/ssl - mkdir -p ${WORK_DIR}/ssl - cp ${PROJECT_ROOT}/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh ${WORK_DIR}/ssl/ - cd ${WORK_DIR}/ssl/ - ./gencerts.sh - cd - + rm -rf "$WORK_DIR/ssl" + mkdir -p "$WORK_DIR/ssl" + cp "$PROJECT_ROOT/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh" "$WORK_DIR/ssl/" + (cd "$WORK_DIR/ssl/" && ./gencerts.sh) - sed "s#WORKDIR#${WORK_DIR}#g" ${TEST_DIR}/zoo.cfg > "${WORK_DIR}/zoo.cfg" - java -Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100 -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain "${WORK_DIR}/zoo.cfg" &> "${WORK_DIR}/zoo.log" & + sed "s#WORKDIR#$WORK_DIR#g" "$TEST_DIR/zoo.cfg" >"$WORK_DIR/zoo.cfg" + java -Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100 \ + org.apache.zookeeper.server.ZooKeeperServerMain "$WORK_DIR/zoo.cfg" &>"$WORK_DIR/zoo.log" & pid=$! - echo -n $! > ${WORK_DIR}/../zk.pid + echo -n $! >"$WORK_DIR"/../zk.pid sleep 5 ;; -stop) + stop) # Already killed above ;; -*) - echo "Unknown command " + $1 + *) + echo "Unknown command $1" exit 2 + ;; esac - diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/README.txt b/zookeeper-contrib/zookeeper-contrib-zktreeutil/README.txt index 43b06fa72c2..6a7ef63dacb 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/README.txt +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/README.txt @@ -26,7 +26,7 @@ UPDATE: Make the incremental changes into the live ZK-tree from saved XML, essen lly after running the diff. DUMP: Dumps the ZK-tree on the standard output device reading either from live ZK -server or XML file. Like export, ZK-subtree can be dumped with optionaly +server or XML file. Like export, ZK-subtree can be dumped with optionally providing the path to the ZK-subtree, and till a certain depth of the (sub)tree. The exported ZK data into XML file can be shortened by only keeping the static ZK @@ -38,7 +38,7 @@ Once ignored, the whole subtree is ignored during DIFF, UPDATE and WRITE. Pre-requisites -------------- 1. Linux system with 2.6.X kernel. -2. Zookeeper C client library (locally built at ../../c/.libs) >= 3.X.X +2. Zookeeper C client library (locally built at ../../zookeeper-client/zookeeper-client-c/target/c/.libs) >= 3.X.X 3. Development build libraries (rpm packages): a. boost-devel >= 1.32.0 b. libxml2-devel >= 2.7.3 @@ -60,7 +60,7 @@ versions. Testing and usage of zktreeutil -------------------------------- 1. Run Zookeeper server locally on port 2181 -2. export LD_LIBRARY_PATH=../../c/.libs/:/usr/local/lib/ +2. export LD_LIBRARY_PATH=../../zookeeper-client/zookeeper-client-c/target/c/.libs 3. ./src/zktreeutil --help # show help 4. ./src/zktreeutil --zookeeper=localhost:2181 --import --xmlfile=tests/zk_sample.xml 2>/dev/null # import sample ZK tree 5. ./src/zktreeutil --zookeeper=localhost:2181 --dump --path=/myapp/version-1.0 2>/dev/null # dump Zk subtree @@ -71,4 +71,4 @@ Testing and usage of zktreeutil 9. ./src/zktreeutil -z localhost:2181 -E 2>/dev/null > zk_sample2.xml # export the mofied ZK tree 10. ./src/zktreeutil -z localhost:2181 -U -x zk_sample.xml -p /myapp/version-1.0/distributions 2>/dev/null # update with incr. changes 11. ./src/zktreeutil --zookeeper=localhost:2181 --import --force --xmlfile=zk_sample2.xml 2>/dev/null # re-prime the ZK tree - +12. ./src/zktreeutil --zookeeper=localhost:2188 --dump --ssl=/path/certs/root_ca.pem,/path/certs/node.crt,/path/certs/node.key # connect with ssl params to the secureClientPort diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/configure.ac b/zookeeper-contrib/zookeeper-contrib-zktreeutil/configure.ac index b4a82a76af8..43867482ff6 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/configure.ac +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/configure.ac @@ -26,8 +26,8 @@ XML2_INCLUDE="/usr/include/libxml2" AC_SUBST(XML2_INCLUDE) # Zookeeper C client -ZOOKEEPER_PATH=${BUILD_PATH}/../../c -AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER="-L${ZOOKEEPER_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/.libs"]) +ZOOKEEPER_PATH=${BUILD_PATH}/../../zookeeper-client/zookeeper-client-c +AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER="-L${ZOOKEEPER_PATH}/target/c/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/target/c/.libs"]) if test -z "${ZOOKEEPER}"; then AC_ERROR("... zookeeper C client not found!") fi @@ -45,6 +45,35 @@ if test -z "${LOG4CXX}"; then AC_ERROR("... log4cxx not found!") fi +dnl OpenSSL +AC_ARG_WITH(openssl, + [AC_HELP_STRING([--with-openssl[=DIR]], [build with openssl (autodetect openssl library by default) )])], + [], [with_openssl=yes]) +AC_MSG_NOTICE([configuring SSL using --with-openssl=$with_openssl]) +saved_CPPFLAGS="$CPPFLAGS" +saved_LDFLAGS="$LDFLAGS" +if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" ; then + CPPFLAGS="$CPPFLAGS -I$with_openssl/include" + LDFLAGS="$LDFLAGS -L$with_openssl/lib" +fi +have_openssl=no +AC_CHECK_HEADER(openssl/ssl.h, [ AC_CHECK_LIB(ssl, SSL_CTX_new, [have_openssl=yes]) ]) +if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" && test "x$have_openssl" != "xyes"; then + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" +fi +if test "x$with_openssl" != xno && test "x$have_openssl" = xno; then + AC_MSG_WARN([cannot build SSL support -- openssl not found]) + with_openssl=no +fi +if test "x$with_openssl" != xno; then + AC_MSG_NOTICE([building with SSL support]) +else + AC_MSG_NOTICE([building without SSL support]) +fi +AM_CONDITIONAL([WANT_OPENSSL],[test "x$with_openssl" != xno]) + + AC_SUBST(LOG4CXX) AC_SUBST(LOG4CXX_VERSION) AC_SUBST(LOG4CXX_INCLUDE) diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/Makefile.am b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/Makefile.am index 641077a88e4..7d665d079c9 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/Makefile.am +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/Makefile.am @@ -14,9 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +if WANT_OPENSSL + OPENSSL_CPPFLAGS = -DHAVE_OPENSSL_H +endif + AM_CXXFLAGS = -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ -I$(top_srcdir)/include -I${LOG4CXX_INCLUDE} -I/usr/include \ - -I${XML2_INCLUDE} + -I${XML2_INCLUDE} -DTHREADED $(OPENSSL_CPPFLAGS) bin_PROGRAMS = zktreeutil diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.cc b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.cc index 1df175a9bac..c1f65916a37 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.cc +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.cc @@ -18,6 +18,7 @@ #include "ZkAdaptor.h" #include +#include #include #include #include @@ -172,12 +173,36 @@ namespace zktreeutil disconnect(); // Establish a new connection to ZooKeeper - mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), - NULL, +#ifdef HAVE_OPENSSL_H + if (!m_zkConfig.getSslParams().empty()) + { + mp_zkHandle = zookeeper_init_ssl( m_zkConfig.getHosts().c_str(), + m_zkConfig.getSslParams().c_str(), + NULL, m_zkConfig.getLeaseTimeout(), 0, NULL, 0); + + } + else + { + mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), + NULL, + m_zkConfig.getLeaseTimeout(), + 0, + NULL, + 0); + } +#else + mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), + NULL, + m_zkConfig.getLeaseTimeout(), + 0, + NULL, + 0); +#endif + if (mp_zkHandle == NULL) { // Invalid handle returned @@ -201,7 +226,7 @@ namespace zktreeutil << std::endl; return; } - else if ( state && state != ZOO_CONNECTING_STATE) + else if ( state && state != ZOO_NOTCONNECTED_STATE && state != ZOO_CONNECTING_STATE) { // Not connecting any more... some other issue std::ostringstream oss; @@ -475,7 +500,7 @@ namespace zktreeutil 0, buffer, &len, stat ); } while (rc != ZOK && rh.handleRC(rc)); - if (rc != ZOK) // checl return code + if (rc != ZOK) // check return code { std::cerr << "[zktreeutil] Error in fetching value of " << path << std::endl; throw ZooKeeperException( string("Unable to get data of node ") + path, rc ); diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.h b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.h index 4b68e28db1f..ffefb28a7da 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.h +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.h @@ -110,15 +110,18 @@ namespace zktreeutil * @param leaseTimeout the lease timeout (heartbeat) * @param autoReconnect whether to allow for auto-reconnect * @param connectTimeout the connect timeout, in milliseconds; + * @param certs ssl parameters to initiate SSL connection; */ ZooKeeperConfig(const string &hosts, int leaseTimeout, bool autoReconnect = true, - long long int connectTimeout = 15000) + long long int connectTimeout = 15000, + const string &sslParams = "") : m_hosts(hosts), m_leaseTimeout(leaseTimeout), m_autoReconnect(autoReconnect), - m_connectTimeout(connectTimeout) {} + m_connectTimeout(connectTimeout), + m_sslParams(sslParams) {} /** * \brief Returns the list of ZK hosts to connect to. @@ -143,6 +146,11 @@ namespace zktreeutil */ long long int getConnectTimeout() const { return m_connectTimeout; } + /** + * \brief Returns the ssl params + */ + string getSslParams() const { return m_sslParams; } + private: /** @@ -156,7 +164,7 @@ namespace zktreeutil const int m_leaseTimeout; /** - * True if this adapater should attempt to autoreconnect in case + * True if this adapter should attempt to autoreconnect in case * the current session has been dropped. */ const bool m_autoReconnect; @@ -166,6 +174,11 @@ namespace zktreeutil * is established to ZK. */ const long long int m_connectTimeout; + + /** + * comma separated ssl parameters to initiate SSL connection. + */ + const string m_sslParams; }; /** diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.cc b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.cc index 270bf3105c7..77fdd96d024 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.cc +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.cc @@ -296,12 +296,12 @@ namespace zktreeutil return zkRootSptr; } - ZooKeeperAdapterSptr ZkTreeUtil::get_zkHandle (const string& zkHosts) + ZooKeeperAdapterSptr ZkTreeUtil::get_zkHandle (const string& zkHosts, const string& cert) { try { // Create an instance of ZK adapter. - ZooKeeperConfig config (zkHosts, 10000); + ZooKeeperConfig config (zkHosts, 10000, true, 15000, cert); ZooKeeperAdapterSptr zkHandleSptr = ZooKeeperAdapterSptr (new ZooKeeperAdapter (config)); return zkHandleSptr; @@ -343,7 +343,7 @@ namespace zktreeutil } // Connect to ZK server - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); + ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts, getSslParams()); std::cerr << "[zktreeutil] connected to ZK serverfor reading" << std::endl; @@ -424,7 +424,7 @@ namespace zktreeutil bool force) const { // Connect to ZK server - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); + ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts, getSslParams()); std::cerr << "[zktreeutil] connected to ZK server for writing" << std::endl; @@ -517,7 +517,7 @@ namespace zktreeutil } // Load the rooted subtree from zookeeper - ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts); + ZooKeeperAdapterSptr zkHandle = get_zkHandle (zkHosts, getSslParams()); std::cerr << "[zktreeutil] connected to ZK server for reading" << std::endl; ZkTreeNodeSptr zkLiveRootSptr = loadZkTree_ (zkHandle, path); @@ -630,7 +630,7 @@ namespace zktreeutil if ((execFlags & EXECUTE) || (execFlags & INTERACTIVE)) { - zkHandleSptr = get_zkHandle (zkHosts); + zkHandleSptr = get_zkHandle (zkHosts, getSslParams()); std::cerr << "[zktreeutil] connected to ZK server for writing" << std::endl; } diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.h b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.h index 0a9be03f842..872bebecbdf 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.h +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.h @@ -185,9 +185,10 @@ namespace zktreeutil * \brief Connects to zookeeper and returns a valid ZK handle * * @param zkHosts comma separated list of host:port forming ZK quorum + * @param cert certificate file path * @param a valid ZK handle */ - static ZooKeeperAdapterSptr get_zkHandle (const string& zkHosts); + static ZooKeeperAdapterSptr get_zkHandle (const string& zkHosts, const string& cert=""); public: @@ -252,10 +253,26 @@ namespace zktreeutil const vector< ZkAction >& zkActions, int execFlags) const; + /** + * \brief Sets the ssl params to be used for SSL connection + * @param cert ssl params + */ + void setSslParams(const string& cert) + { + sslParams_ = cert; + } + + /** + * \brief Gets the ssl params + * @return the cert + */ + string getSslParams() const { return sslParams_; } + private: ZkTreeNodeSptr zkRootSptr_; // ZK tree root node - bool loaded_; // Falg indicating whether ZK tree loaded into memory + bool loaded_; // Flag indicating whether ZK tree loaded into memory + string sslParams_; // Comma separated parameters to initiate SSL connection }; } diff --git a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtilMain.cc b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtilMain.cc index 8afebf6e2f7..28157506247 100644 --- a/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtilMain.cc +++ b/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtilMain.cc @@ -43,9 +43,10 @@ static struct option long_options[] = { {"path", required_argument, 0, 'p'}, {"depth", required_argument, 0, 'd'}, {"zookeeper", required_argument, 0, 'z'}, + {"ssl", required_argument, 0, 's'}, {0, 0, 0, 0} }; -static char *short_options = "IEUFDfx:p:d:hz:"; +static char *short_options = "IEUFDfx:p:d:hz:s:"; static void usage(int argc, char *argv[]) { @@ -128,6 +129,13 @@ static void usage(int argc, char *argv[]) << std::endl << "\t specifies information to connect to zookeeper." << std::endl; + std::cout + << "\t--ssl= or -s : " + << std::endl + << "\t Comma separated parameters to initiate SSL connection." + << std::endl + << "\t e.g.: server_cert.crt,client_cert.crt,client_priv_key.pem,passwd" + << std::endl; } int main(int argc, char **argv) @@ -143,6 +151,7 @@ int main(int argc, char **argv) string zkHosts; string xmlFile; string path = "/"; + string cert; int depth = 0; while (1) { @@ -171,12 +180,16 @@ int main(int argc, char **argv) break; case 'z': zkHosts = optarg; break; + case 's': cert = optarg; + break; case 'h': usage (argc, argv); exit(0); } } ZkTreeUtil zkTreeUtil; + if (!cert.empty()) zkTreeUtil.setSslParams(cert); + switch (op) { case 'I': { diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/README.txt b/zookeeper-contrib/zookeeper-contrib-zooinspector/README.txt index 3c2a58f42e4..937579fb4f5 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/README.txt +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/README.txt @@ -18,20 +18,28 @@ Features -------- Below is a list of features in the current release of ZooInspector. - Load connection settings from a zookeeper properties file - - Plugable DataEncryptionManagers to specify how data should be encrypted and decrypted in the Zookeeper instance - - Browseable tree view of the ZooKeeper instance + - Pluggable DataEncryptionManagers to specify how data should be encrypted and decrypted in the Zookeeper instance + - Browsable tree view of the ZooKeeper instance - View the data in a node - - View the ACL's currently applied to a node - - View the metadata for a node (Version, Number of Children, Last modified Tiem, etc.) - - Plugable NodeViewers interface + - View the ACLs currently applied to a node + - View the metadata for a node (Version, Number of Children, Last modified Time, etc.) + - Pluggable NodeViewers interface - Ability to save/load and set default Node Viewers - -Pre-requisites --------------- - - The main zookeeper build script must have been run before building this module - -Build Instructions ------------------- + +Maven Build Instructions +------------------------- + 0. Pre-requisites + - Run "mvn install" in the root directory of the Zookeeper project + 1. Open a command line + 2. cd into this directory + 3. mvn install + 4. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux) (these scripts will + find the built JAR automatically in the "target" directory as long as you don't move it) + +Ant Build Instructions +----------------------- + 0. Pre-requisites + - The main zookeeper build script must have been run before building this module 1. Open a command line. 2. cd into this directory 3. Run command: ant @@ -40,55 +48,55 @@ Build Instructions copied to this directory during the build 6. By default the zookeeper.cmd and zookeeper.sh files expect zookeeper-3.3.0.jar. If you are using another version you will need to change these files to point to the zookeeper-3.x.x.jar you copied to the lib directory - 7. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using + 7. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar directly Using ZooInspector ------------------ - To start ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using + To start ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux). If you are using zookeeper-3.3.0.jar and do not require any classpath changes you can run the zookeeper-dev-ZooInspector.jar directly. - - Click the play button on the toolbar to bring up the connection dialog. From here you can enter connection - information for your zookeeper instance. You can also load the connection properties from a file. This file can + + Click the play button on the toolbar to bring up the connection dialog. From here you can enter connection + information for your zookeeper instance. You can also load the connection properties from a file. This file can have the format as a normal zookeeper properties file (i.e. hosts and timeout key-value pairs) and van optional have - an encryptionManager key-value pair to specify the DataEncryptionManager to use for this connection + an encryptionManager key-value pair to specify the DataEncryptionManager to use for this connection (DataEncryptionManagers are explained in further detail in the 'Creating and Using Plugins' section below). You can - also set the entered information as the defaults so that when you first start ZooInspector these settings are + also set the entered information as the defaults so that when you first start ZooInspector these settings are automatically loaded into this dialog. Pressing the OK button with connect to your ZooKeeper instance and show the current node tree on the left of the main panel. - - Clicking a node in the node tree will load the data for that node into the node viewers. Three node viewers are + + Clicking a node in the node tree will load the data for that node into the node viewers. Three node viewers are currently distributed with ZooInspector: - 1. Node Data - This enables you to see the data current stored on that node. This data can be modified and - saved. The data is decrypted and encrypted using the DataEncryptionManager specified on the connection + 1. Node Data - This enables you to see the data current stored on that node. This data can be modified and + saved. The data is decrypted and encrypted using the DataEncryptionManager specified on the connection dialog. - 2. Node Metadata - This enables you to see the metadata associiated with this node. This is Essentially the data + 2. Node Metadata - This enables you to see the metadata associated with this node. This is essentially the data obtained from the Stat object for this node. 3. Node ACLs - This allows you to see the ACLs currently applied to this node. Currently there is no ability to change the ACLs on a node, but it is a feature I would like to add. Other custom Node Viewers can be added, this is explained in the 'Creating and Using Plugins' section below. - + Creating and Using Plugins -------------------------- There are two types of plugin which can be used with ZooInspector: - 1. DataEncryptionManager - This specifies how data should be encrypted and decrypted when working with a + 1. DataEncryptionManager - This specifies how data should be encrypted and decrypted when working with a zookeeper instance. 2. ZooInspectorNodeViewer - This is a GUI panel which provides a view of visualisation on a node. More information on these interfaces can be found in the javadocs for this module. - - To use a plugin in ZooInspector, build the plugin to a jar and copy the jar to the lib sub-directory. Edit the + + To use a plugin in ZooInspector, build the plugin to a jar and copy the jar to the lib sub-directory. Edit the zooInspector.cmd and/or zooInspector.sh files to include your new jar on the classpath and run ZooInspector. - - For DataEncryptionManagers, click the play button to open the connection dialog and enter the full class name of - your DataEncryptionManager in the 'Data Encryption Manager' field. You can make this Data Encryption Manager the + + For DataEncryptionManagers, click the play button to open the connection dialog and enter the full class name of + your DataEncryptionManager in the 'Data Encryption Manager' field. You can make this Data Encryption Manager the default by clicking 'Set As Default'. Click the 'OK' button to instantiate and use your plugin. - + For ZooInspectorNodeViewers, Click the 'Change Node Viewers' button on the toolbar (looks like a tree with a pencil) - and enter the full classname for your Node Viewer in the field left of the 'Add' button, then click the 'Add' - button. The Node Viewer will be instantiated and should appear in the list. You can change the order of the Node - viewers by clicking the up and dpwn buttons and delete a Node Viewer by clicking the delete button. You can save + and enter the full classname for your Node Viewer in the field left of the 'Add' button, then click the 'Add' + button. The Node Viewer will be instantiated and should appear in the list. You can change the order of the Node + viewers by clicking the up and down buttons and delete a Node Viewer by clicking the delete button. You can save to configuration to a file or set it as the default if necessary. Then click the 'OK' button and your Node Viewer should appear in the tabs on the right of the main panel. \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/TODO b/zookeeper-contrib/zookeeper-contrib-zooinspector/TODO index 404d5c9c699..eb6e1a15042 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/TODO +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/TODO @@ -10,7 +10,7 @@ http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html -- Rename classes to avoid redundand "ZooInspector" prefix. +- Rename classes to avoid redundant "ZooInspector" prefix. - Ant build file has hard coded log4j dependency. (ZK will move to maven anyways...) diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/ivy.xml b/zookeeper-contrib/zookeeper-contrib-zooinspector/ivy.xml index d841d18b2ce..dfa57dc84b1 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/ivy.xml +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/ivy.xml @@ -39,12 +39,7 @@ - - - + diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/pom.xml b/zookeeper-contrib/zookeeper-contrib-zooinspector/pom.xml old mode 100755 new mode 100644 index 6d474608c4b..b4185ebce44 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/pom.xml +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/pom.xml @@ -23,7 +23,7 @@ org.apache.zookeeper zookeeper-contrib - 3.7.0-SNAPSHOT + 3.10.0-SNAPSHOT zookeeper-contrib-zooinspector @@ -35,10 +35,50 @@ 0.6 - 2.4 - 18.0 + 32.1.3-jre + + + + + src/main/resources + + **/* + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.1 + + + jar-with-dependencies + + + + + org.apache.zookeeper.inspector.ZooInspector + + + + + + make-assembly + package + + single + + + + + + + org.apache.zookeeper @@ -50,28 +90,14 @@ slf4j-api - org.slf4j - slf4j-log4j12 - - - * - * - - + ch.qos.logback + logback-classic + runtime + true - log4j - log4j - - - * - * - - - - - junit - junit + org.junit.vintage + junit-vintage-engine test @@ -84,15 +110,11 @@ apache-rat-tasks ${rat.version} + - commons-lang - commons-lang - ${commons-lang.version} - - - commons-collections - commons-collections + org.mockito + mockito-core + test - - \ No newline at end of file + diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java index 0a4fe94b0b3..126fd65c64c 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java @@ -18,9 +18,6 @@ * * This is a simple example of utilization: * - * import com.nitido.utils.toaster.*; - * import javax.swing.*; - * * public class ToasterTest * { * @@ -36,10 +33,20 @@ */ package com.nitido.utils.toaster; -import java.awt.*; -import javax.swing.*; -import javax.swing.border.*; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JWindow; +import javax.swing.border.EtchedBorder; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Rectangle; /** * Class to show tosters in multiplatform @@ -135,7 +142,7 @@ class SingleToaster extends javax.swing.JWindow /*** - * Simple costructor that initialized components... + * Simple constructor that initialized components... */ public SingleToaster() { diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java index a9e5ac477cf..c8662db62d1 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java @@ -30,6 +30,9 @@ public class BasicDataEncryptionManager implements DataEncryptionManager { * (byte[]) */ public String decryptData(byte[] encrypted) throws Exception { + if(encrypted == null) { + return ""; + } return new String(encrypted); } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/IconResource.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/IconResource.java index 411900a9a54..46ffb0c4f1a 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/IconResource.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/IconResource.java @@ -27,12 +27,15 @@ import org.apache.zookeeper.inspector.logger.LoggerFactory; /** - * @see http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html - * I tried to take icons that are available in the Tango icon set + * @link http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + * I tried to take icons that are available in the Tango icon set. + * + * @link http://tango.freedesktop.org/Tango_Icon_Library + * The Tango icon set can be found under the "Download" section. */ public class IconResource { - public static final String ICON_ChangeNodeViewers = ""; + public static final String ICON_CHANGE_NODE_VIEWERS = "categories/applications-system"; public static final String ICON_TREE_LEAF = "mimetypes/text-x-generic"; public static final String ICON_TREE_OPEN = "places/folder"; public static final String ICON_TREE_CLOSE = "places/folder"; diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/Toolbar.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/Toolbar.java index 06e80a80806..99c780d8d08 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/Toolbar.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/Toolbar.java @@ -64,13 +64,13 @@ private void init() { } public static enum Button { - connect("Connect",IconResource.ICON_START,true), - disconnect("Disconnect",IconResource.ICON_STOP,false), - refresh("Refresh",IconResource.ICON_REFRESH,false), - addNode("Add Node",IconResource.ICON_DOCUMENT_ADD,false), - deleteNode("Delete Node",IconResource.ICON_TRASH,false), - nodeViewers("Change Node Viewers",IconResource.ICON_ChangeNodeViewers,true), - about("About ZooInspector",IconResource.ICON_HELP_ABOUT,true); + connect("Connect", IconResource.ICON_START, true), + disconnect("Disconnect", IconResource.ICON_STOP, false), + refresh("Refresh All", IconResource.ICON_REFRESH, false), + addNode("Add Node", IconResource.ICON_DOCUMENT_ADD, false), + deleteNode("Delete Node", IconResource.ICON_TRASH, false), + nodeViewers("Change Node Viewers", IconResource.ICON_CHANGE_NODE_VIEWERS, true), + about("About ZooInspector", IconResource.ICON_HELP_ABOUT, true); private String toolTip; private String icon; diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java index 58096b26ecf..b6cdf4bf4eb 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java @@ -23,6 +23,7 @@ import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; import java.io.IOException; import javax.swing.JButton; @@ -36,6 +37,9 @@ * The About Dialog for the application */ public class ZooInspectorAboutDialog extends JDialog { + + private static final File aboutHtmlFile = new File("./src/main/resources/about.html"); + /** * @param frame * - the Frame from which the dialog is displayed @@ -53,13 +57,10 @@ public ZooInspectorAboutDialog(Frame frame, IconResource iconResource) { JEditorPane aboutPane = new JEditorPane(); aboutPane.setEditable(false); aboutPane.setOpaque(false); - java.net.URL aboutURL = ZooInspectorAboutDialog.class - .getResource("about.html"); try { - aboutPane.setPage(aboutURL); + aboutPane.setPage(aboutHtmlFile.toURI().toURL()); } catch (IOException e) { - LoggerFactory.getLogger().error( - "Error loading about.html, file may be corrupt", e); + LoggerFactory.getLogger().error("Error loading about.html, file may be corrupt", e); } panel.add(aboutPane, BorderLayout.CENTER); panel.setPreferredSize(new Dimension(600, 200)); diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java index 1647021f513..da847294ec5 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java @@ -56,7 +56,7 @@ public class ZooInspectorConnectionPropertiesDialog extends JDialog { /** * @param lastConnectionProps * - the last connection properties used. if this is the first - * conneciton since starting the applications this will be the + * connection since starting the applications this will be the * default settings * @param connectionPropertiesTemplateAndLabels * - the connection properties and labels to show in this dialog diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java index e3cc7b12bcc..5c4485de5f4 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java @@ -54,7 +54,6 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import org.apache.zookeeper.inspector.gui.Toolbar.Button; import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; import org.apache.zookeeper.inspector.logger.LoggerFactory; import org.apache.zookeeper.inspector.manager.ZooInspectorManager; @@ -90,9 +89,9 @@ public ZooInspectorNodeViewersDialog(Frame frame, final List newViewers = new ArrayList( currentViewers); this.setLayout(new BorderLayout()); - this.setIconImage(iconResource.get(IconResource.ICON_ChangeNodeViewers,"") + this.setIconImage(iconResource.get(IconResource.ICON_CHANGE_NODE_VIEWERS,"Change Node Viewers") .getImage()); - this.setTitle("About ZooInspector"); + this.setTitle("Change Node Viewers"); this.setModal(true); this.setAlwaysOnTop(true); this.setResizable(true); diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java index 05c256b5a85..9c3beb15ecb 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java @@ -25,10 +25,8 @@ import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.TreePath; +import org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener; import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; import org.apache.zookeeper.inspector.manager.ZooInspectorManager; import org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager; @@ -36,10 +34,9 @@ /** * This is the {@link JPanel} which contains the {@link ZooInspectorNodeViewer}s */ -public class ZooInspectorNodeViewersPanel extends JPanel implements - TreeSelectionListener, ChangeListener { +public class ZooInspectorNodeViewersPanel extends JPanel implements ChangeListener, NodeSelectionListener { - private final List nodeVeiwers = new ArrayList(); + private final List nodeViewers = new ArrayList<>(); private final List needsReload = new ArrayList(); private final JTabbedPane tabbedPane; private final List selectedNodes = new ArrayList(); @@ -48,17 +45,17 @@ public class ZooInspectorNodeViewersPanel extends JPanel implements /** * @param zooInspectorManager * - the {@link ZooInspectorManager} for the application - * @param nodeVeiwers + * @param nodeViewers * - the {@link ZooInspectorNodeViewer}s to show */ public ZooInspectorNodeViewersPanel( ZooInspectorNodeManager zooInspectorManager, - List nodeVeiwers) { + List nodeViewers) { this.zooInspectorManager = zooInspectorManager; this.setLayout(new BorderLayout()); tabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT); - setNodeViewers(nodeVeiwers); + setNodeViewers(nodeViewers); tabbedPane.addChangeListener(this); this.add(tabbedPane, BorderLayout.CENTER); reloadSelectedViewer(); @@ -69,11 +66,11 @@ public ZooInspectorNodeViewersPanel( * - the {@link ZooInspectorNodeViewer}s to show */ public void setNodeViewers(List nodeViewers) { - this.nodeVeiwers.clear(); - this.nodeVeiwers.addAll(nodeViewers); + this.nodeViewers.clear(); + this.nodeViewers.addAll(nodeViewers); needsReload.clear(); tabbedPane.removeAll(); - for (ZooInspectorNodeViewer nodeViewer : nodeVeiwers) { + for (ZooInspectorNodeViewer nodeViewer : nodeViewers) { nodeViewer.setZooInspectorManager(zooInspectorManager); needsReload.add(true); tabbedPane.add(nodeViewer.getTitle(), nodeViewer); @@ -85,42 +82,19 @@ public void setNodeViewers(List nodeViewers) { private void reloadSelectedViewer() { int index = this.tabbedPane.getSelectedIndex(); if (index != -1 && this.needsReload.get(index)) { - ZooInspectorNodeViewer viewer = this.nodeVeiwers.get(index); + ZooInspectorNodeViewer viewer = this.nodeViewers.get(index); viewer.nodeSelectionChanged(selectedNodes); this.needsReload.set(index, false); } } - /* - * (non-Javadoc) - * - * @see - * javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event - * .TreeSelectionEvent) + /** + * @see org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener#nodePathSelected(String) */ - public void valueChanged(TreeSelectionEvent e) { - TreePath[] paths = e.getPaths(); + @Override + public void nodePathSelected(String nodePath) { selectedNodes.clear(); - for (TreePath path : paths) { - boolean appended = false; - StringBuilder sb = new StringBuilder(); - Object[] pathArray = path.getPath(); - for (Object o : pathArray) { - if (o != null) { - String nodeName = o.toString(); - if (nodeName != null) { - if (nodeName.length() > 0) { - appended = true; - sb.append("/"); //$NON-NLS-1$ - sb.append(o.toString()); - } - } - } - } - if (appended) { - selectedNodes.add(sb.toString()); - } - } + selectedNodes.add(nodePath); for (int i = 0; i < needsReload.size(); i++) { this.needsReload.set(i, true); } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java index e816ceb4b60..eb08e722d84 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java @@ -26,16 +26,12 @@ import java.util.Properties; import java.util.concurrent.ExecutionException; -import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; -import javax.swing.JToolBar; import javax.swing.SwingWorker; -import org.apache.zookeeper.inspector.gui.actions.AddNodeAction; -import org.apache.zookeeper.inspector.gui.actions.DeleteNodeAction; import org.apache.zookeeper.inspector.gui.nodeviewer.ZooInspectorNodeViewer; import org.apache.zookeeper.inspector.logger.LoggerFactory; import org.apache.zookeeper.inspector.manager.ZooInspectorManager; @@ -45,10 +41,14 @@ */ public class ZooInspectorPanel extends JPanel implements NodeViewersChangeListener { + + /** Control how fast the scroll bar in the tree view window moves */ + private static final int TREE_SCROLL_UNIT_INCREMENT = 16; + private final IconResource iconResource; private final Toolbar toolbar; private final ZooInspectorNodeViewersPanel nodeViewersPanel; - private final ZooInspectorTreeViewer treeViewer; + private final ZooInspectorTreeView treeViewer; private final ZooInspectorManager zooInspectorManager; private final List listeners = new ArrayList(); @@ -81,10 +81,10 @@ public ZooInspectorPanel(final ZooInspectorManager zooInspectorManager, final Ic } nodeViewersPanel = new ZooInspectorNodeViewersPanel( zooInspectorManager, nodeViewers); - treeViewer = new ZooInspectorTreeViewer(zooInspectorManager, - nodeViewersPanel, iconResource); + this.treeViewer = new ZooInspectorTreeView(zooInspectorManager, iconResource); + this.treeViewer.addNodeSelectionListener(this.nodeViewersPanel); this.setLayout(new BorderLayout()); - + toolbar.addActionListener(Toolbar.Button.connect, new ActionListener() { public void actionPerformed(ActionEvent e) { ZooInspectorConnectionPropertiesDialog zicpd = new ZooInspectorConnectionPropertiesDialog( @@ -101,17 +101,20 @@ public void actionPerformed(ActionEvent e) { }); toolbar.addActionListener(Toolbar.Button.refresh, new ActionListener() { public void actionPerformed(ActionEvent e) { - treeViewer.refreshView(); + treeViewer.initialize(); + } + }); + toolbar.addActionListener(Toolbar.Button.addNode, new ActionListener() { + public void actionPerformed(ActionEvent e) { + treeViewer.createNode(); + } + }); + toolbar.addActionListener(Toolbar.Button.deleteNode, new ActionListener() { + public void actionPerformed(ActionEvent e) { + treeViewer.deleteNode(); } }); - - toolbar.addActionListener(Toolbar.Button.addNode, - new AddNodeAction(this, treeViewer, zooInspectorManager)); - toolbar.addActionListener(Toolbar.Button.deleteNode, - new DeleteNodeAction(this, treeViewer, zooInspectorManager)); - toolbar.addActionListener(Toolbar.Button.nodeViewers, new ActionListener() { - public void actionPerformed(ActionEvent e) { ZooInspectorNodeViewersDialog nvd = new ZooInspectorNodeViewersDialog( JOptionPane.getRootFrame(), nodeViewers, listeners, @@ -127,6 +130,7 @@ public void actionPerformed(ActionEvent e) { } }); JScrollPane treeScroller = new JScrollPane(treeViewer); + treeScroller.getVerticalScrollBar().setUnitIncrement(TREE_SCROLL_UNIT_INCREMENT); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeScroller, nodeViewersPanel); splitPane.setResizeWeight(0.25); @@ -152,20 +156,14 @@ protected Boolean doInBackground() throws Exception { protected void done() { try { if (get()) { - treeViewer.refreshView(); + treeViewer.initialize(); toolbar.toggleButtons(true); } else { JOptionPane.showMessageDialog(ZooInspectorPanel.this, "Unable to connect to zookeeper", "Error", JOptionPane.ERROR_MESSAGE); } - } catch (InterruptedException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while connecting to ZooKeeper server", - e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { LoggerFactory .getLogger() .error( @@ -178,9 +176,6 @@ protected void done() { worker.execute(); } - /** - * - */ public void disconnect() { disconnect(false); } @@ -202,16 +197,10 @@ protected Boolean doInBackground() throws Exception { protected void done() { try { if (get()) { - treeViewer.clearView(); + treeViewer.clear(); toolbar.toggleButtons(false); } - } catch (InterruptedException e) { - LoggerFactory - .getLogger() - .error( - "Error occurred while disconnecting from ZooKeeper server", - e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { LoggerFactory .getLogger() .error( diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeView.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeView.java new file mode 100644 index 00000000000..86422a3009e --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeView.java @@ -0,0 +1,642 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.inspector.gui; + +import com.nitido.utils.toaster.Toaster; +import org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener; +import org.apache.zookeeper.inspector.logger.LoggerFactory; +import org.apache.zookeeper.inspector.manager.NodeListener; +import org.apache.zookeeper.inspector.manager.Pair; +import org.apache.zookeeper.inspector.manager.ZooInspectorManager; + +import javax.swing.ImageIcon; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class ZooInspectorTreeView extends JPanel { + private static final String PATH_SEPARATOR = "/"; + private static final String ROOT_PATH = PATH_SEPARATOR; + + private final JTree tree; + private final ZooInspectorTreeModel treeModel; + + private final JPopupMenu rightClickMenu; + private final JMenuItem createChildNodeMenuItem; + private final JMenuItem deleteNodeMenuItem; + private final JMenuItem refreshNodeMenuItem; + private final JMenuItem addWatchMenuItem; + private final JMenuItem removeWatchMenuItem; + + private final Toaster toasterManager; + private final ImageIcon toasterIcon; + + private final List nodeSelectionListeners = new LinkedList<>(); + + public ZooInspectorTreeView(final ZooInspectorManager manager, IconResource iconResource) { + this.toasterManager = new Toaster(); + this.toasterManager.setBorderColor(Color.BLACK); + this.toasterManager.setMessageColor(Color.BLACK); + this.toasterManager.setToasterColor(Color.WHITE); + this.toasterIcon = iconResource.get(IconResource.ICON_INFORMATION, ""); + + // Set up tree to display all ZNodes + this.treeModel = new ZooInspectorTreeModel(manager); + this.tree = new JTree(); + this.tree.setEditable(false); + this.tree.setFocusable(true); + this.tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + this.tree.setModel(this.treeModel); + this.tree.setCellRenderer(new ZooInspectorTreeCellRenderer(iconResource)); + + // Set up right click menu for individual ZNodes + this.rightClickMenu = new JPopupMenu(); + this.createChildNodeMenuItem = new JMenuItem("Create Child Node"); + this.deleteNodeMenuItem = new JMenuItem("Delete Node"); + this.refreshNodeMenuItem = new JMenuItem("Refresh Node"); + this.addWatchMenuItem = new JMenuItem("Add Watch"); + this.removeWatchMenuItem = new JMenuItem("Remove Watch"); + + // Add various event listeners (all implemented as inner classes on this class) + TreeEventHandler treeHandler = new TreeEventHandler(); + MouseEventHandler mouseHandler = new MouseEventHandler(); + KeyEventHandler keyHandler = new KeyEventHandler(); + ActionEventHandler actionHandler = new ActionEventHandler(); + + this.tree.addKeyListener(keyHandler); + this.tree.addTreeExpansionListener(treeHandler); + this.tree.getSelectionModel().addTreeSelectionListener(treeHandler); + this.tree.addMouseListener(mouseHandler); + this.rightClickMenu.add(this.createChildNodeMenuItem).addActionListener(actionHandler); + this.rightClickMenu.add(this.deleteNodeMenuItem).addActionListener(actionHandler); + this.rightClickMenu.add(this.refreshNodeMenuItem).addActionListener(actionHandler); + this.rightClickMenu.add(this.addWatchMenuItem).addActionListener(actionHandler); + this.rightClickMenu.add(this.removeWatchMenuItem).addActionListener(actionHandler); + + setLayout(new BorderLayout()); + add(this.tree, BorderLayout.CENTER); + } + + public void addNodeSelectionListener(NodeSelectionListener l) { + if (!this.nodeSelectionListeners.contains(l)) { + this.nodeSelectionListeners.add(l); + } + } + + @SuppressWarnings("unused") + public void removeNodeSelectionListener(NodeSelectionListener l) { + this.nodeSelectionListeners.remove(l); + } + + ///////////////////////////////// EVENT HANDLERS ///////////////////////////////// + + private class NodeEventHandler implements NodeListener { + @Override + public void processEvent(String nodePath, String eventType, Map eventInfo) { + StringBuilder sb = new StringBuilder(256); + sb.append("Node: "); + sb.append(nodePath); + sb.append("\nEvent: "); + sb.append(eventType); + if (eventInfo != null) { + for (Map.Entry entry : eventInfo.entrySet()) { + sb.append("\n"); + sb.append(entry.getKey()); + sb.append(": "); + sb.append(entry.getValue()); + } + } + toasterManager.showToaster(toasterIcon, sb.toString()); + } + } + + public class TreeEventHandler implements TreeExpansionListener, TreeSelectionListener { + @Override + public void treeExpanded(TreeExpansionEvent event) { + ZooInspectorTreeNode expandingNode = (ZooInspectorTreeNode) event.getPath().getLastPathComponent(); + + // This whole chunk of code before the "refreshNode" call is to deal with the fact that when lazy-loading a + // node, we first give it a single "placeholder" empty child node to mark it as having children, but we don't + // actually load those children until the user decides to expand it. These "if" statements figure out if the + // node has a single placeholder child or not. + + if (expandingNode.isLeaf() || expandingNode.getChildCount() != 1) { + return; + } + + ZooInspectorTreeNode onlyChild = ((ZooInspectorTreeNode) expandingNode.getChildAt(0)); + if (!onlyChild.isPlaceholder()) { + return; + } + + treeModel.refreshNode(expandingNode); + } + + @Override + public void treeCollapsed(TreeExpansionEvent event) { + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + ZooInspectorTreeNode node = (ZooInspectorTreeNode) e.getPath().getLastPathComponent(); + String selectedPath = node.getPathString(); + for (NodeSelectionListener listener : nodeSelectionListeners) { + listener.nodePathSelected(selectedPath); + } + } + } + + private class KeyEventHandler extends KeyAdapter { + @Override + public void keyReleased(KeyEvent e) { + if (!tree.hasFocus()) { + return; + } + + switch (e.getKeyCode()) { + case KeyEvent.VK_D: + deleteNode(); + break; + case KeyEvent.VK_N: + createNode(); + break; + case KeyEvent.VK_R: + refreshNode(); + break; + default: + break; + } + } + } + + private class ActionEventHandler implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == createChildNodeMenuItem) { + createNode(); + } else if (e.getSource() == deleteNodeMenuItem) { + deleteNode(); + } else if (e.getSource() == refreshNodeMenuItem) { + refreshNode(); + } else if (e.getSource() == addWatchMenuItem) { + addWatch(); + } else if (e.getSource() == removeWatchMenuItem) { + removeWatch(); + } + } + } + + private class MouseEventHandler extends MouseAdapter { + + @Override + public void mouseClicked(MouseEvent e) { + ZooInspectorTreeNode selectedNode = getSelectedNode(); + if (selectedNode == null) { + //If there's no node currently selected, see if the mouse is on top of one and select it + int selectedRow = tree.getRowForLocation(e.getX(), e.getY()); + if (selectedRow == -1) { + return; + } + tree.setSelectionRow(selectedRow); + + TreePath selectedPath = tree.getPathForLocation(e.getX(), e.getY()); + tree.setSelectionPath(selectedPath); + } + + boolean shouldShowPopup = e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e); + if (shouldShowPopup) { + rightClickMenu.show(ZooInspectorTreeView.this, e.getX(), e.getY()); + } + } + } + + ///////////////////////////////// BUSINESS LOGIC ///////////////////////////////// + + /** + * Initialize the view by creating a completely new tree by removing and refreshing all nodes. + */ + public void initialize() { + this.treeModel.init(); + } + + /** + * Clear all the existing nodes from the tree view. + */ + public void clear() { + this.treeModel.clear(); + } + + /** + * Start the UI workflow for creating a new ZNode as a child of the selected node. + */ + public void createNode() { + ZooInspectorTreeNode parentNode = getSelectedNode(); + if (parentNode == null) { + return; + } + + final String newNodeName = JOptionPane.showInputDialog( + this, + "Please enter a name for the new node: ", + "Create Child Node", + JOptionPane.INFORMATION_MESSAGE); + + if (newNodeName == null || newNodeName.trim().isEmpty()) { + return; + } + + this.treeModel.createNode(parentNode, newNodeName); + } + + /** + * Start the UI workflow for deleting the selected node. + */ + public void deleteNode() { + ZooInspectorTreeNode nodeToDelete = getSelectedNode(); + if (nodeToDelete == null) { + return; + } + + int answer = JOptionPane.showConfirmDialog( + this, + "Are you sure you want to delete the selected node '" + nodeToDelete.getPathString() + "'?\n" + + "(This action cannot be reverted)", + "Confirm Delete", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE + ); + + if (answer != JOptionPane.YES_OPTION) { + return; + } + + this.treeModel.deleteNode(nodeToDelete); + } + + /** + * Refresh the selected node (i.e. deleting all children and re-fetch them from Zookeeper). + */ + public void refreshNode() { + ZooInspectorTreeNode nodeToRefresh = getSelectedNode(); + if (nodeToRefresh == null) { + return; + } + this.treeModel.refreshNode(nodeToRefresh); + } + + /** + * Add a Zookeeper watch to the selected node. + */ + public void addWatch() { + ZooInspectorTreeNode nodeToWatch = getSelectedNode(); + if (nodeToWatch == null) { + return; + } + this.treeModel.addWatch(nodeToWatch); + } + + /** + * Remove a Zookeeper watch from the selected node (has no effect if the node does not have an existing watch). + */ + public void removeWatch() { + ZooInspectorTreeNode nodeToUnwatch = getSelectedNode(); + if (nodeToUnwatch == null) { + return; + } + this.treeModel.removeWatch(nodeToUnwatch); + } + + /** + * @return The node object corresponding to the selected node or null if there is no node selected. + */ + private ZooInspectorTreeNode getSelectedNode() { + TreePath selected = this.tree.getSelectionPath(); + return selected != null ? ((ZooInspectorTreeNode) selected.getLastPathComponent()) : null; + } + + private void showWarnDialog(String message){ + JOptionPane.showMessageDialog(this, + message, "Error", + JOptionPane.ERROR_MESSAGE); + } + + ///////////////////////////////// BACKING DATA MODEL ///////////////////////////////// + + /** + * An implement of the backing TreeModel data for the JTree in the user interface. Controls what data is actually + * available to be rendered in the UI. + */ + private class ZooInspectorTreeModel extends DefaultTreeModel { + private final ZooInspectorManager manager; + + public ZooInspectorTreeModel(ZooInspectorManager manager) { + super(new ZooInspectorTreeNode(ROOT_PATH, ROOT_PATH, 0)); + this.manager = manager; + } + + /** + * Create a new ZNode in Zookeeper as a child of the given parent node. + * + * @param parentNode The parent node to create a new child ZNode underneath + * @param newNodeName The name of the new child ZNode to create + */ + public void createNode(ZooInspectorTreeNode parentNode, String newNodeName) { + SwingWorker worker = new SwingWorker() { + @Override + protected Boolean doInBackground() { + //runs on a background non-UI thread + return manager.createNode(parentNode.getPathString(), newNodeName); + } + + @Override + protected void done() { + //runs on the UI event thread + boolean success; + try { + success = get(); + } catch (Exception e) { + success = false; + LoggerFactory.getLogger().error("create fail for {} {}", parentNode, newNodeName, e); + showWarnDialog("create " + newNodeName + " in " + parentNode + " fail, exception is " + e.getMessage()); + } + + if (!success) { + showWarnDialog("create " + newNodeName + " in " + parentNode + " fail, see log for more detail"); + } + else { + //extra logic to find the correct spot alphabetically to insert the new node in the tree` + int i = 0; + for (; i < parentNode.getChildCount(); i++) { + ZooInspectorTreeNode existingChild = (ZooInspectorTreeNode) parentNode.getChildAt(i); + if (newNodeName.compareTo(existingChild.getName()) < 0) { + break; + } + } + insertNodeInto(new ZooInspectorTreeNode(newNodeName, parentNode, 0), parentNode, i); + parentNode.setNumDisplayChildren(parentNode.getNumDisplayChildren() + 1); + } + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + }; + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + worker.execute(); + } + + /** + * Delete the specified ZNode in Zookeeper. + * + * @param nodeToDelete The node to delete. + */ + public void deleteNode(ZooInspectorTreeNode nodeToDelete) { + SwingWorker worker = new SwingWorker() { + @Override + protected Boolean doInBackground() { + //runs on a background non-UI thread + return manager.deleteNode(nodeToDelete.getPathString()); + } + + @Override + protected void done() { + //runs on the UI event thread + ZooInspectorTreeNode parent = (ZooInspectorTreeNode) nodeToDelete.getParent(); + parent.setNumDisplayChildren(parent.getNumDisplayChildren() - 1); + removeNodeFromParent(nodeToDelete); + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + }; + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + worker.execute(); + } + + /** + * Refresh the specified node in the UI. Refresh is equivalent to removing all existing children from the UI + * view and re-fetching them from Zookeeper. + * + * @param nodeToRefresh The node whose subtree should be refreshed. + */ + public void refreshNode(ZooInspectorTreeNode nodeToRefresh) { + SwingWorker worker = new SwingWorker() { + final LinkedList> childrenToAdd = new LinkedList<>(); + + @Override + protected Boolean doInBackground() { + //runs on a background non-UI thread + //Make all the network calls here (to get children and their child counts) and collect the children, + //but we can't add them to the UI until we're back on the event thread in done() + List children = manager.getChildren(nodeToRefresh.getPathString()); + if (children == null) { + return false; + } + nodeToRefresh.setNumDisplayChildren(children.size()); + + for (String childName : children) { + ZooInspectorTreeNode childNode = new ZooInspectorTreeNode(childName, nodeToRefresh, 0); + int numChildren = manager.getNumChildren(childNode.getPathString()); + childrenToAdd.add(new Pair<>(childName, numChildren)); + } + return true; + } + + @Override + protected void done() { + //runs on the UI event thread + nodeToRefresh.removeAllChildren(); + + for (Pair childPair : childrenToAdd) { + ZooInspectorTreeNode childNode = new ZooInspectorTreeNode(childPair.getKey(), + nodeToRefresh, + childPair.getValue()); + + if (childPair.getValue() > 0) { + // add a placeholder child so the UI renders this node like it has children + childNode.add(new ZooInspectorTreeNode()); + } + nodeToRefresh.add(childNode); + } + + reload(nodeToRefresh); + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + }; + getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + worker.execute(); + } + + /** + * Add a Zookeeper watch to the specified node (which will display updates in a Toast) + * + * @param nodeToWatch A reference to the node to place a watch on + */ + public void addWatch(ZooInspectorTreeNode nodeToWatch) { + this.manager.addWatchers(Collections.singletonList(nodeToWatch.getPathString()), new NodeEventHandler()); + } + + /** + * Remove a Zookeeper watch from the specified node (has no effect if this node has no existing watch) + * + * @param nodeToUnwatch A reference to the node to remove a watch from + */ + public void removeWatch(ZooInspectorTreeNode nodeToUnwatch) { + this.manager.removeWatchers(Collections.singletonList(nodeToUnwatch.getPathString())); + } + + /** + * Reinitialize the entire tree by refreshing the root node. + */ + public void init() { + refreshNode((ZooInspectorTreeNode) getRoot()); + } + + /** + * Clear the tree by removing all children from the root node. + */ + public void clear() { + ZooInspectorTreeNode root = (ZooInspectorTreeNode) getRoot(); + root.setNumDisplayChildren(0); + root.removeAllChildren(); + reload(); + } + } + + /** + * A representation of a single node in the TreeModel. Keeps track of a node's name, the number of children + * it has and its full Zookeeper path. + */ + private static class ZooInspectorTreeNode extends DefaultMutableTreeNode { + private final String name; + private final String pathString; + private int numDisplayChildren; + + public ZooInspectorTreeNode() { + this("", "", 0); + } + + public ZooInspectorTreeNode(String name, ZooInspectorTreeNode parent, int numDisplayChildren) { + this(name, (PATH_SEPARATOR.equals(parent.getName()) ? "" : parent.getPathString()) + PATH_SEPARATOR + name, numDisplayChildren); + } + + public ZooInspectorTreeNode(String name, String pathString, int numDisplayChildren) { + this.name = name; + this.pathString = pathString; + this.numDisplayChildren = numDisplayChildren; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + ZooInspectorTreeNode that = (ZooInspectorTreeNode) o; + return this.pathString.equals(that.pathString); + } + + @Override + public int hashCode() { + return this.pathString.hashCode(); + } + + public boolean isPlaceholder() { + //A placeholder node renders as "Loading..." on the UI and is identified by having an empty name and path + return this.name.isEmpty() && this.getPathString().isEmpty(); + } + + public String getName() { + return this.name; + } + + public String getPathString() { + return this.pathString; + } + + public int getNumDisplayChildren() { + return this.numDisplayChildren; + } + + public void setNumDisplayChildren(int numDisplayChildren) { + this.numDisplayChildren = numDisplayChildren; + } + + @Override + public String toString() { + //NOTE: Don't mess with this; it's actually used to construct the TreePath entries; if you want to + //change the name on the UI display, use the TreeCellRenderer below + return this.name; + } + } + + /** + * A class that controls how a given tree node is rendered in the tree on the UI (this is what's responsible for + * drawing "Loading..." for a placeholder node or render the # of children for a node). + */ + private static class ZooInspectorTreeCellRenderer extends DefaultTreeCellRenderer { + public ZooInspectorTreeCellRenderer(IconResource iconResource) { + setLeafIcon(iconResource.get(IconResource.ICON_TREE_LEAF, "")); + setOpenIcon(iconResource.get(IconResource.ICON_TREE_OPEN, "")); + setClosedIcon(iconResource.get(IconResource.ICON_TREE_CLOSE, "")); + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, + boolean leaf, int row, boolean hasFocus) { + ZooInspectorTreeNode node = (ZooInspectorTreeNode) value; + String text = node.getName(); + + if (node.isPlaceholder()) { + text = "Loading..."; + } + + if (node.getNumDisplayChildren() > 0) { + text += " (" + node.getNumDisplayChildren() + ")"; + } + + return super.getTreeCellRendererComponent(tree, text, sel, expanded, leaf, row, hasFocus); + } + } +} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java deleted file mode 100644 index e08f2d3b753..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeViewer.java +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.ImageIcon; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JComponent; -import javax.swing.JTree; -import javax.swing.SwingWorker; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import org.apache.zookeeper.inspector.gui.actions.AddNodeAction; -import org.apache.zookeeper.inspector.gui.actions.DeleteNodeAction; -import org.apache.zookeeper.inspector.manager.NodeListener; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -import com.nitido.utils.toaster.Toaster; -import static javax.swing.KeyStroke.getKeyStroke; - -/** - * A {@link JPanel} for showing the tree view of all the nodes in the zookeeper - * instance - */ -public class ZooInspectorTreeViewer extends JPanel implements NodeListener { - private final ZooInspectorManager zooInspectorManager; - private final JTree tree; - private final Toaster toasterManager; - private final ImageIcon toasterIcon; - - /** - * @param zooInspectorManager - * - the {@link ZooInspectorManager} for the application - * @param listener - * - the {@link TreeSelectionListener} to listen for changes in - * the selected node on the node tree - */ - public ZooInspectorTreeViewer( - final ZooInspectorManager zooInspectorManager, - TreeSelectionListener listener, IconResource iconResource) { - - this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) - .put(getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK), "deleteNode"); - - this.getActionMap().put("deleteNode", - new DeleteNodeAction(this, this, zooInspectorManager)); - - this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) - .put(getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK), "addNode"); - - this.getActionMap().put("addNode", - new AddNodeAction(this, this, zooInspectorManager)); - - this.zooInspectorManager = zooInspectorManager; - this.setLayout(new BorderLayout()); - final JPopupMenu popupMenu = new JPopupMenu(); - - final JMenuItem addNode = new JMenuItem("Add Node"); - addNode.addActionListener(new AddNodeAction(this, this, zooInspectorManager)); - - final JMenuItem deleteNode = new JMenuItem("Delete Node"); - deleteNode.addActionListener(new DeleteNodeAction(this, this, zooInspectorManager)); - - final JMenuItem addNotify = new JMenuItem("Add Change Notification"); - this.toasterManager = new Toaster(); - this.toasterManager.setBorderColor(Color.BLACK); - this.toasterManager.setMessageColor(Color.BLACK); - this.toasterManager.setToasterColor(Color.WHITE); - toasterIcon = iconResource.get(IconResource.ICON_INFORMATION,""); - addNotify.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - List selectedNodes = getSelectedNodes(); - zooInspectorManager.addWatchers(selectedNodes, - ZooInspectorTreeViewer.this); - } - }); - final JMenuItem removeNotify = new JMenuItem( - "Remove Change Notification"); - removeNotify.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - List selectedNodes = getSelectedNodes(); - zooInspectorManager.removeWatchers(selectedNodes); - } - }); - tree = new JTree(new DefaultMutableTreeNode()); - tree.setCellRenderer(new ZooInspectorTreeCellRenderer(iconResource)); - tree.setEditable(false); - tree.getSelectionModel().addTreeSelectionListener(listener); - tree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3) { - // TODO only show add if a selected node isn't being - // watched, and only show remove if a selected node is being - // watched - popupMenu.removeAll(); - popupMenu.add(addNode); - popupMenu.add(deleteNode); - popupMenu.add(addNotify); - popupMenu.add(removeNotify); - popupMenu.show(ZooInspectorTreeViewer.this, e.getX(), e - .getY()); - } - } - }); - this.add(tree, BorderLayout.CENTER); - } - - /** - * Refresh the tree view - */ - public void refreshView() { - final Set expandedNodes = new LinkedHashSet(); - int rowCount = tree.getRowCount(); - for (int i = 0; i < rowCount; i++) { - TreePath path = tree.getPathForRow(i); - if (tree.isExpanded(path)) { - expandedNodes.add(path); - } - } - final TreePath[] selectedNodes = tree.getSelectionPaths(); - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - tree.setModel(new DefaultTreeModel(new ZooInspectorTreeNode( - "/", null))); - return true; - } - - @Override - protected void done() { - for (TreePath path : expandedNodes) { - tree.expandPath(path); - } - tree.getSelectionModel().setSelectionPaths(selectedNodes); - } - }; - worker.execute(); - } - - /** - * clear the tree view of all nodes - */ - public void clearView() { - tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode())); - } - - private static class ZooInspectorTreeCellRenderer extends - DefaultTreeCellRenderer { - public ZooInspectorTreeCellRenderer(IconResource iconResource) { - setLeafIcon(iconResource.get(IconResource.ICON_TREE_LEAF,"")); - setOpenIcon(iconResource.get(IconResource.ICON_TREE_OPEN,"")); - setClosedIcon(iconResource.get(IconResource.ICON_TREE_CLOSE,"")); - } - } - - private class ZooInspectorTreeNode implements TreeNode { - private final String nodePath; - private final String nodeName; - private final ZooInspectorTreeNode parent; - - public ZooInspectorTreeNode(String nodePath, ZooInspectorTreeNode parent) { - this.parent = parent; - this.nodePath = nodePath; - int index = nodePath.lastIndexOf("/"); - if (index == -1) { - throw new IllegalArgumentException("Invalid node path" - + nodePath); - } - this.nodeName = nodePath.substring(index + 1); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#children() - */ - public Enumeration children() { - List children = zooInspectorManager - .getChildren(this.nodePath); - Collections.sort(children); - List returnChildren = new ArrayList(); - for (String child : children) { - returnChildren.add(new ZooInspectorTreeNode((this.nodePath - .equals("/") ? "" : this.nodePath) - + "/" + child, this)); - } - return Collections.enumeration(returnChildren); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getAllowsChildren() - */ - public boolean getAllowsChildren() { - return zooInspectorManager.isAllowsChildren(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getChildAt(int) - */ - public TreeNode getChildAt(int childIndex) { - String child = zooInspectorManager.getNodeChild(this.nodePath, - childIndex); - if (child != null) { - return new ZooInspectorTreeNode((this.nodePath.equals("/") ? "" - : this.nodePath) - + "/" + child, this); - } - return null; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getChildCount() - */ - public int getChildCount() { - return zooInspectorManager.getNumChildren(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getIndex(javax.swing.tree.TreeNode) - */ - public int getIndex(TreeNode node) { - return zooInspectorManager.getNodeIndex(this.nodePath); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#getParent() - */ - public TreeNode getParent() { - return this.parent; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.tree.TreeNode#isLeaf() - */ - public boolean isLeaf() { - return !zooInspectorManager.hasChildren(this.nodePath); - } - - @Override - public String toString() { - return this.nodeName; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result - + ((nodePath == null) ? 0 : nodePath.hashCode()); - result = prime * result - + ((parent == null) ? 0 : parent.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; - ZooInspectorTreeNode other = (ZooInspectorTreeNode) obj; - if (!getOuterType().equals(other.getOuterType())) - return false; - if (nodePath == null) { - if (other.nodePath != null) - return false; - } else if (!nodePath.equals(other.nodePath)) - return false; - if (parent == null) { - if (other.parent != null) - return false; - } else if (!parent.equals(other.parent)) - return false; - return true; - } - - private ZooInspectorTreeViewer getOuterType() { - return ZooInspectorTreeViewer.this; - } - - } - - /** - * @return {@link List} of the currently selected nodes - */ - public List getSelectedNodes() { - TreePath[] paths = tree.getSelectionPaths(); - List selectedNodes = new ArrayList(); - if (paths != null) { - for (TreePath path : paths) { - StringBuilder sb = new StringBuilder(); - Object[] pathArray = path.getPath(); - for (Object o : pathArray) { - String nodeName = o.toString(); - if (nodeName.length() > 0) { - sb.append("/"); - sb.append(o.toString()); - } - } - selectedNodes.add(sb.toString()); - } - } - return selectedNodes; - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.zookeeper.inspector.manager.NodeListener#processEvent(java - * .lang.String, java.lang.String, java.util.Map) - */ - public void processEvent(String nodePath, String eventType, - Map eventInfo) { - StringBuilder sb = new StringBuilder(); - sb.append("Node: "); - sb.append(nodePath); - sb.append("\nEvent: "); - sb.append(eventType); - if (eventInfo != null) { - for (Map.Entry entry : eventInfo.entrySet()) { - sb.append("\n"); - sb.append(entry.getKey()); - sb.append(": "); - sb.append(entry.getValue()); - } - } - this.toasterManager.showToaster(toasterIcon, sb.toString()); - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/AddNodeAction.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/AddNodeAction.java deleted file mode 100644 index 30916112a2f..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/AddNodeAction.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.actions; - -import org.apache.zookeeper.inspector.gui.ZooInspectorPanel; -import org.apache.zookeeper.inspector.gui.ZooInspectorTreeViewer; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.awt.event.KeyEvent; - -public class AddNodeAction extends AbstractAction { - - private JPanel panel; - private ZooInspectorTreeViewer treeViewer; - private ZooInspectorManager zooInspectorManager; - - public AddNodeAction(JPanel parentPanel, - ZooInspectorTreeViewer treeViewer, - ZooInspectorManager zooInspectorManager) { - this.panel = parentPanel; - this.treeViewer = treeViewer; - this.zooInspectorManager = zooInspectorManager; - } - - public void actionPerformed(ActionEvent e) { - final List selectedNodes = treeViewer - .getSelectedNodes(); - if (selectedNodes.size() == 1) { - final String nodeName = JOptionPane.showInputDialog( - panel, - "Please Enter a name for the new node", - "Create Node", JOptionPane.INFORMATION_MESSAGE); - if (nodeName != null && nodeName.length() > 0) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - return zooInspectorManager - .createNode(selectedNodes.get(0), - nodeName); - } - - @Override - protected void done() { - treeViewer.refreshView(); - } - }; - worker.execute(); - } - } else { - JOptionPane.showMessageDialog(panel, - "Please select 1 parent node for the new node."); - } - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/DeleteNodeAction.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/DeleteNodeAction.java deleted file mode 100644 index 90016701e68..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/actions/DeleteNodeAction.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zookeeper.inspector.gui.actions; - -import org.apache.zookeeper.inspector.gui.ZooInspectorTreeViewer; -import org.apache.zookeeper.inspector.manager.ZooInspectorManager; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.awt.event.KeyEvent; - -public class DeleteNodeAction extends AbstractAction { - - private JPanel parentPanel; - private ZooInspectorTreeViewer treeViewer; - private ZooInspectorManager zooInspectorManager; - - public DeleteNodeAction(JPanel parentPanel, - ZooInspectorTreeViewer treeViewer, - ZooInspectorManager zooInspectorManager) { - this.parentPanel = parentPanel; - this.treeViewer = treeViewer; - this.zooInspectorManager = zooInspectorManager; - } - - - public void actionPerformed(ActionEvent e) { - final List selectedNodes = treeViewer - .getSelectedNodes(); - if (selectedNodes.size() == 0) { - JOptionPane.showMessageDialog(parentPanel, - "Please select at least 1 node to be deleted"); - } else { - int answer = JOptionPane.showConfirmDialog( - parentPanel, - "Are you sure you want to delete the selected nodes?" - + "(This action cannot be reverted)", - "Confirm Delete", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE - ); - if (answer == JOptionPane.YES_OPTION) { - SwingWorker worker = new SwingWorker() { - - @Override - protected Boolean doInBackground() throws Exception { - for (String nodePath : selectedNodes) { - zooInspectorManager - .deleteNode(nodePath); - } - return true; - } - - @Override - protected void done() { - treeViewer.refreshView(); - } - }; - worker.execute(); - } - } - } -} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeSelectionListener.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeSelectionListener.java new file mode 100644 index 00000000000..8a3a8dcb735 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeSelectionListener.java @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.inspector.gui.nodeviewer; + +/** + * An interface to be implemented by any component that needs notification when a new element + * is selected in the UI JTree representing the set of available ZNodes. + */ +public interface NodeSelectionListener { + void nodePathSelected(String nodePath); +} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java index 6061e17bf8c..32e269ca090 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java @@ -60,7 +60,7 @@ public NodeViewerData() { public void actionPerformed(ActionEvent e) { if (selectedNode != null) { if (JOptionPane.showConfirmDialog(NodeViewerData.this, - "Are you sure you want to save this node?" + "Are you sure you want to save the node '" + selectedNode + "'?\n" + " (this action cannot be reverted)", "Confirm Save", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { @@ -110,11 +110,7 @@ protected void done() { String data = ""; try { data = get(); - } catch (InterruptedException e) { - LoggerFactory.getLogger().error( - "Error retrieving data for node: " - + NodeViewerData.this.selectedNode, e); - } catch (ExecutionException e) { + } catch (InterruptedException | ExecutionException e) { LoggerFactory.getLogger().error( "Error retrieving data for node: " + NodeViewerData.this.selectedNode, e); diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java index 5c2df8d88c5..4523740daf5 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java @@ -118,7 +118,7 @@ protected void done() { for (Map.Entry entry : data.entrySet()) { rowPos = 2 * i + 1; JLabel label = new JLabel(entry.getKey()); - JTextField text = new JTextField(entry.getValue()); + JTextField text = new JTextField(formatValByKeyType(entry.getKey(),entry.getValue())); text.setEditable(false); GridBagConstraints c1 = new GridBagConstraints(); c1.gridx = 0; @@ -164,6 +164,21 @@ protected void done() { NodeViewerMetaData.this.metaDataPanel.revalidate(); NodeViewerMetaData.this.metaDataPanel.repaint(); } + + private String formatValByKeyType(String key, String value) { + if(key==null) return value; + String formattedVal=value; + switch (key){ + case "Ephemeral Owner": + try{ + formattedVal = String.format("0x%x", Long.parseLong(value)); + }catch (NumberFormatException e){ + LoggerFactory.getLogger().warn("parse {}'s value {} to hex fail",key,value,e); + } + break; + } + return formattedVal; + } }; worker.execute(); } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java index e4fae41698b..c775d643e4c 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java @@ -18,19 +18,19 @@ package org.apache.zookeeper.inspector.logger; /** - * Provides a {@link Logger} for use across the entire application + * Provides a {@link org.slf4j.Logger} for use across the entire application * */ public class LoggerFactory { - private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ - /** - * @return {@link Logger} for ZooInspector - */ - public static org.slf4j.Logger getLogger() - { - return logger; - } + /** + * @return {@link org.slf4j.Logger} for ZooInspector + */ + public static org.slf4j.Logger getLogger() + { + return logger; + } } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodesCache.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodesCache.java index 45c5a275272..da30596a6f8 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodesCache.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodesCache.java @@ -26,14 +26,17 @@ import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +/** + * A local cache of ZNodes in front of the Zookeeper server(s) to help cut down on network calls. This class will + * return results from the cache first, if available, and then fetch results remotely from Zookeeper if not. + */ public class NodesCache { public static final int CACHE_SIZE = 40000; - public static final int EXPIRATION_TIME = 100; + public static final int ENTRY_EXPIRATION_TIME_MILLIS = 10000; private final LoadingCache> nodes; @@ -43,18 +46,26 @@ public NodesCache(ZooKeeper zooKeeper) { this.zooKeeper = zooKeeper; this.nodes = CacheBuilder.newBuilder() .maximumSize(CACHE_SIZE) - .expireAfterWrite(EXPIRATION_TIME, TimeUnit.MILLISECONDS) - .build( - new CacheLoader>() { - @Override - public List load(String nodePath) throws Exception { - return getChildren(nodePath); - } - } + .expireAfterWrite(ENTRY_EXPIRATION_TIME_MILLIS, TimeUnit.MILLISECONDS) + .build(new CacheLoader>() { + @Override + public List load(String nodePath) { + return getChildrenRemote(nodePath); + } + } ); } - public List getChildren(String nodePath) { + /** + * Whereas getChildren hits the cache first, getChildrenRemote goes straight (over the network) to Zookeeper to + * get the answer. This is the function used by the cache to insert entries that are requested, but don't exist + * in the cache yet. + * + * @param nodePath The full path to the parent whose children are to be fetched. + * @return The list of children of the given node as a list of full ZNode path strings or null if an + * error occurred. + */ + private List getChildrenRemote(String nodePath) { try { Stat s = zooKeeper.exists(nodePath, false); if (s != null) { @@ -64,23 +75,25 @@ public List getChildren(String nodePath) { } } catch (Exception e) { LoggerFactory.getLogger().error( - "Error occurred retrieving child of node: " + nodePath, e + "Error occurred retrieving children of node: " + nodePath, e ); } return null; } - public String getNodeChild(String nodePath, int index) { - List childNodes = null; + /** + * Fetch the children of the given node from the cache. + * + * @param nodePath The full path to the parent whose children are to be fetched. + * @return The list of children of the given node as a list of full ZNode path strings or null if an + * error occurred. + */ + public List getChildren(String nodePath) { try { - childNodes = nodes.get(nodePath); - return childNodes.get(index); - } catch (ExecutionException e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving child " + index + "of node: " + nodePath, e - ); + return nodes.get(nodePath); + } catch (Exception e) { + LoggerFactory.getLogger().error("Error occurred retrieving children of node: " + nodePath, e); } return null; } - } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java index 74c3cb20e88..9e89adf3499 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java @@ -55,7 +55,7 @@ public interface ZooInspectorManager extends ZooInspectorNodeManager, * {@link JComboBox} with the first selected as default. *
  • a {@link Map} of property keys to the label to show on the UI *
  • - *
      + *
    * */ public Pair>, Map> getConnectionPropertiesTemplate(); @@ -118,13 +118,13 @@ public void setDefaultNodeViewerConfiguration( /** * @param connectionProps * - the connection properties last used to connect to the - * zookeeeper instance + * zookeeper instance */ public void setLastConnectionProps(Properties connectionProps); /** * @return last connection Properties - the connection properties last used - * to connect to the zookeeeper instance + * to connect to the zookeeper instance */ public Properties getLastConnectionProps(); diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java index 2c5790746ad..0897bac9eed 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java @@ -20,9 +20,12 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -91,16 +94,25 @@ public class ZooInspectorManagerImpl implements ZooInspectorManager { */ public static final String AUTH_DATA_KEY = "authData"; + private static final String DEFAULT_ENCRYPTION_MANAGER = + BasicDataEncryptionManager.class.getName(); + private static final int DEFAULT_TIMEOUT = 5000; + private static final String DEFAULT_HOSTS = "localhost:2181"; + private static final String DEFAULT_AUTH_SCHEME = ""; + private static final String DEFAULT_AUTH_VALUE = ""; private static final File defaultNodeViewersFile = new File( "./src/main/resources/defaultNodeViewers.cfg"); private static final File defaultConnectionFile = new File( "./src/main/resources/defaultConnectionSettings.cfg"); - private DataEncryptionManager encryptionManager; + //package visible for test + DataEncryptionManager encryptionManager; private String connectString; private int sessionTimeout; - private ZooKeeper zooKeeper; + + //package visible for test + ZooKeeper zooKeeper; private final Map watchers = new HashMap(); protected boolean connected = true; private Properties lastConnectionProps; @@ -181,7 +193,7 @@ public void process(WatchedEvent event) { e.printStackTrace(); } if (!connected){ - disconnect(); + disconnect(); } else { this.nodesCache = new NodesCache(zooKeeper); } @@ -214,7 +226,7 @@ public boolean disconnect() { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getChildren(java.lang.String) */ public List getChildren(String nodePath) { @@ -251,46 +263,6 @@ public String getData(String nodePath) { return null; } - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNodeChild(java.lang.String, int) - */ - public String getNodeChild(String nodePath, int childIndex) { - if (connected) { - return this.nodesCache.getNodeChild(nodePath, childIndex); - } - return null; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getNodeIndex(java.lang.String) - */ - public int getNodeIndex(String nodePath) { - if (connected) { - int index = nodePath.lastIndexOf("/"); - if (index == -1 - || (!nodePath.equals("/") && nodePath.charAt(nodePath - .length() - 1) == '/')) { - throw new IllegalArgumentException("Invalid node path: " - + nodePath); - } - String parentPath = nodePath.substring(0, index); - String child = nodePath.substring(index + 1); - if (parentPath != null && parentPath.length() > 0) { - List children = this.nodesCache.getChildren(parentPath); - if (children != null) { - return children.indexOf(child); - } - } - } - return -1; - } - /* * (non-Javadoc) * @@ -351,11 +323,7 @@ public List> getACLs(String nodePath) { returnACLs.add(aclMap); } } - } catch (InterruptedException e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving ACLs of node: " + nodePath, - e); - } catch (KeeperException e) { + } catch (InterruptedException | KeeperException e) { LoggerFactory.getLogger().error( "Error occurred retrieving ACLs of node: " + nodePath, e); @@ -367,7 +335,7 @@ public List> getACLs(String nodePath) { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getNodeMeta(java.lang.String) */ public Map getNodeMeta(String nodePath) { @@ -406,81 +374,11 @@ public Map getNodeMeta(String nodePath) { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getNumChildren(java.lang.String) */ public int getNumChildren(String nodePath) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return s.getNumChildren(); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred getting the number of children of node: " - + nodePath, e); - } - } - return -1; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * hasChildren(java.lang.String) - */ - public boolean hasChildren(String nodePath) { - return getNumChildren(nodePath) > 0; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * isAllowsChildren(java.lang.String) - */ - public boolean isAllowsChildren(String nodePath) { - if (connected) { - try { - Stat s = zooKeeper.exists(nodePath, false); - if (s != null) { - return s.getEphemeralOwner() == 0; - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred determining whether node is allowed children: " - + nodePath, e); - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# - * getSessionMeta() - */ - public Map getSessionMeta() { - Map sessionMeta = new LinkedHashMap(); - try { - if (zooKeeper != null) { - - sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper - .getSessionId())); - sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper - .getState().toString())); - sessionMeta.put(CONNECT_STRING, this.connectString); - sessionMeta.put(SESSION_TIMEOUT, String - .valueOf(this.sessionTimeout)); - } - } catch (Exception e) { - LoggerFactory.getLogger().error( - "Error occurred retrieving session meta data.", e); - } - return sessionMeta; + return connected ? getChildren(nodePath).size() : -1; } /* @@ -495,7 +393,14 @@ public boolean createNode(String parent, String nodeName) { try { String[] nodeElements = nodeName.split("/"); for (String nodeElement : nodeElements) { - String node = parent + "/" + nodeElement; + String node; + //for case parent is "/" and maybe other cases + if (parent.endsWith("/")) { + node = parent + nodeElement; + } + else { + node = parent + "/" + nodeElement; + } Stat s = zooKeeper.exists(node, false); if (s == null) { zooKeeper.create(node, this.encryptionManager @@ -567,7 +472,7 @@ public boolean setData(String nodePath, String data) { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * getConnectionPropertiesTemplate() */ public Pair>, Map> getConnectionPropertiesTemplate() { @@ -687,7 +592,7 @@ public void process(WatchedEvent event) { } } catch (Exception e) { LoggerFactory.getLogger().error( - "Error occurred re-adding node watcherfor node " + "Error occurred re-adding node watcher for node " + nodePath, e); } nodeListener.processEvent(event.getPath(), event.getType() @@ -695,9 +600,6 @@ public void process(WatchedEvent event) { } } - /** - * - */ public void stop() { this.closed = true; } @@ -707,67 +609,59 @@ public void stop() { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * loadNodeViewersFile(java.io.File) */ public List loadNodeViewersFile(File selectedFile) throws IOException { List result = new ArrayList(); - if (defaultNodeViewersFile.exists()) { - FileReader reader = new FileReader(selectedFile); - try { - BufferedReader buff = new BufferedReader(reader); - try { - while (buff.ready()) { - String line = buff.readLine(); - if (line != null && line.length() > 0 && !line.startsWith("#")) { - result.add(line); - } + + try(BufferedReader reader = getReaderForFile(selectedFile)) { + if(reader == null) { + return result; + } + + String line = ""; + while (line != null) { + line = reader.readLine(); + if(line != null) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) { + result.add(line); } - } finally { - buff.close(); } - } finally { - reader.close(); } } + return result; } private void loadDefaultConnectionFile() throws IOException { - if (defaultConnectionFile.exists()) { - Properties props = new Properties(); + Properties props = new Properties(); - FileReader reader = new FileReader(defaultConnectionFile); - try { + try(BufferedReader reader = getReaderForFile(defaultConnectionFile)) { + //If reader is null, it's OK. Default values will get set below. + if(reader != null) { props.load(reader); - } finally { - reader.close(); } - defaultEncryptionManager = props - .getProperty(DATA_ENCRYPTION_MANAGER) == null ? "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager" - : props.getProperty(DATA_ENCRYPTION_MANAGER); - defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? "5000" - : props.getProperty(SESSION_TIMEOUT); - defaultHosts = props.getProperty(CONNECT_STRING) == null ? "localhost:2181" - : props.getProperty(CONNECT_STRING); - defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? "" - : props.getProperty(AUTH_SCHEME_KEY); - defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? "" - : props.getProperty(AUTH_DATA_KEY); - } else { - defaultEncryptionManager = "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"; - defaultTimeout = "5000"; - defaultHosts = "localhost:2181"; - defaultAuthScheme = ""; - defaultAuthValue = ""; } + + defaultEncryptionManager = props.getProperty(DATA_ENCRYPTION_MANAGER) == null ? + DEFAULT_ENCRYPTION_MANAGER : props.getProperty(DATA_ENCRYPTION_MANAGER); + defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? + Integer.toString(DEFAULT_TIMEOUT) : props.getProperty(SESSION_TIMEOUT); + defaultHosts = props.getProperty(CONNECT_STRING) == null ? + DEFAULT_HOSTS : props.getProperty(CONNECT_STRING); + defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? + DEFAULT_AUTH_SCHEME : props.getProperty(AUTH_SCHEME_KEY); + defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? + DEFAULT_AUTH_VALUE : props.getProperty(AUTH_DATA_KEY); } /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * saveDefaultConnectionFile(java.util.Properties) */ public void saveDefaultConnectionFile(Properties props) throws IOException { @@ -797,7 +691,7 @@ public void saveDefaultConnectionFile(Properties props) throws IOException { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * saveNodeViewersFile(java.io.File, java.util.List) */ public void saveNodeViewersFile(File selectedFile, @@ -829,7 +723,7 @@ public void saveNodeViewersFile(File selectedFile, /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * setDefaultNodeViewerConfiguration(java.io.File, java.util.List) */ public void setDefaultNodeViewerConfiguration( @@ -856,7 +750,7 @@ public List getDefaultNodeViewerConfiguration() throws IOException { /* * (non-Javadoc) * - * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# + * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * getLastConnectionProps() */ public Properties getLastConnectionProps() { @@ -872,4 +766,26 @@ public Properties getLastConnectionProps() { public void setLastConnectionProps(Properties connectionProps) { this.lastConnectionProps = connectionProps; } + + private static BufferedReader getReaderForFile(File file) { + //check the filesystem first + if (file.exists()) { + try { + return new BufferedReader(new FileReader(file)); + } catch (FileNotFoundException e) { + return null; + } + } + + //fall back to checking the CLASSPATH with only the filename + //(for cases where the file exists in src/main/resources) + InputStream classpathStream = ZooInspectorManagerImpl.class.getClassLoader() + .getResourceAsStream(file.getName()); + if (classpathStream != null) { + return new BufferedReader(new InputStreamReader(classpathStream)); + } + + //couldn't find the file anywhere + return null; + } } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java index d9fdf5c4a08..69ce842a1cb 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java @@ -28,72 +28,36 @@ public interface ZooInspectorReadOnlyManager { /** * @param nodePath - * - the path to the node to delete + * - the path to the node whose data is to be retrieved * @return the data for the node */ public abstract String getData(String nodePath); /** * @param nodePath - * - the path to the node to delete + * - the path to the node whose metadata is to be retrieved * @return the metaData for the node */ public abstract Map getNodeMeta(String nodePath); /** * @param nodePath - * - the path to the node to delete + * - the path to the node whose ACLs are to be retrieved * @return the ACLs set on the node */ public abstract List> getACLs(String nodePath); - /** - * @return the metaData for the current session - */ - public abstract Map getSessionMeta(); - - /** - * @param nodePath - * - the path to the node to delete - * @return true if the node has children - */ - public abstract boolean hasChildren(String nodePath); - - /** - * @param nodePath - * - the path to the node to delete - * @return the index of the node within its siblings - */ - public abstract int getNodeIndex(String nodePath); - /** * @param nodePath - * - the path to the node to delete + * - the path to the node to parent node * @return the number of children of the node */ public abstract int getNumChildren(String nodePath); /** * @param nodePath - * - the path to the node to delete - * @param childIndex - * - the index to the node in the list of node children - * @return the path to the node for the child of the nodePath at childIndex - */ - public abstract String getNodeChild(String nodePath, int childIndex); - - /** - * @param nodePath - * - the path to the node to delete - * @return true if the node allows children nodes - */ - public abstract boolean isAllowsChildren(String nodePath); - - /** - * @param nodePath - * - the path to the node to delete + * - the path to the node whose children to retrieve * @return a {@link List} of the children of the node */ public abstract List getChildren(String nodePath); - } diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ZooKeeperRetry.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ZooKeeperRetry.java index ce959a1a0c2..dff8f24dcf2 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ZooKeeperRetry.java +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ZooKeeperRetry.java @@ -31,7 +31,7 @@ /** * A Class which extends {@link ZooKeeper} and will automatically retry calls to - * zookeeper if a {@link KeeperException.ConnectionLossException} occurs + * zookeeper if a {@link org.apache.zookeeper.KeeperException.ConnectionLossException} occurs. */ public class ZooKeeperRetry extends ZooKeeper { diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/about.html b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/about.html new file mode 100644 index 00000000000..17fb3dca20b --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/about.html @@ -0,0 +1,21 @@ + + + + +ZooInspector v0.1 + + +

    ZooInspector was developed by Colin Goodheart-Smithe and is +available under the Apache Software Licence v2.0.

    +

    The Icons used were sourced from the Eclipse project (http://www.eclipse.org) and licensed +under the Eclipse Public Licence v1.0. [http://www.eclipse.org/org/documents/epl-v10.php] +

    +

    ZooKeeper is available from http://zookeeper.apache.org/ +and is licensed under an Apache Software Licence v2.0

    +

    The ApacheSoftware Licence v2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0

    + + diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/categories/applications-system.png b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/categories/applications-system.png new file mode 100644 index 00000000000..d90ab661cbb Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/categories/applications-system.png differ diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/categories/applications-system.png b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/categories/applications-system.png new file mode 100644 index 00000000000..4decc893f84 Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/categories/applications-system.png differ diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/categories/applications-system.png b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/categories/applications-system.png new file mode 100644 index 00000000000..9c8833865b5 Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/categories/applications-system.png differ diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/categories/applications-system.png b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/categories/applications-system.png new file mode 100644 index 00000000000..565f406dd14 Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/categories/applications-system.png differ diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/log4j.properties b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/log4j.properties deleted file mode 100644 index db05af4d533..00000000000 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/log4j.properties +++ /dev/null @@ -1,9 +0,0 @@ -# ***** Set root logger level to INFO and it appender to stdout. -log4j.rootLogger=INFO,stdout - -# ***** stdout is set to be a ConsoleAppender. -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -# ***** stdout uses PatternLayout. -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -# ***** Pattern to output the caller's file name and line number. -log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n \ No newline at end of file diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/logback.xml b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/logback.xml new file mode 100644 index 00000000000..6f813039d1c --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + %5p [%t] \(%F:%L\) - %m%n + + + + + + diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/LoggerTest.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/LoggerTest.java new file mode 100644 index 00000000000..310e3320133 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/LoggerTest.java @@ -0,0 +1,48 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.inspector; + +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +public class LoggerTest { + Logger LOG = LoggerFactory.getLogger(LoggerTest.class); + String testMessage = "a test message"; + + @Test + public void testLogStdOutConfig() { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + PrintStream realStdOut = System.out; + System.setOut(new PrintStream(byteArrayOutputStream)); + LOG.info(testMessage); + System.setOut(realStdOut); + + //log to stdout for debug + LOG.info(testMessage); + String bufferMessage = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8); + LOG.info(bufferMessage); + + Assert.assertTrue(bufferMessage.contains(testMessage)); + } +} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImplTest.java b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImplTest.java new file mode 100644 index 00000000000..5057d543cba --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImplTest.java @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zookeeper.inspector.manager; + +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.common.PathUtils; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager; +import org.apache.zookeeper.retry.ZooKeeperRetry; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; + +public class ZooInspectorManagerImplTest { + + /** + * test create zookeeper node operation, + * no easy way to create a real zk server so use a mocked client that only validate path + * + * @throws IOException + * @throws KeeperException + * @throws InterruptedException + */ + @Test + public void testNodeCreateRoot() throws IOException, KeeperException, InterruptedException { + ZooKeeper mockedZk = getMockedZk(); + + ZooInspectorManagerImpl manager = getInspectorManagerImpl(mockedZk); + + boolean createSuccess = manager.createNode("/", "test"); + Assert.assertTrue(createSuccess); + } + + /** + * test create a normal child node + * + * @throws IOException + * @throws KeeperException + * @throws InterruptedException + */ + + @Test + public void testNodeCreateNormal() throws IOException, KeeperException, InterruptedException { + ZooKeeper mockedZk = getMockedZk(); + + ZooInspectorManagerImpl manager = getInspectorManagerImpl(mockedZk); + + boolean createSuccess = manager.createNode("/parent", "test"); + Assert.assertTrue(createSuccess); + } + + + /** + * create a mocked zk client only check path validate + * + * @return + * @throws KeeperException + * @throws InterruptedException + */ + private ZooKeeper getMockedZk() throws KeeperException, InterruptedException { + ZooKeeper mockZk = Mockito.mock(ZooKeeperRetry.class); + Mockito.when(mockZk.exists(Mockito.anyString(), Mockito.anyBoolean())).then((Answer) invocation -> { + String path = invocation.getArgument(0); + PathUtils.validatePath(path); + return null; + }); + Mockito.when(mockZk.create(Mockito.anyString(), Mockito.any(), Mockito.any(), + Mockito.any())).then(new Answer() { + @Override + public String answer(InvocationOnMock invocation) throws Throwable { + String path = invocation.getArgument(0); + PathUtils.validatePath(path); + return path; + } + }); + return mockZk; + } + + /** + * create a inspector manager instance from zk + * + * @param zooKeeper + * @return + * @throws IOException + */ + private ZooInspectorManagerImpl getInspectorManagerImpl(ZooKeeper zooKeeper) throws IOException { + ZooInspectorManagerImpl manager = new ZooInspectorManagerImpl(); + manager.zooKeeper = zooKeeper; + manager.connected = true; + manager.encryptionManager = new BasicDataEncryptionManager(); + return manager; + } +} diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector-dev.sh b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector-dev.sh index 9be0b0a238b..4b947bdfa45 100755 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector-dev.sh +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector-dev.sh @@ -1,18 +1,22 @@ -#!/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 +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -java -cp ../../build/contrib/ZooInspector/*:../../../build/*:../../../build/lib/*:lib org.apache.zookeeper.inspector.ZooInspector +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +export CLASSPATH="../../build/contrib/ZooInspector/*:../../../build/*:../../../build/lib/*:lib" +java org.apache.zookeeper.inspector.ZooInspector diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.cmd b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.cmd index 0b9dcf85098..579a27e865f 100644 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.cmd +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.cmd @@ -14,8 +14,13 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. -set CLASSPATH=lib\*;lib -for /F %%f in ('dir /b "%~dp0%\*.jar" 2^>nul') do ( - set CLASSPATH=%%f;%CLASSPATH% +@rem Get the path to the directory containing this script +SET SCRIPT_DIR=%~dp0 + +@rem Get the path to the uber jar for this tool +@rem (Requires "mvn install" or "mvn package" be run first) +set JAVA_LIB= +for /F %%f in ('dir /b "%SCRIPT_DIR%\target\zookeeper-contrib-zooinspector-*-jar-with-dependencies.jar" 2^>nul') do ( + set JAVA_LIB=%SCRIPT_DIR%target\%%f ) -java -cp "%CLASSPATH%" org.apache.zookeeper.inspector.ZooInspector +java -jar %JAVA_LIB% diff --git a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.sh b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.sh index 5e3ebb67d4d..a009d91b617 100755 --- a/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.sh +++ b/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.sh @@ -1,18 +1,26 @@ -#!/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 +#! /usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Get the path to the directory containing this script +SCRIPT_DIR=$(dirname "$0") -java -cp "zookeeper-dev-ZooInspector.jar:lib/*:lib" org.apache.zookeeper.inspector.ZooInspector +# Get the path to the uber jar for this tool +# (Requires "mvn install" or "mvn package" be run first) +java -jar "$SCRIPT_DIR"/target/zookeeper-contrib-zooinspector-*-jar-with-dependencies.jar diff --git a/zookeeper-docs/pom.xml b/zookeeper-docs/pom.xml index a46b880dbc6..9fd096092b5 100644 --- a/zookeeper-docs/pom.xml +++ b/zookeeper-docs/pom.xml @@ -23,13 +23,13 @@ org.apache.zookeeper parent - 3.7.0-SNAPSHOT - .. + 3.10.0-SNAPSHOT zookeeper-docs Apache ZooKeeper - Documentation Documentation + pom @@ -49,6 +49,7 @@ ${project.basedir}/src/main/resources/markdown/html/header.html ${project.basedir}/src/main/resources/markdown/html/footer.html images,skin + TABLES,FENCED_CODE_BLOCKS @@ -58,13 +59,6 @@ true - - org.apache.maven.plugins - maven-javadoc-plugin - - true - - diff --git a/zookeeper-docs/src/main/resources/markdown/html/header.html b/zookeeper-docs/src/main/resources/markdown/html/header.html index ef07e99b325..c308b2d8894 100644 --- a/zookeeper-docs/src/main/resources/markdown/html/header.html +++ b/zookeeper-docs/src/main/resources/markdown/html/header.html @@ -36,7 +36,7 @@ Wiki
  • - ZooKeeper 3.6 Documentation + ZooKeeper 3.8 Documentation
  • @@ -70,11 +70,14 @@