diff --git a/.github/scripts/check-google-java-format.sh b/.github/scripts/check-google-java-format.sh new file mode 100755 index 0000000000..8e12296a74 --- /dev/null +++ b/.github/scripts/check-google-java-format.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -u + +script_name="" + +case "$(uname -sr)" in + + Darwin*) + script_name="google-java-format_darwin-arm64" + ;; + + Linux*) + script_name="google-java-format_linux-x86-64" + ;; + *) + echo 'Unsupported OS' + exit 1 + ;; +esac + +JAVA_FILES=$(find . -name "*.java" -type f) + +invalid_files=0 + +echo "Following files are formatted incorrectly:"; +# TODO: remove '--skip-reflowing-long-strings' once https://github.com/google/google-java-format/issues/566 is fixed +for FILE in $JAVA_FILES; do + if [[ "$*" == *--fix* ]]; then + ./$script_name --skip-reflowing-long-strings --replace "$FILE" > /dev/null + else + ./$script_name --set-exit-if-changed --skip-reflowing-long-strings "$FILE" > /dev/null + fi + if [ $? -ne 0 ]; then + echo "$FILE" + ((invalid_files++)) + fi +done + +if [ "$invalid_files" -ne 0 ]; then + echo "Found $invalid_files incorrectly formatted files (listed above), run google-java-format to fix them."; + exit 1 +else + echo "All files are formatted correctly." +fi + diff --git a/.github/scripts/download_reports.sh b/.github/scripts/download_reports.sh deleted file mode 100644 index 79e93f818c..0000000000 --- a/.github/scripts/download_reports.sh +++ /dev/null @@ -1,41 +0,0 @@ -TMPDIR="test" -mkdir $TMPDIR - -REPO="allegro/hermes" -BUILDS_FILE="builds.json" -PAST_BUILDS="$TMPDIR/$BUILDS_FILE" - -UNIT_TEST_ARTIFACT="check-test-report" -E2E_REPORT_ARTIFACT="integrationTest-test-report" - -gh run list --repo $REPO --branch master --workflow ci --json "status,databaseId" --limit 20 >> $PAST_BUILDS - -cd $TMPDIR - -jq -c '.[]' "$BUILDS_FILE" | while read i; do - STATUS=$(echo $i | jq '.status') - if [[ "$STATUS" == 'completed' ]]; then - continue - fi - RUN_ID=$(echo $i | jq '.databaseId') - - echo "downloading results for run: $RUN_ID" - RUN_DIR=$RUN_ID - - mkdir $RUN_DIR - echo "creating dir $RUN_DIR" - cd $RUN_DIR - - mkdir $UNIT_TEST_ARTIFACT - cd $UNIT_TEST_ARTIFACT - gh run download --repo $REPO -n $UNIT_TEST_ARTIFACT $RUN_ID - echo "Downloaded unit test report" - cd .. - - mkdir $E2E_REPORT_ARTIFACT - cd $E2E_REPORT_ARTIFACT - gh run download --repo $REPO -n $E2E_REPORT_ARTIFACT $RUN_ID - echo "Downloaded integrationTest report" - - cd ../.. -done diff --git a/.github/scripts/reporter.py b/.github/scripts/reporter.py deleted file mode 100755 index 5e124b1471..0000000000 --- a/.github/scripts/reporter.py +++ /dev/null @@ -1,118 +0,0 @@ -import dataclasses -import os -import sys -from typing import List, Set, Dict -import xml.etree.ElementTree as ET - - -@dataclasses.dataclass -class TestResults: - failed: Set[str] - passed: Set[str] - run_id: str - test_cnt: int = 0 - skipped_cnt: int = 0 - failed_cnt: int = 0 - error_cnt: int = 0 - - -def get_test_files(dir: str) -> List[str]: - files = [ - os.path.join(dp, f) - for dp, dn, filenames in os.walk(dir) - for f in filenames - if os.path.splitext(f)[1] == '.xml' - and os.path.splitext(f)[0].startswith("TEST-") - ] - return files - - -def parse_test_file(file: str, run_id: str) -> TestResults: - root = ET.parse(file).getroot() - result = TestResults( - skipped_cnt=int(root.get("skipped")), - test_cnt=int(root.get("tests")), - failed_cnt=int(root.get("failures")), - error_cnt=int(root.get("errors")), - failed=set(), - passed=set(), - run_id=run_id - ) - name = root.get("name") - name = '.'.join(name.split('.')[4:]) # remove common prefix - - for testcase in root.findall("testcase"): - testname = testcase.get("name") - test_failed = testcase.findall("failure") - if test_failed: - result.failed.add(f"{name}#{testname}") - else: - result.passed.add(f"{name}#{testname}") - return result - - -def aggregate_results(test_dir: str, run_id: str) -> TestResults: - test_files = get_test_files(test_dir) - results = [] - for test_file in test_files: - result = parse_test_file(test_file, run_id) - results.append(result) - - agg = TestResults(set(), set(), run_id) - - for result in results: - agg.test_cnt += result.test_cnt - agg.skipped_cnt += result.skipped_cnt - agg.error_cnt += result.error_cnt - agg.failed_cnt += result.failed_cnt - for fail in result.failed: - agg.failed.add(fail) - for pas in result.passed: - agg.passed.add(pas) - return agg - - -def report(type: str, runs: List[TestResults]) -> str: - failed = {} - markdown = "" - for run in runs: - for fail in run.failed: - if fail in failed: - failed[fail]["count"] += 1 - failed[fail]["runs"].append(run.run_id) - else: - failed[fail] = {"count": 1, "runs": [run.run_id]} - markdown += f"## {type} \n" - markdown += "| Test name | Fail count | Failed in runs |\n" - markdown += "|--|--|--|\n" - for k, v in sorted(failed.items(), key=lambda item: -item[1]["count"]): - markdown += f"| {k} | {v['count']} | {v['runs']} |\n" - markdown += "\n" - return markdown - - -if __name__ == '__main__': - root = sys.argv[1] - print(f"Parsing tests results from directory: {root}") - run_dirs = [f.path for f in os.scandir(root) if f.is_dir()] - print(f"Found {len(run_dirs)} run dirs") - unittest_runs = [] - e2e_runs = [] - - for run in run_dirs: - run_id = os.path.basename(os.path.normpath(run)) - unit_test_dir = os.path.join(run, "check-test-report") - unit_test_results = aggregate_results(unit_test_dir, run_id) - unittest_runs.append(unit_test_results) - - e2e_test_dir = os.path.join(run, "integrationTest-test-report") - e2e_test_results = aggregate_results(e2e_test_dir, run_id) - e2e_runs.append(e2e_test_results) - - step_summary = "# Failing tests report\n" - step_summary += report("Unit tests", unittest_runs) - step_summary += report("Integration tests", e2e_runs) - - result_file = os.environ["GITHUB_STEP_SUMMARY"] - with open(result_file, 'w') as f: - f.write(step_summary) diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml deleted file mode 100644 index 8d99e40474..0000000000 --- a/.github/workflows/checkstyle.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Checkstyle - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - checkstyle: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - uses: reviewdog/action-setup@v1 - with: - reviewdog_version: latest - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: 17 - distribution: 'temurin' - - name: Run check style - # ignore lengthy console setup tasks - run: ./gradlew --continue clean checkstyleMain checkstyleTest checkstyleIntegration checkstyleJmh -PmaxCheckstyleWarnings=0 -x attachHermesConsole -x prepareIndexTemplate - - name: Run reviewdog - if: ${{ success() || failure() }} - env: - REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - for f in $(find . -regex '.*/build/reports/checkstyle/.*\.xml'); do - module_name=$(echo "$f" | cut -d "/" -f2) - reviewdog -f=checkstyle -level=warning -filter-mode=nofilter -reporter=github-check -name="checkstyle-$module_name" < $f - done diff --git a/.github/workflows/ci-console.yml b/.github/workflows/ci-console.yml deleted file mode 100644 index 628fa9a7ee..0000000000 --- a/.github/workflows/ci-console.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Console CI - -# Configured for all pushes and PRs for the sake of new console development on dev branches. -# Later it could be changed to pushes and PRs only to master branch to match main CI config. -on: [ push, pull_request ] - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - working-directory: ./hermes-console-vue - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Run linter - run: yarn && yarn lint - - name: Run frontend tests - run: yarn test:unit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 691ef82521..03e0af5fb5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,16 +2,40 @@ name: CI on: push: - branches: [ master, integration_tests_framework ] + branches: [ master ] pull_request: - branches: [ master, integration_tests_framework ] + branches: [ master ] jobs: + console: + name: ci-console + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./hermes-console + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Run linter + run: yarn && yarn lint + - name: Run frontend tests + run: yarn test:unit + - name: Upload artifact + if: always() && github.ref == 'refs/heads/master' + uses: actions/upload-artifact@v4 + with: + name: ci-console + path: ./hermes-console/allure-results build: runs-on: ubuntu-latest strategy: matrix: tasks: [ + # Add/remove task in Allure Report job also {alias: "unitTests", name: "check"}, {alias: "integrationTests", name: "integrationTest"}, {alias: "slowIntegrationTests", name: "slowIntegrationTest"}, @@ -20,22 +44,16 @@ jobs: fail-fast: false name: ${{ matrix.tasks.alias }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Build with Gradle run: ./gradlew assemble - name: Run task with Gradle @@ -53,3 +71,61 @@ jobs: with: paths: '**/build/test-results/**/TEST-*.xml' show: fail, skip + - name: Upload artifact + if: always() && github.ref == 'refs/heads/master' && matrix.tasks.alias != 'benchmark' + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.tasks.alias }} + path: build/allure-results + + allureReport: + if: always() && github.ref == 'refs/heads/master' + name: Generate Allure Report + needs: [ build, console ] + runs-on: ubuntu-latest + steps: + - name: Download artifact unitTests + uses: actions/download-artifact@v4 + if: always() + with: + name: unitTests + path: allure-results + - name: Download artifact integrationTests + uses: actions/download-artifact@v4 + if: always() + with: + name: integrationTests + path: allure-results + - name: Download artifact slowIntegrationTests + uses: actions/download-artifact@v4 + if: always() + with: + name: slowIntegrationTests + path: allure-results + - name: Download artifact ci-console + uses: actions/download-artifact@v4 + if: always() + with: + name: ci-console + path: allure-results + - name: Load test report history + uses: actions/checkout@v4 + if: always() + continue-on-error: true + with: + ref: gh-pages + path: gh-pages + - name: Build test report + uses: simple-elf/allure-report-action@v1.9 + if: always() + with: + gh_pages: gh-pages + allure_history: allure-history + allure_results: allure-results + - name: Publish test report + uses: peaceiris/actions-gh-pages@v4 + if: always() + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: gh-pages + publish_dir: allure-history \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 56c5ff5084..c43d90c2db 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,17 +35,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/google-java-format.yml b/.github/workflows/google-java-format.yml new file mode 100644 index 0000000000..2ec1c5bf8b --- /dev/null +++ b/.github/workflows/google-java-format.yml @@ -0,0 +1,26 @@ +name: Google java format + +on: + workflow_dispatch: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + + - name: Download and run google java format + run: | + ls -la + curl -sSLO "https://github.com/google/google-java-format/releases/download/v$VERSION/google-java-format_linux-x86-64" + chmod a+x google-java-format_linux-x86-64 + ./.github/scripts/check-google-java-format.sh + shell: bash + env: + VERSION: 1.23.0 + diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index d3c1263959..eda69f9d27 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -12,5 +12,5 @@ jobs: name: "Validation" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/markdown-links-check.yml b/.github/workflows/markdown-links-check.yml index 6920411bd5..4d186efa1e 100644 --- a/.github/workflows/markdown-links-check.yml +++ b/.github/workflows/markdown-links-check.yml @@ -12,7 +12,7 @@ jobs: check-links: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: gaurav-nelson/github-action-markdown-link-check@v1 with: use-quiet-mode: 'yes' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6485311cc..9d0bec195f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,15 +16,16 @@ jobs: environment: ci steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: gradle/wrapper-validation-action@v1 - name: Set up JDK - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 - name: Release if: github.ref == 'refs/heads/master' run: ./gradlew release -Prelease.customPassword=${GITHUB_TOKEN} -Prelease.customUsername=${GITHUB_ACTOR} -Prelease.forceVersion=${FORCE_VERSION} diff --git a/.github/workflows/test_report.yml b/.github/workflows/test_report.yml deleted file mode 100644 index 2b52cbedb8..0000000000 --- a/.github/workflows/test_report.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: "Test report" -on: - push: - branches: [ master ] - -jobs: - validation: - name: "Test report" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Grant execute permission for report downloader - run: chmod +x ./.github/scripts/download_reports.sh - - name: Download past reports - run: ./.github/scripts/download_reports.sh - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - name: Aggregate reports - run: python ./.github/scripts/reporter.py "test" diff --git a/.gitignore b/.gitignore index d085ce8969..475005bac9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ build .gradle classes -.idea *.iml *.ipr *.iws @@ -39,3 +38,38 @@ scripts/lib/ scripts/pip-selfcheck.json .DS_Store + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries diff --git a/.idea/externalDependencies.xml b/.idea/externalDependencies.xml new file mode 100644 index 0000000000..679f74ca17 --- /dev/null +++ b/.idea/externalDependencies.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml new file mode 100644 index 0000000000..8b57f4527a --- /dev/null +++ b/.idea/google-java-format.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..4336a05b15 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000..35eb1ddfbb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5377d8bfe..262c86384e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,5 +27,5 @@ You can also use other *magic words* from [GitHub handbook](https://help.github. * use `spock` when writing new unit tests in all modules * when changing old tests use your best judgement as to when rewrite them to `spock` -* use `TestNG` with defined environment in `integration` module +* use `JUnit5` with defined environment in `integration-tests` module * prepend configuration options with module name, i.e. `frontend.` or `consumer.` when it applies to single module diff --git a/README.md b/README.md index 9b0545b9c9..eadb83bfd6 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,40 @@ If you have any question or idea regarding the project, please feel free to reac ## License **hermes** is published under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). + +## Development + +### Code formatting +For code formatting we use [google-java-format](https://github.com/google/google-java-format/tree/master). +Following steps are required for optimal dev experience in IJ: + +1. Download [google-java-format plugin](https://plugins.jetbrains.com/plugin/8527-google-java-format) +2. [Set custom VM options required for IJ plugin](https://github.com/google/google-java-format/tree/master?tab=readme-ov-file#intellij-jre-config) +3. Go to `Settings > google-java-format` and click `Enable google java-format` (should be checked by default) +4. Go to `Settings > Tools > Actions on Save` and enable `Reformat code` and `Optimize imports` for Java files + +Each save should automatically trigger reformat. + +If you want to debug the CLI check on macOS: + +```shell +wget https://github.com/google/google-java-format/releases/download/v1.23.0/google-java-format_darwin-arm64 +chmod a+x google-java-format_darwin-arm64 +chmod a+x .github/scripts/check-google-java-format.sh +./.github/scripts/check-google-java-format.sh +``` + +or if you are on Linux: + +```shell +wget https://github.com/google/google-java-format/releases/download/v1.23.0/google-java-format_linux-x86-64 +chmod a+x google-java-format_linux-x86-64 +chmod a+x .github/scripts/check-google-java-format.sh +./.github/scripts/check-google-java-format.sh +``` + +You can also run the following command to fix formatting for the whole project: + +```shell +./.github/scripts/check-google-java-format.sh --fix +``` diff --git a/build.gradle b/build.gradle index b89f19fe60..02f86f4e6d 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,6 @@ nexusPublishing { allprojects { apply plugin: 'java' apply plugin: 'groovy' - apply plugin: 'checkstyle' group = 'pl.allegro.tech.hermes' version = scmVersion.version @@ -49,26 +48,28 @@ allprojects { project.ext.versions = [ kafka : '2.8.2', - guava : '23.0', - jackson : '2.15.2', - jersey : '3.1.2', - jetty : '11.0.15', + guava : '33.1.0-jre', + jackson : '2.17.0', + jersey : '3.1.6', + jetty : '12.0.8', curator : '5.4.0', - dropwizard_metrics: '4.1.0', - micrometer_metrics: '1.11.1', - wiremock : '3.0.1', - spock : '2.4-M1-groovy-4.0', - groovy : '4.0.12', - avro : '1.9.1', + dropwizard_metrics: '4.2.25', + micrometer_metrics: '1.12.5', + wiremock : '3.9.0', + spock : '2.4-M4-groovy-4.0', + groovy : '4.0.21', + avro : '1.11.3', json2avro : '0.2.14', + // TODO: newest version requires subject alternative name in a certificate during host verification, current test cert does not have a one okhttp : '3.9.1', - undertow : '2.0.29.Final', - spring_web : '6.0.8', - failsafe : '2.3.1', - junit_jupiter : '5.9.1', - testcontainers : '1.18.1', - spring : '3.0.6', - assertj : '3.24.2' + undertow : '2.3.12.Final', + spring_web : '6.1.6', + failsafe : '2.4.4', + junit_jupiter : '5.10.2', + testcontainers : '1.19.8', + spring : '3.2.4', + assertj : '3.25.3', + allure : '2.24.0' ] repositories { @@ -77,15 +78,23 @@ allprojects { dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.4' - implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9' + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0' + + // Allure Spock adapter + testImplementation(platform("io.qameta.allure:allure-bom:${versions.allure}")) + testImplementation("io.qameta.allure:allure-spock2") + testImplementation("io.qameta.allure:allure-junit-platform") + + // Spock framework + testImplementation(platform("org.spockframework:spock-bom:${versions.spock}")) + testImplementation("org.spockframework:spock-core") testImplementation group: 'junit', name: 'junit', version: '4.11' testImplementation group: 'com.tngtech.java', name: 'junit-dataprovider', version: '1.10.0' testImplementation group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.2' - testImplementation group: 'org.mockito', name: 'mockito-all', version: '1.9.5' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.11.0' testImplementation group: 'org.assertj', name: 'assertj-core', version: versions.assertj - testImplementation group: 'com.jayway.awaitility', name: 'awaitility', version: '1.6.1' - testImplementation group: 'com.googlecode.catch-exception', name: 'catch-exception', version: '1.2.0' + testImplementation group: 'org.awaitility', name: 'awaitility', version: '4.2.1' annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: versions.spring } @@ -113,7 +122,7 @@ allprojects { } -configure(subprojects - project(':integration')) { +configure(subprojects - project(':integration-tests')) { apply plugin: 'jacoco' apply plugin: 'maven-publish' apply plugin: 'signing' @@ -123,6 +132,8 @@ configure(subprojects - project(':integration')) { withSourcesJar() } + javadoc.options.addStringOption('Xdoclint:none', '-quiet') + publishing { publications { mavenJava(MavenPublication) { @@ -194,18 +205,6 @@ subprojects { events 'passed', 'skipped', 'failed' } } - - tasks.withType(Checkstyle) { - reports { - xml.required = true - html.required = false - } - } - - checkstyle { - toolVersion '10.3.4' - maxWarnings getIntProperty('maxCheckstyleWarnings', Integer.MAX_VALUE) - } } def getIntProperty(String name, int defaultValue) { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml deleted file mode 100644 index 68ce4fd71d..0000000000 --- a/config/checkstyle/checkstyle.xml +++ /dev/null @@ -1,439 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml deleted file mode 100644 index 91465ba4ee..0000000000 --- a/config/checkstyle/suppressions.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index dc2d859510..9f07d83fc8 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -30,19 +30,6 @@ services: - kafka_data:/var/lib/kafka/data - kafka_secrets:/etc/kafka/secrets - graphite: - image: graphiteapp/graphite-statsd:1.1.3 - ports: - - '2003-2004:2003-2004' - - '2023-2024:2023-2024' - - '8125:8125/udp' - - '8126:8126' - - '8082:80' - volumes: - - graphite_conf:/opt/graphite/conf - - graphite_data:/opt/graphite/storage - - statsd_data:/opt/statsd - frontend: build: context: ../ @@ -52,7 +39,6 @@ services: depends_on: - zk - kafka - - graphite consumers: build: @@ -61,7 +47,6 @@ services: depends_on: - zk - kafka - - graphite management: build: @@ -72,7 +57,6 @@ services: depends_on: - zk - kafka - - graphite schema-registry: image: "confluentinc/cp-schema-registry:${CONFLUENT_IMAGES_TAG}" @@ -87,9 +71,6 @@ services: - "8081:8081" volumes: - graphite_conf: - graphite_data: - statsd_data: zk_secrets: zk_data: zk_log: diff --git a/docker/latest/consumers/consumers.yaml b/docker/latest/consumers/consumers.yaml index 76af83d9b5..f64008630f 100644 --- a/docker/latest/consumers/consumers.yaml +++ b/docker/latest/consumers/consumers.yaml @@ -8,11 +8,6 @@ consumer: clusters: - datacenter: "dc" brokerList: "kafka:29092" - graphite: - host: "graphite" - metrics: - metric-registry: - graphiteReporterEnabled: true workload: consumerPerSubscription: 1 schema: diff --git a/docker/latest/frontend/frontend.yaml b/docker/latest/frontend/frontend.yaml index 2ccd0f3432..0ba33ab769 100644 --- a/docker/latest/frontend/frontend.yaml +++ b/docker/latest/frontend/frontend.yaml @@ -8,11 +8,6 @@ frontend: clusters: - datacenter: "dc" brokerList: "kafka:29092" - graphite: - host: "graphite" - metrics: - metric-registry: - graphiteReporterEnabled: true schema: cache: refreshAfterWrite: 1m diff --git a/docker/latest/management/management.yaml b/docker/latest/management/management.yaml index 0953e3660d..8aaec73c04 100644 --- a/docker/latest/management/management.yaml +++ b/docker/latest/management/management.yaml @@ -17,11 +17,6 @@ kafka: connectionTimeout: 3000 bootstrapKafkaServer: kafka:29092 -graphite: - client: - enabled: true - externalMonitoringUrl: graphite:8082 - server: port: 8090 @@ -59,6 +54,10 @@ schema.repository: serverUrl: http://schema-registry:8081 validationEnabled: true +prometheus: + client: + enabled: true + console: configurationLocation: console/config-local.json configurationType: classpath_resource diff --git a/docs/docs/configuration/buffer-persistence.md b/docs/docs/configuration/buffer-persistence.md index a4e3b9ca66..875776f508 100644 --- a/docs/docs/configuration/buffer-persistence.md +++ b/docs/docs/configuration/buffer-persistence.md @@ -1,4 +1,4 @@ -# Publishing buffer persistence +# Publishing buffer persistence [deprecated] Hermes Frontend API has option to register callbacks triggered during different phases of message lifetime: @@ -15,7 +15,7 @@ to disk. Map structure is continuously persisted to disk, as it is stored in off When Hermes Frontend starts up it scans filesystem in search of existing persisted map. If found, it is read and any persisted events are sent to Message Store. This way recovering after crash is fully automatic. If Hermes process or -server crashes, nothing is lost. +server crashes, events that were flushed to disk are recovered. There is additional protection against flooding subscribers with outdated events. When reading events from persisted storage, Hermes filters out messages older than N hours, where N is a system parameter and is set to 3 days by default. diff --git a/docs/docs/configuration/console.md b/docs/docs/configuration/console.md index f327d0dd63..a67d509c6d 100644 --- a/docs/docs/configuration/console.md +++ b/docs/docs/configuration/console.md @@ -15,14 +15,12 @@ dashboard.docs | link to documentation, available on Console home page ## Metric Store integration -Hermes Console can be integrated with Metric Store. This means, that metrics shown in Console can link to actual graphs -plotted by Metric Store. At the moment only Graphite is supported. - -Option | Description ------------------------ | --------------------------------------------------------------------------- -metrics.type | type of metrics storage to link to (currently only `graphite` is supported) -metrics.graphite.url | URL to graphite -metrics.graphite.prefix | prefix to graphite metrics +Hermes console could have a button on the topics and subscriptions view that takes you to a dashboard with metrics. +In order to make it work you have to provide an implementation of `pl.allegro.tech.hermes.management.domain.MetricsDashboardUrlService`. + + Option | Description +-------------------------------------|-------------------------------------------------------------------------------------- + metrics.fetchingDashboardUrlEnabled | enable fetching dashboard url from hermes-management and show the referring UI button ## Authorization diff --git a/docs/docs/configuration/consumers-tuning.md b/docs/docs/configuration/consumers-tuning.md index f4433b819f..904df5eede 100644 --- a/docs/docs/configuration/consumers-tuning.md +++ b/docs/docs/configuration/consumers-tuning.md @@ -2,18 +2,18 @@ ## HTTP Sender -Option | Description | Default value ----------------------------------------------------- | ----------------------------------------------------------- | ------------- -consumer.http-client.serial.http1.threadPoolSize | size of thread pool for sender threads (global) | 30 -consumer.http-client.serial.http1.maxConnectionsPerDestination | max connections per remote host | 100 +| Option | Description | Default value | +|----------------------------------------------------------------|-------------------------------------------------|---------------| +| consumer.http-client.serial.http1.threadPoolSize | size of thread pool for sender threads (global) | 30 | +| consumer.http-client.serial.http1.maxConnectionsPerDestination | max connections per remote host | 100 | ## Consumers core -Option | Description | Default value ------------------------------ | ------------------------------------------------------------------------ | ------------- -consumer.commit.offset.period | interval between committing offsets to Kafka | 60s -consumer.threadPoolSize | thread pool for threads involved in consuming, 1 thread per subscription | 500 -consumer.serialConsumer.inflightSize | how many messages can be kept in send queue, per subscription | 100 +| Option | Description | Default value | +|--------------------------------------|--------------------------------------------------------------------------|---------------| +| consumer.commit.offset.period | interval between committing offsets to Kafka | 60s | +| consumer.threadPoolSize | thread pool for threads involved in consuming, 1 thread per subscription | 500 | +| consumer.serialConsumer.inflightSize | how many messages can be kept in send queue, per subscription | 100 | ## Workload constraints management @@ -26,10 +26,10 @@ subscriptions assigned to itself. These numbers can be configured: -Option | Description | Default value ---------------------------------------------------- | ----------------------------------------- | --------------------- -consumer.workload.consumersPerSubscription | Number of consumers to which the subscription will be assigned. If this value is greater than the number of available consumers, Hermes will assign the subscription to all available consumers. | 2 -consumer.workload.maxSubscriptionsPerConsumer | The maximum number of subscriptions assigned to a single consumer. If all consumers have the maximum number of subscriptions assigned, a new subscription will not be activated until a new consumer is added or another subscription is unassigned. | 200 +| Option | Description | Default value | +|-----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| +| consumer.workload.consumersPerSubscription | Number of consumers to which the subscription will be assigned. If this value is greater than the number of available consumers, Hermes will assign the subscription to all available consumers. | 2 | +| consumer.workload.maxSubscriptionsPerConsumer | The maximum number of subscriptions assigned to a single consumer. If all consumers have the maximum number of subscriptions assigned, a new subscription will not be activated until a new consumer is added or another subscription is unassigned. | 200 | Additionally, Hermes allows to configure the property `consumer.workload.consumersPerSubscription` for specific topics or subscriptions in the runtime via REST API. diff --git a/docs/docs/configuration/metrics.md b/docs/docs/configuration/metrics.md index 86f78fe134..5e172d4ebf 100644 --- a/docs/docs/configuration/metrics.md +++ b/docs/docs/configuration/metrics.md @@ -12,9 +12,9 @@ Option | Description {modulePrefix}.metrics.prometheus.step | The step size to use in computing windowed statistics | 60s {modulePrefix}.metrics.prometheus.descriptions | If meter descriptions should be sent to Prometheus | true -In order to be able to access basic metrics via Management API, it needs to be configured to reach VictoriaMetrics API: +In order to be able to access basic metrics via Management API, it needs to be configured to reach Prometheus API: Option | Description | Default value ------------------------------------------|-----------------------------------------------| ------------- prometheus.client.enabled | Should fetch external metrics from Prometheus | true -prometheus.client.externalMonitoringUrl | URI to VictoriaMetrics HTTP API | http://localhost:18090 +prometheus.client.externalMonitoringUrl | URI to Prometheus HTTP API | http://localhost:18090 diff --git a/docs/docs/overview/architecture.md b/docs/docs/overview/architecture.md index 7726745068..6d647e64d2 100644 --- a/docs/docs/overview/architecture.md +++ b/docs/docs/overview/architecture.md @@ -16,7 +16,7 @@ Hermes integrates with multiple systems, each having different role. * **Message Store** - stores and routes messages, current implementation: Kafka * **Metadata Store** - shared metadata storage for all Hermes modules, current implementation: Zookeeper -* **Metrics Store** *[optional]* - stores metrics gathered by Hermes, current implementation: Graphite +* **Metrics Store** *[optional]* - stores metrics gathered by Hermes, currently Hermes exposes metrics in Prometheus format * **Tracking Store** *[optional]* - stores tracking (message trace) information, current implementation: ElasticSearch ## Message flow diff --git a/docs/docs/quickstart.md b/docs/docs/quickstart.md index c31db5a01a..3ad00ea47b 100644 --- a/docs/docs/quickstart.md +++ b/docs/docs/quickstart.md @@ -56,7 +56,7 @@ image: allegro/hermes-management:hermes-[specific version tag] ## Development The default `docker-compose` setup will start all hermes modules (consumers, frontend, management), together -with its dependencies (Kafka, ZooKeeper, Graphite, Schema Registry). To run a specific module with gradle/IntelliJ, +with its dependencies (Kafka, ZooKeeper, Schema Registry). To run a specific module with gradle/IntelliJ, just comment out the module in `services` section of the `docker-compose.yml` file, and start the java process locally: `./gradlew -p hermes-frontend run` @@ -175,7 +175,6 @@ management: depends_on: - zk - kafka - - graphite [...] ``` diff --git a/docs/docs/user/java-client.md b/docs/docs/user/java-client.md index 57e78b766b..942c9675a7 100644 --- a/docs/docs/user/java-client.md +++ b/docs/docs/user/java-client.md @@ -19,7 +19,7 @@ At the moment there are four implementations of `HermesSender`: for asynchronous transmission * **WebClientHermesSender** - for services using [Spring WebFlux](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html); uses [WebClient](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html) -* **JerseyHermesSender** - recommended for services using [Jersey]() +* **JerseyHermesSender** - recommended for services using [Jersey]() * **OkHttpHermesSender** - supports both HTTP/1.1 and HTTP/2 protocols, uses [OkHttp3 client](http://square.github.io/okhttp/) diff --git a/docs/docs/user/publishing.md b/docs/docs/user/publishing.md index 16ba94f0ff..044535aa61 100644 --- a/docs/docs/user/publishing.md +++ b/docs/docs/user/publishing.md @@ -134,22 +134,21 @@ Failure statuses: Each topic can define level of acknowledgement (ACK): * leader ACK - only one Kafka node (leader) needs to acknowledge reception of message -* all ACK - all nodes that hold copy of message need to acknowledge reception of message +* all ACK - at least [min.insync.replicas](https://kafka.apache.org/documentation/#brokerconfigs_min.insync.replicas) nodes must acknowledge reception of message -For most of the topic leader ACK is enough. This guarantees roughly 99.999..% reception rate. Only in rare cases, during -Kafka cluster rebalancing or nodes outage Kafka might confirm that message was received, while it was not saved and it -will be lost. +ACK configuration has the following consequences: -What does it mean in practice? Numbers differ per case and they are affected by multiple factors like frequency of -rebalancing taking place on Kafka clusters, Kafka version etc. In our production environment using ACK leader means we falsely -believe message was received by Kafka once per 20 million events. This is a very rough estimate that should show you -the scale, if you need numbers to base your decision on - please conduct own measurements. +- with `ACK leader` message writes are replicated asynchronously, thus the acknowledgment latency will be low. However, message write may be lost +when there is a topic leadership change - e.g. due to rebalance or broker restart. +- with `ACK all` messages writes are synchronously replicated to replicas. Write acknowledgement latency will be much higher than with leader ACK, +it will also have higher variance due to tail latency. However, messages will be persisted as long as the whole replica set does not go down simultaneously. -If you need 100% guarantee that message was saved, force all replicas to send ACK. The downside of this is much longer -response times, they tend to vary a lot as well. Thanks to Hermes buffering (described in paragraphs below), we are able -to guarantee some sane response times to our clients even in *ACK all* mode. +Publishers are advised to select topic ACK level based on their latency and durability requirements. -## Buffering +Hermes also provides a feature called Buffering (described in paragraphs below) which provides consistent write latency +despite long Kafka response times. Note that, however, this mode may decrease message durability for `ACK all` setting. + +## Buffering [deprecated] Hermes administrator can set maximum time, for which Hermes will wait for Kafka acknowledgment. By default, it is set to 65ms. After that time, **202** response is sent to client. Event is kept in Kafka producer buffer and it's delivery will @@ -161,10 +160,54 @@ Kafka is back online. ### Buffer persistence -By default events are buffered in memory only. This raises the question about what happens in case of Hermes node failure +By default, events are buffered in memory only. This raises the question about what happens in case of Hermes node failure (or force kill of process). Hermes Frontend API exposes callbacks that can be used to implement persistence model of buffered events. Default implementation uses [OpenHFT ChronicleMap](https://github.com/OpenHFT/Chronicle-Map) to persist unsent messages to disk. Map structure is continuously persisted to disk, as it is stored in offheap memory as [memory mapped file](https://en.wikipedia.org/wiki/Memory-mapped_file). + +Using buffering with ACK all setting means that durability of events may be lowered when **202** status code is received. If Hermes instance +is killed before message is spilled to disk or the data on disk becomes corrupted, the message is gone. Thus `ACK all` with **202** status code +is similar to `ACK leader` because a single node failure could cause the message be lost. + +### Deprecation notice +The buffering mechanism in Hermes is considered deprecated and is set to be removed in the future. + +## Remote DC fallback + +Hermes supports a remote datacenter fallback mechanism for [multi datacenter deployments](https://hermes-pubsub.readthedocs.io/en/latest/configuration/kafka-and-zookeeper/#multiple-kafka-and-zookeeper-clusters). + +Fallback is configured on per topic basis, using a `fallbackToRemoteDatacenterEnabled` property: + +```http +PUT /topics/my.group.my-topic + +{ + "fallbackToRemoteDatacenterEnabled": true +} +``` + +Using this setting automatically disables buffering mechanism for a topic. + +When using this setting for a topic, Hermes will try to send a message to a local datacenter Kafka first and will fall back to remote datacenter Kafka +if the local send fails. + +Hermes also provides a speculative fallback mechanism which will send messages to remote Kafka if the local Kafka is not responding in a timely manner. +Speculative send is performed after `frontend.kafka.fail-fast-producer.speculativeSendDelay` elapses. + +When using remote DC fallback, Hermes attempts to send a message to Kafka for the duration of `frontend.handlers.maxPublishRequestDuration` property. If after +`maxPublishRequestDuration` Hermes has not received an acknowledgment from Kafka, it will respond with **500** status code to the client. + +Table below summarizes remote fallback configuration options: + +| Option | Scope | Default value | +|--------------------------------------------------------|--------|---------------| +| fallbackToRemoteDatacenterEnabled | topic | false | +| frontend.kafka.fail-fast-producer.speculativeSendDelay | global | 250ms | +| frontend.handlers.maxPublishRequestDuration | global | 500ms | + +## Partition assignment +`Partition-Key` header can be used by publishers to specify Kafka `key` which will be used for partition assignment for a message. This will ensure +that all messages with given `Partition-Key` will be sent to the same Kafka partition. diff --git a/docs/docs/user/subscribing.md b/docs/docs/user/subscribing.md index df0321155e..8112b6a809 100644 --- a/docs/docs/user/subscribing.md +++ b/docs/docs/user/subscribing.md @@ -39,22 +39,17 @@ Minimal request: All options: -Option | Description | Default value ------------------------------------- | ----------------------------------------------------| ------------- -trackingMode | track outgoing messages | trackingOff -subscriptionPolicy.rate | maximum sending speed in rps (per DC) | 400 -subscriptionPolicy.messageTtl | inflight Time To Live in seconds | 3600 -subscriptionPolicy.messageBackoff | backoff time between retry attempts in millis | 1000 -subscriptionPolicy.retryClientErrors | retry on receiving 4xx status | false -subscriptionPolicy.requestTimeout | request timeout in millis | 1000 -subscriptionPolicy.socketTimeout | maximum time of inactivity between two data packets | infinity -subscriptionPolicy.inflightSize | max number of pending requests | 100 -subscriptionPolicy.backoffMultiplier | backoff multiplier for calculating message backoff | 1 -subscriptionPolicy.backoffMaxIntervalInSec | maximal retry backoff in seconds | 600 -headers | additional HTTP request headers | [] (array of headers) -filters | used for skipping unwanted messages | [] (array of filters) -endpointAddressResolverMetadata | additional address resolver metadata | {} (map) -subscriptionIdentityHeadersEnabled | attach HTTP headers with subscription identity | false +| Option | Description | Default value | +|------------------------------------|---------------------------------------------------------------------------------------|------------------------------------------------------| +| trackingMode | track outgoing messages | trackingOff | +| contentType | delivered message format (JSON or BATCH) | JSON | +| deliveryType | delivery type (SERIAL or BATCH) | SERIAL | +| subscriptionPolicy | see [delivery types](#delivery-type) | [serial](#serial-delivery), [batch](#batch-delivery) | +| mode | whether to send message to single (ANYCAST) or all (BROADCAST) subscription endpoints | ANYCAST | +| headers | additional HTTP request headers | [] (array of headers) | +| filters | used for skipping unwanted messages | [] (array of filters) | +| endpointAddressResolverMetadata | additional address resolver metadata | {} (map) | +| subscriptionIdentityHeadersEnabled | attach HTTP headers with subscription identity | false | Possible values for **trackingMode** are: @@ -76,6 +71,7 @@ Request that specifies all available options: "id": "My Team" }, "contact": "my-team@my-company.com", + "deliveryType": "SERIAL", "subscriptionPolicy": { "rate": 100, "messageTtl": 3600, @@ -87,6 +83,7 @@ Request that specifies all available options: "backoffMultiplier": 1.0, "backoffMaxIntervalInSec": 600 }, + "mode": "ANYCAST", "headers": [ {"name": "SOME_HEADER", "value": "ABC"}, {"name": "OTHER_HEADER", "value": "123"} @@ -141,6 +138,66 @@ no matter how many times Hermes would try to deliver it. This behavior can be ch flag on subscription. When this flag is set to true, message with response code **4xx** will be retried, also causing slowing down overall sending speed. See [back pressure section](#back-pressure) for more details. +## Content type + +Hermes can deliver messages to subscribers in two formats: + +- JSON - if topic content type was of type JSON or AVRO +- AVRO - if topic content type was of type AVRO + +## Delivery type + +Hermes supports two delivery types: SERIAL and BATCH. + +### Serial delivery +With serial delivery, each hermes consumer will process at most `inflightSize` messages concurrently. Messages are sent individually +(number of published messages = number of messages sent to subscriber). + + +Options for `subscriptionPolicy`: + +| Option | Description | Default value | +|-------------------------|-----------------------------------------------------|---------------| +| rate | maximum sending speed in rps (per DC) | 400 | +| messageTtl | inflight Time To Live in seconds | 3600 | +| messageBackoff | backoff time between retry attempts in millis | 1000 | +| retryClientErrors | retry on receiving 4xx status | false | +| requestTimeout | request timeout in millis | 1000 | +| socketTimeout | maximum time of inactivity between two data packets | infinity | +| inflightSize | max number of pending requests | 100 | +| backoffMultiplier | backoff multiplier for calculating message backoff | 1 | +| backoffMaxIntervalInSec | maximal retry backoff in seconds | 600 | + + +### Batch delivery +With batch delivery hermes consumer aggregates messages in a batch before sending the batch to the subscriber. There are 3 +configurable thresholds that determine when the batch will be ready to be sent - number of messages in the batch, duration of the batch +and size of the batch in bytes. Batch is considered ready whenever one of these thresholds is surpassed. + +Messages will be sent as an array of JSON messages, e.g. + +```json +[{"foo": "bar1"}, {"foo": "bar2"}, ...] +``` + +Options for `subscriptionPolicy`: + +| Option | Description | Default value | +|-------------------------|-----------------------------------------------------------------|---------------| +| messageTtl | inflight Time To Live in seconds | 3600 | +| messageBackoff | backoff time between retry attempts in millis | 500 | +| retryClientErrors | retry on receiving 4xx status | false | +| requestTimeout | request timeout in millis | 30000 | +| batchSize | maximum number of messages in a batch | 100 | +| batchTime | maximum duration in millis for which messages can be aggregated | 30000 | +| batchVolume | maximum batch size in bytes | 64000 | + +#### Limitations +Following subscription options are not available with batch delivery: + +- AVRO subscription contentType +- BROADCAST mode + ## Retries Hermes Consumers have been optimized towards maximizing chances of successful message delivery. Retry policy is @@ -178,12 +235,11 @@ current_backoff = previous_backoff * backoff_multiplier ``` This has the following consequences: -Backoff multiplier | Retry policy type ----------------------------------|-------------------------------- -1 | Constant retry backoff - above 1 | Exponential retry backoff - - +| Backoff multiplier | Retry policy type | +|--------------------|---------------------------| +| 1 | Constant retry backoff | +| above 1 | Exponential retry backoff | + The hard limit to current backoff is defined by maximum backoff parameter and by default is equal to 600 s. It is worth mentioning that the calculation of current backoff is ignored when the **Retry-After** header is used. @@ -261,6 +317,17 @@ It's ignored by the default implementation. See [console integration](../configuration/console.md#subscription-configuration) for more information. +## Mode + +Hermes supports two delivery modes: + +- ANYCAST - messages will be sent to endpoint returned by `EndpointAddressResolver#resolve` +- BROADCAST - messages will be sent to endpoint returned by `EndpointAddressResolver#resolveAll` + +Example usage of this feature would be to provide `EndpointAddressResolver` implementation which returns any subscriber address (e.g. single +service instance) for `resolve` and all subscriber addresses for `resolveAll` (e.g. all instances of a service). ANYCAST subscription messages +would then be delivered to any subscribing service instance and BROADCAST subscription messages would then be delivered to all subscribing service instances. + ## Message filtering Each subscription can define set of filters that are going to be applied after receiving message from kafka in order @@ -271,10 +338,10 @@ of their declaration. This mainly concerns message content type. Filtering is done *before* any conversion takes place so all messages have the same content type as topic on which they were published. -Topic content-type | Filter type ---------------------- | ----------- -avro | avropath -json | jsonpath +| Topic content-type | Filter type | +|--------------------|-------------| +| avro | avropath | +| json | jsonpath | ### Matching strategy @@ -304,12 +371,12 @@ In case when `matchingStrategy` would be set to `any` then all messages with *GB JsonPath filter is based on popular [library](https://github.com/jayway/JsonPath) of the same name that can query json documents. In this case it is used as a selector to retrieve value that is later matched by regexp. -Option | Description ---------------------- | --------------------------------------------------- -type | type of filter -path | JsonPath expression to query json document -matcher | regexp expression to match value from json document -matchingStrategy | type of matching strategy. Default is `all` +| Option | Description | +|------------------|-----------------------------------------------------| +| type | type of filter | +| path | JsonPath expression to query json document | +| matcher | regexp expression to match value from json document | +| matchingStrategy | type of matching strategy. Default is `all` | Example: ``` @@ -323,12 +390,12 @@ avro so we decided to introduce very simple dotted path format without any advan understand if you're familiar with JsonPath. Right now array and basic selectors that point to specific fields are supported. -Option | Description ---------------------- | --------------------------------------------------- -type | type of filter -path | dotted expression to query avro document. When array selector is used then wildcard sign `*` can be used as index -matcher | regexp expression to match value from avro document -matchingStrategy | type of matching strategy. Default is `all` +| Option | Description | +|------------------|-------------------------------------------------------------------------------------------------------------------| +| type | type of filter | +| path | dotted expression to query avro document. When array selector is used then wildcard sign `*` can be used as index | +| matcher | regexp expression to match value from avro document | +| matchingStrategy | type of matching strategy. Default is `all` | Example: ``` @@ -664,3 +731,32 @@ It returns array of message tracking information in following format: Sending delay can be defined for each serial subscription. Consumers will wait for a given time before trying to deliver a message. This might be useful in situations when there are multiple topics that sends events in the same time, but you want to increase chance that events from one topic will be delivered later than events from another topic. + +## Ordering guarantees +For subscriptions with `SERIAL` deliveryType hermes will deliver `inflightSize` messages concurrently. +Because of that messages may be delivered out of partition order (unless `inflightSize=1` but this can have poor performance). + +With `BATCH` deliveryType messages are guaranteed to be delivered in partition order (batches are sent sequentially). + +Note that by default Hermes does not give any guarantees about assigning messages to partitions. To do that, publishers must specify [partition key explicitly](publishing.md#partition-assignment). + +When messages are published with `parition-key` and consumed with `BATCH` mode (or `SERIAL` with `inflightSize=1`) they will be ordered as long as they were published to one DC. +Publishing messages with same `parition-key` to multiple DCs does not guarantee ordering because messages are stored in separate kafka clusters. + +## Message duplication + +Hermes messages can be duplicated on different levels. Publishers are advised to include an idempotency key in message schema to allow +consumers to process messages idempotently, relying on Hermes `messageId` is not sufficient. + +Scenarios in which subscribes may receive logically duplicated messages: + +- Publisher sends a message to Hermes, the request is timed out and retried. In this case both the first and the second message may end up on Kafka and +they will have different Hermes `messageId`. Both messages are later sent to subscriber. +- Once message is received by Hermes it is relayed to Kafka. Message may then be duplicated because of Kafka producer level retries. Duplicated messages will +have the same Hermes `messageId`. +- When using [remote DC fallback](https://hermes-pubsub.readthedocs.io/en/latest/user/publishing/#remote-dc-fallback), slow messages are speculatively +sent to remote DC. This may result in a message being duplicated across DCs if both the local and remote message are saved, both copies will have the +same `messageId`. +- Even if there is one instance of a given message on a given topic, the message may still be delivered to subscriber multiple times. +When message is acknowledged by a subscriber its offset is not commited to Kafka synchronously. If Hermes instance is restarted +after receiving an ack from subscriber but before commiting the message offset to Kafka, the message will be redelivered. diff --git a/hermes-api/build.gradle b/hermes-api/build.gradle index c6554f612b..9a3542da0a 100644 --- a/hermes-api/build.gradle +++ b/hermes-api/build.gradle @@ -4,20 +4,19 @@ plugins { } dependencies { - api group: 'org.hibernate.validator', name: 'hibernate-validator', version: '8.0.0.Final' + api group: 'org.hibernate.validator', name: 'hibernate-validator', version: '8.0.1.Final' api group: 'jakarta.ws.rs', name: 'jakarta.ws.rs-api', version: '3.1.0' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: versions.jackson api group: 'com.fasterxml.jackson.jakarta.rs', name: 'jackson-jakarta-rs-json-provider', version: versions.jackson api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: versions.jackson implementation group: 'com.google.guava', name: 'guava', version: versions.guava - api group: 'com.damnhandy', name: 'handy-uri-templates', version: '2.0.2' + api group: 'com.damnhandy', name: 'handy-uri-templates', version: '2.1.8' api group: 'jakarta.xml.bind', name: 'jakarta.xml.bind-api', version: '4.0.0' - implementation group: 'com.sun.xml.bind', name: 'jaxb-core', version: '4.0.3' - implementation group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '4.0.3' - implementation group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '2.1.1' - + implementation group: 'com.sun.xml.bind', name: 'jaxb-core', version: '4.0.5' + implementation group: 'com.sun.xml.bind', name: 'jaxb-impl', version: '4.0.5' + implementation group: 'jakarta.annotation', name: 'jakarta.annotation-api', version: '3.0.0' testImplementation group: 'org.spockframework', name: 'spock-core', version: versions.spock testImplementation group: 'org.spockframework', name: 'spock-junit4', version: versions.spock diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Anonymizable.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Anonymizable.java index ca360e371d..272b8ccccb 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Anonymizable.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Anonymizable.java @@ -1,5 +1,5 @@ package pl.allegro.tech.hermes.api; public interface Anonymizable { - Anonymizable anonymize(); + Anonymizable anonymize(); } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/AvroMediaType.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/AvroMediaType.java index 165befe5a7..6d1faa8844 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/AvroMediaType.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/AvroMediaType.java @@ -2,7 +2,7 @@ public class AvroMediaType { - public static final String AVRO_BINARY = "avro/binary"; + public static final String AVRO_BINARY = "avro/binary"; - public static final String AVRO_JSON = "avro/json"; + public static final String AVRO_JSON = "avro/json"; } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BatchSubscriptionPolicy.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BatchSubscriptionPolicy.java index 1c06f0ee03..79b26f3bb5 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BatchSubscriptionPolicy.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BatchSubscriptionPolicy.java @@ -3,202 +3,208 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.google.common.base.MoreObjects; import jakarta.validation.constraints.Min; -import pl.allegro.tech.hermes.api.helpers.Patch; - import java.util.Map; import java.util.Objects; +import pl.allegro.tech.hermes.api.helpers.Patch; public class BatchSubscriptionPolicy { - private static final int DEFAULT_MESSAGE_TTL = 60; - private static final int DEFAULT_MESSAGE_BACKOFF = 500; - private static final int DEFAULT_REQUEST_TIMEOUT = 30 * 1000; - private static final int DEFAULT_BATCH_SIZE = 100; - private static final int DEFAULT_BATCH_TIME = 30 * 1000; - private static final int DEFAULT_BATCH_VOLUME = 64 * 1000; - - @Min(0) - private int messageTtl; - - private boolean retryClientErrors; - - @Min(0) - private int messageBackoff; - - @Min(1) - private int requestTimeout; - - @Min(1) - private int batchSize; - - @Min(1) - private int batchTime; - - @Min(1) - private int batchVolume; - - private BatchSubscriptionPolicy() {} - - public BatchSubscriptionPolicy(int messageTtl, - boolean retryClientErrors, - int messageBackoff, - int requestTimeout, - int batchSize, - int batchTime, - int batchVolume) { - this.messageTtl = messageTtl; - this.retryClientErrors = retryClientErrors; - this.messageBackoff = messageBackoff; - this.requestTimeout = requestTimeout; - this.batchSize = batchSize; - this.batchTime = batchTime; - this.batchVolume = batchVolume; + private static final int DEFAULT_MESSAGE_TTL = 60; + private static final int DEFAULT_MESSAGE_BACKOFF = 500; + private static final int DEFAULT_REQUEST_TIMEOUT = 30 * 1000; + private static final int DEFAULT_BATCH_SIZE = 100; + private static final int DEFAULT_BATCH_TIME = 30 * 1000; + private static final int DEFAULT_BATCH_VOLUME = 64 * 1000; + + @Min(0) + private int messageTtl; + + private boolean retryClientErrors; + + @Min(0) + private int messageBackoff; + + @Min(1) + private int requestTimeout; + + @Min(1) + private int batchSize; + + @Min(1) + private int batchTime; + + @Min(1) + private int batchVolume; + + private BatchSubscriptionPolicy() {} + + public BatchSubscriptionPolicy( + int messageTtl, + boolean retryClientErrors, + int messageBackoff, + int requestTimeout, + int batchSize, + int batchTime, + int batchVolume) { + this.messageTtl = messageTtl; + this.retryClientErrors = retryClientErrors; + this.messageBackoff = messageBackoff; + this.requestTimeout = requestTimeout; + this.batchSize = batchSize; + this.batchTime = batchTime; + this.batchVolume = batchVolume; + } + + @JsonCreator + public static BatchSubscriptionPolicy create(Map properties) { + return new BatchSubscriptionPolicy( + (Integer) properties.getOrDefault("messageTtl", DEFAULT_MESSAGE_TTL), + (Boolean) properties.getOrDefault("retryClientErrors", false), + (Integer) properties.getOrDefault("messageBackoff", DEFAULT_MESSAGE_BACKOFF), + (Integer) properties.getOrDefault("requestTimeout", DEFAULT_REQUEST_TIMEOUT), + (Integer) properties.getOrDefault("batchSize", DEFAULT_BATCH_SIZE), + (Integer) properties.getOrDefault("batchTime", DEFAULT_BATCH_TIME), + (Integer) properties.getOrDefault("batchVolume", DEFAULT_BATCH_VOLUME)); + } + + @Override + public int hashCode() { + return Objects.hash( + messageTtl, + retryClientErrors, + messageBackoff, + requestTimeout, + batchSize, + batchTime, + batchVolume); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @JsonCreator - public static BatchSubscriptionPolicy create(Map properties) { - return new BatchSubscriptionPolicy( - (Integer) properties.getOrDefault("messageTtl", DEFAULT_MESSAGE_TTL), - (Boolean) properties.getOrDefault("retryClientErrors", false), - (Integer) properties.getOrDefault("messageBackoff", DEFAULT_MESSAGE_BACKOFF), - (Integer) properties.getOrDefault("requestTimeout", DEFAULT_REQUEST_TIMEOUT), - (Integer) properties.getOrDefault("batchSize", DEFAULT_BATCH_SIZE), - (Integer) properties.getOrDefault("batchTime", DEFAULT_BATCH_TIME), - (Integer) properties.getOrDefault("batchVolume", DEFAULT_BATCH_VOLUME) - ); + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final BatchSubscriptionPolicy other = (BatchSubscriptionPolicy) obj; + return Objects.equals(this.messageTtl, other.messageTtl) + && Objects.equals(this.retryClientErrors, other.retryClientErrors) + && Objects.equals(this.messageBackoff, other.messageBackoff) + && Objects.equals(this.requestTimeout, other.requestTimeout) + && Objects.equals(this.batchSize, other.batchSize) + && Objects.equals(this.batchTime, other.batchTime) + && Objects.equals(this.batchVolume, other.batchVolume); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("messageTtl", messageTtl) + .add("messageBackoff", messageBackoff) + .add("retryClientErrors", retryClientErrors) + .add("batchSize", batchSize) + .add("batchTime", batchTime) + .add("batchVolume", batchVolume) + .add("requestTimeout", requestTimeout) + .toString(); + } + + public Integer getMessageTtl() { + return messageTtl; + } + + public Integer getMessageBackoff() { + return messageBackoff; + } + + public Boolean isRetryClientErrors() { + return retryClientErrors; + } + + public Integer getBatchSize() { + return batchSize; + } + + public Integer getBatchTime() { + return batchTime; + } + + public Integer getBatchVolume() { + return batchVolume; + } + + public Integer getRequestTimeout() { + return requestTimeout; + } + + public static class Builder { + + private BatchSubscriptionPolicy subscriptionPolicy; + + public static Builder batchSubscriptionPolicy() { + return new Builder(); } - @Override - public int hashCode() { - return Objects.hash(messageTtl, retryClientErrors, messageBackoff, requestTimeout, batchSize, batchTime, batchVolume); + public Builder() { + subscriptionPolicy = new BatchSubscriptionPolicy(); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final BatchSubscriptionPolicy other = (BatchSubscriptionPolicy) obj; - return Objects.equals(this.messageTtl, other.messageTtl) - && Objects.equals(this.retryClientErrors, other.retryClientErrors) - && Objects.equals(this.messageBackoff, other.messageBackoff) - && Objects.equals(this.requestTimeout, other.requestTimeout) - && Objects.equals(this.batchSize, other.batchSize) - && Objects.equals(this.batchTime, other.batchTime) - && Objects.equals(this.batchVolume, other.batchVolume); + public Builder withMessageTtl(int messageTtl) { + subscriptionPolicy.messageTtl = messageTtl; + return this; } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("messageTtl", messageTtl) - .add("messageBackoff", messageBackoff) - .add("retryClientErrors", retryClientErrors) - .add("batchSize", batchSize) - .add("batchTime", batchTime) - .add("batchVolume", batchVolume) - .add("requestTimeout", requestTimeout) - .toString(); + public Builder withMessageBackoff(int messageBackoff) { + subscriptionPolicy.messageBackoff = messageBackoff; + return this; } - public Integer getMessageTtl() { - return messageTtl; + public Builder withClientErrorRetry(boolean retryClientErrors) { + subscriptionPolicy.retryClientErrors = retryClientErrors; + return this; } - public Integer getMessageBackoff() { - return messageBackoff; + public Builder withBatchSize(int batchSize) { + subscriptionPolicy.batchSize = batchSize; + return this; } - public Boolean isRetryClientErrors() { - return retryClientErrors; + public Builder withBatchTime(int batchTime) { + subscriptionPolicy.batchTime = batchTime; + return this; } - public Integer getBatchSize() { - return batchSize; + public Builder withBatchVolume(int batchVolume) { + subscriptionPolicy.batchVolume = batchVolume; + return this; } - public Integer getBatchTime() { - return batchTime; + public Builder withRequestTimeout(int requestTimeout) { + subscriptionPolicy.requestTimeout = requestTimeout; + return this; } - public Integer getBatchVolume() { - return batchVolume; + public BatchSubscriptionPolicy build() { + return new BatchSubscriptionPolicy( + subscriptionPolicy.messageTtl, + subscriptionPolicy.retryClientErrors, + subscriptionPolicy.messageBackoff, + subscriptionPolicy.requestTimeout, + subscriptionPolicy.batchSize, + subscriptionPolicy.batchTime, + subscriptionPolicy.batchVolume); } - public Integer getRequestTimeout() { - return requestTimeout; + public Builder applyDefaults() { + return this; } - public static class Builder { - - private BatchSubscriptionPolicy subscriptionPolicy; - - public static Builder batchSubscriptionPolicy() { - return new Builder(); - } - - public Builder() { - subscriptionPolicy = new BatchSubscriptionPolicy(); - } - - public Builder withMessageTtl(int messageTtl) { - subscriptionPolicy.messageTtl = messageTtl; - return this; - } - - public Builder withMessageBackoff(int messageBackoff) { - subscriptionPolicy.messageBackoff = messageBackoff; - return this; - } - - public Builder withClientErrorRetry(boolean retryClientErrors) { - subscriptionPolicy.retryClientErrors = retryClientErrors; - return this; - } - - public Builder withBatchSize(int batchSize) { - subscriptionPolicy.batchSize = batchSize; - return this; - } - - public Builder withBatchTime(int batchTime) { - subscriptionPolicy.batchTime = batchTime; - return this; - } - - public Builder withBatchVolume(int batchVolume) { - subscriptionPolicy.batchVolume = batchVolume; - return this; - } - - public Builder withRequestTimeout(int requestTimeout) { - subscriptionPolicy.requestTimeout = requestTimeout; - return this; - } - - public BatchSubscriptionPolicy build() { - return new BatchSubscriptionPolicy( - subscriptionPolicy.messageTtl, - subscriptionPolicy.retryClientErrors, - subscriptionPolicy.messageBackoff, - subscriptionPolicy.requestTimeout, - subscriptionPolicy.batchSize, - subscriptionPolicy.batchTime, - subscriptionPolicy.batchVolume); - } - - public Builder applyDefaults() { - return this; - } - - public Builder applyPatch(PatchData patch) { - if (patch != null) { - subscriptionPolicy = Patch.apply(subscriptionPolicy, patch); - } - return this; - } + public Builder applyPatch(PatchData patch) { + if (patch != null) { + subscriptionPolicy = Patch.apply(subscriptionPolicy, patch); + } + return this; } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BlacklistStatus.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BlacklistStatus.java index 8be8d8d53b..250b9a54f5 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BlacklistStatus.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/BlacklistStatus.java @@ -2,39 +2,38 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public final class BlacklistStatus { - public static final BlacklistStatus BLACKLISTED = new BlacklistStatus(true); - public static final BlacklistStatus NOT_BLACKLISTED = new BlacklistStatus(false); + public static final BlacklistStatus BLACKLISTED = new BlacklistStatus(true); + public static final BlacklistStatus NOT_BLACKLISTED = new BlacklistStatus(false); - private final boolean blacklisted; + private final boolean blacklisted; - @JsonCreator - private BlacklistStatus(@JsonProperty("blacklisted") boolean blacklisted) { - this.blacklisted = blacklisted; - } + @JsonCreator + private BlacklistStatus(@JsonProperty("blacklisted") boolean blacklisted) { + this.blacklisted = blacklisted; + } - public boolean isBlacklisted() { - return blacklisted; - } + public boolean isBlacklisted() { + return blacklisted; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BlacklistStatus that = (BlacklistStatus) o; - return blacklisted == that.blacklisted; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(blacklisted); + if (o == null || getClass() != o.getClass()) { + return false; } + BlacklistStatus that = (BlacklistStatus) o; + return blacklisted == that.blacklisted; + } + + @Override + public int hashCode() { + return Objects.hash(blacklisted); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Constraints.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Constraints.java index 60d71c6e40..fcf82f100a 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Constraints.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Constraints.java @@ -3,37 +3,36 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.Min; - import java.util.Objects; public class Constraints { - @Min(1) - private final int consumersNumber; + @Min(1) + private final int consumersNumber; - @JsonCreator - public Constraints(@JsonProperty("consumersNumber") int consumersNumber) { - this.consumersNumber = consumersNumber; - } + @JsonCreator + public Constraints(@JsonProperty("consumersNumber") int consumersNumber) { + this.consumersNumber = consumersNumber; + } - public int getConsumersNumber() { - return consumersNumber; - } + public int getConsumersNumber() { + return consumersNumber; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Constraints that = (Constraints) o; - return consumersNumber == that.consumersNumber; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(consumersNumber); + if (o == null || getClass() != o.getClass()) { + return false; } + Constraints that = (Constraints) o; + return consumersNumber == that.consumersNumber; + } + + @Override + public int hashCode() { + return Objects.hash(consumersNumber); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroup.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroup.java index d7acd8e37e..61528237df 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroup.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroup.java @@ -2,62 +2,62 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Set; public class ConsumerGroup { - private final String clusterName; - private final String groupId; - private final String state; - - private final Set members; - - @JsonCreator - public ConsumerGroup(@JsonProperty("clusterName") String clusterName, - @JsonProperty("groupId") String groupId, - @JsonProperty("state") String state, - @JsonProperty("members") Set members) { - this.clusterName = clusterName; - this.groupId = groupId; - this.state = state; - this.members = members; - } - - public String getClusterName() { - return clusterName; - } - - public String getGroupId() { - return groupId; - } - - public String getState() { - return state; - } - - public Set getMembers() { - return members; + private final String clusterName; + private final String groupId; + private final String state; + + private final Set members; + + @JsonCreator + public ConsumerGroup( + @JsonProperty("clusterName") String clusterName, + @JsonProperty("groupId") String groupId, + @JsonProperty("state") String state, + @JsonProperty("members") Set members) { + this.clusterName = clusterName; + this.groupId = groupId; + this.state = state; + this.members = members; + } + + public String getClusterName() { + return clusterName; + } + + public String getGroupId() { + return groupId; + } + + public String getState() { + return state; + } + + public Set getMembers() { + return members; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConsumerGroup that = (ConsumerGroup) o; - return Objects.equals(clusterName, that.clusterName) - && Objects.equals(groupId, that.groupId) - && Objects.equals(state, that.state) - && Objects.equals(members, that.members); - } - - @Override - public int hashCode() { - return Objects.hash(clusterName, groupId, state, members); + if (o == null || getClass() != o.getClass()) { + return false; } + ConsumerGroup that = (ConsumerGroup) o; + return Objects.equals(clusterName, that.clusterName) + && Objects.equals(groupId, that.groupId) + && Objects.equals(state, that.state) + && Objects.equals(members, that.members); + } + + @Override + public int hashCode() { + return Objects.hash(clusterName, groupId, state, members); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroupMember.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroupMember.java index 62348cb9b6..94d0491b9d 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroupMember.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ConsumerGroupMember.java @@ -2,61 +2,61 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Set; public class ConsumerGroupMember { - private final String consumerId; - private final String clientId; - private final String host; - - private final Set partitions; - - @JsonCreator - public ConsumerGroupMember(@JsonProperty("consumerId") String consumerId, - @JsonProperty("clientId") String clientId, - @JsonProperty("host")String host, - @JsonProperty("partitions") Set partitions) { - this.consumerId = consumerId; - this.clientId = clientId; - this.host = host; - this.partitions = partitions; - } - - public String getConsumerId() { - return consumerId; - } - - public String getClientId() { - return clientId; - } - - public String getHost() { - return host; - } - - public Set getPartitions() { - return partitions; + private final String consumerId; + private final String clientId; + private final String host; + + private final Set partitions; + + @JsonCreator + public ConsumerGroupMember( + @JsonProperty("consumerId") String consumerId, + @JsonProperty("clientId") String clientId, + @JsonProperty("host") String host, + @JsonProperty("partitions") Set partitions) { + this.consumerId = consumerId; + this.clientId = clientId; + this.host = host; + this.partitions = partitions; + } + + public String getConsumerId() { + return consumerId; + } + + public String getClientId() { + return clientId; + } + + public String getHost() { + return host; + } + + public Set getPartitions() { + return partitions; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConsumerGroupMember that = (ConsumerGroupMember) o; - return Objects.equals(consumerId, that.consumerId) - && Objects.equals(clientId, that.clientId) - && Objects.equals(host, that.host) - && Objects.equals(partitions, that.partitions); - } - - @Override - public int hashCode() { - return Objects.hash(consumerId, clientId, host, partitions); + if (o == null || getClass() != o.getClass()) { + return false; } + ConsumerGroupMember that = (ConsumerGroupMember) o; + return Objects.equals(consumerId, that.consumerId) + && Objects.equals(clientId, that.clientId) + && Objects.equals(host, that.host) + && Objects.equals(partitions, that.partitions); + } + + @Override + public int hashCode() { + return Objects.hash(consumerId, clientId, host, partitions); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ContentType.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ContentType.java index 383f1cdf47..1980a07a1e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ContentType.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ContentType.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.api; public enum ContentType { - JSON, AVRO + JSON, + AVRO } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroupDescription.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroupDescription.java deleted file mode 100644 index 8e0a65db96..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroupDescription.java +++ /dev/null @@ -1,18 +0,0 @@ -package pl.allegro.tech.hermes.api; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class CrowdGroupDescription { - - private final String name; - - public CrowdGroupDescription(@JsonProperty("name") String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroups.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroups.java deleted file mode 100644 index 16bc708a10..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/CrowdGroups.java +++ /dev/null @@ -1,20 +0,0 @@ -package pl.allegro.tech.hermes.api; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.List; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class CrowdGroups { - - private final List crowdGroupDescriptions; - - public CrowdGroups(@JsonProperty("groups") List crowdGroupDescriptions) { - this.crowdGroupDescriptions = crowdGroupDescriptions; - } - - public List getCrowdGroupDescriptions() { - return crowdGroupDescriptions; - } -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DatacenterReadiness.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DatacenterReadiness.java index 024c9d32b1..279bffc33e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DatacenterReadiness.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DatacenterReadiness.java @@ -2,57 +2,52 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class DatacenterReadiness { - private final String datacenter; - private final ReadinessStatus status; - - @JsonCreator - public DatacenterReadiness(@JsonProperty("datacenter") String datacenter, - @JsonProperty("status") ReadinessStatus status) { - this.datacenter = datacenter; - this.status = status; - } - - public String getDatacenter() { - return datacenter; - } - - public ReadinessStatus getStatus() { - return status; - } - - @Override - public String toString() { - return "DatacenterReadiness{" - + "datacenter='" + datacenter + '\'' - + ", status=" + status - + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DatacenterReadiness)) { - return false; - } - DatacenterReadiness that = (DatacenterReadiness) o; - return status == that.status - && Objects.equals(datacenter, that.datacenter); + private final String datacenter; + private final ReadinessStatus status; + + @JsonCreator + public DatacenterReadiness( + @JsonProperty("datacenter") String datacenter, + @JsonProperty("status") ReadinessStatus status) { + this.datacenter = datacenter; + this.status = status; + } + + public String getDatacenter() { + return datacenter; + } + + public ReadinessStatus getStatus() { + return status; + } + + @Override + public String toString() { + return "DatacenterReadiness{" + "datacenter='" + datacenter + '\'' + ", status=" + status + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(datacenter, status); - } - - public enum ReadinessStatus { - READY, - NOT_READY, - UNDEFINED + if (!(o instanceof DatacenterReadiness)) { + return false; } + DatacenterReadiness that = (DatacenterReadiness) o; + return status == that.status && Objects.equals(datacenter, that.datacenter); + } + + @Override + public int hashCode() { + return Objects.hash(datacenter, status); + } + + public enum ReadinessStatus { + READY, + NOT_READY + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DeliveryType.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DeliveryType.java index 9edad2adb3..ade2c08947 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DeliveryType.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/DeliveryType.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.api; public enum DeliveryType { - SERIAL, BATCH + SERIAL, + BATCH } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddress.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddress.java index 140a932de1..0dfdeacff9 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddress.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddress.java @@ -5,145 +5,145 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import pl.allegro.tech.hermes.api.jackson.EndpointAddressDeserializer; -import pl.allegro.tech.hermes.api.jackson.EndpointAddressSerializer; - import java.net.URI; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import pl.allegro.tech.hermes.api.jackson.EndpointAddressDeserializer; +import pl.allegro.tech.hermes.api.jackson.EndpointAddressSerializer; @JsonDeserialize(using = EndpointAddressDeserializer.class) @JsonSerialize(using = EndpointAddressSerializer.class) public class EndpointAddress implements Anonymizable { - private static final String ANONYMIZED_PASSWORD = "*****"; + private static final String ANONYMIZED_PASSWORD = "*****"; - private static final Pattern URL_PATTERN = Pattern.compile("([a-zA-Z0-9]*)://(([a-zA-Z0-9\\.\\~\\-\\_]*):(.*)@)?(.*)"); + private static final Pattern URL_PATTERN = + Pattern.compile("([a-zA-Z0-9]*)://(([a-zA-Z0-9\\.\\~\\-\\_]*):(.*)@)?(.*)"); - private static final int PROTOCOL_GROUP = 1; + private static final int PROTOCOL_GROUP = 1; - private static final int ADDRESS_GROUP = 5; + private static final int ADDRESS_GROUP = 5; - private static final int USER_INFO_GROUP = 2; + private static final int USER_INFO_GROUP = 2; - private static final int USERNAME_GROUP = 3; + private static final int USERNAME_GROUP = 3; - private static final int PASSWORD_GROUP = 4; + private static final int PASSWORD_GROUP = 4; - private final boolean containsCredentials; + private final boolean containsCredentials; - private final String protocol; + private final String protocol; - private final String username; + private final String username; - private final String password; + private final String password; - private final String endpoint; + private final String endpoint; - private final String rawEndpoint; + private final String rawEndpoint; - public EndpointAddress(String endpoint) { - this.rawEndpoint = endpoint; + public EndpointAddress(String endpoint) { + this.rawEndpoint = endpoint; - Matcher matcher = URL_PATTERN.matcher(endpoint); - if (matcher.matches()) { - this.protocol = matcher.group(PROTOCOL_GROUP); - this.containsCredentials = !Strings.isNullOrEmpty(matcher.group(USER_INFO_GROUP)); + Matcher matcher = URL_PATTERN.matcher(endpoint); + if (matcher.matches()) { + this.protocol = matcher.group(PROTOCOL_GROUP); + this.containsCredentials = !Strings.isNullOrEmpty(matcher.group(USER_INFO_GROUP)); - this.username = containsCredentials ? matcher.group(USERNAME_GROUP) : null; - this.password = containsCredentials ? matcher.group(PASSWORD_GROUP) : null; + this.username = containsCredentials ? matcher.group(USERNAME_GROUP) : null; + this.password = containsCredentials ? matcher.group(PASSWORD_GROUP) : null; - this.endpoint = containsCredentials ? protocol + "://" + matcher.group(ADDRESS_GROUP) : endpoint; - } else { - this.protocol = null; - this.containsCredentials = false; - this.username = null; - this.password = null; - this.endpoint = endpoint; - } + this.endpoint = + containsCredentials ? protocol + "://" + matcher.group(ADDRESS_GROUP) : endpoint; + } else { + this.protocol = null; + this.containsCredentials = false; + this.username = null; + this.password = null; + this.endpoint = endpoint; } + } - private EndpointAddress(String protocol, String endpoint, String username) { - this.protocol = protocol; - this.endpoint = endpoint; - this.containsCredentials = true; - this.username = username; - this.password = ANONYMIZED_PASSWORD; + private EndpointAddress(String protocol, String endpoint, String username) { + this.protocol = protocol; + this.endpoint = endpoint; + this.containsCredentials = true; + this.username = username; + this.password = ANONYMIZED_PASSWORD; - this.rawEndpoint = protocol + "://" + username + ":" + password + "@" + endpoint.replace(protocol + "://", ""); - } + this.rawEndpoint = + protocol + "://" + username + ":" + password + "@" + endpoint.replace(protocol + "://", ""); + } - public static EndpointAddress of(String endpoint) { - return new EndpointAddress(endpoint); - } + public static EndpointAddress of(String endpoint) { + return new EndpointAddress(endpoint); + } - public static EndpointAddress of(URI endpoint) { - return new EndpointAddress(endpoint.toString()); - } + public static EndpointAddress of(URI endpoint) { + return new EndpointAddress(endpoint.toString()); + } - public static String extractProtocolFromAddress(String endpoint) { - Preconditions.checkArgument(endpoint.indexOf(':') != -1); + public static String extractProtocolFromAddress(String endpoint) { + Preconditions.checkArgument(endpoint.indexOf(':') != -1); - return endpoint.substring(0, endpoint.indexOf(':')); - } + return endpoint.substring(0, endpoint.indexOf(':')); + } - public String getEndpoint() { - return endpoint; - } + public String getEndpoint() { + return endpoint; + } - public String getRawEndpoint() { - return rawEndpoint; - } + public String getRawEndpoint() { + return rawEndpoint; + } - public URI getUri() { - return URI.create(endpoint); - } + public URI getUri() { + return URI.create(endpoint); + } - public String getProtocol() { - return protocol; - } + public String getProtocol() { + return protocol; + } - @Override - public int hashCode() { - return Objects.hash(rawEndpoint); - } + @Override + public int hashCode() { + return Objects.hash(rawEndpoint); + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final EndpointAddress other = (EndpointAddress) obj; - return Objects.equals(this.rawEndpoint, other.rawEndpoint); + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("endpoint", endpoint) - .toString(); + if (obj == null || getClass() != obj.getClass()) { + return false; } + final EndpointAddress other = (EndpointAddress) obj; + return Objects.equals(this.rawEndpoint, other.rawEndpoint); + } - public boolean containsCredentials() { - return containsCredentials; - } + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("endpoint", endpoint).toString(); + } - public String getPassword() { - return password; - } + public boolean containsCredentials() { + return containsCredentials; + } - public String getUsername() { - return username; - } + public String getPassword() { + return password; + } - public EndpointAddress anonymize() { - if (containsCredentials) { - return new EndpointAddress(protocol, endpoint, username); - } - return this; - } + public String getUsername() { + return username; + } + + public EndpointAddress anonymize() { + if (containsCredentials) { + return new EndpointAddress(protocol, endpoint, username); + } + return this; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddressResolverMetadata.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddressResolverMetadata.java index 8cd3f20bd8..cf863ee8d9 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddressResolverMetadata.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/EndpointAddressResolverMetadata.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.google.common.collect.ImmutableMap; import jakarta.validation.constraints.NotNull; - import java.io.IOException; import java.util.Collections; import java.util.HashMap; @@ -15,81 +14,84 @@ import java.util.Objects; import java.util.Optional; -@JsonSerialize(using = EndpointAddressResolverMetadata.EndpointAddressResolverMetadataSerializer.class) +@JsonSerialize( + using = EndpointAddressResolverMetadata.EndpointAddressResolverMetadataSerializer.class) public class EndpointAddressResolverMetadata { - private static final EndpointAddressResolverMetadata EMPTY_INSTANCE = new EndpointAddressResolverMetadata(Collections.emptyMap()); + private static final EndpointAddressResolverMetadata EMPTY_INSTANCE = + new EndpointAddressResolverMetadata(Collections.emptyMap()); - @NotNull - private Map entries; + @NotNull private Map entries; - @JsonCreator - public EndpointAddressResolverMetadata(Map entries) { - this.entries = ImmutableMap.copyOf(entries); - } + @JsonCreator + public EndpointAddressResolverMetadata(Map entries) { + this.entries = ImmutableMap.copyOf(entries); + } - public static EndpointAddressResolverMetadata empty() { - return EMPTY_INSTANCE; - } + public static EndpointAddressResolverMetadata empty() { + return EMPTY_INSTANCE; + } - public static Builder endpointAddressResolverMetadata() { - return new Builder(); - } + public static Builder endpointAddressResolverMetadata() { + return new Builder(); + } - public Optional get(String key) { - return Optional.ofNullable(entries.get(key)); - } + public Optional get(String key) { + return Optional.ofNullable(entries.get(key)); + } - @SuppressWarnings("unchecked") - public T getOrDefault(String key, T defaultValue) { - return (T) entries.getOrDefault(key, defaultValue); - } + @SuppressWarnings("unchecked") + public T getOrDefault(String key, T defaultValue) { + return (T) entries.getOrDefault(key, defaultValue); + } - public Map getEntries() { - return entries; - } + public Map getEntries() { + return entries; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - EndpointAddressResolverMetadata that = (EndpointAddressResolverMetadata) o; - return Objects.equals(entries, that.entries); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(entries); + if (o == null || getClass() != o.getClass()) { + return false; } + EndpointAddressResolverMetadata that = (EndpointAddressResolverMetadata) o; + return Objects.equals(entries, that.entries); + } - public static class EndpointAddressResolverMetadataSerializer extends StdSerializer { + @Override + public int hashCode() { + return Objects.hash(entries); + } - protected EndpointAddressResolverMetadataSerializer() { - super(EndpointAddressResolverMetadata.class); - } + public static class EndpointAddressResolverMetadataSerializer + extends StdSerializer { - @Override - public void serialize(EndpointAddressResolverMetadata metadata, JsonGenerator jgen, SerializerProvider provider) - throws IOException { - jgen.writeObject(metadata.entries); - } + protected EndpointAddressResolverMetadataSerializer() { + super(EndpointAddressResolverMetadata.class); } - public static class Builder { + @Override + public void serialize( + EndpointAddressResolverMetadata metadata, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeObject(metadata.entries); + } + } + + public static class Builder { - private Map entries = new HashMap<>(); + private Map entries = new HashMap<>(); - public Builder withEntry(String key, Object value) { - entries.put(key, value); - return this; - } + public Builder withEntry(String key, Object value) { + entries.put(key, value); + return this; + } - public EndpointAddressResolverMetadata build() { - return new EndpointAddressResolverMetadata(entries); - } + public EndpointAddressResolverMetadata build() { + return new EndpointAddressResolverMetadata(entries); } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorCode.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorCode.java index 9e48226597..a000f436e1 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorCode.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorCode.java @@ -1,81 +1,81 @@ package pl.allegro.tech.hermes.api; -import jakarta.ws.rs.BadRequestException; -import jakarta.ws.rs.core.Response; - import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.FORBIDDEN; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static jakarta.ws.rs.core.Response.Status.NOT_ACCEPTABLE; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; +import static jakarta.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE; + +import jakarta.ws.rs.core.Response; public enum ErrorCode { - TIMEOUT(REQUEST_TIMEOUT), - TOPIC_ALREADY_EXISTS(BAD_REQUEST), - TOPIC_NOT_EXISTS(NOT_FOUND), - GROUP_NOT_EXISTS(NOT_FOUND), - GROUP_NAME_IS_INVALID(BAD_REQUEST), - SUBSCRIPTION_NOT_EXISTS(BAD_REQUEST), - SUBSCRIPTION_ALREADY_EXISTS(BAD_REQUEST), - VALIDATION_ERROR(BAD_REQUEST), - INTERNAL_ERROR(INTERNAL_SERVER_ERROR), - FORMAT_ERROR(BAD_REQUEST), - GROUP_NOT_EMPTY(FORBIDDEN), - TOPIC_NOT_EMPTY(FORBIDDEN), - GROUP_ALREADY_EXISTS(BAD_REQUEST), - OPERATION_DISABLED(NOT_ACCEPTABLE), - OTHER(INTERNAL_SERVER_ERROR), - UNAVAILABLE_RATE(BAD_REQUEST), - SINGLE_MESSAGE_READER_EXCEPTION(INTERNAL_SERVER_ERROR), - PARTITIONS_NOT_FOUND_FOR_TOPIC(NOT_FOUND), - OFFSET_NOT_FOUND_EXCEPTION(NOT_FOUND), - OFFSETS_NOT_AVAILABLE_EXCEPTION(INTERNAL_SERVER_ERROR), - UNABLE_TO_MOVE_OFFSETS_EXCEPTION(INTERNAL_SERVER_ERROR), - BROKERS_CLUSTER_NOT_FOUND_EXCEPTION(NOT_FOUND), - BROKERS_CLUSTER_COMMUNICATION_EXCEPTION(INTERNAL_SERVER_ERROR), - SIMPLE_CONSUMER_POOL_EXCEPTION(INTERNAL_SERVER_ERROR), - RETRANSMISSION_EXCEPTION(INTERNAL_SERVER_ERROR), - TOKEN_NOT_PROVIDED(FORBIDDEN), - GROUP_NOT_PROVIDED(FORBIDDEN), - AUTH_ERROR(FORBIDDEN), - SCHEMA_REPOSITORY_INTERNAL_ERROR(INTERNAL_SERVER_ERROR), - SCHEMA_BAD_REQUEST(BAD_REQUEST), - SCHEMA_COULD_NOT_BE_LOADED(INTERNAL_SERVER_ERROR), - SCHEMA_VERSION_DOES_NOT_EXIST(BAD_REQUEST), - SCHEMA_ALREADY_EXISTS(BAD_REQUEST), - AVRO_SCHEMA_INVALID_METADATA(BAD_REQUEST), - SUBSCRIPTION_ENDPOINT_ADDRESS_CHANGE_EXCEPTION(INTERNAL_SERVER_ERROR), - OAUTH_PROVIDER_NOT_EXISTS(NOT_FOUND), - OAUTH_PROVIDER_ALREADY_EXISTS(BAD_REQUEST), - CROWD_GROUPS_COULD_NOT_BE_LOADED(INTERNAL_SERVER_ERROR), - TOPIC_BLACKLISTED(FORBIDDEN), - THROUGHPUT_QUOTA_VIOLATION(429), - TOPIC_NOT_UNBLACKLISTED(BAD_REQUEST), - TOPIC_CONSTRAINTS_ALREADY_EXIST(BAD_REQUEST), - TOPIC_CONSTRAINTS_DO_NOT_EXIST(BAD_REQUEST), - SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST(BAD_REQUEST), - SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST(BAD_REQUEST), - OWNER_SOURCE_NOT_FOUND(NOT_FOUND), - OWNER_SOURCE_DOESNT_SUPPORT_AUTOCOMPLETE(BAD_REQUEST), - OWNER_NOT_FOUND(NOT_FOUND), - PERMISSION_DENIED(FORBIDDEN), - UNKNOWN_MIGRATION(NOT_FOUND), - INVALID_QUERY(BAD_REQUEST), - IMPLEMENTATION_ABSENT(NOT_FOUND), - MOVING_SUBSCRIPTION_OFFSETS_VALIDATION_ERROR(BAD_REQUEST); + TIMEOUT(REQUEST_TIMEOUT), + TOPIC_ALREADY_EXISTS(BAD_REQUEST), + TOPIC_NOT_EXISTS(NOT_FOUND), + GROUP_NOT_EXISTS(NOT_FOUND), + GROUP_NAME_IS_INVALID(BAD_REQUEST), + SUBSCRIPTION_NOT_EXISTS(BAD_REQUEST), + SUBSCRIPTION_ALREADY_EXISTS(BAD_REQUEST), + VALIDATION_ERROR(BAD_REQUEST), + INTERNAL_ERROR(INTERNAL_SERVER_ERROR), + FORMAT_ERROR(BAD_REQUEST), + GROUP_NOT_EMPTY(FORBIDDEN), + TOPIC_NOT_EMPTY(FORBIDDEN), + GROUP_ALREADY_EXISTS(BAD_REQUEST), + OPERATION_DISABLED(NOT_ACCEPTABLE), + OTHER(INTERNAL_SERVER_ERROR), + UNAVAILABLE_RATE(BAD_REQUEST), + SINGLE_MESSAGE_READER_EXCEPTION(INTERNAL_SERVER_ERROR), + PARTITIONS_NOT_FOUND_FOR_TOPIC(NOT_FOUND), + OFFSET_NOT_FOUND_EXCEPTION(NOT_FOUND), + OFFSETS_NOT_AVAILABLE_EXCEPTION(INTERNAL_SERVER_ERROR), + UNABLE_TO_MOVE_OFFSETS_EXCEPTION(INTERNAL_SERVER_ERROR), + BROKERS_CLUSTER_NOT_FOUND_EXCEPTION(NOT_FOUND), + BROKERS_CLUSTER_COMMUNICATION_EXCEPTION(INTERNAL_SERVER_ERROR), + SIMPLE_CONSUMER_POOL_EXCEPTION(INTERNAL_SERVER_ERROR), + RETRANSMISSION_EXCEPTION(INTERNAL_SERVER_ERROR), + TOKEN_NOT_PROVIDED(FORBIDDEN), + GROUP_NOT_PROVIDED(FORBIDDEN), + AUTH_ERROR(FORBIDDEN), + SCHEMA_REPOSITORY_INTERNAL_ERROR(INTERNAL_SERVER_ERROR), + SCHEMA_BAD_REQUEST(BAD_REQUEST), + SCHEMA_COULD_NOT_BE_LOADED(INTERNAL_SERVER_ERROR), + SCHEMA_VERSION_DOES_NOT_EXIST(BAD_REQUEST), + SCHEMA_ALREADY_EXISTS(BAD_REQUEST), + AVRO_SCHEMA_INVALID_METADATA(BAD_REQUEST), + SUBSCRIPTION_ENDPOINT_ADDRESS_CHANGE_EXCEPTION(INTERNAL_SERVER_ERROR), + OAUTH_PROVIDER_NOT_EXISTS(NOT_FOUND), + OAUTH_PROVIDER_ALREADY_EXISTS(BAD_REQUEST), + TOPIC_BLACKLISTED(FORBIDDEN), + THROUGHPUT_QUOTA_VIOLATION(429), + TOPIC_NOT_UNBLACKLISTED(BAD_REQUEST), + TOPIC_CONSTRAINTS_ALREADY_EXIST(BAD_REQUEST), + TOPIC_CONSTRAINTS_DO_NOT_EXIST(BAD_REQUEST), + SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST(BAD_REQUEST), + SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST(BAD_REQUEST), + OWNER_SOURCE_NOT_FOUND(NOT_FOUND), + OWNER_SOURCE_DOESNT_SUPPORT_AUTOCOMPLETE(BAD_REQUEST), + OWNER_NOT_FOUND(NOT_FOUND), + PERMISSION_DENIED(FORBIDDEN), + UNKNOWN_MIGRATION(NOT_FOUND), + INVALID_QUERY(BAD_REQUEST), + IMPLEMENTATION_ABSENT(NOT_FOUND), + MOVING_SUBSCRIPTION_OFFSETS_VALIDATION_ERROR(BAD_REQUEST), + SENDING_TO_KAFKA_TIMEOUT(SERVICE_UNAVAILABLE); - private final int httpCode; + private final int httpCode; - ErrorCode(Response.Status httpCode) { - this.httpCode = httpCode.getStatusCode(); - } + ErrorCode(Response.Status httpCode) { + this.httpCode = httpCode.getStatusCode(); + } - ErrorCode(int httpCode) { - this.httpCode = httpCode; - } + ErrorCode(int httpCode) { + this.httpCode = httpCode; + } - public int getHttpCode() { - return httpCode; - } + public int getHttpCode() { + return httpCode; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorDescription.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorDescription.java index 2e542f4ec2..bb4038fcd2 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorDescription.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/ErrorDescription.java @@ -5,24 +5,25 @@ public class ErrorDescription { - private final String message; - private final ErrorCode code; + private final String message; + private final ErrorCode code; - public static ErrorDescription error(String message, ErrorCode errorCode) { - return new ErrorDescription(message, errorCode); - } + public static ErrorDescription error(String message, ErrorCode errorCode) { + return new ErrorDescription(message, errorCode); + } - @JsonCreator - public ErrorDescription(@JsonProperty("message") String message, @JsonProperty("code") ErrorCode code) { - this.message = message; - this.code = code; - } + @JsonCreator + public ErrorDescription( + @JsonProperty("message") String message, @JsonProperty("code") ErrorCode code) { + this.message = message; + this.code = code; + } - public String getMessage() { - return message; - } + public String getMessage() { + return message; + } - public ErrorCode getCode() { - return code; - } + public ErrorCode getCode() { + return code; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Group.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Group.java index 6d58c2f8b2..e0a05eb857 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Group.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Group.java @@ -3,47 +3,45 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; - import java.util.Objects; public class Group { - @NotNull - private final String groupName; + @NotNull private final String groupName; - @JsonCreator - public Group(@JsonProperty("groupName") String groupName) { - this.groupName = groupName; - } + @JsonCreator + public Group(@JsonProperty("groupName") String groupName) { + this.groupName = groupName; + } - public static Group from(String groupName) { - return new Group(groupName); - } + public static Group from(String groupName) { + return new Group(groupName); + } - public String getGroupName() { - return groupName; - } + public String getGroupName() { + return groupName; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Group)) { - return false; - } - Group group = (Group) o; - - return Objects.equals(this.getGroupName(), group.getGroupName()); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(groupName); + if (!(o instanceof Group)) { + return false; } + Group group = (Group) o; - @Override - public String toString() { - return "Group(" + groupName + ")"; - } + return Objects.equals(this.getGroupName(), group.getGroupName()); + } + + @Override + public int hashCode() { + return Objects.hash(groupName); + } + + @Override + public String toString() { + return "Group(" + groupName + ")"; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Header.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Header.java index af96c6371e..9b11b464fa 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Header.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Header.java @@ -3,46 +3,42 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; - import java.util.Objects; public class Header { - @NotNull - private String name; + @NotNull private String name; - @NotNull - private String value; + @NotNull private String value; - @JsonCreator - public Header(@JsonProperty("name") String name, @JsonProperty("value") String value) { - this.name = name; - this.value = value; - } + @JsonCreator + public Header(@JsonProperty("name") String name, @JsonProperty("value") String value) { + this.name = name; + this.value = value; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Header header = (Header) o; - return Objects.equals(name, header.name) - && Objects.equals(value, header.value); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(name, value); + if (o == null || getClass() != o.getClass()) { + return false; } + Header header = (Header) o; + return Objects.equals(name, header.name) && Objects.equals(value, header.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, value); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentGroup.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentGroup.java index 2cf02419ed..8eaec6eabf 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentGroup.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentGroup.java @@ -2,32 +2,32 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; public class InconsistentGroup { - private final String name; - private final List inconsistentMetadata; - private final List inconsistentTopics; + private final String name; + private final List inconsistentMetadata; + private final List inconsistentTopics; - @JsonCreator - public InconsistentGroup(@JsonProperty("name") String name, - @JsonProperty("inconsistentMetadata") List inconsistentMetadata, - @JsonProperty("inconsistentTopics") List inconsistentTopics) { - this.name = name; - this.inconsistentMetadata = inconsistentMetadata; - this.inconsistentTopics = inconsistentTopics; - } + @JsonCreator + public InconsistentGroup( + @JsonProperty("name") String name, + @JsonProperty("inconsistentMetadata") List inconsistentMetadata, + @JsonProperty("inconsistentTopics") List inconsistentTopics) { + this.name = name; + this.inconsistentMetadata = inconsistentMetadata; + this.inconsistentTopics = inconsistentTopics; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public List getInconsistentMetadata() { - return inconsistentMetadata; - } + public List getInconsistentMetadata() { + return inconsistentMetadata; + } - public List getInconsistentTopics() { - return inconsistentTopics; - } + public List getInconsistentTopics() { + return inconsistentTopics; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentMetadata.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentMetadata.java index 27e6e9bc4d..e94ec573d0 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentMetadata.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentMetadata.java @@ -4,21 +4,21 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class InconsistentMetadata { - private final String datacenter; - private final String content; + private final String datacenter; + private final String content; - @JsonCreator - public InconsistentMetadata(@JsonProperty("datacenter") String datacenter, - @JsonProperty("content") String content) { - this.datacenter = datacenter; - this.content = content; - } + @JsonCreator + public InconsistentMetadata( + @JsonProperty("datacenter") String datacenter, @JsonProperty("content") String content) { + this.datacenter = datacenter; + this.content = content; + } - public String getDatacenter() { - return datacenter; - } + public String getDatacenter() { + return datacenter; + } - public String getContent() { - return content; - } + public String getContent() { + return content; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentSubscription.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentSubscription.java index 7418459f55..27e239078e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentSubscription.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentSubscription.java @@ -2,25 +2,25 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; public class InconsistentSubscription { - private final String name; - private final List inconsistentMetadata; + private final String name; + private final List inconsistentMetadata; - @JsonCreator - public InconsistentSubscription(@JsonProperty("name") String name, - @JsonProperty("inconsistentMetadata") List inconsistentMetadata) { - this.name = name; - this.inconsistentMetadata = inconsistentMetadata; - } + @JsonCreator + public InconsistentSubscription( + @JsonProperty("name") String name, + @JsonProperty("inconsistentMetadata") List inconsistentMetadata) { + this.name = name; + this.inconsistentMetadata = inconsistentMetadata; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public List getInconsistentMetadata() { - return inconsistentMetadata; - } + public List getInconsistentMetadata() { + return inconsistentMetadata; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentTopic.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentTopic.java index 3b4c609efc..549f30554e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentTopic.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/InconsistentTopic.java @@ -2,32 +2,33 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.List; public class InconsistentTopic { - private final String name; - private final List inconsistentMetadata; - private final List inconsistentSubscriptions; + private final String name; + private final List inconsistentMetadata; + private final List inconsistentSubscriptions; - @JsonCreator - public InconsistentTopic(@JsonProperty("name") String name, - @JsonProperty("inconsistentMetadata") List inconsistentMetadata, - @JsonProperty("inconsistentSubscriptions") List inconsistentSubscriptions) { - this.name = name; - this.inconsistentMetadata = inconsistentMetadata; - this.inconsistentSubscriptions = inconsistentSubscriptions; - } + @JsonCreator + public InconsistentTopic( + @JsonProperty("name") String name, + @JsonProperty("inconsistentMetadata") List inconsistentMetadata, + @JsonProperty("inconsistentSubscriptions") + List inconsistentSubscriptions) { + this.name = name; + this.inconsistentMetadata = inconsistentMetadata; + this.inconsistentSubscriptions = inconsistentSubscriptions; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public List getInconsistentMetadata() { - return inconsistentMetadata; - } + public List getInconsistentMetadata() { + return inconsistentMetadata; + } - public List getInconsistentSubscriptions() { - return inconsistentSubscriptions; - } + public List getInconsistentSubscriptions() { + return inconsistentSubscriptions; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFilterSpecification.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFilterSpecification.java index 018d8d7c11..bc0293ebbb 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFilterSpecification.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFilterSpecification.java @@ -2,69 +2,67 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - import java.util.Map; import java.util.Objects; public class MessageFilterSpecification { - private final String type; - private final Map spec; + private final String type; + private final Map spec; - @JsonCreator - public MessageFilterSpecification(Map spec) { - this.spec = spec; - this.type = getStringValue("type"); - } + @JsonCreator + public MessageFilterSpecification(Map spec) { + this.spec = spec; + this.type = getStringValue("type"); + } - public String getType() { - return type; - } + public String getType() { + return type; + } - public String getPath() { - return getStringValue("path"); - } + public String getPath() { + return getStringValue("path"); + } - public String getMatcher() { - return getStringValue("matcher"); - } + public String getMatcher() { + return getStringValue("matcher"); + } - public String getHeader() { - return getStringValue("header"); - } + public String getHeader() { + return getStringValue("header"); + } - public String getMatchingStrategy() { - return getStringValue("matchingStrategy"); - } + public String getMatchingStrategy() { + return getStringValue("matchingStrategy"); + } - @SuppressWarnings("unchecked") - public T getFieldValue(String key) { - return (T) spec.get(key); - } + @SuppressWarnings("unchecked") + public T getFieldValue(String key) { + return (T) spec.get(key); + } - public String getStringValue(String key) { - return getFieldValue(key); - } + public String getStringValue(String key) { + return getFieldValue(key); + } - @JsonValue - public Object getJsonValue() { - return spec; - } + @JsonValue + public Object getJsonValue() { + return spec; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MessageFilterSpecification that = (MessageFilterSpecification) o; - return Objects.equals(type, that.type) - && Objects.equals(spec, that.spec); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(type, spec); + if (o == null || getClass() != o.getClass()) { + return false; } + MessageFilterSpecification that = (MessageFilterSpecification) o; + return Objects.equals(type, that.type) && Objects.equals(spec, that.spec); + } + + @Override + public int hashCode() { + return Objects.hash(type, spec); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationInput.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationInput.java index eecb9e6dda..2d81c2d371 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationInput.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationInput.java @@ -3,27 +3,26 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; - import java.util.List; public class MessageFiltersVerificationInput { - private final List filters; + private final List filters; - @NotNull - private final byte[] message; + @NotNull private final byte[] message; - @JsonCreator - public MessageFiltersVerificationInput(@JsonProperty("filters") List filters, - @JsonProperty("message") byte[] message) { - this.filters = filters; - this.message = message; - } + @JsonCreator + public MessageFiltersVerificationInput( + @JsonProperty("filters") List filters, + @JsonProperty("message") byte[] message) { + this.filters = filters; + this.message = message; + } - public List getFilters() { - return filters; - } + public List getFilters() { + return filters; + } - public byte[] getMessage() { - return message; - } + public byte[] getMessage() { + return message; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationResult.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationResult.java index 17ea37cb0f..39f0bfb01f 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationResult.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageFiltersVerificationResult.java @@ -4,27 +4,28 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class MessageFiltersVerificationResult { - private final VerificationStatus status; - private final String errorMessage; + private final VerificationStatus status; + private final String errorMessage; - public enum VerificationStatus { - NOT_MATCHED, - MATCHED, - ERROR - } + public enum VerificationStatus { + NOT_MATCHED, + MATCHED, + ERROR + } - @JsonCreator - public MessageFiltersVerificationResult(@JsonProperty("status") VerificationStatus status, - @JsonProperty("errorMessage") String errorMessage) { - this.status = status; - this.errorMessage = errorMessage; - } + @JsonCreator + public MessageFiltersVerificationResult( + @JsonProperty("status") VerificationStatus status, + @JsonProperty("errorMessage") String errorMessage) { + this.status = status; + this.errorMessage = errorMessage; + } - public VerificationStatus getStatus() { - return status; - } + public VerificationStatus getStatus() { + return status; + } - public String getErrorMessage() { - return errorMessage; - } + public String getErrorMessage() { + return errorMessage; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTextPreview.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTextPreview.java index fe475434bd..c756ddb4e0 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTextPreview.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTextPreview.java @@ -5,21 +5,22 @@ public class MessageTextPreview { - private final String content; + private final String content; - private final boolean truncated; + private final boolean truncated; - @JsonCreator - public MessageTextPreview(@JsonProperty("content") String content, @JsonProperty("truncated") boolean truncated) { - this.content = content; - this.truncated = truncated; - } + @JsonCreator + public MessageTextPreview( + @JsonProperty("content") String content, @JsonProperty("truncated") boolean truncated) { + this.content = content; + this.truncated = truncated; + } - public String getContent() { - return content; - } + public String getContent() { + return content; + } - public boolean isTruncated() { - return truncated; - } + public boolean isTruncated() { + return truncated; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTrace.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTrace.java index d84f209e26..4250dc00bc 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTrace.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MessageTrace.java @@ -1,4 +1,3 @@ package pl.allegro.tech.hermes.api; -public interface MessageTrace { -} +public interface MessageTrace {} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricDecimalValue.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricDecimalValue.java index ff033e775a..3fa2e56fbe 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricDecimalValue.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricDecimalValue.java @@ -1,68 +1,76 @@ package pl.allegro.tech.hermes.api; +import static java.lang.Double.parseDouble; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - import java.util.Objects; -import static java.lang.Double.parseDouble; - public class MetricDecimalValue { - private static final String UNAVAILABLE_STRING = "unavailable"; - private static final MetricDecimalValue UNAVAILABLE = new MetricDecimalValue(false, "-1.0"); + private static final String UNAVAILABLE_STRING = "unavailable"; + private static final MetricDecimalValue UNAVAILABLE = new MetricDecimalValue(false, "-1.0"); + private static final MetricDecimalValue DEFAULT_VALUE = new MetricDecimalValue(true, "0.0"); - private final boolean available; - private final String value; + private final boolean available; + private final String value; - private MetricDecimalValue(boolean available, String value) { - this.available = available; - this.value = value; - } + private MetricDecimalValue(boolean available, String value) { + this.available = available; + this.value = value; + } - public static MetricDecimalValue unavailable() { - return UNAVAILABLE; - } + public static MetricDecimalValue unavailable() { + return UNAVAILABLE; + } - public static MetricDecimalValue of(String value) { - return new MetricDecimalValue(true, value); - } + public static MetricDecimalValue defaultValue() { + return DEFAULT_VALUE; + } - @JsonCreator - public static MetricDecimalValue deserialize(String value) { - if (UNAVAILABLE_STRING.equals(value)) { - return unavailable(); - } - return of(value); - } + public static MetricDecimalValue of(String value) { + return new MetricDecimalValue(true, value); + } - @JsonValue - public String asString() { - return available ? value : UNAVAILABLE_STRING; + @JsonCreator + public static MetricDecimalValue deserialize(String value) { + if (UNAVAILABLE_STRING.equals(value)) { + return unavailable(); } + return of(value); + } - public double toDouble() { - return parseDouble(value); - } + @JsonValue + public String asString() { + return available ? value : UNAVAILABLE_STRING; + } - public boolean isAvailable() { - return available; - } + public double toDouble() { + return parseDouble(value); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MetricDecimalValue that = (MetricDecimalValue) o; - return available == that.available - && Objects.equals(value, that.value); - } + public boolean isAvailable() { + return available; + } - @Override - public int hashCode() { - return Objects.hash(available, value); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetricDecimalValue that = (MetricDecimalValue) o; + return available == that.available && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(available, value); + } + + @Override + public String toString() { + return "MetricDecimalValue{" + "available=" + available + ", value='" + value + '\'' + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricLongValue.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricLongValue.java index 77753fb51a..648154b301 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricLongValue.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MetricLongValue.java @@ -2,65 +2,63 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; - import java.util.Objects; public class MetricLongValue { - private static final String UNAVAILABLE_STRING = "unavailable"; - private static final MetricLongValue UNAVAILABLE = new MetricLongValue(false, -1); + private static final String UNAVAILABLE_STRING = "unavailable"; + private static final MetricLongValue UNAVAILABLE = new MetricLongValue(false, -1); - private final boolean available; - private final long value; + private final boolean available; + private final long value; - private MetricLongValue(boolean available, long value) { - this.available = available; - this.value = value; - } + private MetricLongValue(boolean available, long value) { + this.available = available; + this.value = value; + } - public static MetricLongValue unavailable() { - return UNAVAILABLE; - } + public static MetricLongValue unavailable() { + return UNAVAILABLE; + } - public static MetricLongValue of(long value) { - return new MetricLongValue(true, value); - } + public static MetricLongValue of(long value) { + return new MetricLongValue(true, value); + } - @JsonCreator - public static MetricLongValue deserialize(String value) { - if (UNAVAILABLE_STRING.equals(value)) { - return unavailable(); - } - return of(Long.valueOf(value)); + @JsonCreator + public static MetricLongValue deserialize(String value) { + if (UNAVAILABLE_STRING.equals(value)) { + return unavailable(); } + return of(Long.valueOf(value)); + } - @JsonValue - public String asString() { - return available ? String.valueOf(value) : UNAVAILABLE_STRING; - } + @JsonValue + public String asString() { + return available ? String.valueOf(value) : UNAVAILABLE_STRING; + } - public long toLong() { - return value; - } + public long toLong() { + return value; + } - public boolean isAvailable() { - return available; - } + public boolean isAvailable() { + return available; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MetricLongValue that = (MetricLongValue) o; - return available == that.available - && value == that.value; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(available, value); + if (o == null || getClass() != o.getClass()) { + return false; } + MetricLongValue that = (MetricLongValue) o; + return available == that.available && value == that.value; + } + + @Override + public int hashCode() { + return Objects.hash(available, value); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MonitoringDetails.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MonitoringDetails.java index d35e99cc26..502a646c2d 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MonitoringDetails.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/MonitoringDetails.java @@ -3,65 +3,58 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; - import java.util.Objects; public final class MonitoringDetails { - public static final MonitoringDetails EMPTY = new MonitoringDetails(Severity.NON_IMPORTANT, ""); + public static final MonitoringDetails EMPTY = new MonitoringDetails(Severity.NON_IMPORTANT, ""); - @NotNull - private final Severity severity; + @NotNull private final Severity severity; - @NotNull - private final String reaction; + @NotNull private final String reaction; - @JsonCreator - public MonitoringDetails(@JsonProperty("severity") Severity severity, - @JsonProperty("reaction") String reaction) { - this.severity = severity; - this.reaction = reaction; - } + @JsonCreator + public MonitoringDetails( + @JsonProperty("severity") Severity severity, @JsonProperty("reaction") String reaction) { + this.severity = severity; + this.reaction = reaction; + } - public Severity getSeverity() { - return severity; - } + public Severity getSeverity() { + return severity; + } - public String getReaction() { - return reaction; - } + public String getReaction() { + return reaction; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof MonitoringDetails)) { - return false; - } - MonitoringDetails that = (MonitoringDetails) o; - return severity == that.severity - && Objects.equals(reaction, that.reaction); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(severity, reaction); - } - - @Override - public String toString() { - return "MonitoringDetails{" - + "severity=" + severity - + ", reaction='" + reaction + '\'' - + '}'; - } - - public enum Severity { - @JsonProperty("CRITICAL") - CRITICAL, - @JsonProperty("IMPORTANT") - IMPORTANT, - @JsonProperty("NON_IMPORTANT") - NON_IMPORTANT + if (!(o instanceof MonitoringDetails)) { + return false; } + MonitoringDetails that = (MonitoringDetails) o; + return severity == that.severity && Objects.equals(reaction, that.reaction); + } + + @Override + public int hashCode() { + return Objects.hash(severity, reaction); + } + + @Override + public String toString() { + return "MonitoringDetails{" + "severity=" + severity + ", reaction='" + reaction + '\'' + '}'; + } + + public enum Severity { + @JsonProperty("CRITICAL") + CRITICAL, + @JsonProperty("IMPORTANT") + IMPORTANT, + @JsonProperty("NON_IMPORTANT") + NON_IMPORTANT + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OAuthProvider.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OAuthProvider.java index 2654d1990d..a9accea130 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OAuthProvider.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OAuthProvider.java @@ -1,5 +1,7 @@ package pl.allegro.tech.hermes.api; +import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.Max; @@ -7,119 +9,121 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; - import java.util.Objects; -import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; - public class OAuthProvider implements Anonymizable { - private static final String ANONYMIZED_CLIENT_SECRET = "******"; - private static final int DEFAULT_SOCKET_TIMEOUT = 0; - - @NotEmpty - @Pattern(regexp = ALLOWED_NAME_REGEX) - private final String name; - - @NotEmpty - private final String tokenEndpoint; - - @NotEmpty - private final String clientId; - - @NotEmpty - private final String clientSecret; - - @Min(0) - @Max(3600_000) - @NotNull - private final Integer tokenRequestInitialDelay; - - @Min(0) - @Max(3600_000) - @NotNull - private final Integer tokenRequestMaxDelay; - - @Min(100) - @Max(60_000) - @NotNull - private Integer requestTimeout; - - @Min(0) - @Max(60_000) - @NotNull - private Integer socketTimeout; - - @JsonCreator - public OAuthProvider(@JsonProperty("name") String name, - @JsonProperty("tokenEndpoint") String tokenEndpoint, - @JsonProperty("clientId") String clientId, - @JsonProperty("clientSecret") String clientSecret, - @JsonProperty("tokenRequestInitialDelay") Integer tokenRequestInitialDelay, - @JsonProperty("tokenRequestMaxDelay") Integer tokenRequestMaxDelay, - @JsonProperty("requestTimeout") Integer requestTimeout, - @JsonProperty("socketTimeout") Integer socketTimeout) { - this.name = name; - this.tokenEndpoint = tokenEndpoint; - this.clientId = clientId; - this.clientSecret = clientSecret; - this.tokenRequestInitialDelay = tokenRequestInitialDelay; - this.tokenRequestMaxDelay = tokenRequestMaxDelay; - this.requestTimeout = requestTimeout; - this.socketTimeout = socketTimeout != null ? socketTimeout : DEFAULT_SOCKET_TIMEOUT; - } - - public String getName() { - return name; - } - - public String getTokenEndpoint() { - return tokenEndpoint; - } - - public String getClientId() { - return clientId; - } - - public String getClientSecret() { - return clientSecret; + private static final String ANONYMIZED_CLIENT_SECRET = "******"; + private static final int DEFAULT_SOCKET_TIMEOUT = 0; + + @NotEmpty + @Pattern(regexp = ALLOWED_NAME_REGEX) + private final String name; + + @NotEmpty private final String tokenEndpoint; + + @NotEmpty private final String clientId; + + @NotEmpty private final String clientSecret; + + @Min(0) + @Max(3600_000) + @NotNull + private final Integer tokenRequestInitialDelay; + + @Min(0) + @Max(3600_000) + @NotNull + private final Integer tokenRequestMaxDelay; + + @Min(100) + @Max(60_000) + @NotNull + private Integer requestTimeout; + + @Min(0) + @Max(60_000) + @NotNull + private Integer socketTimeout; + + @JsonCreator + public OAuthProvider( + @JsonProperty("name") String name, + @JsonProperty("tokenEndpoint") String tokenEndpoint, + @JsonProperty("clientId") String clientId, + @JsonProperty("clientSecret") String clientSecret, + @JsonProperty("tokenRequestInitialDelay") Integer tokenRequestInitialDelay, + @JsonProperty("tokenRequestMaxDelay") Integer tokenRequestMaxDelay, + @JsonProperty("requestTimeout") Integer requestTimeout, + @JsonProperty("socketTimeout") Integer socketTimeout) { + this.name = name; + this.tokenEndpoint = tokenEndpoint; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.tokenRequestInitialDelay = tokenRequestInitialDelay; + this.tokenRequestMaxDelay = tokenRequestMaxDelay; + this.requestTimeout = requestTimeout; + this.socketTimeout = socketTimeout != null ? socketTimeout : DEFAULT_SOCKET_TIMEOUT; + } + + public String getName() { + return name; + } + + public String getTokenEndpoint() { + return tokenEndpoint; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public Integer getTokenRequestInitialDelay() { + return tokenRequestInitialDelay; + } + + public Integer getTokenRequestMaxDelay() { + return tokenRequestMaxDelay; + } + + public Integer getRequestTimeout() { + return requestTimeout; + } + + public Integer getSocketTimeout() { + return socketTimeout; + } + + public OAuthProvider anonymize() { + return new OAuthProvider( + name, + tokenEndpoint, + clientId, + ANONYMIZED_CLIENT_SECRET, + tokenRequestInitialDelay, + tokenRequestMaxDelay, + requestTimeout, + socketTimeout); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - public Integer getTokenRequestInitialDelay() { - return tokenRequestInitialDelay; - } - - public Integer getTokenRequestMaxDelay() { - return tokenRequestMaxDelay; - } - - public Integer getRequestTimeout() { - return requestTimeout; - } - - public Integer getSocketTimeout() { - return socketTimeout; - } - - public OAuthProvider anonymize() { - return new OAuthProvider(name, tokenEndpoint, clientId, ANONYMIZED_CLIENT_SECRET, - tokenRequestInitialDelay, tokenRequestMaxDelay, requestTimeout, socketTimeout); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - OAuthProvider that = (OAuthProvider) o; - return Objects.equals(name, that.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); + if (o == null || getClass() != o.getClass()) { + return false; } + OAuthProvider that = (OAuthProvider) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetentionTime.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetentionTime.java index 850b2fa389..9aad1854a6 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetentionTime.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetentionTime.java @@ -2,52 +2,51 @@ import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.Min; - import java.util.Objects; public class OfflineRetentionTime { - @Min(1) - private final Integer duration; + @Min(1) + private final Integer duration; - private final boolean infinite; + private final boolean infinite; - public OfflineRetentionTime(@JsonProperty("duration") Integer duration, @JsonProperty("infinite") boolean infinite) { - this.infinite = infinite; - this.duration = infinite ? null : duration; - } + public OfflineRetentionTime( + @JsonProperty("duration") Integer duration, @JsonProperty("infinite") boolean infinite) { + this.infinite = infinite; + this.duration = infinite ? null : duration; + } - public static OfflineRetentionTime of(int duration) { - return new OfflineRetentionTime(duration, false); - } + public static OfflineRetentionTime of(int duration) { + return new OfflineRetentionTime(duration, false); + } - public static OfflineRetentionTime infinite() { - return new OfflineRetentionTime(null, false); - } + public static OfflineRetentionTime infinite() { + return new OfflineRetentionTime(null, false); + } - public Integer getDuration() { - return duration; - } + public Integer getDuration() { + return duration; + } - public boolean isInfinite() { - return infinite; - } + public boolean isInfinite() { + return infinite; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof OfflineRetentionTime)) { - return false; - } - OfflineRetentionTime that = (OfflineRetentionTime) o; - return infinite == that.infinite - && Objects.equals(duration, that.duration); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(duration, infinite); + if (!(o instanceof OfflineRetentionTime)) { + return false; } + OfflineRetentionTime that = (OfflineRetentionTime) o; + return infinite == that.infinite && Objects.equals(duration, that.duration); + } + + @Override + public int hashCode() { + return Objects.hash(duration, infinite); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionRequest.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionRequest.java index ef79fef7fd..dfa21e8569 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionRequest.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionRequest.java @@ -5,57 +5,102 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; -import pl.allegro.tech.hermes.api.jackson.InstantIsoSerializer; - import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pl.allegro.tech.hermes.api.constraints.OneSourceRetransmission; +import pl.allegro.tech.hermes.api.jackson.InstantIsoSerializer; +@OneSourceRetransmission public class OfflineRetransmissionRequest { - @NotEmpty - private final String sourceTopic; - @NotEmpty - private final String targetTopic; - @NotNull - private final Instant startTimestamp; - @NotNull - private final Instant endTimestamp; - - @JsonCreator - public OfflineRetransmissionRequest( - @JsonProperty("sourceTopic") String sourceTopic, - @JsonProperty("targetTopic") String targetTopic, - @JsonProperty("startTimestamp") Instant startTimestamp, - @JsonProperty("endTimestamp") Instant endTimestamp) { - this.sourceTopic = sourceTopic; - this.targetTopic = targetTopic; - this.startTimestamp = startTimestamp; - this.endTimestamp = endTimestamp; - } - public String getSourceTopic() { - return sourceTopic; - } + private static final List formatters = + List.of( + DateTimeFormatter.ISO_INSTANT, + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").withZone(ZoneId.of("UTC")), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'").withZone(ZoneId.of("UTC"))); + private static final Logger logger = LoggerFactory.getLogger(OfflineRetransmissionRequest.class); - public String getTargetTopic() { - return targetTopic; - } + private final String sourceViewPath; + private final String sourceTopic; + @NotEmpty private final String targetTopic; + @NotNull private Instant startTimestamp; + @NotNull private Instant endTimestamp; - @JsonSerialize(using = InstantIsoSerializer.class) - public Instant getStartTimestamp() { - return startTimestamp; - } + @JsonCreator + public OfflineRetransmissionRequest( + @JsonProperty("sourceViewPath") String sourceViewPath, + @JsonProperty("sourceTopic") String sourceTopic, + @JsonProperty("targetTopic") String targetTopic, + @JsonProperty("startTimestamp") String startTimestamp, + @JsonProperty("endTimestamp") String endTimestamp) { + this.sourceViewPath = sourceViewPath; + this.sourceTopic = sourceTopic; + this.targetTopic = targetTopic; + this.startTimestamp = initializeTimestamp(startTimestamp); + this.endTimestamp = initializeTimestamp(endTimestamp); + } - @JsonSerialize(using = InstantIsoSerializer.class) - public Instant getEndTimestamp() { - return endTimestamp; + private Instant initializeTimestamp(String timestamp) { + if (timestamp == null) { + return null; } - @Override - public String toString() { - return "OfflineRetransmissionRequest{" - + "sourceTopic='" + sourceTopic + '\'' - + ", targetTopic='" + targetTopic + '\'' - + ", startTimestamp=" + startTimestamp - + ", endTimestamp=" + endTimestamp - + '}'; + for (DateTimeFormatter formatter : formatters) { + try { + return formatter.parse(timestamp, Instant::from); + } catch (DateTimeParseException e) { + // ignore + } } + + logger.warn("Provided date [{}] has an invalid format", timestamp); + return null; + } + + public Optional getSourceViewPath() { + return Optional.ofNullable(sourceViewPath); + } + + public Optional getSourceTopic() { + return Optional.ofNullable(sourceTopic); + } + + public String getTargetTopic() { + return targetTopic; + } + + @JsonSerialize(using = InstantIsoSerializer.class) + public Instant getStartTimestamp() { + return startTimestamp; + } + + @JsonSerialize(using = InstantIsoSerializer.class) + public Instant getEndTimestamp() { + return endTimestamp; + } + + @Override + public String toString() { + return "OfflineRetransmissionRequest{" + + "sourceTopic='" + + sourceTopic + + '\'' + + ", sourceViewPath='" + + sourceViewPath + + '\'' + + ", targetTopic='" + + targetTopic + + '\'' + + ", startTimestamp=" + + startTimestamp + + ", endTimestamp=" + + endTimestamp + + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionTask.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionTask.java index ed1c69753e..a94a6d54c9 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionTask.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OfflineRetransmissionTask.java @@ -4,69 +4,80 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import pl.allegro.tech.hermes.api.jackson.InstantIsoSerializer; - import java.time.Instant; +import java.util.Optional; +import pl.allegro.tech.hermes.api.jackson.InstantIsoSerializer; public class OfflineRetransmissionTask { - private final String taskId; - private final OfflineRetransmissionRequest request; - private final Instant createdAt; + private final String taskId; + private final OfflineRetransmissionRequest request; + private final Instant createdAt; + + @JsonCreator + public OfflineRetransmissionTask( + @JsonProperty("taskId") String taskId, + @JsonProperty("sourceViewPath") String sourceViewPath, + @JsonProperty("sourceTopic") String sourceTopic, + @JsonProperty("targetTopic") String targetTopic, + @JsonProperty("startTimestamp") Instant startTimestamp, + @JsonProperty("endTimestamp") Instant endTimestamp, + @JsonProperty("createdAt") Instant createdAt) { + this( + taskId, + new OfflineRetransmissionRequest( + sourceViewPath, + sourceTopic, + targetTopic, + startTimestamp.toString(), + endTimestamp.toString()), + createdAt); + } - @JsonCreator - public OfflineRetransmissionTask( - @JsonProperty("taskId") String taskId, - @JsonProperty("sourceTopic") String sourceTopic, - @JsonProperty("targetTopic") String targetTopic, - @JsonProperty("startTimestamp") Instant startTimestamp, - @JsonProperty("endTimestamp") Instant endTimestamp, - @JsonProperty("createdAt") Instant createdAt) { - this(taskId, new OfflineRetransmissionRequest(sourceTopic, targetTopic, startTimestamp, endTimestamp), createdAt); - } + public OfflineRetransmissionTask( + String taskId, OfflineRetransmissionRequest request, Instant createdAt) { + this.taskId = taskId; + this.request = request; + this.createdAt = createdAt; + } - public OfflineRetransmissionTask(String taskId, OfflineRetransmissionRequest request, Instant createdAt) { - this.taskId = taskId; - this.request = request; - this.createdAt = createdAt; - } + public String getTaskId() { + return taskId; + } - public String getTaskId() { - return taskId; - } + public Optional getSourceTopic() { + return request.getSourceTopic(); + } - public String getSourceTopic() { - return request.getSourceTopic(); - } + public Optional getSourceViewPath() { + return request.getSourceViewPath(); + } - public String getTargetTopic() { - return request.getTargetTopic(); - } + public String getTargetTopic() { + return request.getTargetTopic(); + } - @JsonSerialize(using = InstantIsoSerializer.class) - public Instant getStartTimestamp() { - return request.getStartTimestamp(); - } + @JsonSerialize(using = InstantIsoSerializer.class) + public Instant getStartTimestamp() { + return request.getStartTimestamp(); + } - @JsonSerialize(using = InstantIsoSerializer.class) - public Instant getEndTimestamp() { - return request.getEndTimestamp(); - } + @JsonSerialize(using = InstantIsoSerializer.class) + public Instant getEndTimestamp() { + return request.getEndTimestamp(); + } - @JsonSerialize(using = InstantIsoSerializer.class) - public Instant getCreatedAt() { - return createdAt; - } + @JsonSerialize(using = InstantIsoSerializer.class) + public Instant getCreatedAt() { + return createdAt; + } - @JsonIgnore - public OfflineRetransmissionRequest getRequest() { - return request; - } + @JsonIgnore + public OfflineRetransmissionRequest getRequest() { + return request; + } - @Override - public String toString() { - return "OfflineRetransmissionTask{" - + "taskId='" + taskId + '\'' - + ", request=" + request - + '}'; - } + @Override + public String toString() { + return "OfflineRetransmissionTask{" + "taskId='" + taskId + '\'' + ", request=" + request + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OffsetRetransmissionDate.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OffsetRetransmissionDate.java index acd1270616..63e8d202b8 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OffsetRetransmissionDate.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OffsetRetransmissionDate.java @@ -3,21 +3,20 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jakarta.validation.constraints.NotNull; -import pl.allegro.tech.hermes.api.jackson.OffsetDateTimeSerializer; - import java.time.OffsetDateTime; +import pl.allegro.tech.hermes.api.jackson.OffsetDateTimeSerializer; public class OffsetRetransmissionDate { - @NotNull - private final OffsetDateTime retransmissionDate; + @NotNull private final OffsetDateTime retransmissionDate; - public OffsetRetransmissionDate(@JsonProperty("retransmissionDate") OffsetDateTime retransmissionDate) { - this.retransmissionDate = retransmissionDate; - } + public OffsetRetransmissionDate( + @JsonProperty("retransmissionDate") OffsetDateTime retransmissionDate) { + this.retransmissionDate = retransmissionDate; + } - @JsonSerialize(using = OffsetDateTimeSerializer.class) - public OffsetDateTime getRetransmissionDate() { - return retransmissionDate; - } + @JsonSerialize(using = OffsetDateTimeSerializer.class) + public OffsetDateTime getRetransmissionDate() { + return retransmissionDate; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Owner.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Owner.java index e3f960303f..29f0ba1f25 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Owner.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Owner.java @@ -4,65 +4,61 @@ public class Owner { - private final String id; - private final String name; - private final String url; + private final String id; + private final String name; + private final String url; - public Owner() { - this.id = null; - this.name = null; - this.url = null; - } + public Owner() { + this.id = null; + this.name = null; + this.url = null; + } - public Owner(String id, String name) { - this.id = id; - this.name = name; - this.url = null; - } + public Owner(String id, String name) { + this.id = id; + this.name = name; + this.url = null; + } - public Owner(String id, String name, String url) { - this.id = id; - this.name = name; - this.url = url; - } + public Owner(String id, String name, String url) { + this.id = id; + this.name = name; + this.url = url; + } - public String getId() { - return id; - } + public String getId() { + return id; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Owner owner = (Owner) o; - return Objects.equals(id, owner.id) - && Objects.equals(name, owner.name) - && Objects.equals(url, owner.url); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(id, name, url); + if (o == null || getClass() != o.getClass()) { + return false; } + Owner owner = (Owner) o; + return Objects.equals(id, owner.id) + && Objects.equals(name, owner.name) + && Objects.equals(url, owner.url); + } - @Override - public String toString() { - return "Owner{" - + "id='" + id + '\'' - + ", name='" + name + '\'' - + ", url='" + url + '\'' - + '}'; - } + @Override + public int hashCode() { + return Objects.hash(id, name, url); + } + + @Override + public String toString() { + return "Owner{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", url='" + url + '\'' + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OwnerId.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OwnerId.java index b8838b9279..50afd1fbfc 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OwnerId.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/OwnerId.java @@ -3,55 +3,47 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.constraints.NotNull; - import java.util.Objects; public final class OwnerId { - @NotNull - private final String source; + @NotNull private final String source; - @NotNull - private final String id; + @NotNull private final String id; - @JsonCreator - public OwnerId(@JsonProperty("source") String source, - @JsonProperty("id") String id) { - this.source = source; - this.id = id; - } + @JsonCreator + public OwnerId(@JsonProperty("source") String source, @JsonProperty("id") String id) { + this.source = source; + this.id = id; + } - public String getSource() { - return source; - } + public String getSource() { + return source; + } - public String getId() { - return id; - } + public String getId() { + return id; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - OwnerId that = (OwnerId) o; - return Objects.equals(source, that.source) - && Objects.equals(id, that.id); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(source, id); - } - - @Override - public String toString() { - return "OwnerId{" - + "source='" + source + '\'' - + ", id='" + id + '\'' - + '}'; + if (o == null || getClass() != o.getClass()) { + return false; } + OwnerId that = (OwnerId) o; + return Objects.equals(source, that.source) && Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(source, id); + } + + @Override + public String toString() { + return "OwnerId{" + "source='" + source + '\'' + ", id='" + id + '\'' + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PatchData.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PatchData.java index 5cb408bba5..d4a1aaaed0 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PatchData.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PatchData.java @@ -2,49 +2,48 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import pl.allegro.tech.hermes.api.jackson.PatchDataDeserializer; -import pl.allegro.tech.hermes.api.jackson.PatchDataSerializer; - import java.util.HashMap; import java.util.Map; +import pl.allegro.tech.hermes.api.jackson.PatchDataDeserializer; +import pl.allegro.tech.hermes.api.jackson.PatchDataSerializer; @JsonDeserialize(using = PatchDataDeserializer.class) @JsonSerialize(using = PatchDataSerializer.class) public class PatchData { - private final Map patch; + private final Map patch; - public PatchData(Map patch) { - this.patch = patch; - } + public PatchData(Map patch) { + this.patch = patch; + } - public static PatchData from(Map patch) { - return new PatchData(patch); - } + public static PatchData from(Map patch) { + return new PatchData(patch); + } - public static Builder patchData() { - return new Builder(); - } + public static Builder patchData() { + return new Builder(); + } - public Map getPatch() { - return patch; - } + public Map getPatch() { + return patch; + } - public boolean valueChanged(String field, Object originalValue) { - return patch.containsKey(field) && !patch.get(field).equals(originalValue); - } + public boolean valueChanged(String field, Object originalValue) { + return patch.containsKey(field) && !patch.get(field).equals(originalValue); + } - public static class Builder { + public static class Builder { - private final Map map = new HashMap<>(); + private final Map map = new HashMap<>(); - public PatchData build() { - return PatchData.from(map); - } + public PatchData build() { + return PatchData.from(map); + } - public Builder set(String field, Object value) { - this.map.put(field, value); - return this; - } + public Builder set(String field, Object value) { + this.map.put(field, value); + return this; } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PersistentSubscriptionMetrics.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PersistentSubscriptionMetrics.java index b3cf53ee4f..0ad876225e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PersistentSubscriptionMetrics.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PersistentSubscriptionMetrics.java @@ -5,40 +5,41 @@ public class PersistentSubscriptionMetrics { - private long delivered; - private long discarded; - private long volume; - - @JsonCreator - public PersistentSubscriptionMetrics(@JsonProperty("delivered") long delivered, - @JsonProperty("discarded") long discarded, - @JsonProperty("volume") long volume) { - this.delivered = delivered; - this.discarded = discarded; - this.volume = volume; - } - - public long getDelivered() { - return delivered; - } - - public void setDelivered(long delivered) { - this.delivered = delivered; - } - - public long getDiscarded() { - return discarded; - } - - public void setDiscarded(long discarded) { - this.discarded = discarded; - } - - public long getVolume() { - return volume; - } - - public void setVolume(long volume) { - this.volume = volume; - } + private long delivered; + private long discarded; + private long volume; + + @JsonCreator + public PersistentSubscriptionMetrics( + @JsonProperty("delivered") long delivered, + @JsonProperty("discarded") long discarded, + @JsonProperty("volume") long volume) { + this.delivered = delivered; + this.discarded = discarded; + this.volume = volume; + } + + public long getDelivered() { + return delivered; + } + + public void setDelivered(long delivered) { + this.delivered = delivered; + } + + public long getDiscarded() { + return discarded; + } + + public void setDiscarded(long discarded) { + this.discarded = discarded; + } + + public long getVolume() { + return volume; + } + + public void setVolume(long volume) { + this.volume = volume; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTrace.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTrace.java index 5e6fa397eb..bda7e00508 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTrace.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTrace.java @@ -4,92 +4,100 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; @JsonIgnoreProperties(ignoreUnknown = true) public class PublishedMessageTrace implements MessageTrace { - private final String messageId; - private final long timestamp; - private final PublishedMessageTraceStatus status; - private final TopicName topicName; - private final String reason; - private final String message; - private final String cluster; - private final String extraRequestHeaders; - - @JsonCreator - public PublishedMessageTrace(@JsonProperty("messageId") String messageId, - @JsonProperty("timestamp") Long timestamp, - @JsonProperty("topicName") String topicName, - @JsonProperty("status") PublishedMessageTraceStatus status, - @JsonProperty("reason") String reason, - @JsonProperty("message") String message, - @JsonProperty("cluster") String cluster, - @JsonProperty("extraRequestHeaders") String extraRequestHeaders) { - this.messageId = messageId; - this.timestamp = timestamp; - this.status = status; - this.topicName = TopicName.fromQualifiedName(topicName); - this.reason = reason; - this.message = message; - this.cluster = cluster; - this.extraRequestHeaders = extraRequestHeaders; - } - - public String getMessageId() { - return messageId; - } - - public Long getTimestamp() { - return timestamp; - } - - public String getReason() { - return reason; - } - - @JsonProperty("topicName") - public String getQualifiedTopicName() { - return TopicName.toQualifiedName(topicName); - } - - @JsonIgnore - public TopicName getTopicName() { - return topicName; + private final String messageId; + private final long timestamp; + private final PublishedMessageTraceStatus status; + private final TopicName topicName; + private final String reason; + private final String message; + private final String cluster; + private final String extraRequestHeaders; + private final String storageDatacenter; + + @JsonCreator + public PublishedMessageTrace( + @JsonProperty("messageId") String messageId, + @JsonProperty("timestamp") Long timestamp, + @JsonProperty("topicName") String topicName, + @JsonProperty("status") PublishedMessageTraceStatus status, + @JsonProperty("reason") String reason, + @JsonProperty("message") String message, + @JsonProperty("cluster") String cluster, + @JsonProperty("extraRequestHeaders") String extraRequestHeaders, + @JsonProperty("storageDc") String storageDatacenter) { + this.messageId = messageId; + this.timestamp = timestamp; + this.status = status; + this.topicName = TopicName.fromQualifiedName(topicName); + this.reason = reason; + this.message = message; + this.cluster = cluster; + this.extraRequestHeaders = extraRequestHeaders; + this.storageDatacenter = storageDatacenter; + } + + public String getMessageId() { + return messageId; + } + + public Long getTimestamp() { + return timestamp; + } + + public String getReason() { + return reason; + } + + @JsonProperty("topicName") + public String getQualifiedTopicName() { + return TopicName.toQualifiedName(topicName); + } + + @JsonIgnore + public TopicName getTopicName() { + return topicName; + } + + public String getMessage() { + return message; + } + + public PublishedMessageTraceStatus getStatus() { + return status; + } + + public String getCluster() { + return cluster; + } + + public String getExtraRequestHeaders() { + return extraRequestHeaders; + } + + @JsonProperty("storageDc") + public String getStorageDatacenter() { + return storageDatacenter; + } + + @Override + public int hashCode() { + return Objects.hash(messageId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - public String getMessage() { - return message; - } - - public PublishedMessageTraceStatus getStatus() { - return status; - } - - public String getCluster() { - return cluster; - } - - public String getExtraRequestHeaders() { - return extraRequestHeaders; - } - - @Override - public int hashCode() { - return Objects.hash(messageId); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final PublishedMessageTrace other = (PublishedMessageTrace) obj; - return Objects.equals(this.messageId, other.messageId); + if (obj == null || getClass() != obj.getClass()) { + return false; } + final PublishedMessageTrace other = (PublishedMessageTrace) obj; + return Objects.equals(this.messageId, other.messageId); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTraceStatus.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTraceStatus.java index ea3bab63be..d386e91c13 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTraceStatus.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishedMessageTraceStatus.java @@ -1,5 +1,7 @@ package pl.allegro.tech.hermes.api; public enum PublishedMessageTraceStatus { - INFLIGHT, SUCCESS, ERROR + INFLIGHT, + SUCCESS, + ERROR } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingAuth.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingAuth.java index 3b09192b47..1ef9ee9a2b 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingAuth.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingAuth.java @@ -2,63 +2,63 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.ArrayList; import java.util.List; import java.util.Objects; public class PublishingAuth { - private final List publishers; - private final boolean enabled; - private final boolean unauthenticatedAccessEnabled; + private final List publishers; + private final boolean enabled; + private final boolean unauthenticatedAccessEnabled; - @JsonCreator - public PublishingAuth(@JsonProperty("publishers") List publishers, - @JsonProperty("enabled") boolean enabled, - @JsonProperty("unauthenticatedAccessEnabled") boolean unauthenticatedAccessEnabled) { + @JsonCreator + public PublishingAuth( + @JsonProperty("publishers") List publishers, + @JsonProperty("enabled") boolean enabled, + @JsonProperty("unauthenticatedAccessEnabled") boolean unauthenticatedAccessEnabled) { - this.publishers = publishers; - this.enabled = enabled; - this.unauthenticatedAccessEnabled = unauthenticatedAccessEnabled; - } + this.publishers = publishers; + this.enabled = enabled; + this.unauthenticatedAccessEnabled = unauthenticatedAccessEnabled; + } - public boolean isEnabled() { - return enabled; - } + public boolean isEnabled() { + return enabled; + } - public boolean isUnauthenticatedAccessEnabled() { - return unauthenticatedAccessEnabled; - } + public boolean isUnauthenticatedAccessEnabled() { + return unauthenticatedAccessEnabled; + } - public boolean hasPermission(String publisher) { - return publishers.contains(publisher); - } + public boolean hasPermission(String publisher) { + return publishers.contains(publisher); + } - public List getPublishers() { - return publishers; - } + public List getPublishers() { + return publishers; + } - public static PublishingAuth disabled() { - return new PublishingAuth(new ArrayList<>(), false, true); - } + public static PublishingAuth disabled() { + return new PublishingAuth(new ArrayList<>(), false, true); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PublishingAuth that = (PublishingAuth) o; - return enabled == that.enabled - && unauthenticatedAccessEnabled == that.unauthenticatedAccessEnabled - && Objects.equals(publishers, that.publishers); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(publishers, enabled, unauthenticatedAccessEnabled); + if (o == null || getClass() != o.getClass()) { + return false; } + PublishingAuth that = (PublishingAuth) o; + return enabled == that.enabled + && unauthenticatedAccessEnabled == that.unauthenticatedAccessEnabled + && Objects.equals(publishers, that.publishers); + } + + @Override + public int hashCode() { + return Objects.hash(publishers, enabled, unauthenticatedAccessEnabled); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingChaosPolicy.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingChaosPolicy.java new file mode 100644 index 0000000000..1dfb0e3d4a --- /dev/null +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/PublishingChaosPolicy.java @@ -0,0 +1,46 @@ +package pl.allegro.tech.hermes.api; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collections; +import java.util.Map; + +public record PublishingChaosPolicy( + ChaosMode mode, ChaosPolicy globalPolicy, Map datacenterPolicies) { + + @JsonCreator + public PublishingChaosPolicy( + @JsonProperty("mode") ChaosMode mode, + @JsonProperty("globalPolicy") ChaosPolicy globalPolicy, + @JsonProperty("datacenterPolicies") Map datacenterPolicies) { + this.mode = mode; + this.globalPolicy = globalPolicy; + this.datacenterPolicies = datacenterPolicies == null ? Map.of() : datacenterPolicies; + } + + public static PublishingChaosPolicy disabled() { + return new PublishingChaosPolicy(ChaosMode.DISABLED, null, Collections.emptyMap()); + } + + public record ChaosPolicy( + int probability, int delayFrom, int delayTo, boolean completeWithError) { + + @JsonCreator + public ChaosPolicy( + @JsonProperty("probability") int probability, + @JsonProperty("delayFrom") int delayFrom, + @JsonProperty("delayTo") int delayTo, + @JsonProperty("completeWithError") boolean completeWithError) { + this.probability = probability; + this.delayFrom = delayFrom; + this.delayTo = delayTo; + this.completeWithError = completeWithError; + } + } + + public enum ChaosMode { + DISABLED, + GLOBAL, + DATACENTER + } +} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Query.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Query.java index a004e97f46..28a53016a7 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Query.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Query.java @@ -5,15 +5,15 @@ public interface Query { - Stream filter(Stream input); + Stream filter(Stream input); - default Stream filter(Collection input) { - return filter(input.stream()); - } + default Stream filter(Collection input) { + return filter(input.stream()); + } - Stream filterNames(Stream input); + Stream filterNames(Stream input); - default Stream filterNames(Collection input) { - return filterNames(input.stream()); - } + default Stream filterNames(Collection input) { + return filterNames(input.stream()); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchema.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchema.java index 132566500a..9554f2d8d6 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchema.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchema.java @@ -1,47 +1,47 @@ package pl.allegro.tech.hermes.api; -import java.util.Objects; - import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.emptyToNull; +import java.util.Objects; + public final class RawSchema { - private final String value; + private final String value; - private RawSchema(String value) { - this.value = checkNotNull(emptyToNull(value)); - } + private RawSchema(String value) { + this.value = checkNotNull(emptyToNull(value)); + } - public static RawSchema valueOf(String schema) { - return new RawSchema(schema); - } + public static RawSchema valueOf(String schema) { + return new RawSchema(schema); + } - public String value() { - return value; - } + public String value() { + return value; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } - RawSchema that = (RawSchema) o; + RawSchema that = (RawSchema) o; - return value.equals(that.value); - } + return value.equals(that.value); + } - @Override - public int hashCode() { - return Objects.hashCode(value); - } + @Override + public int hashCode() { + return Objects.hashCode(value); + } - @Override - public String toString() { - return "RawSource(" + value + ")"; - } + @Override + public String toString() { + return "RawSource(" + value + ")"; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchemaWithMetadata.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchemaWithMetadata.java index bb6b833172..4e9094b00e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchemaWithMetadata.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RawSchemaWithMetadata.java @@ -1,66 +1,67 @@ package pl.allegro.tech.hermes.api; import com.google.common.base.MoreObjects; - import java.util.Objects; public final class RawSchemaWithMetadata { - private final RawSchema schema; - private final int id; - private final int version; + private final RawSchema schema; + private final int id; + private final int version; - private RawSchemaWithMetadata(RawSchema schema, int id, int version) { - this.schema = schema; - this.id = id; - this.version = version; - } + private RawSchemaWithMetadata(RawSchema schema, int id, int version) { + this.schema = schema; + this.id = id; + this.version = version; + } - public static RawSchemaWithMetadata of(String schema, int id, int version) { - return new RawSchemaWithMetadata(RawSchema.valueOf(schema), id, version); - } + public static RawSchemaWithMetadata of(String schema, int id, int version) { + return new RawSchemaWithMetadata(RawSchema.valueOf(schema), id, version); + } - public RawSchema getSchema() { - return schema; - } + public RawSchema getSchema() { + return schema; + } - public String getSchemaString() { - return schema.value(); - } + public String getSchemaString() { + return schema.value(); + } - public int getId() { - return id; - } + public int getId() { + return id; + } - public int getVersion() { - return version; - } + public int getVersion() { + return version; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } - RawSchemaWithMetadata that = (RawSchemaWithMetadata) o; + RawSchemaWithMetadata that = (RawSchemaWithMetadata) o; - return schema.equals(that.schema) && Objects.equals(id, that.id) && Objects.equals(version, that.version); - } + return schema.equals(that.schema) + && Objects.equals(id, that.id) + && Objects.equals(version, that.version); + } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("schema", schema) - .add("id", id) - .add("version", version) - .toString(); - } + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("schema", schema) + .add("id", id) + .add("version", version) + .toString(); + } - @Override - public int hashCode() { - return Objects.hash(schema, id, version); - } + @Override + public int hashCode() { + return Objects.hash(schema, id, version); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Readiness.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Readiness.java index 4223df0e1c..d730b467e2 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Readiness.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Readiness.java @@ -6,16 +6,15 @@ import jakarta.validation.constraints.NotNull; public class Readiness { - @NotNull - private final boolean isReady; + @NotNull private final boolean isReady; - @JsonCreator - public Readiness(@JsonProperty("isReady") boolean isReady) { - this.isReady = isReady; - } + @JsonCreator + public Readiness(@JsonProperty("isReady") boolean isReady) { + this.isReady = isReady; + } - @JsonGetter("isReady") - public boolean isReady() { - return isReady; - } + @JsonGetter("isReady") + public boolean isReady() { + return isReady; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RetentionTime.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RetentionTime.java index bf6478b702..8d2c8668dc 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RetentionTime.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/RetentionTime.java @@ -2,58 +2,62 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; -import pl.allegro.tech.hermes.api.constraints.AdminPermitted; - +import java.util.EnumSet; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; public class RetentionTime { - private static final TimeUnit DEFAULT_UNIT = TimeUnit.DAYS; + private static final TimeUnit DEFAULT_UNIT = TimeUnit.DAYS; - @Min(0) - @Max(value = 7, groups = AdminPermitted.class) - private final int duration; + public static RetentionTime MAX = new RetentionTime(7, TimeUnit.DAYS); + public static Set allowedUnits = + EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS); - private final TimeUnit retentionUnit; + @Min(0) + private final int duration; - public RetentionTime(@JsonProperty("duration") int duration, @JsonProperty("retentionUnit") TimeUnit unit) { - this.duration = duration; - this.retentionUnit = unit == null ? DEFAULT_UNIT : unit; - } + private final TimeUnit retentionUnit; - public static RetentionTime of(int duration, TimeUnit unit) { - return new RetentionTime(duration, unit); - } + public RetentionTime( + @JsonProperty("duration") int duration, @JsonProperty("retentionUnit") TimeUnit unit) { + this.duration = duration; + this.retentionUnit = unit == null ? DEFAULT_UNIT : unit; + } - public int getDuration() { - return duration; - } + public static RetentionTime of(int duration, TimeUnit unit) { + return new RetentionTime(duration, unit); + } - @JsonIgnore - public long getDurationInMillis() { - return retentionUnit.toMillis(duration); - } + public int getDuration() { + return duration; + } - public TimeUnit getRetentionUnit() { - return retentionUnit; - } + @JsonIgnore + public long getDurationInMillis() { + return retentionUnit.toMillis(duration); + } - @Override - public int hashCode() { - return Objects.hash(duration); - } + public TimeUnit getRetentionUnit() { + return retentionUnit; + } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final RetentionTime other = (RetentionTime) obj; - return Objects.equals(this.duration, other.duration) && Objects.equals(this.retentionUnit, other.retentionUnit); + @Override + public int hashCode() { + return Objects.hash(duration); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; } + final RetentionTime other = (RetentionTime) obj; + return Objects.equals(this.duration, other.duration) + && Objects.equals(this.retentionUnit, other.retentionUnit); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTrace.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTrace.java index eb03b91350..494660d899 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTrace.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTrace.java @@ -4,191 +4,202 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; @JsonIgnoreProperties(ignoreUnknown = true) public class SentMessageTrace implements MessageTrace { + private final String messageId; + private final String batchId; + private final String subscription; + private final long timestamp; + private final SentMessageTraceStatus status; + private final Integer partition; + private final Long offset; + private final TopicName topicName; + private final String reason; + private final String message; + private final String cluster; + + @JsonCreator + public SentMessageTrace( + @JsonProperty("messageId") String messageId, + @JsonProperty("batchId") String batchId, + @JsonProperty("timestamp") Long timestamp, + @JsonProperty("subscription") String subscription, + @JsonProperty("topicName") String topicName, + @JsonProperty("status") SentMessageTraceStatus status, + @JsonProperty("reason") String reason, + @JsonProperty("message") String message, + @JsonProperty("partition") Integer partition, + @JsonProperty("offset") Long offset, + @JsonProperty("cluster") String cluster) { + this.messageId = messageId; + this.batchId = batchId; + this.timestamp = timestamp; + this.subscription = subscription; + this.status = status; + this.partition = partition; + this.offset = offset; + this.topicName = TopicName.fromQualifiedName(topicName); + this.reason = reason; + this.message = message; + this.cluster = cluster; + } + + public String getMessageId() { + return messageId; + } + + public String getBatchId() { + return batchId; + } + + public Long getTimestamp() { + return timestamp; + } + + public String getSubscription() { + return subscription; + } + + public String getReason() { + return reason; + } + + public Integer getPartition() { + return partition; + } + + public Long getOffset() { + return offset; + } + + @JsonProperty("topicName") + public String getQualifiedTopicName() { + return TopicName.toQualifiedName(topicName); + } + + @JsonIgnore + public TopicName getTopicName() { + return topicName; + } + + public String getMessage() { + return message; + } + + public SentMessageTraceStatus getStatus() { + return status; + } + + public String getCluster() { + return cluster; + } + + @Override + public int hashCode() { + return Objects.hash(messageId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final SentMessageTrace other = (SentMessageTrace) obj; + return Objects.equals(this.messageId, other.messageId) + && Objects.equals(this.subscription, other.subscription) + && Objects.equals(this.timestamp, other.timestamp) + && Objects.equals(this.status, other.status); + } + + public static class Builder { + private final String messageId; private final String batchId; - private final String subscription; - private final long timestamp; private final SentMessageTraceStatus status; - private final Integer partition; - private final Long offset; - private final TopicName topicName; - private final String reason; - private final String message; - private final String cluster; - - @JsonCreator - public SentMessageTrace(@JsonProperty("messageId") String messageId, - @JsonProperty("batchId") String batchId, - @JsonProperty("timestamp") Long timestamp, - @JsonProperty("subscription") String subscription, - @JsonProperty("topicName") String topicName, - @JsonProperty("status") SentMessageTraceStatus status, - @JsonProperty("reason") String reason, - @JsonProperty("message") String message, - @JsonProperty("partition") Integer partition, - @JsonProperty("offset") Long offset, - @JsonProperty("cluster") String cluster) { - this.messageId = messageId; - this.batchId = batchId; - this.timestamp = timestamp; - this.subscription = subscription; - this.status = status; - this.partition = partition; - this.offset = offset; - this.topicName = TopicName.fromQualifiedName(topicName); - this.reason = reason; - this.message = message; - this.cluster = cluster; - } - - public String getMessageId() { - return messageId; - } - - public String getBatchId() { - return batchId; - } - public Long getTimestamp() { - return timestamp; - } + private String subscription; + private long timestamp; + private Integer partition; + private Long offset; + private String topicName; + private String reason; + private String message; + private String cluster; - public String getSubscription() { - return subscription; + private Builder(String messageId, String batchId, SentMessageTraceStatus status) { + this.messageId = messageId; + this.batchId = batchId; + this.status = status; } - public String getReason() { - return reason; + public Builder withSubscription(String subscription) { + this.subscription = subscription; + return this; } - public Integer getPartition() { - return partition; + public Builder withTimestamp(long timestamp) { + this.timestamp = timestamp; + return this; } - public Long getOffset() { - return offset; + public Builder withPartition(Integer partition) { + this.partition = partition; + return this; } - @JsonProperty("topicName") - public String getQualifiedTopicName() { - return TopicName.toQualifiedName(topicName); + public Builder withOffset(Long offset) { + this.offset = offset; + return this; } - @JsonIgnore - public TopicName getTopicName() { - return topicName; + public Builder withTopicName(String topicName) { + this.topicName = topicName; + return this; } - public String getMessage() { - return message; + public Builder withReason(String reason) { + this.reason = reason; + return this; } - public SentMessageTraceStatus getStatus() { - return status; + public Builder withMessage(String message) { + this.message = message; + return this; } - public String getCluster() { - return cluster; + public Builder withCluster(String cluster) { + this.cluster = cluster; + return this; } - @Override - public int hashCode() { - return Objects.hash(messageId); + public static Builder sentMessageTrace( + String messageId, String batchId, SentMessageTraceStatus status) { + return new Builder(messageId, batchId, status); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final SentMessageTrace other = (SentMessageTrace) obj; - return Objects.equals(this.messageId, other.messageId) - && Objects.equals(this.subscription, other.subscription) - && Objects.equals(this.timestamp, other.timestamp) - && Objects.equals(this.status, other.status); + public static Builder undeliveredMessage() { + return new Builder(null, null, SentMessageTraceStatus.DISCARDED); } - public static class Builder { - - private final String messageId; - private final String batchId; - private final SentMessageTraceStatus status; - - private String subscription; - private long timestamp; - private Integer partition; - private Long offset; - private String topicName; - private String reason; - private String message; - private String cluster; - - private Builder(String messageId, String batchId, SentMessageTraceStatus status) { - this.messageId = messageId; - this.batchId = batchId; - this.status = status; - } - - public Builder withSubscription(String subscription) { - this.subscription = subscription; - return this; - } - - public Builder withTimestamp(long timestamp) { - this.timestamp = timestamp; - return this; - } - - public Builder withPartition(Integer partition) { - this.partition = partition; - return this; - } - - public Builder withOffset(Long offset) { - this.offset = offset; - return this; - } - - public Builder withTopicName(String topicName) { - this.topicName = topicName; - return this; - } - - public Builder withReason(String reason) { - this.reason = reason; - return this; - } - - public Builder withMessage(String message) { - this.message = message; - return this; - } - - public Builder withCluster(String cluster) { - this.cluster = cluster; - return this; - } - - public static Builder sentMessageTrace(String messageId, String batchId, SentMessageTraceStatus status) { - return new Builder(messageId, batchId, status); - } - - public static Builder undeliveredMessage() { - return new Builder(null, null, SentMessageTraceStatus.DISCARDED); - } - - public SentMessageTrace build() { - return new SentMessageTrace(messageId, batchId, timestamp, subscription, topicName, status, - reason, message, partition, offset, cluster); - } + public SentMessageTrace build() { + return new SentMessageTrace( + messageId, + batchId, + timestamp, + subscription, + topicName, + status, + reason, + message, + partition, + offset, + cluster); } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTraceStatus.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTraceStatus.java index f194a7cb63..5597d71be9 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTraceStatus.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SentMessageTraceStatus.java @@ -1,5 +1,9 @@ package pl.allegro.tech.hermes.api; public enum SentMessageTraceStatus { - INFLIGHT, SUCCESS, FAILED, DISCARDED, FILTERED + INFLIGHT, + SUCCESS, + FAILED, + DISCARDED, + FILTERED } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Stats.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Stats.java index fbcf73a1c3..8dea4566a5 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Stats.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Stats.java @@ -2,52 +2,48 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class Stats { - private final TopicStats topicStats; - private final SubscriptionStats subscriptionStats; - - @JsonCreator - public Stats( - @JsonProperty("topicStats") - TopicStats topicStats, - @JsonProperty("subscriptionStats") SubscriptionStats subscriptionStats) { - this.topicStats = topicStats; - this.subscriptionStats = subscriptionStats; - } - - public TopicStats getTopicStats() { - return topicStats; - } - - public SubscriptionStats getSubscriptionStats() { - return subscriptionStats; + private final TopicStats topicStats; + private final SubscriptionStats subscriptionStats; + + @JsonCreator + public Stats( + @JsonProperty("topicStats") TopicStats topicStats, + @JsonProperty("subscriptionStats") SubscriptionStats subscriptionStats) { + this.topicStats = topicStats; + this.subscriptionStats = subscriptionStats; + } + + public TopicStats getTopicStats() { + return topicStats; + } + + public SubscriptionStats getSubscriptionStats() { + return subscriptionStats; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - Stats stats = (Stats) o; - return Objects.equals(topicStats, stats.topicStats) && Objects.equals(subscriptionStats, stats.subscriptionStats); - } - - @Override - public int hashCode() { - return Objects.hash(topicStats, subscriptionStats); - } - - @Override - public String toString() { - return "Stats{" - + "topicStats=" + topicStats - + ", subscriptionStats=" + subscriptionStats - + '}'; + if (o == null || getClass() != o.getClass()) { + return false; } + Stats stats = (Stats) o; + return Objects.equals(topicStats, stats.topicStats) + && Objects.equals(subscriptionStats, stats.subscriptionStats); + } + + @Override + public int hashCode() { + return Objects.hash(topicStats, subscriptionStats); + } + + @Override + public String toString() { + return "Stats{" + "topicStats=" + topicStats + ", subscriptionStats=" + subscriptionStats + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Subscription.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Subscription.java index 3a8c673590..359206d487 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Subscription.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Subscription.java @@ -1,5 +1,7 @@ package pl.allegro.tech.hermes.api; +import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -8,8 +10,6 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; -import pl.allegro.tech.hermes.api.constraints.ValidContentType; - import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -17,439 +17,523 @@ import java.util.List; import java.util.Map; import java.util.Objects; - -import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; +import pl.allegro.tech.hermes.api.constraints.ValidContentType; @ValidContentType(message = "AVRO content type is not supported in BATCH delivery mode") -@JsonIgnoreProperties(value = {"createdAt", "modifiedAt"}, allowGetters = true) +@JsonIgnoreProperties( + value = {"createdAt", "modifiedAt"}, + allowGetters = true) public class Subscription implements Anonymizable { - @NotNull - private final MonitoringDetails monitoringDetails; - private final SubscriptionName subscriptionName; - @Valid - @NotNull - private TopicName topicName; - @NotEmpty - @Pattern(regexp = ALLOWED_NAME_REGEX) - private String name; - private State state = State.PENDING; - @NotNull - @Valid - private EndpointAddress endpoint; - @NotNull - private ContentType contentType = ContentType.JSON; - @NotNull - private String description; - @Valid - private SubscriptionPolicy serialSubscriptionPolicy; - @Valid - private BatchSubscriptionPolicy batchSubscriptionPolicy; - /** - * Use trackingMode field instead. - */ - @Deprecated - private boolean trackingEnabled = false; - private TrackingMode trackingMode = TrackingMode.TRACKING_OFF; - private boolean http2Enabled = false; - @Valid - @NotNull - private OwnerId owner; - @NotNull - private DeliveryType deliveryType = DeliveryType.SERIAL; - @NotNull - private SubscriptionMode mode = SubscriptionMode.ANYCAST; - private List filters = new ArrayList<>(); - - private List
headers; - - private EndpointAddressResolverMetadata endpointAddressResolverMetadata; - - @Valid - private SubscriptionOAuthPolicy oAuthPolicy; - - private boolean subscriptionIdentityHeadersEnabled; - - private boolean autoDeleteWithTopicEnabled; - - private Instant createdAt; - - private Instant modifiedAt; - - private Subscription(TopicName topicName, - String name, - EndpointAddress endpoint, - State state, - String description, - Object subscriptionPolicy, - boolean trackingEnabled, - TrackingMode trackingMode, - OwnerId owner, - MonitoringDetails monitoringDetails, - ContentType contentType, - DeliveryType deliveryType, - List filters, - SubscriptionMode mode, - List
headers, - EndpointAddressResolverMetadata endpointAddressResolverMetadata, - SubscriptionOAuthPolicy oAuthPolicy, - boolean http2Enabled, - boolean subscriptionIdentityHeadersEnabled, - boolean autoDeleteWithTopicEnabled) { - this.topicName = topicName; - this.name = name; - this.endpoint = endpoint; - this.state = state != null ? state : State.PENDING; - this.description = description; - this.trackingEnabled = trackingEnabled; - this.trackingMode = trackingMode; - this.owner = owner; - this.monitoringDetails = monitoringDetails == null ? MonitoringDetails.EMPTY : monitoringDetails; - this.contentType = contentType == null ? ContentType.JSON : contentType; - this.deliveryType = deliveryType; - this.batchSubscriptionPolicy = this.deliveryType == DeliveryType.BATCH ? (BatchSubscriptionPolicy) subscriptionPolicy : null; - this.serialSubscriptionPolicy = this.deliveryType == DeliveryType.SERIAL ? (SubscriptionPolicy) subscriptionPolicy : null; - this.filters = filters; - this.mode = mode; - this.http2Enabled = http2Enabled; - this.subscriptionName = new SubscriptionName(name, topicName); - this.headers = headers; - this.endpointAddressResolverMetadata = endpointAddressResolverMetadata; - this.oAuthPolicy = oAuthPolicy; - this.subscriptionIdentityHeadersEnabled = subscriptionIdentityHeadersEnabled; - this.autoDeleteWithTopicEnabled = autoDeleteWithTopicEnabled; - } - - public static Subscription createSerialSubscription(TopicName topicName, - String name, - EndpointAddress endpoint, - State state, - String description, - SubscriptionPolicy subscriptionPolicy, - boolean trackingEnabled, - TrackingMode trackingMode, - OwnerId owner, - MonitoringDetails monitoringDetails, - ContentType contentType, - List filters, - SubscriptionMode mode, - List
headers, - EndpointAddressResolverMetadata endpointAddressResolverMetadata, - SubscriptionOAuthPolicy oAuthPolicy, - boolean http2Enabled, - boolean subscriptionIdentityHeadersEnabled, - boolean autoDeleteWithTopicEnabled) { - return new Subscription(topicName, name, endpoint, state, description, subscriptionPolicy, trackingEnabled, trackingMode, - owner, monitoringDetails, contentType, DeliveryType.SERIAL, filters, mode, headers, - endpointAddressResolverMetadata, oAuthPolicy, http2Enabled, subscriptionIdentityHeadersEnabled, autoDeleteWithTopicEnabled); - } - - public static Subscription createBatchSubscription(TopicName topicName, - String name, - EndpointAddress endpoint, - State state, - String description, - BatchSubscriptionPolicy subscriptionPolicy, - boolean trackingEnabled, - TrackingMode trackingMode, - OwnerId owner, - MonitoringDetails monitoringDetails, - ContentType contentType, - List filters, - List
headers, - EndpointAddressResolverMetadata endpointAddressResolverMetadata, - SubscriptionOAuthPolicy oAuthPolicy, - boolean http2Enabled, - boolean subscriptionIdentityHeadersEnabled, - boolean autoDeleteWithTopicEnabled) { - return new Subscription(topicName, name, endpoint, state, description, subscriptionPolicy, trackingEnabled, trackingMode, - owner, monitoringDetails, contentType, DeliveryType.BATCH, filters, SubscriptionMode.ANYCAST, headers, - endpointAddressResolverMetadata, oAuthPolicy, http2Enabled, subscriptionIdentityHeadersEnabled, autoDeleteWithTopicEnabled); - } - - @JsonCreator - public static Subscription create( - @JsonProperty("topicName") String topicName, - @JsonProperty("name") String name, - @JsonProperty("endpoint") EndpointAddress endpoint, - @JsonProperty("state") State state, - @JsonProperty("description") String description, - @JsonProperty("subscriptionPolicy") Map subscriptionPolicy, - @JsonProperty("trackingEnabled") boolean trackingEnabled, - @JsonProperty("trackingMode") String trackingMode, - @JsonProperty("owner") OwnerId owner, - @JsonProperty("monitoringDetails") MonitoringDetails monitoringDetails, - @JsonProperty("contentType") ContentType contentType, - @JsonProperty("deliveryType") DeliveryType deliveryType, - @JsonProperty("filters") List filters, - @JsonProperty("mode") SubscriptionMode mode, - @JsonProperty("headers") List
headers, - @JsonProperty("endpointAddressResolverMetadata") EndpointAddressResolverMetadata endpointAddressResolverMetadata, - @JsonProperty("oAuthPolicy") SubscriptionOAuthPolicy oAuthPolicy, - @JsonProperty("http2Enabled") boolean http2Enabled, - @JsonProperty("subscriptionIdentityHeadersEnabled") boolean subscriptionIdentityHeadersEnabled, - @JsonProperty("autoDeleteWithTopicEnabled") boolean autoDeleteWithTopicEnabled) { - - DeliveryType validDeliveryType = deliveryType == null ? DeliveryType.SERIAL : deliveryType; - SubscriptionMode subscriptionMode = mode == null ? SubscriptionMode.ANYCAST : mode; - Map validSubscriptionPolicy = subscriptionPolicy == null ? new HashMap<>() : subscriptionPolicy; - - TrackingMode validTrackingMode = TrackingMode.fromString(trackingMode) - .orElse(trackingEnabled ? TrackingMode.TRACK_ALL : TrackingMode.TRACKING_OFF); - boolean validTrackingEnabled = validTrackingMode != TrackingMode.TRACKING_OFF; - - return new Subscription( - TopicName.fromQualifiedName(topicName), - name, - endpoint, - state, - description, - validDeliveryType == DeliveryType.SERIAL - ? SubscriptionPolicy.create(validSubscriptionPolicy) - : BatchSubscriptionPolicy.create(validSubscriptionPolicy), - validTrackingEnabled, - validTrackingMode, - owner, - monitoringDetails, - contentType, - validDeliveryType, - filters == null ? Collections.emptyList() : filters, - subscriptionMode, - headers == null ? Collections.emptyList() : headers, - endpointAddressResolverMetadata == null ? EndpointAddressResolverMetadata.empty() : endpointAddressResolverMetadata, - oAuthPolicy, - http2Enabled, - subscriptionIdentityHeadersEnabled, - autoDeleteWithTopicEnabled - ); - } - - @Override - public int hashCode() { - return Objects.hash(endpoint, topicName, name, description, serialSubscriptionPolicy, batchSubscriptionPolicy, - trackingEnabled, trackingMode, owner, monitoringDetails, contentType, filters, mode, headers, - endpointAddressResolverMetadata, oAuthPolicy, http2Enabled, subscriptionIdentityHeadersEnabled); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final Subscription other = (Subscription) obj; - - return Objects.equals(this.endpoint, other.endpoint) - && Objects.equals(this.topicName, other.topicName) - && Objects.equals(this.name, other.name) - && Objects.equals(this.description, other.description) - && Objects.equals(this.serialSubscriptionPolicy, other.serialSubscriptionPolicy) - && Objects.equals(this.batchSubscriptionPolicy, other.batchSubscriptionPolicy) - && Objects.equals(this.trackingEnabled, other.trackingEnabled) - && Objects.equals(this.trackingMode, other.trackingMode) - && Objects.equals(this.owner, other.owner) - && Objects.equals(this.monitoringDetails, other.monitoringDetails) - && Objects.equals(this.contentType, other.contentType) - && Objects.equals(this.filters, other.filters) - && Objects.equals(this.mode, other.mode) - && Objects.equals(this.headers, other.headers) - && Objects.equals(this.endpointAddressResolverMetadata, other.endpointAddressResolverMetadata) - && Objects.equals(this.http2Enabled, other.http2Enabled) - && Objects.equals(this.oAuthPolicy, other.oAuthPolicy) - && Objects.equals(this.subscriptionIdentityHeadersEnabled, other.subscriptionIdentityHeadersEnabled) - && Objects.equals(this.autoDeleteWithTopicEnabled, other.autoDeleteWithTopicEnabled); - } - - @JsonIgnore - public SubscriptionName getQualifiedName() { - return subscriptionName; - } - - public EndpointAddress getEndpoint() { - return endpoint; - } - - @JsonProperty("topicName") - public String getQualifiedTopicName() { - return TopicName.toQualifiedName(topicName); - } - - @JsonIgnore - public TopicName getTopicName() { - return topicName; - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public State getState() { - return state; - } - - public void setState(State state) { - this.state = state; - } - - @JsonProperty("subscriptionPolicy") - public Object getSubscriptionPolicy() { - return isBatchSubscription() ? batchSubscriptionPolicy : serialSubscriptionPolicy; - } - - public boolean isTrackingEnabled() { - return trackingMode != TrackingMode.TRACKING_OFF; - } - - @JsonProperty("trackingMode") - public String getTrackingModeString() { - return trackingMode.getValue(); - } - - @JsonIgnore - public TrackingMode getTrackingMode() { - return trackingMode; - } - - public OwnerId getOwner() { - return owner; - } - - public MonitoringDetails getMonitoringDetails() { - return monitoringDetails; - } - - public ContentType getContentType() { - return contentType; - } - - public DeliveryType getDeliveryType() { - return deliveryType; - } - - public List getFilters() { - return Collections.unmodifiableList(filters); - } - - public List
getHeaders() { - return Collections.unmodifiableList(headers); - } - - public EndpointAddressResolverMetadata getEndpointAddressResolverMetadata() { - return endpointAddressResolverMetadata; - } - - @JsonIgnore - public boolean isBatchSubscription() { - return this.deliveryType == DeliveryType.BATCH; - } - - @JsonIgnore - public BatchSubscriptionPolicy getBatchSubscriptionPolicy() { - return batchSubscriptionPolicy; - } - - @JsonIgnore - public SubscriptionPolicy getSerialSubscriptionPolicy() { - return serialSubscriptionPolicy; - } - - public void setSerialSubscriptionPolicy(SubscriptionPolicy serialSubscriptionPolicy) { - this.serialSubscriptionPolicy = serialSubscriptionPolicy; - } - - @JsonIgnore - public boolean isActive() { - return state == State.ACTIVE || state == State.PENDING; - } - - public SubscriptionMode getMode() { - return mode; - } - - @JsonProperty("oAuthPolicy") - public SubscriptionOAuthPolicy getOAuthPolicy() { - return oAuthPolicy; - } - - @JsonIgnore - public boolean hasOAuthPolicy() { - return oAuthPolicy != null; - } - - @JsonIgnore - public boolean isSeverityNotImportant() { - return getMonitoringDetails().getSeverity() == MonitoringDetails.Severity.NON_IMPORTANT; - } - - public boolean isHttp2Enabled() { - return http2Enabled; - } - - public boolean isSubscriptionIdentityHeadersEnabled() { - return subscriptionIdentityHeadersEnabled; - } - - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Long createdAt) { - this.createdAt = Instant.ofEpochMilli(createdAt); - } - - public Instant getModifiedAt() { - return modifiedAt; - } - - public void setModifiedAt(Long modifiedAt) { - this.modifiedAt = Instant.ofEpochMilli(modifiedAt); - } - - public boolean isAutoDeleteWithTopicEnabled() { - return autoDeleteWithTopicEnabled; - } - - @Override - public Subscription anonymize() { - if (getEndpoint() != null && getEndpoint().containsCredentials() || hasOAuthPolicy()) { - return new Subscription( - topicName, - name, - endpoint.anonymize(), - state, - description, - deliveryType == DeliveryType.BATCH ? batchSubscriptionPolicy : serialSubscriptionPolicy, - trackingEnabled, - trackingMode, - owner, - monitoringDetails, - contentType, - deliveryType, - filters, - mode, - headers, - endpointAddressResolverMetadata, - oAuthPolicy != null ? oAuthPolicy.anonymize() : null, - http2Enabled, - subscriptionIdentityHeadersEnabled, - autoDeleteWithTopicEnabled - ); - } - return this; - } - - @Override - public String toString() { - return "Subscription(" + getQualifiedName() + ")"; - } - - public enum State { - PENDING, ACTIVE, SUSPENDED - } + @NotNull private final MonitoringDetails monitoringDetails; + private final SubscriptionName subscriptionName; + @Valid @NotNull private TopicName topicName; + + @NotEmpty + @Pattern(regexp = ALLOWED_NAME_REGEX) + private String name; + + private State state = State.PENDING; + @NotNull @Valid private EndpointAddress endpoint; + @NotNull private ContentType contentType = ContentType.JSON; + @NotNull private String description; + @Valid private SubscriptionPolicy serialSubscriptionPolicy; + @Valid private BatchSubscriptionPolicy batchSubscriptionPolicy; + + /** Use trackingMode field instead. */ + @Deprecated private boolean trackingEnabled = false; + + private TrackingMode trackingMode = TrackingMode.TRACKING_OFF; + private boolean http2Enabled = false; + private boolean profilingEnabled = false; + private long profilingThresholdMs = 0; + @Valid @NotNull private OwnerId owner; + @NotNull private DeliveryType deliveryType = DeliveryType.SERIAL; + @NotNull private SubscriptionMode mode = SubscriptionMode.ANYCAST; + private List filters = new ArrayList<>(); + + private List
headers; + + private EndpointAddressResolverMetadata endpointAddressResolverMetadata; + + @Valid private SubscriptionOAuthPolicy oAuthPolicy; + + private boolean subscriptionIdentityHeadersEnabled; + + private boolean autoDeleteWithTopicEnabled; + + private Instant createdAt; + + private Instant modifiedAt; + + private Subscription( + TopicName topicName, + String name, + EndpointAddress endpoint, + State state, + String description, + Object subscriptionPolicy, + boolean trackingEnabled, + TrackingMode trackingMode, + OwnerId owner, + MonitoringDetails monitoringDetails, + ContentType contentType, + DeliveryType deliveryType, + List filters, + SubscriptionMode mode, + List
headers, + EndpointAddressResolverMetadata endpointAddressResolverMetadata, + SubscriptionOAuthPolicy oAuthPolicy, + boolean http2Enabled, + boolean profilingEnabled, + long profilingThresholdMs, + boolean subscriptionIdentityHeadersEnabled, + boolean autoDeleteWithTopicEnabled) { + this.topicName = topicName; + this.name = name; + this.endpoint = endpoint; + this.state = state != null ? state : State.PENDING; + this.description = description; + this.trackingEnabled = trackingEnabled; + this.trackingMode = trackingMode; + this.owner = owner; + this.monitoringDetails = + monitoringDetails == null ? MonitoringDetails.EMPTY : monitoringDetails; + this.contentType = contentType == null ? ContentType.JSON : contentType; + this.deliveryType = deliveryType; + this.batchSubscriptionPolicy = + this.deliveryType == DeliveryType.BATCH + ? (BatchSubscriptionPolicy) subscriptionPolicy + : null; + this.serialSubscriptionPolicy = + this.deliveryType == DeliveryType.SERIAL ? (SubscriptionPolicy) subscriptionPolicy : null; + this.filters = filters; + this.mode = mode; + this.http2Enabled = http2Enabled; + this.profilingEnabled = profilingEnabled; + this.profilingThresholdMs = profilingThresholdMs; + this.subscriptionName = new SubscriptionName(name, topicName); + this.headers = headers; + this.endpointAddressResolverMetadata = endpointAddressResolverMetadata; + this.oAuthPolicy = oAuthPolicy; + this.subscriptionIdentityHeadersEnabled = subscriptionIdentityHeadersEnabled; + this.autoDeleteWithTopicEnabled = autoDeleteWithTopicEnabled; + } + + public static Subscription createSerialSubscription( + TopicName topicName, + String name, + EndpointAddress endpoint, + State state, + String description, + SubscriptionPolicy subscriptionPolicy, + boolean trackingEnabled, + TrackingMode trackingMode, + OwnerId owner, + MonitoringDetails monitoringDetails, + ContentType contentType, + List filters, + SubscriptionMode mode, + List
headers, + EndpointAddressResolverMetadata endpointAddressResolverMetadata, + SubscriptionOAuthPolicy oAuthPolicy, + boolean http2Enabled, + boolean profilingEnabled, + long profilingThresholdMs, + boolean subscriptionIdentityHeadersEnabled, + boolean autoDeleteWithTopicEnabled) { + return new Subscription( + topicName, + name, + endpoint, + state, + description, + subscriptionPolicy, + trackingEnabled, + trackingMode, + owner, + monitoringDetails, + contentType, + DeliveryType.SERIAL, + filters, + mode, + headers, + endpointAddressResolverMetadata, + oAuthPolicy, + http2Enabled, + profilingEnabled, + profilingThresholdMs, + subscriptionIdentityHeadersEnabled, + autoDeleteWithTopicEnabled); + } + + public static Subscription createBatchSubscription( + TopicName topicName, + String name, + EndpointAddress endpoint, + State state, + String description, + BatchSubscriptionPolicy subscriptionPolicy, + boolean trackingEnabled, + TrackingMode trackingMode, + OwnerId owner, + MonitoringDetails monitoringDetails, + ContentType contentType, + List filters, + List
headers, + EndpointAddressResolverMetadata endpointAddressResolverMetadata, + SubscriptionOAuthPolicy oAuthPolicy, + boolean http2Enabled, + boolean subscriptionIdentityHeadersEnabled, + boolean autoDeleteWithTopicEnabled) { + return new Subscription( + topicName, + name, + endpoint, + state, + description, + subscriptionPolicy, + trackingEnabled, + trackingMode, + owner, + monitoringDetails, + contentType, + DeliveryType.BATCH, + filters, + SubscriptionMode.ANYCAST, + headers, + endpointAddressResolverMetadata, + oAuthPolicy, + http2Enabled, + false, + 0, + subscriptionIdentityHeadersEnabled, + autoDeleteWithTopicEnabled); + } + + @JsonCreator + public static Subscription create( + @JsonProperty("topicName") String topicName, + @JsonProperty("name") String name, + @JsonProperty("endpoint") EndpointAddress endpoint, + @JsonProperty("state") State state, + @JsonProperty("description") String description, + @JsonProperty("subscriptionPolicy") Map subscriptionPolicy, + @JsonProperty("trackingEnabled") boolean trackingEnabled, + @JsonProperty("trackingMode") String trackingMode, + @JsonProperty("owner") OwnerId owner, + @JsonProperty("monitoringDetails") MonitoringDetails monitoringDetails, + @JsonProperty("contentType") ContentType contentType, + @JsonProperty("deliveryType") DeliveryType deliveryType, + @JsonProperty("filters") List filters, + @JsonProperty("mode") SubscriptionMode mode, + @JsonProperty("headers") List
headers, + @JsonProperty("endpointAddressResolverMetadata") + EndpointAddressResolverMetadata endpointAddressResolverMetadata, + @JsonProperty("oAuthPolicy") SubscriptionOAuthPolicy oAuthPolicy, + @JsonProperty("http2Enabled") boolean http2Enabled, + @JsonProperty("profilingEnabled") boolean profilingEnabled, + @JsonProperty("profilingThresholdMs") long profilingThresholdMs, + @JsonProperty("subscriptionIdentityHeadersEnabled") + boolean subscriptionIdentityHeadersEnabled, + @JsonProperty("autoDeleteWithTopicEnabled") boolean autoDeleteWithTopicEnabled) { + + DeliveryType validDeliveryType = deliveryType == null ? DeliveryType.SERIAL : deliveryType; + SubscriptionMode subscriptionMode = mode == null ? SubscriptionMode.ANYCAST : mode; + Map validSubscriptionPolicy = + subscriptionPolicy == null ? new HashMap<>() : subscriptionPolicy; + + TrackingMode validTrackingMode = + TrackingMode.fromString(trackingMode) + .orElse(trackingEnabled ? TrackingMode.TRACK_ALL : TrackingMode.TRACKING_OFF); + boolean validTrackingEnabled = validTrackingMode != TrackingMode.TRACKING_OFF; + + return new Subscription( + TopicName.fromQualifiedName(topicName), + name, + endpoint, + state, + description, + validDeliveryType == DeliveryType.SERIAL + ? SubscriptionPolicy.create(validSubscriptionPolicy) + : BatchSubscriptionPolicy.create(validSubscriptionPolicy), + validTrackingEnabled, + validTrackingMode, + owner, + monitoringDetails, + contentType, + validDeliveryType, + filters == null ? Collections.emptyList() : filters, + subscriptionMode, + headers == null ? Collections.emptyList() : headers, + endpointAddressResolverMetadata == null + ? EndpointAddressResolverMetadata.empty() + : endpointAddressResolverMetadata, + oAuthPolicy, + http2Enabled, + profilingEnabled, + profilingThresholdMs, + subscriptionIdentityHeadersEnabled, + autoDeleteWithTopicEnabled); + } + + @Override + public int hashCode() { + return Objects.hash( + endpoint, + topicName, + name, + description, + serialSubscriptionPolicy, + batchSubscriptionPolicy, + trackingEnabled, + trackingMode, + owner, + monitoringDetails, + contentType, + filters, + mode, + headers, + endpointAddressResolverMetadata, + oAuthPolicy, + http2Enabled, + subscriptionIdentityHeadersEnabled); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final Subscription other = (Subscription) obj; + + return Objects.equals(this.endpoint, other.endpoint) + && Objects.equals(this.topicName, other.topicName) + && Objects.equals(this.name, other.name) + && Objects.equals(this.description, other.description) + && Objects.equals(this.serialSubscriptionPolicy, other.serialSubscriptionPolicy) + && Objects.equals(this.batchSubscriptionPolicy, other.batchSubscriptionPolicy) + && Objects.equals(this.trackingEnabled, other.trackingEnabled) + && Objects.equals(this.trackingMode, other.trackingMode) + && Objects.equals(this.owner, other.owner) + && Objects.equals(this.monitoringDetails, other.monitoringDetails) + && Objects.equals(this.contentType, other.contentType) + && Objects.equals(this.filters, other.filters) + && Objects.equals(this.mode, other.mode) + && Objects.equals(this.headers, other.headers) + && Objects.equals( + this.endpointAddressResolverMetadata, other.endpointAddressResolverMetadata) + && Objects.equals(this.http2Enabled, other.http2Enabled) + && Objects.equals(this.profilingEnabled, other.profilingEnabled) + && Objects.equals(this.profilingThresholdMs, other.profilingThresholdMs) + && Objects.equals(this.oAuthPolicy, other.oAuthPolicy) + && Objects.equals( + this.subscriptionIdentityHeadersEnabled, other.subscriptionIdentityHeadersEnabled) + && Objects.equals(this.autoDeleteWithTopicEnabled, other.autoDeleteWithTopicEnabled); + } + + @JsonIgnore + public SubscriptionName getQualifiedName() { + return subscriptionName; + } + + public EndpointAddress getEndpoint() { + return endpoint; + } + + @JsonProperty("topicName") + public String getQualifiedTopicName() { + return TopicName.toQualifiedName(topicName); + } + + @JsonIgnore + public TopicName getTopicName() { + return topicName; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + @JsonProperty("subscriptionPolicy") + public Object getSubscriptionPolicy() { + return isBatchSubscription() ? batchSubscriptionPolicy : serialSubscriptionPolicy; + } + + public boolean isTrackingEnabled() { + return trackingMode != TrackingMode.TRACKING_OFF; + } + + @JsonProperty("trackingMode") + public String getTrackingModeString() { + return trackingMode.getValue(); + } + + @JsonIgnore + public TrackingMode getTrackingMode() { + return trackingMode; + } + + public OwnerId getOwner() { + return owner; + } + + public MonitoringDetails getMonitoringDetails() { + return monitoringDetails; + } + + public ContentType getContentType() { + return contentType; + } + + public DeliveryType getDeliveryType() { + return deliveryType; + } + + public List getFilters() { + return Collections.unmodifiableList(filters); + } + + public List
getHeaders() { + return Collections.unmodifiableList(headers); + } + + public EndpointAddressResolverMetadata getEndpointAddressResolverMetadata() { + return endpointAddressResolverMetadata; + } + + @JsonIgnore + public boolean isBatchSubscription() { + return this.deliveryType == DeliveryType.BATCH; + } + + @JsonIgnore + public BatchSubscriptionPolicy getBatchSubscriptionPolicy() { + return batchSubscriptionPolicy; + } + + @JsonIgnore + public SubscriptionPolicy getSerialSubscriptionPolicy() { + return serialSubscriptionPolicy; + } + + public void setSerialSubscriptionPolicy(SubscriptionPolicy serialSubscriptionPolicy) { + this.serialSubscriptionPolicy = serialSubscriptionPolicy; + } + + @JsonIgnore + public boolean isActive() { + return state == State.ACTIVE || state == State.PENDING; + } + + public SubscriptionMode getMode() { + return mode; + } + + @JsonProperty("oAuthPolicy") + public SubscriptionOAuthPolicy getOAuthPolicy() { + return oAuthPolicy; + } + + @JsonIgnore + public boolean hasOAuthPolicy() { + return oAuthPolicy != null; + } + + @JsonIgnore + public boolean isSeverityNotImportant() { + return getMonitoringDetails().getSeverity() == MonitoringDetails.Severity.NON_IMPORTANT; + } + + public boolean isHttp2Enabled() { + return http2Enabled; + } + + public boolean isProfilingEnabled() { + return profilingEnabled; + } + + public long getProfilingThresholdMs() { + return profilingThresholdMs; + } + + public boolean isSubscriptionIdentityHeadersEnabled() { + return subscriptionIdentityHeadersEnabled; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Long createdAt) { + this.createdAt = Instant.ofEpochMilli(createdAt); + } + + public Instant getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(Long modifiedAt) { + this.modifiedAt = Instant.ofEpochMilli(modifiedAt); + } + + public boolean isAutoDeleteWithTopicEnabled() { + return autoDeleteWithTopicEnabled; + } + + @Override + public Subscription anonymize() { + if (getEndpoint() != null && getEndpoint().containsCredentials() || hasOAuthPolicy()) { + return new Subscription( + topicName, + name, + endpoint.anonymize(), + state, + description, + deliveryType == DeliveryType.BATCH ? batchSubscriptionPolicy : serialSubscriptionPolicy, + trackingEnabled, + trackingMode, + owner, + monitoringDetails, + contentType, + deliveryType, + filters, + mode, + headers, + endpointAddressResolverMetadata, + oAuthPolicy != null ? oAuthPolicy.anonymize() : null, + http2Enabled, + profilingEnabled, + profilingThresholdMs, + subscriptionIdentityHeadersEnabled, + autoDeleteWithTopicEnabled); + } + return this; + } + + @Override + public String toString() { + return "Subscription(" + getQualifiedName() + ")"; + } + + public enum State { + PENDING, + ACTIVE, + SUSPENDED + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionConstraints.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionConstraints.java index 3ba492ec74..09254c5c8c 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionConstraints.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionConstraints.java @@ -6,22 +6,22 @@ public class SubscriptionConstraints { - private final SubscriptionName subscriptionName; - @Valid - private final Constraints constraints; + private final SubscriptionName subscriptionName; + @Valid private final Constraints constraints; - @JsonCreator - public SubscriptionConstraints(@JsonProperty("subscriptionName") String subscriptionName, - @JsonProperty("constraints") Constraints constraints) { - this.subscriptionName = SubscriptionName.fromString(subscriptionName); - this.constraints = constraints; - } + @JsonCreator + public SubscriptionConstraints( + @JsonProperty("subscriptionName") String subscriptionName, + @JsonProperty("constraints") Constraints constraints) { + this.subscriptionName = SubscriptionName.fromString(subscriptionName); + this.constraints = constraints; + } - public SubscriptionName getSubscriptionName() { - return subscriptionName; - } + public SubscriptionName getSubscriptionName() { + return subscriptionName; + } - public Constraints getConstraints() { - return constraints; - } + public Constraints getConstraints() { + return constraints; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealth.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealth.java index 033d87ba0c..fca6976c83 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealth.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealth.java @@ -1,73 +1,73 @@ package pl.allegro.tech.hermes.api; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.emptySet; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; - import java.util.Objects; import java.util.Set; -import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.Collections.emptySet; - public final class SubscriptionHealth { - public static final SubscriptionHealth HEALTHY = new SubscriptionHealth(Status.HEALTHY, emptySet()); - public static final SubscriptionHealth NO_DATA = new SubscriptionHealth(Status.NO_DATA, emptySet()); + public static final SubscriptionHealth HEALTHY = + new SubscriptionHealth(Status.HEALTHY, emptySet()); + public static final SubscriptionHealth NO_DATA = + new SubscriptionHealth(Status.NO_DATA, emptySet()); - private final Status status; - private final ImmutableSet problems; + private final Status status; + private final ImmutableSet problems; - @JsonCreator - private SubscriptionHealth(@JsonProperty("status") Status status, - @JsonProperty("problems") Set problems) { - this.status = status; - this.problems = ImmutableSet.copyOf(problems); - } + @JsonCreator + private SubscriptionHealth( + @JsonProperty("status") Status status, + @JsonProperty("problems") Set problems) { + this.status = status; + this.problems = ImmutableSet.copyOf(problems); + } - public Status getStatus() { - return status; - } + public Status getStatus() { + return status; + } - public Set getProblems() { - return problems; - } + public Set getProblems() { + return problems; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SubscriptionHealth)) { - return false; - } - SubscriptionHealth that = (SubscriptionHealth) o; - return status == that.status - && Objects.equals(problems, that.problems); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(status, problems); + if (!(o instanceof SubscriptionHealth)) { + return false; } + SubscriptionHealth that = (SubscriptionHealth) o; + return status == that.status && Objects.equals(problems, that.problems); + } - @Override - public String toString() { - return "SubscriptionHealth{" - + "status=" + status - + ", problems=" + problems - + '}'; - } + @Override + public int hashCode() { + return Objects.hash(status, problems); + } - public static SubscriptionHealth of(Set problems) { - checkNotNull(problems, "Set of health problems cannot be null"); - if (problems.isEmpty()) { - return HEALTHY; - } else { - return new SubscriptionHealth(Status.UNHEALTHY, problems); - } - } + @Override + public String toString() { + return "SubscriptionHealth{" + "status=" + status + ", problems=" + problems + '}'; + } - public enum Status { - HEALTHY, UNHEALTHY, NO_DATA + public static SubscriptionHealth of(Set problems) { + checkNotNull(problems, "Set of health problems cannot be null"); + if (problems.isEmpty()) { + return HEALTHY; + } else { + return new SubscriptionHealth(Status.UNHEALTHY, problems); } + } + + public enum Status { + HEALTHY, + UNHEALTHY, + NO_DATA + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealthProblem.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealthProblem.java index 2740a909f5..2f928427af 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealthProblem.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionHealthProblem.java @@ -1,10 +1,5 @@ package pl.allegro.tech.hermes.api; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Objects; - import static java.lang.String.format; import static pl.allegro.tech.hermes.api.SubscriptionHealthProblem.ProblemCode.LAGGING; import static pl.allegro.tech.hermes.api.SubscriptionHealthProblem.ProblemCode.MALFUNCTIONING; @@ -12,93 +7,101 @@ import static pl.allegro.tech.hermes.api.SubscriptionHealthProblem.ProblemCode.TIMING_OUT; import static pl.allegro.tech.hermes.api.SubscriptionHealthProblem.ProblemCode.UNREACHABLE; -public class SubscriptionHealthProblem { - - public enum ProblemCode { - LAGGING, UNREACHABLE, TIMING_OUT, MALFUNCTIONING, RECEIVING_MALFORMED_MESSAGES - } - - private final ProblemCode code; - private final String description; - - @JsonCreator - private SubscriptionHealthProblem(@JsonProperty("code") ProblemCode code, - @JsonProperty("description") String description) { - this.code = code; - this.description = description; - } - - public static SubscriptionHealthProblem lagging(long subscriptionLag, String subscriptionName) { - return new SubscriptionHealthProblem( - LAGGING, - format("Lag is growing on subscription %s, current value is %d messages", subscriptionName, subscriptionLag) - ); - } - - public static SubscriptionHealthProblem malfunctioning(double code5xxErrorsRate, String subscriptionName) { - return new SubscriptionHealthProblem( - MALFUNCTIONING, - format("Consuming service returns a lot of 5xx codes for subscription %s, currently %.0f 5xx/s", - subscriptionName, - code5xxErrorsRate) - ); - } - - public static SubscriptionHealthProblem receivingMalformedMessages(double code4xxErrorsRate, String subscriptionName) { - return new SubscriptionHealthProblem( - RECEIVING_MALFORMED_MESSAGES, - format("Consuming service returns a lot of 4xx codes for subscription %s, currently %.0f 4xx/s", - subscriptionName, - code4xxErrorsRate) - ); - } - - public static SubscriptionHealthProblem timingOut(double timeoutsRate, String subscriptionName) { - return new SubscriptionHealthProblem( - TIMING_OUT, - format("Consuming service times out a lot for subscription %s, currently %.0f timeouts/s", - subscriptionName, - timeoutsRate) - ); - } - - public static SubscriptionHealthProblem unreachable(double otherErrorsRate, String subscriptionName) { - return new SubscriptionHealthProblem( - UNREACHABLE, - format("Unable to connect to consuming service instances for subscription %s, current rate is %.0f failures/s", - subscriptionName, - otherErrorsRate) - ); - } - - public ProblemCode getCode() { - return code; - } - - public String getDescription() { - return description; - } +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; - @Override - public String toString() { - return code.name(); - } +public class SubscriptionHealthProblem { - @Override - public int hashCode() { - return Objects.hash(code, description); + public enum ProblemCode { + LAGGING, + UNREACHABLE, + TIMING_OUT, + MALFUNCTIONING, + RECEIVING_MALFORMED_MESSAGES + } + + private final ProblemCode code; + private final String description; + + @JsonCreator + private SubscriptionHealthProblem( + @JsonProperty("code") ProblemCode code, @JsonProperty("description") String description) { + this.code = code; + this.description = description; + } + + public static SubscriptionHealthProblem lagging(long subscriptionLag, String subscriptionName) { + return new SubscriptionHealthProblem( + LAGGING, + format( + "Lag is growing on subscription %s, current value is %d messages", + subscriptionName, subscriptionLag)); + } + + public static SubscriptionHealthProblem malfunctioning( + double code5xxErrorsRate, String subscriptionName) { + return new SubscriptionHealthProblem( + MALFUNCTIONING, + format( + "Consuming service returns a lot of 5xx codes for subscription %s, currently %.0f 5xx/s", + subscriptionName, code5xxErrorsRate)); + } + + public static SubscriptionHealthProblem receivingMalformedMessages( + double code4xxErrorsRate, String subscriptionName) { + return new SubscriptionHealthProblem( + RECEIVING_MALFORMED_MESSAGES, + format( + "Consuming service returns a lot of 4xx codes for subscription %s, currently %.0f 4xx/s", + subscriptionName, code4xxErrorsRate)); + } + + public static SubscriptionHealthProblem timingOut(double timeoutsRate, String subscriptionName) { + return new SubscriptionHealthProblem( + TIMING_OUT, + format( + "Consuming service times out a lot for subscription %s, currently %.0f timeouts/s", + subscriptionName, timeoutsRate)); + } + + public static SubscriptionHealthProblem unreachable( + double otherErrorsRate, String subscriptionName) { + return new SubscriptionHealthProblem( + UNREACHABLE, + format( + "Unable to connect to consuming service instances for subscription %s, current rate is %.0f failures/s", + subscriptionName, otherErrorsRate)); + } + + public ProblemCode getCode() { + return code; + } + + public String getDescription() { + return description; + } + + @Override + public String toString() { + return code.name(); + } + + @Override + public int hashCode() { + return Objects.hash(code, description); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - SubscriptionHealthProblem other = (SubscriptionHealthProblem) obj; - return Objects.equals(this.code, other.code) - && Objects.equals(this.description, other.description); + if (obj == null || getClass() != obj.getClass()) { + return false; } + SubscriptionHealthProblem other = (SubscriptionHealthProblem) obj; + return Objects.equals(this.code, other.code) + && Objects.equals(this.description, other.description); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMetrics.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMetrics.java index a4a5aafe08..d621922e86 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMetrics.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMetrics.java @@ -4,180 +4,192 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class SubscriptionMetrics { - private long delivered; - private long discarded; - private long volume; - private MetricDecimalValue timeouts; - private MetricDecimalValue otherErrors; - private MetricDecimalValue codes2xx; - private MetricDecimalValue codes4xx; - private MetricDecimalValue codes5xx; - private MetricLongValue lag; - private Subscription.State state; - private MetricDecimalValue rate; - private MetricDecimalValue throughput; - private MetricDecimalValue batchRate; + private long delivered; + private long discarded; + private long volume; + private MetricDecimalValue timeouts; + private MetricDecimalValue otherErrors; + private MetricDecimalValue codes2xx; + private MetricDecimalValue codes4xx; + private MetricDecimalValue codes5xx; + private MetricDecimalValue retries; + private MetricLongValue lag; + private Subscription.State state; + private MetricDecimalValue rate; + private MetricDecimalValue throughput; + private MetricDecimalValue batchRate; + + private SubscriptionMetrics() {} + + @JsonCreator + public SubscriptionMetrics( + @JsonProperty("delivered") long delivered, + @JsonProperty("discarded") long discarded, + @JsonProperty("volume") long volume, + @JsonProperty("timeouts") MetricDecimalValue timeouts, + @JsonProperty("otherErrors") MetricDecimalValue otherErrors, + @JsonProperty("codes2xx") MetricDecimalValue codes2xx, + @JsonProperty("codes4xx") MetricDecimalValue codes4xx, + @JsonProperty("codes5xx") MetricDecimalValue codes5xx, + @JsonProperty("retries") MetricDecimalValue retries, + @JsonProperty("Subscription") Subscription.State state, + @JsonProperty("rate") MetricDecimalValue rate, + @JsonProperty("throughput") MetricDecimalValue throughput, + @JsonProperty("batchRate") MetricDecimalValue batchRate) { + this.delivered = delivered; + this.discarded = discarded; + this.volume = volume; + this.timeouts = timeouts; + this.otherErrors = otherErrors; + this.codes2xx = codes2xx; + this.codes4xx = codes4xx; + this.codes5xx = codes5xx; + this.retries = retries; + this.state = state; + this.rate = rate; + this.throughput = throughput; + this.batchRate = batchRate; + } + + public long getDelivered() { + return delivered; + } + + public long getDiscarded() { + return discarded; + } - private SubscriptionMetrics() { - } + public MetricDecimalValue getTimeouts() { + return timeouts; + } - @JsonCreator - public SubscriptionMetrics(@JsonProperty("delivered") long delivered, - @JsonProperty("discarded") long discarded, - @JsonProperty("volume") long volume, - @JsonProperty("timeouts") MetricDecimalValue timeouts, - @JsonProperty("otherErrors") MetricDecimalValue otherErrors, - @JsonProperty("codes2xx") MetricDecimalValue codes2xx, - @JsonProperty("codes4xx") MetricDecimalValue codes4xx, - @JsonProperty("codes5xx") MetricDecimalValue codes5xx, - @JsonProperty("Subscription") Subscription.State state, - @JsonProperty("rate") MetricDecimalValue rate, - @JsonProperty("throughput") MetricDecimalValue throughput, - @JsonProperty("batchRate") MetricDecimalValue batchRate) { - this.delivered = delivered; - this.discarded = discarded; - this.volume = volume; - this.timeouts = timeouts; - this.otherErrors = otherErrors; - this.codes2xx = codes2xx; - this.codes4xx = codes4xx; - this.codes5xx = codes5xx; - this.state = state; - this.rate = rate; - this.throughput = throughput; - this.batchRate = batchRate; - } + public MetricLongValue getLag() { + return lag; + } - public long getDelivered() { - return delivered; - } + public MetricDecimalValue getRate() { + return rate; + } - public long getDiscarded() { - return discarded; - } + public MetricDecimalValue getOtherErrors() { + return otherErrors; + } - public MetricDecimalValue getTimeouts() { - return timeouts; - } + public MetricDecimalValue getCodes2xx() { + return codes2xx; + } - public MetricLongValue getLag() { - return lag; - } + public MetricDecimalValue getCodes4xx() { + return codes4xx; + } - public MetricDecimalValue getRate() { - return rate; - } - - public MetricDecimalValue getOtherErrors() { - return otherErrors; - } + public MetricDecimalValue getCodes5xx() { + return codes5xx; + } - public MetricDecimalValue getCodes2xx() { - return codes2xx; - } + public MetricDecimalValue getRetries() { + return retries; + } - public MetricDecimalValue getCodes4xx() { - return codes4xx; - } + public Subscription.State getState() { + return state; + } - public MetricDecimalValue getCodes5xx() { - return codes5xx; - } + public MetricDecimalValue getThroughput() { + return throughput; + } - public Subscription.State getState() { - return state; - } + public MetricDecimalValue getBatchRate() { + return batchRate; + } - public MetricDecimalValue getThroughput() { - return throughput; - } + public long getVolume() { + return volume; + } - public MetricDecimalValue getBatchRate() { - return batchRate; - } + public static class Builder { + private SubscriptionMetrics subscriptionMetrics; - public long getVolume() { - return volume; + public Builder() { + subscriptionMetrics = new SubscriptionMetrics(); } - public static class Builder { - private SubscriptionMetrics subscriptionMetrics; - - public Builder() { - subscriptionMetrics = new SubscriptionMetrics(); - } + public Builder withDelivered(long delivered) { + subscriptionMetrics.delivered = delivered; + return this; + } - public Builder withDelivered(long delivered) { - subscriptionMetrics.delivered = delivered; - return this; - } + public Builder withDiscarded(long discarded) { + subscriptionMetrics.discarded = discarded; + return this; + } - public Builder withDiscarded(long discarded) { - subscriptionMetrics.discarded = discarded; - return this; - } + public Builder withVolume(long volume) { + subscriptionMetrics.volume = volume; + return this; + } - public Builder withVolume(long volume) { - subscriptionMetrics.volume = volume; - return this; - } + public Builder withOtherErrors(MetricDecimalValue otherErrors) { + subscriptionMetrics.otherErrors = otherErrors; + return this; + } - public Builder withOtherErrors(MetricDecimalValue otherErrors) { - subscriptionMetrics.otherErrors = otherErrors; - return this; - } + public Builder withTimeouts(MetricDecimalValue timeouts) { + subscriptionMetrics.timeouts = timeouts; + return this; + } - public Builder withTimeouts(MetricDecimalValue timeouts) { - subscriptionMetrics.timeouts = timeouts; - return this; - } + public Builder withCodes2xx(MetricDecimalValue count) { + subscriptionMetrics.codes2xx = count; + return this; + } - public Builder withCodes2xx(MetricDecimalValue count) { - subscriptionMetrics.codes2xx = count; - return this; - } + public Builder withCodes4xx(MetricDecimalValue count) { + subscriptionMetrics.codes4xx = count; + return this; + } - public Builder withCodes4xx(MetricDecimalValue count) { - subscriptionMetrics.codes4xx = count; - return this; - } + public Builder withCodes5xx(MetricDecimalValue count) { + subscriptionMetrics.codes5xx = count; + return this; + } - public Builder withCodes5xx(MetricDecimalValue count) { - subscriptionMetrics.codes5xx = count; - return this; - } + public Builder withRetries(MetricDecimalValue retries) { + subscriptionMetrics.retries = retries; + return this; + } - public Builder withRate(MetricDecimalValue rate) { - subscriptionMetrics.rate = rate; - return this; - } + public Builder withRate(MetricDecimalValue rate) { + subscriptionMetrics.rate = rate; + return this; + } - public Builder withState(Subscription.State state) { - subscriptionMetrics.state = state; - return this; - } + public Builder withState(Subscription.State state) { + subscriptionMetrics.state = state; + return this; + } - public Builder withLag(MetricLongValue lag) { - subscriptionMetrics.lag = lag; - return this; - } + public Builder withLag(MetricLongValue lag) { + subscriptionMetrics.lag = lag; + return this; + } - public Builder withThroughput(MetricDecimalValue throughput) { - subscriptionMetrics.throughput = throughput; - return this; - } + public Builder withThroughput(MetricDecimalValue throughput) { + subscriptionMetrics.throughput = throughput; + return this; + } - public Builder withBatchRate(MetricDecimalValue batchRate) { - subscriptionMetrics.batchRate = batchRate; - return this; - } + public Builder withBatchRate(MetricDecimalValue batchRate) { + subscriptionMetrics.batchRate = batchRate; + return this; + } - public static Builder subscriptionMetrics() { - return new Builder(); - } + public static Builder subscriptionMetrics() { + return new Builder(); + } - public SubscriptionMetrics build() { - return subscriptionMetrics; - } + public SubscriptionMetrics build() { + return subscriptionMetrics; } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMode.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMode.java index 4d9332d508..743d51ee95 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMode.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionMode.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.api; public enum SubscriptionMode { - ANYCAST, BROADCAST + ANYCAST, + BROADCAST } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionName.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionName.java index 5ff2d69ef3..58a5685bce 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionName.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionName.java @@ -1,63 +1,65 @@ package pl.allegro.tech.hermes.api; +import static com.google.common.base.Preconditions.checkArgument; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; -import static com.google.common.base.Preconditions.checkArgument; - public class SubscriptionName { - private final String name; - private final TopicName topicName; + private final String name; + private final TopicName topicName; - @JsonCreator - public SubscriptionName(@JsonProperty("name") String name, @JsonProperty("topicName") TopicName topicName) { - this.name = name; - this.topicName = topicName; - } + @JsonCreator + public SubscriptionName( + @JsonProperty("name") String name, @JsonProperty("topicName") TopicName topicName) { + this.name = name; + this.topicName = topicName; + } - @JsonIgnore - public String getQualifiedName() { - return toString(); - } + @JsonIgnore + public String getQualifiedName() { + return toString(); + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public TopicName getTopicName() { - return topicName; - } + public TopicName getTopicName() { + return topicName; + } - public static SubscriptionName fromString(String string) { - String[] tokens = string.split("\\$"); - checkArgument(tokens.length > 1, "Incorrect string format. Expected 'topic$subscription'. Found:'%s'", string); - return new SubscriptionName(tokens[1], TopicName.fromQualifiedName(tokens[0])); - } + public static SubscriptionName fromString(String string) { + String[] tokens = string.split("\\$"); + checkArgument( + tokens.length > 1, + "Incorrect string format. Expected 'topic$subscription'. Found:'%s'", + string); + return new SubscriptionName(tokens[1], TopicName.fromQualifiedName(tokens[0])); + } - @Override - public String toString() { - return topicName.qualifiedName() + "$" + name; - } + @Override + public String toString() { + return topicName.qualifiedName() + "$" + name; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SubscriptionName that = (SubscriptionName) o; - return Objects.equals(name, that.name) - && Objects.equals(topicName, that.topicName); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(name, topicName); + if (o == null || getClass() != o.getClass()) { + return false; } + SubscriptionName that = (SubscriptionName) o; + return Objects.equals(name, that.name) && Objects.equals(topicName, that.topicName); + } + + @Override + public int hashCode() { + return Objects.hash(name, topicName); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionNameWithMetrics.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionNameWithMetrics.java index e06da8b8ab..a03d8f282a 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionNameWithMetrics.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionNameWithMetrics.java @@ -2,110 +2,124 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class SubscriptionNameWithMetrics { - private final String topicName; - private final String name; - private final long delivered; - private final long discarded; - private final long volume; - private final MetricDecimalValue timeouts; - private final MetricLongValue lag; - private final MetricDecimalValue rate; - private final MetricDecimalValue throughput; - - @JsonCreator - public SubscriptionNameWithMetrics( - @JsonProperty("topicName") String topicName, - @JsonProperty("name") String name, - @JsonProperty("delivered") long delivered, - @JsonProperty("discarded") long discarded, - @JsonProperty("volume") long volume, - @JsonProperty("timeouts") MetricDecimalValue timeouts, - @JsonProperty("lag") MetricLongValue lag, - @JsonProperty("rate") MetricDecimalValue rate, - @JsonProperty("throughput") MetricDecimalValue throughput - ) { - this.topicName = topicName; - this.name = name; - this.delivered = delivered; - this.discarded = discarded; - this.volume = volume; - this.timeouts = timeouts; - this.lag = lag; - this.rate = rate; - this.throughput = throughput; - } - - public static SubscriptionNameWithMetrics from(SubscriptionMetrics metrics, String name, String topicQualifiedName) { - return new SubscriptionNameWithMetrics(topicQualifiedName, name, metrics.getDelivered(), - metrics.getDiscarded(), metrics.getVolume(), metrics.getTimeouts(), metrics.getLag(), - metrics.getRate(), metrics.getThroughput()); - } - - public String getTopicName() { - return topicName; - } - - public String getName() { - return name; - } - - public long getDelivered() { - return delivered; + private final String topicName; + private final String name; + private final long delivered; + private final long discarded; + private final long volume; + private final MetricDecimalValue timeouts; + private final MetricLongValue lag; + private final MetricDecimalValue rate; + private final MetricDecimalValue throughput; + + @JsonCreator + public SubscriptionNameWithMetrics( + @JsonProperty("topicName") String topicName, + @JsonProperty("name") String name, + @JsonProperty("delivered") long delivered, + @JsonProperty("discarded") long discarded, + @JsonProperty("volume") long volume, + @JsonProperty("timeouts") MetricDecimalValue timeouts, + @JsonProperty("lag") MetricLongValue lag, + @JsonProperty("rate") MetricDecimalValue rate, + @JsonProperty("throughput") MetricDecimalValue throughput) { + this.topicName = topicName; + this.name = name; + this.delivered = delivered; + this.discarded = discarded; + this.volume = volume; + this.timeouts = timeouts; + this.lag = lag; + this.rate = rate; + this.throughput = throughput; + } + + public static SubscriptionNameWithMetrics from( + SubscriptionMetrics metrics, String name, String topicQualifiedName) { + return new SubscriptionNameWithMetrics( + topicQualifiedName, + name, + metrics.getDelivered(), + metrics.getDiscarded(), + metrics.getVolume(), + metrics.getTimeouts(), + metrics.getLag(), + metrics.getRate(), + metrics.getThroughput()); + } + + public String getTopicName() { + return topicName; + } + + public String getName() { + return name; + } + + public long getDelivered() { + return delivered; + } + + public long getDiscarded() { + return discarded; + } + + public MetricDecimalValue getTimeouts() { + return timeouts; + } + + public MetricLongValue getLag() { + return lag; + } + + public MetricDecimalValue getRate() { + return rate; + } + + public MetricDecimalValue getThroughput() { + return throughput; + } + + public long getVolume() { + return volume; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - public long getDiscarded() { - return discarded; - } - - public MetricDecimalValue getTimeouts() { - return timeouts; + if (o == null || getClass() != o.getClass()) { + return false; } - public MetricLongValue getLag() { - return lag; - } - - public MetricDecimalValue getRate() { - return rate; - } - - public MetricDecimalValue getThroughput() { - return throughput; - } - - public long getVolume() { - return volume; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - SubscriptionNameWithMetrics that = (SubscriptionNameWithMetrics) o; - - return Objects.equals(this.topicName, that.topicName) - && Objects.equals(this.name, that.name) - && Objects.equals(this.delivered, that.delivered) - && Objects.equals(this.discarded, that.discarded) - && Objects.equals(this.timeouts, that.timeouts) - && Objects.equals(this.lag, that.lag) - && Objects.equals(this.rate, that.rate) - && Objects.equals(this.throughput, that.throughput) - && Objects.equals(this.volume, that.volume); - } - - @Override - public int hashCode() { - return Objects.hash(this.topicName, this.name, this.delivered, this.discarded, - this.timeouts, this.lag, this.rate, this.throughput, this.volume); - } + SubscriptionNameWithMetrics that = (SubscriptionNameWithMetrics) o; + + return Objects.equals(this.topicName, that.topicName) + && Objects.equals(this.name, that.name) + && Objects.equals(this.delivered, that.delivered) + && Objects.equals(this.discarded, that.discarded) + && Objects.equals(this.timeouts, that.timeouts) + && Objects.equals(this.lag, that.lag) + && Objects.equals(this.rate, that.rate) + && Objects.equals(this.throughput, that.throughput) + && Objects.equals(this.volume, that.volume); + } + + @Override + public int hashCode() { + return Objects.hash( + this.topicName, + this.name, + this.delivered, + this.discarded, + this.timeouts, + this.lag, + this.rate, + this.throughput, + this.volume); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionOAuthPolicy.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionOAuthPolicy.java index 769f0d0ae0..a62945e9ae 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionOAuthPolicy.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionOAuthPolicy.java @@ -4,136 +4,135 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; import jakarta.validation.constraints.NotNull; - import java.util.Objects; public class SubscriptionOAuthPolicy { - private static final String ANONYMIZED_PASSWORD = "******"; - @NotNull - private final GrantType grantType; - @NotNull - private final String providerName; - private final String scope; - private final String username; - private final String password; - - @JsonCreator - public SubscriptionOAuthPolicy( - @JsonProperty("grantType") GrantType grantType, - @JsonProperty("providerName") String providerName, - @JsonProperty("scope") String scope, - @JsonProperty("username") String username, - @JsonProperty("password") String password) { - - this.grantType = grantType; - this.providerName = providerName; - this.scope = scope; - this.username = username; - this.password = password; + private static final String ANONYMIZED_PASSWORD = "******"; + @NotNull private final GrantType grantType; + @NotNull private final String providerName; + private final String scope; + private final String username; + private final String password; + + @JsonCreator + public SubscriptionOAuthPolicy( + @JsonProperty("grantType") GrantType grantType, + @JsonProperty("providerName") String providerName, + @JsonProperty("scope") String scope, + @JsonProperty("username") String username, + @JsonProperty("password") String password) { + + this.grantType = grantType; + this.providerName = providerName; + this.scope = scope; + this.username = username; + this.password = password; + } + + public static Builder passwordGrantOAuthPolicy(String providerName) { + return new Builder(providerName, GrantType.USERNAME_PASSWORD); + } + + public static Builder clientCredentialsGrantOAuthPolicy(String providerName) { + return new Builder(providerName, GrantType.CLIENT_CREDENTIALS); + } + + public GrantType getGrantType() { + return grantType; + } + + public String getProviderName() { + return providerName; + } + + public String getScope() { + return scope; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public SubscriptionOAuthPolicy anonymize() { + if (GrantType.USERNAME_PASSWORD.equals(grantType)) { + return new SubscriptionOAuthPolicy( + grantType, providerName, scope, username, ANONYMIZED_PASSWORD); } + return this; + } - public static Builder passwordGrantOAuthPolicy(String providerName) { - return new Builder(providerName, GrantType.USERNAME_PASSWORD); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - public static Builder clientCredentialsGrantOAuthPolicy(String providerName) { - return new Builder(providerName, GrantType.CLIENT_CREDENTIALS); + if (o == null || getClass() != o.getClass()) { + return false; } - - public GrantType getGrantType() { - return grantType; - } - - public String getProviderName() { - return providerName; + SubscriptionOAuthPolicy that = (SubscriptionOAuthPolicy) o; + return grantType == that.grantType + && Objects.equals(providerName, that.providerName) + && Objects.equals(scope, that.scope) + && Objects.equals(username, that.username) + && Objects.equals(password, that.password); + } + + @Override + public int hashCode() { + return Objects.hash(grantType, providerName, scope, username, password); + } + + public enum GrantType { + CLIENT_CREDENTIALS("clientCredentials"), + USERNAME_PASSWORD("password"); + + private final String name; + + GrantType(String name) { + this.name = name; } - public String getScope() { - return scope; + @JsonValue + public String getName() { + return name; } + } - public String getUsername() { - return username; - } + public static class Builder { - public String getPassword() { - return password; - } + private final String providerName; + private final GrantType grantType; + private String scope; + private String username; + private String password; - public SubscriptionOAuthPolicy anonymize() { - if (GrantType.USERNAME_PASSWORD.equals(grantType)) { - return new SubscriptionOAuthPolicy(grantType, providerName, scope, username, ANONYMIZED_PASSWORD); - } - return this; + public Builder(String providerName, GrantType grantType) { + this.providerName = providerName; + this.grantType = grantType; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SubscriptionOAuthPolicy that = (SubscriptionOAuthPolicy) o; - return grantType == that.grantType - && Objects.equals(providerName, that.providerName) - && Objects.equals(scope, that.scope) - && Objects.equals(username, that.username) - && Objects.equals(password, that.password); + public Builder withScope(String scope) { + this.scope = scope; + return this; } - @Override - public int hashCode() { - return Objects.hash(grantType, providerName, scope, username, password); + public Builder withUsername(String username) { + this.username = username; + return this; } - public enum GrantType { - CLIENT_CREDENTIALS("clientCredentials"), USERNAME_PASSWORD("password"); - - private final String name; - - GrantType(String name) { - this.name = name; - } - - @JsonValue - public String getName() { - return name; - } + public Builder withPassword(String password) { + this.password = password; + return this; } - public static class Builder { - - private final String providerName; - private final GrantType grantType; - private String scope; - private String username; - private String password; - - public Builder(String providerName, GrantType grantType) { - this.providerName = providerName; - this.grantType = grantType; - } - - public Builder withScope(String scope) { - this.scope = scope; - return this; - } - - public Builder withUsername(String username) { - this.username = username; - return this; - } - - public Builder withPassword(String password) { - this.password = password; - return this; - } - - public SubscriptionOAuthPolicy build() { - return new SubscriptionOAuthPolicy(grantType, providerName, scope, username, password); - } + public SubscriptionOAuthPolicy build() { + return new SubscriptionOAuthPolicy(grantType, providerName, scope, username, password); } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionPolicy.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionPolicy.java index b1f33c0a78..2a2bc95e34 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionPolicy.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionPolicy.java @@ -5,272 +5,276 @@ import jakarta.annotation.Nullable; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.Null; -import pl.allegro.tech.hermes.api.constraints.AdminPermitted; -import pl.allegro.tech.hermes.api.helpers.Patch; - import java.util.Map; import java.util.Objects; +import pl.allegro.tech.hermes.api.helpers.Patch; public class SubscriptionPolicy { - private static final int DEFAULT_RATE = 400; - private static final int DEFAULT_MESSAGE_TTL = 3600; - private static final int DEFAULT_MESSAGE_BACKOFF = 100; - private static final int DEFAULT_REQUEST_TIMEOUT = 1000; - private static final int DEFAULT_SOCKET_TIMEOUT = 0; - private static final int DEFAULT_SENDING_DELAY = 0; - private static final double DEFAULT_BACKOFF_MULTIPLIER = 1; - private static final int DEFAULT_BACKOFF_MAX_INTERVAL = 600; - - @Min(1) - private int rate = DEFAULT_RATE; - - @Min(0) - @Max(7200) - private int messageTtl = DEFAULT_MESSAGE_TTL; - - @Min(0) - private int messageBackoff = DEFAULT_MESSAGE_BACKOFF; - - @Min(100) - @Max(300_000) - private int requestTimeout = DEFAULT_REQUEST_TIMEOUT; - - @Min(0) - @Max(300_000) - private int socketTimeout = DEFAULT_SOCKET_TIMEOUT; - - @Min(1) - @Null(groups = AdminPermitted.class) - private Integer inflightSize; - - @Min(0) - @Max(5000) - private int sendingDelay = DEFAULT_SENDING_DELAY; - - @Min(1) - @Max(10) - private double backoffMultiplier = DEFAULT_BACKOFF_MULTIPLIER; - - @Min(1) - @Max(600) - private int backoffMaxIntervalInSec = DEFAULT_BACKOFF_MAX_INTERVAL; - - private boolean retryClientErrors = false; - - private SubscriptionPolicy() { - } - - public SubscriptionPolicy(int rate, - int messageTtl, - int requestTimeout, - int socketTimeout, - boolean retryClientErrors, - int messageBackoff, - Integer inflightSize, - int sendingDelay, - double backoffMultiplier, - int backoffMaxIntervalInSec) { - this.rate = rate; - this.messageTtl = messageTtl; - this.requestTimeout = requestTimeout; - this.socketTimeout = socketTimeout; - this.retryClientErrors = retryClientErrors; - this.messageBackoff = messageBackoff; - this.inflightSize = inflightSize; - this.sendingDelay = sendingDelay; - this.backoffMultiplier = backoffMultiplier; - this.backoffMaxIntervalInSec = backoffMaxIntervalInSec; + private static final int DEFAULT_RATE = 400; + private static final int DEFAULT_MESSAGE_TTL = 3600; + private static final int DEFAULT_MESSAGE_BACKOFF = 100; + private static final int DEFAULT_REQUEST_TIMEOUT = 1000; + private static final int DEFAULT_SOCKET_TIMEOUT = 0; + private static final int DEFAULT_SENDING_DELAY = 0; + private static final double DEFAULT_BACKOFF_MULTIPLIER = 1; + private static final int DEFAULT_BACKOFF_MAX_INTERVAL = 600; + + @Min(1) + private int rate = DEFAULT_RATE; + + @Min(0) + @Max(7200) + private int messageTtl = DEFAULT_MESSAGE_TTL; + + @Min(0) + private int messageBackoff = DEFAULT_MESSAGE_BACKOFF; + + @Min(100) + @Max(300_000) + private int requestTimeout = DEFAULT_REQUEST_TIMEOUT; + + @Min(0) + @Max(300_000) + private int socketTimeout = DEFAULT_SOCKET_TIMEOUT; + + @Min(1) + private Integer inflightSize; + + @Min(0) + @Max(5000) + private int sendingDelay = DEFAULT_SENDING_DELAY; + + @Min(1) + @Max(10) + private double backoffMultiplier = DEFAULT_BACKOFF_MULTIPLIER; + + @Min(1) + @Max(600) + private int backoffMaxIntervalInSec = DEFAULT_BACKOFF_MAX_INTERVAL; + + private boolean retryClientErrors = false; + + private SubscriptionPolicy() {} + + public SubscriptionPolicy( + int rate, + int messageTtl, + int requestTimeout, + int socketTimeout, + boolean retryClientErrors, + int messageBackoff, + Integer inflightSize, + int sendingDelay, + double backoffMultiplier, + int backoffMaxIntervalInSec) { + this.rate = rate; + this.messageTtl = messageTtl; + this.requestTimeout = requestTimeout; + this.socketTimeout = socketTimeout; + this.retryClientErrors = retryClientErrors; + this.messageBackoff = messageBackoff; + this.inflightSize = inflightSize; + this.sendingDelay = sendingDelay; + this.backoffMultiplier = backoffMultiplier; + this.backoffMaxIntervalInSec = backoffMaxIntervalInSec; + } + + @JsonCreator + public static SubscriptionPolicy create(Map properties) { + return new SubscriptionPolicy( + (Integer) properties.getOrDefault("rate", DEFAULT_RATE), + (Integer) properties.getOrDefault("messageTtl", DEFAULT_MESSAGE_TTL), + (Integer) properties.getOrDefault("requestTimeout", DEFAULT_REQUEST_TIMEOUT), + (Integer) properties.getOrDefault("socketTimeout", DEFAULT_SOCKET_TIMEOUT), + (Boolean) properties.getOrDefault("retryClientErrors", false), + (Integer) properties.getOrDefault("messageBackoff", DEFAULT_MESSAGE_BACKOFF), + (Integer) properties.getOrDefault("inflightSize", null), + (Integer) properties.getOrDefault("sendingDelay", DEFAULT_SENDING_DELAY), + ((Number) properties.getOrDefault("backoffMultiplier", DEFAULT_BACKOFF_MULTIPLIER)) + .doubleValue(), + (Integer) properties.getOrDefault("backoffMaxIntervalInSec", DEFAULT_BACKOFF_MAX_INTERVAL)); + } + + @Override + public int hashCode() { + return Objects.hash( + rate, + messageTtl, + messageBackoff, + retryClientErrors, + requestTimeout, + socketTimeout, + inflightSize, + sendingDelay, + backoffMultiplier, + backoffMaxIntervalInSec); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @JsonCreator - public static SubscriptionPolicy create(Map properties) { - return new SubscriptionPolicy( - (Integer) properties.getOrDefault("rate", DEFAULT_RATE), - (Integer) properties.getOrDefault("messageTtl", DEFAULT_MESSAGE_TTL), - (Integer) properties.getOrDefault("requestTimeout", DEFAULT_REQUEST_TIMEOUT), - (Integer) properties.getOrDefault("socketTimeout", DEFAULT_SOCKET_TIMEOUT), - (Boolean) properties.getOrDefault("retryClientErrors", false), - (Integer) properties.getOrDefault("messageBackoff", DEFAULT_MESSAGE_BACKOFF), - (Integer) properties.getOrDefault("inflightSize", null), - (Integer) properties.getOrDefault("sendingDelay", DEFAULT_SENDING_DELAY), - ((Number) properties.getOrDefault("backoffMultiplier", DEFAULT_BACKOFF_MULTIPLIER)).doubleValue(), - (Integer) properties.getOrDefault("backoffMaxIntervalInSec", DEFAULT_BACKOFF_MAX_INTERVAL) - ); + if (obj == null || getClass() != obj.getClass()) { + return false; } - - @Override - public int hashCode() { - return Objects.hash(rate, messageTtl, messageBackoff, retryClientErrors, - requestTimeout, socketTimeout, inflightSize, sendingDelay, backoffMultiplier, - backoffMaxIntervalInSec); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final SubscriptionPolicy other = (SubscriptionPolicy) obj; - return Objects.equals(this.rate, other.rate) - && Objects.equals(this.messageTtl, other.messageTtl) - && Objects.equals(this.messageBackoff, other.messageBackoff) - && Objects.equals(this.retryClientErrors, other.retryClientErrors) - && Objects.equals(this.requestTimeout, other.requestTimeout) - && Objects.equals(this.socketTimeout, other.socketTimeout) - && Objects.equals(this.inflightSize, other.inflightSize) - && Objects.equals(this.sendingDelay, other.sendingDelay) - && Objects.equals(this.backoffMultiplier, other.backoffMultiplier) - && Objects.equals(this.backoffMaxIntervalInSec, other.backoffMaxIntervalInSec); + final SubscriptionPolicy other = (SubscriptionPolicy) obj; + return Objects.equals(this.rate, other.rate) + && Objects.equals(this.messageTtl, other.messageTtl) + && Objects.equals(this.messageBackoff, other.messageBackoff) + && Objects.equals(this.retryClientErrors, other.retryClientErrors) + && Objects.equals(this.requestTimeout, other.requestTimeout) + && Objects.equals(this.socketTimeout, other.socketTimeout) + && Objects.equals(this.inflightSize, other.inflightSize) + && Objects.equals(this.sendingDelay, other.sendingDelay) + && Objects.equals(this.backoffMultiplier, other.backoffMultiplier) + && Objects.equals(this.backoffMaxIntervalInSec, other.backoffMaxIntervalInSec); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("rate", rate) + .add("messageTtl", messageTtl) + .add("requestTimeout", requestTimeout) + .add("socketTimeout", socketTimeout) + .add("messageBackoff", messageBackoff) + .add("retryClientErrors", retryClientErrors) + .add("inflightSize", inflightSize) + .add("sendingDelay", sendingDelay) + .add("backoffMultiplier", backoffMultiplier) + .add("backoffMaxIntervalInSec", backoffMaxIntervalInSec) + .toString(); + } + + public Integer getRate() { + return rate; + } + + public void setRate(int rate) { + this.rate = rate; + } + + public Integer getMessageTtl() { + return messageTtl; + } + + public Boolean isRetryClientErrors() { + return retryClientErrors; + } + + public Integer getMessageBackoff() { + return messageBackoff; + } + + public Integer getRequestTimeout() { + return requestTimeout; + } + + public Integer getSocketTimeout() { + return socketTimeout; + } + + @Nullable + public Integer getInflightSize() { + return inflightSize; + } + + public Integer getSendingDelay() { + return sendingDelay; + } + + public Double getBackoffMultiplier() { + return backoffMultiplier; + } + + public Integer getBackoffMaxIntervalInSec() { + return backoffMaxIntervalInSec; + } + + public Long getBackoffMaxIntervalMillis() { + return backoffMaxIntervalInSec * 1000L; + } + + public static class Builder { + + private SubscriptionPolicy subscriptionPolicy; + + public Builder() { + subscriptionPolicy = new SubscriptionPolicy(); } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("rate", rate) - .add("messageTtl", messageTtl) - .add("requestTimeout", requestTimeout) - .add("socketTimeout", socketTimeout) - .add("messageBackoff", messageBackoff) - .add("retryClientErrors", retryClientErrors) - .add("inflightSize", inflightSize) - .add("sendingDelay", sendingDelay) - .add("backoffMultiplier", backoffMultiplier) - .add("backoffMaxIntervalInSec", backoffMaxIntervalInSec) - .toString(); + public static Builder subscriptionPolicy() { + return new Builder(); } - public Integer getRate() { - return rate; + public Builder applyDefaults() { + subscriptionPolicy.rate = DEFAULT_RATE; + subscriptionPolicy.messageTtl = DEFAULT_MESSAGE_TTL; + subscriptionPolicy.backoffMultiplier = DEFAULT_BACKOFF_MULTIPLIER; + return this; } - public void setRate(int rate) { - this.rate = rate; + public Builder withRate(int rate) { + subscriptionPolicy.rate = rate; + return this; } - public Integer getMessageTtl() { - return messageTtl; + public Builder withMessageTtl(int ttl) { + subscriptionPolicy.messageTtl = ttl; + return this; } - public Boolean isRetryClientErrors() { - return retryClientErrors; + public Builder withRequestTimeout(int timeout) { + subscriptionPolicy.requestTimeout = timeout; + return this; } - public Integer getMessageBackoff() { - return messageBackoff; + public Builder withSocketTimeout(int timeout) { + subscriptionPolicy.socketTimeout = timeout; + return this; } - public Integer getRequestTimeout() { - return requestTimeout; + public Builder withInflightSize(Integer inflightSize) { + subscriptionPolicy.inflightSize = inflightSize; + return this; } - public Integer getSocketTimeout() { - return socketTimeout; + public Builder withMessageBackoff(int backoff) { + subscriptionPolicy.messageBackoff = backoff; + return this; } - @Nullable - public Integer getInflightSize() { - return inflightSize; + public Builder withClientErrorRetry() { + subscriptionPolicy.retryClientErrors = true; + return this; } - public Integer getSendingDelay() { - return sendingDelay; + public Builder withSendingDelay(int sendingDelay) { + subscriptionPolicy.sendingDelay = sendingDelay; + return this; } - public Double getBackoffMultiplier() { - return backoffMultiplier; + public Builder withBackoffMultiplier(double backoffMultiplier) { + subscriptionPolicy.backoffMultiplier = backoffMultiplier; + return this; } - public Integer getBackoffMaxIntervalInSec() { - return backoffMaxIntervalInSec; + public Builder withBackoffMaxIntervalInSec(int backoffMaxIntervalInSec) { + subscriptionPolicy.backoffMaxIntervalInSec = backoffMaxIntervalInSec; + return this; } - public Long getBackoffMaxIntervalMillis() { - return backoffMaxIntervalInSec * 1000L; + public SubscriptionPolicy build() { + return subscriptionPolicy; } - public static class Builder { - - private SubscriptionPolicy subscriptionPolicy; - - public Builder() { - subscriptionPolicy = new SubscriptionPolicy(); - } - - public static Builder subscriptionPolicy() { - return new Builder(); - } - - public Builder applyDefaults() { - subscriptionPolicy.rate = DEFAULT_RATE; - subscriptionPolicy.messageTtl = DEFAULT_MESSAGE_TTL; - subscriptionPolicy.backoffMultiplier = DEFAULT_BACKOFF_MULTIPLIER; - return this; - } - - public Builder withRate(int rate) { - subscriptionPolicy.rate = rate; - return this; - } - - public Builder withMessageTtl(int ttl) { - subscriptionPolicy.messageTtl = ttl; - return this; - } - - public Builder withRequestTimeout(int timeout) { - subscriptionPolicy.requestTimeout = timeout; - return this; - } - - public Builder withSocketTimeout(int timeout) { - subscriptionPolicy.socketTimeout = timeout; - return this; - } - - public Builder withInflightSize(Integer inflightSize) { - subscriptionPolicy.inflightSize = inflightSize; - return this; - } - - public Builder withMessageBackoff(int backoff) { - subscriptionPolicy.messageBackoff = backoff; - return this; - } - - public Builder withClientErrorRetry() { - subscriptionPolicy.retryClientErrors = true; - return this; - } - - public Builder withSendingDelay(int sendingDelay) { - subscriptionPolicy.sendingDelay = sendingDelay; - return this; - } - - public Builder withBackoffMultiplier(double backoffMultiplier) { - subscriptionPolicy.backoffMultiplier = backoffMultiplier; - return this; - } - - public Builder withBackoffMaxIntervalInSec(int backoffMaxIntervalInSec) { - subscriptionPolicy.backoffMaxIntervalInSec = backoffMaxIntervalInSec; - return this; - } - - public SubscriptionPolicy build() { - return subscriptionPolicy; - } - - public Builder applyPatch(PatchData update) { - if (update != null) { - subscriptionPolicy = Patch.apply(subscriptionPolicy, update); - } - return this; - } + public Builder applyPatch(PatchData update) { + if (update != null) { + subscriptionPolicy = Patch.apply(subscriptionPolicy, update); + } + return this; } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionStats.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionStats.java index 743170e0a4..9dabd16d37 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionStats.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/SubscriptionStats.java @@ -1,63 +1,64 @@ package pl.allegro.tech.hermes.api; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class SubscriptionStats { - private final long subscriptionCount; - private final long trackingEnabledSubscriptionCount; - private final long avroSubscriptionCount; - - @JsonCreator - public SubscriptionStats( - @JsonProperty("subscriptionCount") long subscriptionCount, - @JsonProperty("trackingEnabledSubscriptionCount") long trackingEnabledSubscriptionCount, - @JsonProperty("avroSubscriptionCount") long avroSubscriptionCount) { - this.subscriptionCount = subscriptionCount; - this.trackingEnabledSubscriptionCount = trackingEnabledSubscriptionCount; - this.avroSubscriptionCount = avroSubscriptionCount; - } + private final long subscriptionCount; + private final long trackingEnabledSubscriptionCount; + private final long avroSubscriptionCount; - public long getSubscriptionCount() { - return subscriptionCount; - } + @JsonCreator + public SubscriptionStats( + @JsonProperty("subscriptionCount") long subscriptionCount, + @JsonProperty("trackingEnabledSubscriptionCount") long trackingEnabledSubscriptionCount, + @JsonProperty("avroSubscriptionCount") long avroSubscriptionCount) { + this.subscriptionCount = subscriptionCount; + this.trackingEnabledSubscriptionCount = trackingEnabledSubscriptionCount; + this.avroSubscriptionCount = avroSubscriptionCount; + } - public long getTrackingEnabledSubscriptionCount() { - return trackingEnabledSubscriptionCount; - } + public long getSubscriptionCount() { + return subscriptionCount; + } - public long getAvroSubscriptionCount() { - return avroSubscriptionCount; - } + public long getTrackingEnabledSubscriptionCount() { + return trackingEnabledSubscriptionCount; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - SubscriptionStats that = (SubscriptionStats) o; - return subscriptionCount == that.subscriptionCount - && trackingEnabledSubscriptionCount == that.trackingEnabledSubscriptionCount - && avroSubscriptionCount == that.avroSubscriptionCount; - } + public long getAvroSubscriptionCount() { + return avroSubscriptionCount; + } - @Override - public int hashCode() { - return Objects.hash(subscriptionCount, trackingEnabledSubscriptionCount, avroSubscriptionCount); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public String toString() { - return "SubscriptionStats{" - + "subscriptionCount=" + subscriptionCount - + ", trackingEnabledSubscriptionCount=" + trackingEnabledSubscriptionCount - + ", avroSubscriptionCount=" + avroSubscriptionCount - + '}'; + if (o == null || getClass() != o.getClass()) { + return false; } + SubscriptionStats that = (SubscriptionStats) o; + return subscriptionCount == that.subscriptionCount + && trackingEnabledSubscriptionCount == that.trackingEnabledSubscriptionCount + && avroSubscriptionCount == that.avroSubscriptionCount; + } + + @Override + public int hashCode() { + return Objects.hash(subscriptionCount, trackingEnabledSubscriptionCount, avroSubscriptionCount); + } + + @Override + public String toString() { + return "SubscriptionStats{" + + "subscriptionCount=" + + subscriptionCount + + ", trackingEnabledSubscriptionCount=" + + trackingEnabledSubscriptionCount + + ", avroSubscriptionCount=" + + avroSubscriptionCount + + '}'; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Topic.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Topic.java index 069511a0f5..b6ae679118 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Topic.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/Topic.java @@ -10,253 +10,316 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; - import java.time.Instant; import java.util.Collections; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; -@JsonIgnoreProperties(value = {"createdAt", "modifiedAt"}, allowGetters = true) +@JsonIgnoreProperties( + value = {"createdAt", "modifiedAt"}, + allowGetters = true) public class Topic { - public static final int MIN_MESSAGE_SIZE = 1024; - public static final int MAX_MESSAGE_SIZE = 2 * 1024 * 1024; - public static final String DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY = "defaultSchemaIdAwareSerializationEnabled"; - private static final int DEFAULT_MAX_MESSAGE_SIZE = 50 * 1024; - private final boolean schemaIdAwareSerializationEnabled; - private final TopicDataOfflineStorage offlineStorage; - @Valid - @NotNull - private TopicName name; - @NotNull - private String description; - @Valid - @NotNull - private OwnerId owner; - private boolean jsonToAvroDryRunEnabled = false; - @NotNull - private Ack ack; - @NotNull - private ContentType contentType; - @Min(MIN_MESSAGE_SIZE) - @Max(MAX_MESSAGE_SIZE) - private int maxMessageSize; - @Valid - @NotNull - private RetentionTime retentionTime = RetentionTime.of(1, TimeUnit.DAYS); - private boolean trackingEnabled = false; - private boolean migratedFromJsonType = false; - private boolean subscribingRestricted = false; - - private PublishingAuth publishingAuth; - @Valid - private Set labels; - private Instant createdAt; - private Instant modifiedAt; - - public Topic(TopicName name, String description, OwnerId owner, RetentionTime retentionTime, - boolean migratedFromJsonType, Ack ack, boolean trackingEnabled, ContentType contentType, boolean jsonToAvroDryRunEnabled, - @JacksonInject(value = DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, useInput = OptBoolean.TRUE) - Boolean schemaIdAwareSerializationEnabled, - int maxMessageSize, PublishingAuth publishingAuth, boolean subscribingRestricted, - TopicDataOfflineStorage offlineStorage, Set labels, Instant createdAt, Instant modifiedAt) { - this.name = name; - this.description = description; - this.owner = owner; - this.retentionTime = retentionTime; - this.ack = (ack == null ? Ack.LEADER : ack); - this.trackingEnabled = trackingEnabled; - this.migratedFromJsonType = migratedFromJsonType; - this.contentType = contentType; - this.jsonToAvroDryRunEnabled = jsonToAvroDryRunEnabled; - this.schemaIdAwareSerializationEnabled = schemaIdAwareSerializationEnabled; - this.maxMessageSize = maxMessageSize; - this.publishingAuth = publishingAuth; - this.subscribingRestricted = subscribingRestricted; - this.offlineStorage = offlineStorage; - this.labels = labels; - this.createdAt = createdAt; - this.modifiedAt = modifiedAt; - } - - @JsonCreator - public Topic( - @JsonProperty("name") String qualifiedName, - @JsonProperty("description") String description, - @JsonProperty("owner") OwnerId owner, - @JsonProperty("retentionTime") RetentionTime retentionTime, - @JsonProperty("jsonToAvroDryRun") boolean jsonToAvroDryRunEnabled, - @JsonProperty("ack") Ack ack, - @JsonProperty("trackingEnabled") boolean trackingEnabled, - @JsonProperty("migratedFromJsonType") boolean migratedFromJsonType, - @JsonProperty("schemaIdAwareSerializationEnabled") - @JacksonInject(value = DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, useInput = OptBoolean.TRUE) - Boolean schemaIdAwareSerializationEnabled, - @JsonProperty("contentType") ContentType contentType, - @JsonProperty("maxMessageSize") Integer maxMessageSize, - @JsonProperty("auth") PublishingAuth publishingAuth, - @JsonProperty("subscribingRestricted") boolean subscribingRestricted, - @JsonProperty("offlineStorage") TopicDataOfflineStorage offlineStorage, - @JsonProperty("labels") Set labels, - @JsonProperty("createdAt") Instant createdAt, - @JsonProperty("modifiedAt") Instant modifiedAt - ) { - this(TopicName.fromQualifiedName(qualifiedName), description, owner, retentionTime, migratedFromJsonType, ack, - trackingEnabled, contentType, jsonToAvroDryRunEnabled, schemaIdAwareSerializationEnabled, - maxMessageSize == null ? DEFAULT_MAX_MESSAGE_SIZE : maxMessageSize, - publishingAuth == null ? PublishingAuth.disabled() : publishingAuth, - subscribingRestricted, - offlineStorage == null ? TopicDataOfflineStorage.defaultOfflineStorage() : offlineStorage, - labels == null ? Collections.emptySet() : labels, - createdAt, modifiedAt - ); - } - - public RetentionTime getRetentionTime() { - return retentionTime; - } - - @Override - public int hashCode() { - return Objects.hash(name, description, owner, retentionTime, migratedFromJsonType, trackingEnabled, ack, contentType, - jsonToAvroDryRunEnabled, schemaIdAwareSerializationEnabled, maxMessageSize, - publishingAuth, subscribingRestricted, offlineStorage, labels); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final Topic other = (Topic) obj; - - return Objects.equals(this.name, other.name) - && Objects.equals(this.description, other.description) - && Objects.equals(this.owner, other.owner) - && Objects.equals(this.retentionTime, other.retentionTime) - && Objects.equals(this.jsonToAvroDryRunEnabled, other.jsonToAvroDryRunEnabled) - && Objects.equals(this.trackingEnabled, other.trackingEnabled) - && Objects.equals(this.migratedFromJsonType, other.migratedFromJsonType) - && Objects.equals(this.schemaIdAwareSerializationEnabled, other.schemaIdAwareSerializationEnabled) - && Objects.equals(this.ack, other.ack) - && Objects.equals(this.contentType, other.contentType) - && Objects.equals(this.maxMessageSize, other.maxMessageSize) - && Objects.equals(this.subscribingRestricted, other.subscribingRestricted) - && Objects.equals(this.publishingAuth, other.publishingAuth) - && Objects.equals(this.offlineStorage, other.offlineStorage) - && Objects.equals(this.labels, other.labels); - } - - @JsonProperty("name") - public String getQualifiedName() { - return TopicName.toQualifiedName(name); - } - - @JsonIgnore - public TopicName getName() { - return name; - } - - public String getDescription() { - return description; - } - - public OwnerId getOwner() { - return owner; - } - - @JsonProperty("jsonToAvroDryRun") - public boolean isJsonToAvroDryRunEnabled() { - return jsonToAvroDryRunEnabled; - } - - public Ack getAck() { - return ack; - } - - public ContentType getContentType() { - return contentType; - } - - public boolean isTrackingEnabled() { - return trackingEnabled; - } - - @JsonProperty("migratedFromJsonType") - public boolean wasMigratedFromJsonType() { - return migratedFromJsonType; - } - - @JsonIgnore - public boolean isReplicationConfirmRequired() { - return getAck() == Ack.ALL; + public static final int MIN_MESSAGE_SIZE = 1024; + public static final int MAX_MESSAGE_SIZE = 2 * 1024 * 1024; + public static final String DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY = + "defaultSchemaIdAwareSerializationEnabled"; + private static final int DEFAULT_MAX_MESSAGE_SIZE = 50 * 1024; + private final boolean schemaIdAwareSerializationEnabled; + private final TopicDataOfflineStorage offlineStorage; + @Valid @NotNull private TopicName name; + @NotNull private String description; + @Valid @NotNull private OwnerId owner; + private boolean jsonToAvroDryRunEnabled = false; + @NotNull private Ack ack; + public static final String DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY = + "defaultFallbackToRemoteDatacenterEnabled"; + private final boolean fallbackToRemoteDatacenterEnabled; + private PublishingChaosPolicy chaos; + @NotNull private ContentType contentType; + + @Min(MIN_MESSAGE_SIZE) + @Max(MAX_MESSAGE_SIZE) + private int maxMessageSize; + + @Valid @NotNull private RetentionTime retentionTime = RetentionTime.of(1, TimeUnit.DAYS); + private boolean trackingEnabled = false; + private boolean migratedFromJsonType = false; + private boolean subscribingRestricted = false; + + private PublishingAuth publishingAuth; + @Valid private Set labels; + private Instant createdAt; + private Instant modifiedAt; + + public Topic( + TopicName name, + String description, + OwnerId owner, + RetentionTime retentionTime, + boolean migratedFromJsonType, + Ack ack, + @JacksonInject(value = DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY, useInput = OptBoolean.TRUE) + Boolean fallbackToRemoteDatacenterEnabled, + PublishingChaosPolicy chaos, + boolean trackingEnabled, + ContentType contentType, + boolean jsonToAvroDryRunEnabled, + @JacksonInject( + value = DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, + useInput = OptBoolean.TRUE) + Boolean schemaIdAwareSerializationEnabled, + int maxMessageSize, + PublishingAuth publishingAuth, + boolean subscribingRestricted, + TopicDataOfflineStorage offlineStorage, + Set labels, + Instant createdAt, + Instant modifiedAt) { + this.name = name; + this.description = description; + this.owner = owner; + this.retentionTime = retentionTime; + this.ack = (ack == null ? Ack.LEADER : ack); + this.fallbackToRemoteDatacenterEnabled = fallbackToRemoteDatacenterEnabled; + this.chaos = chaos; + this.trackingEnabled = trackingEnabled; + this.migratedFromJsonType = migratedFromJsonType; + this.contentType = contentType; + this.jsonToAvroDryRunEnabled = jsonToAvroDryRunEnabled; + this.schemaIdAwareSerializationEnabled = schemaIdAwareSerializationEnabled; + this.maxMessageSize = maxMessageSize; + this.publishingAuth = publishingAuth; + this.subscribingRestricted = subscribingRestricted; + this.offlineStorage = offlineStorage; + this.labels = labels; + this.createdAt = createdAt; + this.modifiedAt = modifiedAt; + } + + @JsonCreator + public Topic( + @JsonProperty("name") String qualifiedName, + @JsonProperty("description") String description, + @JsonProperty("owner") OwnerId owner, + @JsonProperty("retentionTime") RetentionTime retentionTime, + @JsonProperty("jsonToAvroDryRun") boolean jsonToAvroDryRunEnabled, + @JsonProperty("ack") Ack ack, + @JacksonInject(value = DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY, useInput = OptBoolean.TRUE) + @JsonProperty("fallbackToRemoteDatacenterEnabled") + Boolean fallbackToRemoteDatacenterEnabled, + @JsonProperty("chaos") PublishingChaosPolicy chaos, + @JsonProperty("trackingEnabled") boolean trackingEnabled, + @JsonProperty("migratedFromJsonType") boolean migratedFromJsonType, + @JsonProperty("schemaIdAwareSerializationEnabled") + @JacksonInject( + value = DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, + useInput = OptBoolean.TRUE) + Boolean schemaIdAwareSerializationEnabled, + @JsonProperty("contentType") ContentType contentType, + @JsonProperty("maxMessageSize") Integer maxMessageSize, + @JsonProperty("auth") PublishingAuth publishingAuth, + @JsonProperty("subscribingRestricted") boolean subscribingRestricted, + @JsonProperty("offlineStorage") TopicDataOfflineStorage offlineStorage, + @JsonProperty("labels") Set labels, + @JsonProperty("createdAt") Instant createdAt, + @JsonProperty("modifiedAt") Instant modifiedAt) { + this( + TopicName.fromQualifiedName(qualifiedName), + description, + owner, + retentionTime, + migratedFromJsonType, + ack, + fallbackToRemoteDatacenterEnabled, + chaos == null ? PublishingChaosPolicy.disabled() : chaos, + trackingEnabled, + contentType, + jsonToAvroDryRunEnabled, + schemaIdAwareSerializationEnabled, + maxMessageSize == null ? DEFAULT_MAX_MESSAGE_SIZE : maxMessageSize, + publishingAuth == null ? PublishingAuth.disabled() : publishingAuth, + subscribingRestricted, + offlineStorage == null ? TopicDataOfflineStorage.defaultOfflineStorage() : offlineStorage, + labels == null ? Collections.emptySet() : labels, + createdAt, + modifiedAt); + } + + public RetentionTime getRetentionTime() { + return retentionTime; + } + + @Override + public int hashCode() { + return Objects.hash( + name, + description, + owner, + retentionTime, + migratedFromJsonType, + trackingEnabled, + ack, + fallbackToRemoteDatacenterEnabled, + chaos, + contentType, + jsonToAvroDryRunEnabled, + schemaIdAwareSerializationEnabled, + maxMessageSize, + publishingAuth, + subscribingRestricted, + offlineStorage, + labels); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - public boolean isSchemaIdAwareSerializationEnabled() { - return schemaIdAwareSerializationEnabled; - } - - public int getMaxMessageSize() { - return maxMessageSize; - } - - @JsonProperty("auth") - public PublishingAuth getPublishingAuth() { - return publishingAuth; - } - - @JsonIgnore - public boolean isAuthEnabled() { - return publishingAuth.isEnabled(); - } - - @JsonIgnore - public boolean isUnauthenticatedAccessEnabled() { - return publishingAuth.isUnauthenticatedAccessEnabled(); - } - - public boolean hasPermission(String publisher) { - return publishingAuth.hasPermission(publisher); - } - - public boolean isSubscribingRestricted() { - return subscribingRestricted; - } - - public TopicDataOfflineStorage getOfflineStorage() { - return offlineStorage; - } - - public Set getLabels() { - return Collections.unmodifiableSet(labels); - } - - public Instant getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Long createdAt) { - this.createdAt = Instant.ofEpochMilli(createdAt); - } - - public Instant getModifiedAt() { - return modifiedAt; - } - - public void setModifiedAt(Long modifiedAt) { - this.modifiedAt = Instant.ofEpochMilli(modifiedAt); - } - - @Override - public String toString() { - return "Topic(" + getQualifiedName() + ")"; - } - - public enum Ack { - NONE, LEADER, ALL + if (obj == null || getClass() != obj.getClass()) { + return false; } + final Topic other = (Topic) obj; + + return Objects.equals(this.name, other.name) + && Objects.equals(this.description, other.description) + && Objects.equals(this.owner, other.owner) + && Objects.equals(this.retentionTime, other.retentionTime) + && Objects.equals(this.jsonToAvroDryRunEnabled, other.jsonToAvroDryRunEnabled) + && Objects.equals(this.trackingEnabled, other.trackingEnabled) + && Objects.equals(this.migratedFromJsonType, other.migratedFromJsonType) + && Objects.equals( + this.schemaIdAwareSerializationEnabled, other.schemaIdAwareSerializationEnabled) + && Objects.equals(this.ack, other.ack) + && Objects.equals( + this.fallbackToRemoteDatacenterEnabled, other.fallbackToRemoteDatacenterEnabled) + && Objects.equals(this.chaos, other.chaos) + && Objects.equals(this.contentType, other.contentType) + && Objects.equals(this.maxMessageSize, other.maxMessageSize) + && Objects.equals(this.subscribingRestricted, other.subscribingRestricted) + && Objects.equals(this.publishingAuth, other.publishingAuth) + && Objects.equals(this.offlineStorage, other.offlineStorage) + && Objects.equals(this.labels, other.labels); + } + + @JsonProperty("name") + public String getQualifiedName() { + return TopicName.toQualifiedName(name); + } + + @JsonIgnore + public TopicName getName() { + return name; + } + + public String getDescription() { + return description; + } + + public OwnerId getOwner() { + return owner; + } + + @JsonProperty("jsonToAvroDryRun") + public boolean isJsonToAvroDryRunEnabled() { + return jsonToAvroDryRunEnabled; + } + + public Ack getAck() { + return ack; + } + + public boolean isFallbackToRemoteDatacenterEnabled() { + return fallbackToRemoteDatacenterEnabled; + } + + public PublishingChaosPolicy getChaos() { + return chaos; + } + + public ContentType getContentType() { + return contentType; + } + + public boolean isTrackingEnabled() { + return trackingEnabled; + } + + @JsonProperty("migratedFromJsonType") + public boolean wasMigratedFromJsonType() { + return migratedFromJsonType; + } + + @JsonIgnore + public boolean isReplicationConfirmRequired() { + return getAck() == Ack.ALL; + } + + public boolean isSchemaIdAwareSerializationEnabled() { + return schemaIdAwareSerializationEnabled; + } + + public int getMaxMessageSize() { + return maxMessageSize; + } + + @JsonProperty("auth") + public PublishingAuth getPublishingAuth() { + return publishingAuth; + } + + @JsonIgnore + public boolean isAuthEnabled() { + return publishingAuth.isEnabled(); + } + + @JsonIgnore + public boolean isUnauthenticatedAccessEnabled() { + return publishingAuth.isUnauthenticatedAccessEnabled(); + } + + public boolean hasPermission(String publisher) { + return publishingAuth.hasPermission(publisher); + } + + public boolean isSubscribingRestricted() { + return subscribingRestricted; + } + + public TopicDataOfflineStorage getOfflineStorage() { + return offlineStorage; + } + + public Set getLabels() { + return Collections.unmodifiableSet(labels); + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Long createdAt) { + this.createdAt = Instant.ofEpochMilli(createdAt); + } + + public Instant getModifiedAt() { + return modifiedAt; + } + + public void setModifiedAt(Long modifiedAt) { + this.modifiedAt = Instant.ofEpochMilli(modifiedAt); + } + + @Override + public String toString() { + return "Topic(" + getQualifiedName() + ")"; + } + + public enum Ack { + NONE, + LEADER, + ALL + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicConstraints.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicConstraints.java index e507b9eb35..031fba4a04 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicConstraints.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicConstraints.java @@ -6,22 +6,22 @@ public class TopicConstraints { - private final TopicName topicName; - @Valid - private final Constraints constraints; + private final TopicName topicName; + @Valid private final Constraints constraints; - @JsonCreator - public TopicConstraints(@JsonProperty("topicName") String topicName, - @JsonProperty("constraints") Constraints constraints) { - this.topicName = TopicName.fromQualifiedName(topicName); - this.constraints = constraints; - } + @JsonCreator + public TopicConstraints( + @JsonProperty("topicName") String topicName, + @JsonProperty("constraints") Constraints constraints) { + this.topicName = TopicName.fromQualifiedName(topicName); + this.constraints = constraints; + } - public TopicName getTopicName() { - return topicName; - } + public TopicName getTopicName() { + return topicName; + } - public Constraints getConstraints() { - return constraints; - } + public Constraints getConstraints() { + return constraints; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicDataOfflineStorage.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicDataOfflineStorage.java index 2ac58c2ada..eb99107b8c 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicDataOfflineStorage.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicDataOfflineStorage.java @@ -4,54 +4,52 @@ import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; - import java.util.Objects; /** - * Topic offline storage metadata - not used in Hermes, but exposed as part of API for other systems to use. + * Topic offline storage metadata - not used in Hermes, but exposed as part of API for other systems + * to use. */ public class TopicDataOfflineStorage { - private final boolean enabled; + private final boolean enabled; - @Valid - @NotNull - private final OfflineRetentionTime retentionTime; + @Valid @NotNull private final OfflineRetentionTime retentionTime; - @JsonCreator - public TopicDataOfflineStorage(@JsonProperty("enabled") boolean enabled, - @JsonProperty("retentionTime") OfflineRetentionTime retentionTime) { - this.enabled = enabled; - this.retentionTime = retentionTime; - } + @JsonCreator + public TopicDataOfflineStorage( + @JsonProperty("enabled") boolean enabled, + @JsonProperty("retentionTime") OfflineRetentionTime retentionTime) { + this.enabled = enabled; + this.retentionTime = retentionTime; + } - public static TopicDataOfflineStorage defaultOfflineStorage() { - return new TopicDataOfflineStorage(false, OfflineRetentionTime.of(0)); - } + public static TopicDataOfflineStorage defaultOfflineStorage() { + return new TopicDataOfflineStorage(false, OfflineRetentionTime.of(0)); + } - public boolean isEnabled() { - return enabled; - } + public boolean isEnabled() { + return enabled; + } - public OfflineRetentionTime getRetentionTime() { - return retentionTime; - } + public OfflineRetentionTime getRetentionTime() { + return retentionTime; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TopicDataOfflineStorage)) { - return false; - } - TopicDataOfflineStorage that = (TopicDataOfflineStorage) o; - return enabled == that.enabled - && Objects.equals(retentionTime, that.retentionTime); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(enabled, retentionTime); + if (!(o instanceof TopicDataOfflineStorage)) { + return false; } + TopicDataOfflineStorage that = (TopicDataOfflineStorage) o; + return enabled == that.enabled && Objects.equals(retentionTime, that.retentionTime); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, retentionTime); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicLabel.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicLabel.java index 96528ce2e5..e02ef4bc4d 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicLabel.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicLabel.java @@ -7,37 +7,36 @@ public class TopicLabel { - @NotEmpty - private final String value; + @NotEmpty private final String value; - @JsonCreator - public TopicLabel(@JsonProperty("value") String value) { - this.value = value; - } + @JsonCreator + public TopicLabel(@JsonProperty("value") String value) { + this.value = value; + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TopicLabel that = (TopicLabel) o; - return Objects.equal(value, that.value); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hashCode(value); - } - - @Override - public String toString() { - return "TopicLabel(" + value + ")"; + if (o == null || getClass() != o.getClass()) { + return false; } + TopicLabel that = (TopicLabel) o; + return Objects.equal(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + + @Override + public String toString() { + return "TopicLabel(" + value + ")"; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicMetrics.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicMetrics.java index ddc89bd6cc..564653a28f 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicMetrics.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicMetrics.java @@ -1,116 +1,117 @@ package pl.allegro.tech.hermes.api; -import java.util.Objects; - import static pl.allegro.tech.hermes.api.MetricDecimalValue.of; +import java.util.Objects; + public class TopicMetrics { - private long published; - private long volume; - private MetricDecimalValue rate = of("0.0"); - private MetricDecimalValue deliveryRate = of("0.0"); - private int subscriptions; - private MetricDecimalValue throughput = of("0.0"); - - public long getPublished() { - return published; + private long published; + private long volume; + private MetricDecimalValue rate = of("0.0"); + private MetricDecimalValue deliveryRate = of("0.0"); + private int subscriptions; + private MetricDecimalValue throughput = of("0.0"); + + public long getPublished() { + return published; + } + + public long getVolume() { + return volume; + } + + public MetricDecimalValue getRate() { + return rate; + } + + public MetricDecimalValue getDeliveryRate() { + return deliveryRate; + } + + public int getSubscriptions() { + return subscriptions; + } + + public MetricDecimalValue getThroughput() { + return throughput; + } + + @Override + public int hashCode() { + return Objects.hash(published, rate, deliveryRate, subscriptions, throughput, volume); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - public long getVolume() { - return volume; + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final TopicMetrics other = (TopicMetrics) obj; + return Objects.equals(this.published, other.published) + && Objects.equals(this.rate, other.rate) + && Objects.equals(this.deliveryRate, other.deliveryRate) + && Objects.equals(this.subscriptions, other.subscriptions) + && Objects.equals(this.throughput, other.throughput) + && Objects.equals(this.volume, other.volume); + } + + public static TopicMetrics unavailable() { + return Builder.topicMetrics() + .withRate(MetricDecimalValue.unavailable()) + .withDeliveryRate(MetricDecimalValue.unavailable()) + .withPublished(0) + .withVolume(0) + .withSubscriptions(0) + .withThroughput(MetricDecimalValue.unavailable()) + .build(); + } + + public static class Builder { + private final TopicMetrics topicMetrics; + + public Builder() { + topicMetrics = new TopicMetrics(); } - public MetricDecimalValue getRate() { - return rate; + public Builder withPublished(long published) { + topicMetrics.published = published; + return this; } - public MetricDecimalValue getDeliveryRate() { - return deliveryRate; + public Builder withRate(MetricDecimalValue rate) { + topicMetrics.rate = rate; + return this; } - public int getSubscriptions() { - return subscriptions; + public Builder withDeliveryRate(MetricDecimalValue deliveryRate) { + topicMetrics.deliveryRate = deliveryRate; + return this; } - public MetricDecimalValue getThroughput() { - return throughput; + public Builder withSubscriptions(int subscriptions) { + topicMetrics.subscriptions = subscriptions; + return this; } - @Override - public int hashCode() { - return Objects.hash(published, rate, deliveryRate, subscriptions, throughput, volume); + public Builder withThroughput(MetricDecimalValue throughput) { + topicMetrics.throughput = throughput; + return this; } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final TopicMetrics other = (TopicMetrics) obj; - return Objects.equals(this.published, other.published) - && Objects.equals(this.rate, other.rate) - && Objects.equals(this.deliveryRate, other.deliveryRate) - && Objects.equals(this.subscriptions, other.subscriptions) - && Objects.equals(this.throughput, other.throughput) - && Objects.equals(this.volume, other.volume); + public static Builder topicMetrics() { + return new Builder(); } - public static TopicMetrics unavailable() { - return Builder.topicMetrics().withRate(MetricDecimalValue.unavailable()) - .withDeliveryRate(MetricDecimalValue.unavailable()) - .withPublished(0) - .withVolume(0) - .withSubscriptions(0) - .withThroughput(MetricDecimalValue.unavailable()) - .build(); + public TopicMetrics build() { + return topicMetrics; } - public static class Builder { - private final TopicMetrics topicMetrics; - - public Builder() { - topicMetrics = new TopicMetrics(); - } - - public Builder withPublished(long published) { - topicMetrics.published = published; - return this; - } - - public Builder withRate(MetricDecimalValue rate) { - topicMetrics.rate = rate; - return this; - } - - public Builder withDeliveryRate(MetricDecimalValue deliveryRate) { - topicMetrics.deliveryRate = deliveryRate; - return this; - } - - public Builder withSubscriptions(int subscriptions) { - topicMetrics.subscriptions = subscriptions; - return this; - } - - public Builder withThroughput(MetricDecimalValue throughput) { - topicMetrics.throughput = throughput; - return this; - } - - public static Builder topicMetrics() { - return new Builder(); - } - - public TopicMetrics build() { - return topicMetrics; - } - - public Builder withVolume(long volume) { - topicMetrics.volume = volume; - return this; - } + public Builder withVolume(long volume) { + topicMetrics.volume = volume; + return this; } + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicName.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicName.java index 32c903fdb2..5e30d9d631 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicName.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicName.java @@ -1,86 +1,84 @@ package pl.allegro.tech.hermes.api; +import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Strings; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Pattern; - import java.util.Objects; -import static pl.allegro.tech.hermes.api.constraints.Names.ALLOWED_NAME_REGEX; - public class TopicName { - public static final char GROUP_SEPARATOR = '.'; + public static final char GROUP_SEPARATOR = '.'; + + @NotEmpty + @Pattern(regexp = ALLOWED_NAME_REGEX) + private final String groupName; + + @NotEmpty + @Pattern(regexp = ALLOWED_NAME_REGEX) + private final String name; - @NotEmpty - @Pattern(regexp = ALLOWED_NAME_REGEX) - private final String groupName; + @JsonCreator + public TopicName(@JsonProperty("groupName") String groupName, @JsonProperty("name") String name) { + this.groupName = groupName; + this.name = name; + } - @NotEmpty - @Pattern(regexp = ALLOWED_NAME_REGEX) - private final String name; + public static String toQualifiedName(TopicName topicName) { + return topicName != null ? topicName.qualifiedName() : null; + } - @JsonCreator - public TopicName(@JsonProperty("groupName") String groupName, @JsonProperty("name") String name) { - this.groupName = groupName; - this.name = name; + public static TopicName fromQualifiedName(String qualifiedName) { + if (Strings.isNullOrEmpty(qualifiedName)) { + return null; } - public static String toQualifiedName(TopicName topicName) { - return topicName != null ? topicName.qualifiedName() : null; + int index = qualifiedName.lastIndexOf(GROUP_SEPARATOR); + if (index == -1) { + throw new IllegalArgumentException("Invalid qualified name " + qualifiedName); } - public static TopicName fromQualifiedName(String qualifiedName) { - if (Strings.isNullOrEmpty(qualifiedName)) { - return null; - } + String groupName = qualifiedName.substring(0, index); + String topicName = qualifiedName.substring(index + 1, qualifiedName.length()); + return new TopicName(groupName, topicName); + } - int index = qualifiedName.lastIndexOf(GROUP_SEPARATOR); - if (index == -1) { - throw new IllegalArgumentException("Missing group"); - } + public String getGroupName() { + return groupName; + } - String groupName = qualifiedName.substring(0, index); - String topicName = qualifiedName.substring(index + 1, qualifiedName.length()); - return new TopicName(groupName, topicName); - } + public String getName() { + return name; + } - public String getGroupName() { - return groupName; - } + public String qualifiedName() { + return groupName + GROUP_SEPARATOR + name; + } - public String getName() { - return name; - } + @Override + public String toString() { + return qualifiedName(); + } - public String qualifiedName() { - return groupName + GROUP_SEPARATOR + name; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public String toString() { - return qualifiedName(); + if (o == null || getClass() != o.getClass()) { + return false; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + TopicName that = (TopicName) o; - TopicName that = (TopicName) o; + return Objects.equals(this.name, that.name) && Objects.equals(this.groupName, that.groupName); + } - return Objects.equals(this.name, that.name) - && Objects.equals(this.groupName, that.groupName); - } - - @Override - public int hashCode() { - return Objects.hash(name, groupName); - } + @Override + public int hashCode() { + return Objects.hash(name, groupName); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicNameWithMetrics.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicNameWithMetrics.java index 3d4d2c9368..8f36fb7149 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicNameWithMetrics.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicNameWithMetrics.java @@ -2,102 +2,100 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class TopicNameWithMetrics { - private final long published; - private final MetricDecimalValue rate; - private final MetricDecimalValue deliveryRate; - private final int subscriptions; - private final MetricDecimalValue throughput; - private final long volume; - - private final TopicName topicName; - - @JsonCreator - public TopicNameWithMetrics( - @JsonProperty("published") long published, - @JsonProperty("rate") MetricDecimalValue rate, - @JsonProperty("deliveryRate") MetricDecimalValue deliveryRate, - @JsonProperty("subscriptions") int subscriptions, - @JsonProperty("throughput") MetricDecimalValue throughput, - @JsonProperty("volume") long volume, - @JsonProperty("name") String qualifiedName - ) { - this.published = published; - this.rate = rate; - this.deliveryRate = deliveryRate; - this.subscriptions = subscriptions; - this.throughput = throughput; - this.volume = volume; - this.topicName = TopicName.fromQualifiedName(qualifiedName); - } - - public static TopicNameWithMetrics from(TopicMetrics metrics, String qualifiedName) { - return new TopicNameWithMetrics( - metrics.getPublished(), - metrics.getRate(), - metrics.getDeliveryRate(), - metrics.getSubscriptions(), - metrics.getThroughput(), - metrics.getVolume(), - qualifiedName - ); - } - - public long getPublished() { - return published; + private final long published; + private final MetricDecimalValue rate; + private final MetricDecimalValue deliveryRate; + private final int subscriptions; + private final MetricDecimalValue throughput; + private final long volume; + + private final TopicName topicName; + + @JsonCreator + public TopicNameWithMetrics( + @JsonProperty("published") long published, + @JsonProperty("rate") MetricDecimalValue rate, + @JsonProperty("deliveryRate") MetricDecimalValue deliveryRate, + @JsonProperty("subscriptions") int subscriptions, + @JsonProperty("throughput") MetricDecimalValue throughput, + @JsonProperty("volume") long volume, + @JsonProperty("name") String qualifiedName) { + this.published = published; + this.rate = rate; + this.deliveryRate = deliveryRate; + this.subscriptions = subscriptions; + this.throughput = throughput; + this.volume = volume; + this.topicName = TopicName.fromQualifiedName(qualifiedName); + } + + public static TopicNameWithMetrics from(TopicMetrics metrics, String qualifiedName) { + return new TopicNameWithMetrics( + metrics.getPublished(), + metrics.getRate(), + metrics.getDeliveryRate(), + metrics.getSubscriptions(), + metrics.getThroughput(), + metrics.getVolume(), + qualifiedName); + } + + public long getPublished() { + return published; + } + + public long getVolume() { + return volume; + } + + public MetricDecimalValue getRate() { + return rate; + } + + public MetricDecimalValue getDeliveryRate() { + return deliveryRate; + } + + public int getSubscriptions() { + return subscriptions; + } + + public MetricDecimalValue getThroughput() { + return throughput; + } + + @JsonProperty("name") + public String getName() { + return topicName.qualifiedName(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - public long getVolume() { - return volume; - } - - public MetricDecimalValue getRate() { - return rate; - } - - public MetricDecimalValue getDeliveryRate() { - return deliveryRate; + if (o == null || getClass() != o.getClass()) { + return false; } - public int getSubscriptions() { - return subscriptions; - } - - public MetricDecimalValue getThroughput() { - return throughput; - } - - @JsonProperty("name") - public String getName() { - return topicName.qualifiedName(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - TopicNameWithMetrics that = (TopicNameWithMetrics) o; - - return Objects.equals(this.published, that.published) - && Objects.equals(this.rate, that.rate) - && Objects.equals(this.deliveryRate, that.deliveryRate) - && Objects.equals(this.subscriptions, that.subscriptions) - && Objects.equals(this.throughput, that.throughput) - && Objects.equals(this.volume, that.volume) - && Objects.equals(this.topicName, that.topicName); - } - - @Override - public int hashCode() { - return Objects.hash(published, rate, deliveryRate, subscriptions, throughput, topicName, volume); - } + TopicNameWithMetrics that = (TopicNameWithMetrics) o; + + return Objects.equals(this.published, that.published) + && Objects.equals(this.rate, that.rate) + && Objects.equals(this.deliveryRate, that.deliveryRate) + && Objects.equals(this.subscriptions, that.subscriptions) + && Objects.equals(this.throughput, that.throughput) + && Objects.equals(this.volume, that.volume) + && Objects.equals(this.topicName, that.topicName); + } + + @Override + public int hashCode() { + return Objects.hash( + published, rate, deliveryRate, subscriptions, throughput, topicName, volume); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicPartition.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicPartition.java index 760a935e1b..c664068e87 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicPartition.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicPartition.java @@ -2,87 +2,88 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class TopicPartition { - private final int partition; - private final String topic; - private final long currentOffset; - private final long logEndOffset; - private final long lag; - private final String offsetMetadata; - private final ContentType contentType; + private final int partition; + private final String topic; + private final long currentOffset; + private final long logEndOffset; + private final long lag; + private final String offsetMetadata; + private final ContentType contentType; - @JsonCreator - public TopicPartition(@JsonProperty("partition") int partition, - @JsonProperty("topic") String topic, - @JsonProperty("currentOffset") long currentOffset, - @JsonProperty("logEndOffset") long logEndOffset, - @JsonProperty("offsetMetadata") String offsetMetadata, - @JsonProperty("contentType") ContentType contentType) { - this.partition = partition; - this.topic = topic; - this.currentOffset = currentOffset; - this.logEndOffset = logEndOffset; - this.offsetMetadata = offsetMetadata; - this.contentType = contentType; + @JsonCreator + public TopicPartition( + @JsonProperty("partition") int partition, + @JsonProperty("topic") String topic, + @JsonProperty("currentOffset") long currentOffset, + @JsonProperty("logEndOffset") long logEndOffset, + @JsonProperty("offsetMetadata") String offsetMetadata, + @JsonProperty("contentType") ContentType contentType) { + this.partition = partition; + this.topic = topic; + this.currentOffset = currentOffset; + this.logEndOffset = logEndOffset; + this.offsetMetadata = offsetMetadata; + this.contentType = contentType; - this.lag = calculateLag(currentOffset, logEndOffset); - } + this.lag = calculateLag(currentOffset, logEndOffset); + } - private long calculateLag(long currentOffset, long logEndOffset) { - return logEndOffset - currentOffset; - } + private long calculateLag(long currentOffset, long logEndOffset) { + return logEndOffset - currentOffset; + } - public int getPartition() { - return partition; - } + public int getPartition() { + return partition; + } - public String getTopic() { - return topic; - } + public String getTopic() { + return topic; + } - public long getCurrentOffset() { - return currentOffset; - } + public long getCurrentOffset() { + return currentOffset; + } - public String getOffsetMetadata() { - return offsetMetadata; - } + public String getOffsetMetadata() { + return offsetMetadata; + } - public long getLogEndOffset() { - return logEndOffset; - } + public long getLogEndOffset() { + return logEndOffset; + } - public long getLag() { - return lag; - } + public long getLag() { + return lag; + } - public ContentType getContentType() { - return contentType; - } + public ContentType getContentType() { + return contentType; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TopicPartition that = (TopicPartition) o; - return partition == that.partition - && currentOffset == that.currentOffset - && logEndOffset == that.logEndOffset - && contentType == that.contentType - && lag == that.lag - && Objects.equals(topic, that.topic) - && Objects.equals(offsetMetadata, that.offsetMetadata); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(partition, topic, currentOffset, logEndOffset, contentType, lag, offsetMetadata); + if (o == null || getClass() != o.getClass()) { + return false; } + TopicPartition that = (TopicPartition) o; + return partition == that.partition + && currentOffset == that.currentOffset + && logEndOffset == that.logEndOffset + && contentType == that.contentType + && lag == that.lag + && Objects.equals(topic, that.topic) + && Objects.equals(offsetMetadata, that.offsetMetadata); + } + + @Override + public int hashCode() { + return Objects.hash( + partition, topic, currentOffset, logEndOffset, contentType, lag, offsetMetadata); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicStats.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicStats.java index 9571b8a214..741c07cb69 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicStats.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicStats.java @@ -2,69 +2,73 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; public class TopicStats { - private final long topicCount; - private final long ackAllTopicCount; - private final long trackingEnabledTopicCount; - private final long avroTopicCount; + private final long topicCount; + private final long ackAllTopicCount; + private final long trackingEnabledTopicCount; + private final long avroTopicCount; - @JsonCreator - public TopicStats(@JsonProperty("topicCount") long topicCount, - @JsonProperty("ackAllTopicCount") long ackAllTopicCount, - @JsonProperty("trackingEnabledTopicCount") long trackingEnabledTopicCount, - @JsonProperty("avroTopicCount") long avroTopicCount) { - this.topicCount = topicCount; - this.ackAllTopicCount = ackAllTopicCount; - this.trackingEnabledTopicCount = trackingEnabledTopicCount; - this.avroTopicCount = avroTopicCount; - } + @JsonCreator + public TopicStats( + @JsonProperty("topicCount") long topicCount, + @JsonProperty("ackAllTopicCount") long ackAllTopicCount, + @JsonProperty("trackingEnabledTopicCount") long trackingEnabledTopicCount, + @JsonProperty("avroTopicCount") long avroTopicCount) { + this.topicCount = topicCount; + this.ackAllTopicCount = ackAllTopicCount; + this.trackingEnabledTopicCount = trackingEnabledTopicCount; + this.avroTopicCount = avroTopicCount; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - TopicStats that = (TopicStats) o; - return topicCount == that.topicCount - && ackAllTopicCount == that.ackAllTopicCount - && trackingEnabledTopicCount == that.trackingEnabledTopicCount - && avroTopicCount == that.avroTopicCount; + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(topicCount, ackAllTopicCount, trackingEnabledTopicCount, avroTopicCount); + if (o == null || getClass() != o.getClass()) { + return false; } + TopicStats that = (TopicStats) o; + return topicCount == that.topicCount + && ackAllTopicCount == that.ackAllTopicCount + && trackingEnabledTopicCount == that.trackingEnabledTopicCount + && avroTopicCount == that.avroTopicCount; + } - @Override - public String toString() { - return "TopicStats{" - + "topicCount=" + topicCount - + ", ackAllTopicCount=" + ackAllTopicCount - + ", trackingEnabledTopicCount=" + trackingEnabledTopicCount - + ", avroTopicCount=" + avroTopicCount - + '}'; - } + @Override + public int hashCode() { + return Objects.hash(topicCount, ackAllTopicCount, trackingEnabledTopicCount, avroTopicCount); + } - public long getTopicCount() { - return topicCount; - } + @Override + public String toString() { + return "TopicStats{" + + "topicCount=" + + topicCount + + ", ackAllTopicCount=" + + ackAllTopicCount + + ", trackingEnabledTopicCount=" + + trackingEnabledTopicCount + + ", avroTopicCount=" + + avroTopicCount + + '}'; + } - public long getAckAllTopicCount() { - return ackAllTopicCount; - } + public long getTopicCount() { + return topicCount; + } - public long getTrackingEnabledTopicCount() { - return trackingEnabledTopicCount; - } + public long getAckAllTopicCount() { + return ackAllTopicCount; + } - public long getAvroTopicCount() { - return avroTopicCount; - } + public long getTrackingEnabledTopicCount() { + return trackingEnabledTopicCount; + } + + public long getAvroTopicCount() { + return avroTopicCount; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicWithSchema.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicWithSchema.java index c3c1a75059..7294e4e049 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicWithSchema.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TopicWithSchema.java @@ -5,96 +5,151 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.OptBoolean; - import java.time.Instant; import java.util.Objects; import java.util.Set; public class TopicWithSchema extends Topic { - private final Topic topic; + private final Topic topic; - private String schema; + private String schema; - public TopicWithSchema(Topic topic, String schema) { - this(schema, topic.getQualifiedName(), topic.getDescription(), topic.getOwner(), topic.getRetentionTime(), - topic.isJsonToAvroDryRunEnabled(), topic.getAck(), topic.isTrackingEnabled(), topic.wasMigratedFromJsonType(), - topic.isSchemaIdAwareSerializationEnabled(), topic.getContentType(), topic.getMaxMessageSize(), - topic.getPublishingAuth(), topic.isSubscribingRestricted(), topic.getOfflineStorage(), topic.getLabels(), - topic.getCreatedAt(), topic.getModifiedAt()); - } + public TopicWithSchema(Topic topic, String schema) { + this( + schema, + topic.getQualifiedName(), + topic.getDescription(), + topic.getOwner(), + topic.getRetentionTime(), + topic.isJsonToAvroDryRunEnabled(), + topic.getAck(), + topic.isFallbackToRemoteDatacenterEnabled(), + topic.getChaos(), + topic.isTrackingEnabled(), + topic.wasMigratedFromJsonType(), + topic.isSchemaIdAwareSerializationEnabled(), + topic.getContentType(), + topic.getMaxMessageSize(), + topic.getPublishingAuth(), + topic.isSubscribingRestricted(), + topic.getOfflineStorage(), + topic.getLabels(), + topic.getCreatedAt(), + topic.getModifiedAt()); + } - @JsonCreator - public TopicWithSchema(@JsonProperty("schema") String schema, - @JsonProperty("name") String qualifiedName, - @JsonProperty("description") String description, - @JsonProperty("owner") OwnerId owner, - @JsonProperty("retentionTime") RetentionTime retentionTime, - @JsonProperty("jsonToAvroDryRun") boolean jsonToAvroDryRunEnabled, - @JsonProperty("ack") Ack ack, - @JsonProperty("trackingEnabled") boolean trackingEnabled, - @JsonProperty("migratedFromJsonType") boolean migratedFromJsonType, - @JsonProperty("schemaIdAwareSerializationEnabled") - @JacksonInject(value = Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, useInput = OptBoolean.TRUE) - Boolean schemaIdAwareSerializationEnabled, - @JsonProperty("contentType") ContentType contentType, - @JsonProperty("maxMessageSize") Integer maxMessageSize, - @JsonProperty("auth") PublishingAuth publishingAuth, - @JsonProperty("subscribingRestricted") boolean subscribingRestricted, - @JsonProperty("offlineStorage") TopicDataOfflineStorage offlineStorage, - @JsonProperty("labels") Set labels, - @JsonProperty("createdAt") Instant createdAt, - @JsonProperty("modifiedAt") Instant modifiedAt) { - super(qualifiedName, description, owner, retentionTime, jsonToAvroDryRunEnabled, ack, trackingEnabled, - migratedFromJsonType, schemaIdAwareSerializationEnabled, contentType, maxMessageSize, - publishingAuth, subscribingRestricted, offlineStorage, labels, createdAt, modifiedAt); - this.topic = convertToTopic(); - this.schema = schema; - } + @JsonCreator + public TopicWithSchema( + @JsonProperty("schema") String schema, + @JsonProperty("name") String qualifiedName, + @JsonProperty("description") String description, + @JsonProperty("owner") OwnerId owner, + @JsonProperty("retentionTime") RetentionTime retentionTime, + @JsonProperty("jsonToAvroDryRun") boolean jsonToAvroDryRunEnabled, + @JsonProperty("ack") Ack ack, + @JsonProperty("fallbackToRemoteDatacenterEnabled") + @JacksonInject( + value = DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY, + useInput = OptBoolean.TRUE) + boolean fallbackToRemoteDatacenterEnabled, + @JsonProperty("chaos") PublishingChaosPolicy chaos, + @JsonProperty("trackingEnabled") boolean trackingEnabled, + @JsonProperty("migratedFromJsonType") boolean migratedFromJsonType, + @JsonProperty("schemaIdAwareSerializationEnabled") + @JacksonInject( + value = Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, + useInput = OptBoolean.TRUE) + Boolean schemaIdAwareSerializationEnabled, + @JsonProperty("contentType") ContentType contentType, + @JsonProperty("maxMessageSize") Integer maxMessageSize, + @JsonProperty("auth") PublishingAuth publishingAuth, + @JsonProperty("subscribingRestricted") boolean subscribingRestricted, + @JsonProperty("offlineStorage") TopicDataOfflineStorage offlineStorage, + @JsonProperty("labels") Set labels, + @JsonProperty("createdAt") Instant createdAt, + @JsonProperty("modifiedAt") Instant modifiedAt) { + super( + qualifiedName, + description, + owner, + retentionTime, + jsonToAvroDryRunEnabled, + ack, + fallbackToRemoteDatacenterEnabled, + chaos, + trackingEnabled, + migratedFromJsonType, + schemaIdAwareSerializationEnabled, + contentType, + maxMessageSize, + publishingAuth, + subscribingRestricted, + offlineStorage, + labels, + createdAt, + modifiedAt); + this.topic = convertToTopic(); + this.schema = schema; + } - public static TopicWithSchema topicWithSchema(Topic topic, String schema) { - return new TopicWithSchema(topic, schema); - } + public static TopicWithSchema topicWithSchema(Topic topic, String schema) { + return new TopicWithSchema(topic, schema); + } - public static TopicWithSchema topicWithSchema(Topic topic) { - return new TopicWithSchema(topic, null); - } + public static TopicWithSchema topicWithSchema(Topic topic) { + return new TopicWithSchema(topic, null); + } - private Topic convertToTopic() { - return new Topic(this.getQualifiedName(), this.getDescription(), this.getOwner(), this.getRetentionTime(), - this.isJsonToAvroDryRunEnabled(), this.getAck(), this.isTrackingEnabled(), this.wasMigratedFromJsonType(), - this.isSchemaIdAwareSerializationEnabled(), this.getContentType(), this.getMaxMessageSize(), - this.getPublishingAuth(), this.isSubscribingRestricted(), this.getOfflineStorage(), this.getLabels(), - this.getCreatedAt(), this.getModifiedAt()); - } + private Topic convertToTopic() { + return new Topic( + this.getQualifiedName(), + this.getDescription(), + this.getOwner(), + this.getRetentionTime(), + this.isJsonToAvroDryRunEnabled(), + this.getAck(), + this.isFallbackToRemoteDatacenterEnabled(), + this.getChaos(), + this.isTrackingEnabled(), + this.wasMigratedFromJsonType(), + this.isSchemaIdAwareSerializationEnabled(), + this.getContentType(), + this.getMaxMessageSize(), + this.getPublishingAuth(), + this.isSubscribingRestricted(), + this.getOfflineStorage(), + this.getLabels(), + this.getCreatedAt(), + this.getModifiedAt()); + } - public String getSchema() { - return schema; - } + public String getSchema() { + return schema; + } - @JsonIgnore - public Topic getTopic() { - return topic; - } + @JsonIgnore + public Topic getTopic() { + return topic; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - TopicWithSchema that = (TopicWithSchema) o; - return Objects.equals(topic, that.topic) - && Objects.equals(schema, that.schema); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), topic, schema); + if (o == null || getClass() != o.getClass()) { + return false; } + if (!super.equals(o)) { + return false; + } + TopicWithSchema that = (TopicWithSchema) o; + return Objects.equals(topic, that.topic) && Objects.equals(schema, that.schema); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), topic, schema); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TrackingMode.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TrackingMode.java index d55d2041d3..650dd24540 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TrackingMode.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/TrackingMode.java @@ -3,40 +3,39 @@ import java.util.Optional; public enum TrackingMode { + TRACK_ALL("trackingAll"), + TRACK_DISCARDED_ONLY("discardedOnly"), + TRACKING_OFF("trackingOff"); - TRACK_ALL("trackingAll"), - TRACK_DISCARDED_ONLY("discardedOnly"), - TRACKING_OFF("trackingOff"); + private String value; - private String value; + TrackingMode(String s) { + this.value = s; + } - TrackingMode(String s) { - this.value = s; - } + public static Optional fromString(String trackingMode) { - public static Optional fromString(String trackingMode) { - - if (trackingMode == null) { - return Optional.empty(); - } - - switch (trackingMode) { - case "trackingAll": - return Optional.of(TRACK_ALL); - case "discardedOnly": - return Optional.of(TRACK_DISCARDED_ONLY); - case "trackingOff": - default: - return Optional.of(TRACKING_OFF); - } + if (trackingMode == null) { + return Optional.empty(); } - public String getValue() { - return value; + switch (trackingMode) { + case "trackingAll": + return Optional.of(TRACK_ALL); + case "discardedOnly": + return Optional.of(TRACK_DISCARDED_ONLY); + case "trackingOff": + default: + return Optional.of(TRACKING_OFF); } + } - @Override - public String toString() { - return this.value; - } + public String getValue() { + return value; + } + + @Override + public String toString() { + return this.value; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/UnhealthySubscription.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/UnhealthySubscription.java index ce27767c37..a265585404 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/UnhealthySubscription.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/UnhealthySubscription.java @@ -2,84 +2,90 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; import java.util.Set; public class UnhealthySubscription { - private final String name; - private final String qualifiedTopicName; - private final MonitoringDetails.Severity severity; - private final Set problems; + private final String name; + private final String qualifiedTopicName; + private final MonitoringDetails.Severity severity; + private final Set problems; - @JsonCreator - public UnhealthySubscription( - @JsonProperty("name") String name, - @JsonProperty("topicName") String qualifiedTopicName, - @JsonProperty("severity") MonitoringDetails.Severity severity, - @JsonProperty("problems") Set problems) { - this.name = name; - this.qualifiedTopicName = qualifiedTopicName; - this.severity = severity; - this.problems = problems; - } + @JsonCreator + public UnhealthySubscription( + @JsonProperty("name") String name, + @JsonProperty("topicName") String qualifiedTopicName, + @JsonProperty("severity") MonitoringDetails.Severity severity, + @JsonProperty("problems") Set problems) { + this.name = name; + this.qualifiedTopicName = qualifiedTopicName; + this.severity = severity; + this.problems = problems; + } - public static UnhealthySubscription from(Subscription subscription, SubscriptionHealth subscriptionHealth) { - return new UnhealthySubscription( - subscription.getName(), - subscription.getQualifiedTopicName(), - subscription.getMonitoringDetails().getSeverity(), - subscriptionHealth.getProblems()); - } + public static UnhealthySubscription from( + Subscription subscription, SubscriptionHealth subscriptionHealth) { + return new UnhealthySubscription( + subscription.getName(), + subscription.getQualifiedTopicName(), + subscription.getMonitoringDetails().getSeverity(), + subscriptionHealth.getProblems()); + } - @JsonProperty("name") - public String getName() { - return name; - } + @JsonProperty("name") + public String getName() { + return name; + } - @JsonProperty("topicName") - public String getQualifiedTopicName() { - return qualifiedTopicName; - } + @JsonProperty("topicName") + public String getQualifiedTopicName() { + return qualifiedTopicName; + } - @JsonProperty("severity") - public MonitoringDetails.Severity getSeverity() { - return severity; - } + @JsonProperty("severity") + public MonitoringDetails.Severity getSeverity() { + return severity; + } - @JsonProperty("problems") - public Set getProblems() { - return problems; - } + @JsonProperty("problems") + public Set getProblems() { + return problems; + } - @Override - public String toString() { - return "UnhealthySubscription{" - + "name='" + name + '\'' - + ", qualifiedTopicName='" + qualifiedTopicName + '\'' - + ", severity=" + severity - + ", problems=" + problems - + '}'; - } + @Override + public String toString() { + return "UnhealthySubscription{" + + "name='" + + name + + '\'' + + ", qualifiedTopicName='" + + qualifiedTopicName + + '\'' + + ", severity=" + + severity + + ", problems=" + + problems + + '}'; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UnhealthySubscription that = (UnhealthySubscription) o; - return Objects.equals(name, that.name) - && Objects.equals(qualifiedTopicName, that.qualifiedTopicName) - && severity == that.severity - && Objects.equals(problems, that.problems); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(name, qualifiedTopicName, severity, problems); + if (o == null || getClass() != o.getClass()) { + return false; } + UnhealthySubscription that = (UnhealthySubscription) o; + return Objects.equals(name, that.name) + && Objects.equals(qualifiedTopicName, that.qualifiedTopicName) + && severity == that.severity + && Objects.equals(problems, that.problems); + } + + @Override + public int hashCode() { + return Objects.hash(name, qualifiedTopicName, severity, problems); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/AdminPermitted.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/AdminPermitted.java index 9fe56ddffc..41e36084ee 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/AdminPermitted.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/AdminPermitted.java @@ -1,4 +1,3 @@ package pl.allegro.tech.hermes.api.constraints; -public interface AdminPermitted { -} +public interface AdminPermitted {} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ContentTypeValidator.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ContentTypeValidator.java index 1874fbff9c..3f90dfffad 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ContentTypeValidator.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ContentTypeValidator.java @@ -8,13 +8,12 @@ public class ContentTypeValidator implements ConstraintValidator { - @Override - public void initialize(ValidContentType constraintAnnotation) { + @Override + public void initialize(ValidContentType constraintAnnotation) {} - } - - @Override - public boolean isValid(Subscription subscription, ConstraintValidatorContext context) { - return !(subscription.getDeliveryType() == DeliveryType.BATCH && subscription.getContentType() == ContentType.AVRO); - } + @Override + public boolean isValid(Subscription subscription, ConstraintValidatorContext context) { + return !(subscription.getDeliveryType() == DeliveryType.BATCH + && subscription.getContentType() == ContentType.AVRO); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/Names.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/Names.java index 5214757d71..582487d271 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/Names.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/Names.java @@ -1,5 +1,5 @@ package pl.allegro.tech.hermes.api.constraints; public final class Names { - public static final String ALLOWED_NAME_REGEX = "[a-zA-Z0-9_.-]+"; + public static final String ALLOWED_NAME_REGEX = "[a-zA-Z0-9_.-]+"; } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmission.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmission.java new file mode 100644 index 0000000000..4c7fda0a5d --- /dev/null +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmission.java @@ -0,0 +1,22 @@ +package pl.allegro.tech.hermes.api.constraints; + +import static java.lang.annotation.ElementType.TYPE; + +import jakarta.validation.Constraint; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({TYPE}) +@Constraint(validatedBy = OneSourceRetransmissionValidator.class) +public @interface OneSourceRetransmission { + String message() default + "must contain one defined source of retransmission data - source topic or source view"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidator.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidator.java new file mode 100644 index 0000000000..f0a89a888c --- /dev/null +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidator.java @@ -0,0 +1,26 @@ +package pl.allegro.tech.hermes.api.constraints; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import pl.allegro.tech.hermes.api.OfflineRetransmissionRequest; + +public class OneSourceRetransmissionValidator + implements ConstraintValidator { + + public static final String EMPTY_STRING = ""; + + @Override + public boolean isValid( + OfflineRetransmissionRequest offlineRetransmissionRequest, + ConstraintValidatorContext context) { + var sourceViewPath = offlineRetransmissionRequest.getSourceViewPath(); + var sourceTopic = offlineRetransmissionRequest.getSourceTopic(); + + return (nonBlank(sourceViewPath.orElse(EMPTY_STRING)) && sourceTopic.isEmpty()) + || (nonBlank(sourceTopic.orElse(EMPTY_STRING)) && sourceViewPath.isEmpty()); + } + + private static boolean nonBlank(String value) { + return value != null && !value.isBlank(); + } +} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ValidContentType.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ValidContentType.java index 7c4785c578..30344e8903 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ValidContentType.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/constraints/ValidContentType.java @@ -1,22 +1,21 @@ package pl.allegro.tech.hermes.api.constraints; -import jakarta.validation.Constraint; +import static java.lang.annotation.ElementType.TYPE; +import jakarta.validation.Constraint; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.TYPE; - @Documented @Retention(RetentionPolicy.RUNTIME) @Target({TYPE}) @Constraint(validatedBy = ContentTypeValidator.class) public @interface ValidContentType { - String message(); + String message(); - Class[] groups() default {}; + Class[] groups() default {}; - Class[] payload() default {}; + Class[] payload() default {}; } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/AllTopicClientsEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/AllTopicClientsEndpoint.java deleted file mode 100644 index c206f43c37..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/AllTopicClientsEndpoint.java +++ /dev/null @@ -1,18 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("topics/{topic}/clients") -public interface AllTopicClientsEndpoint { - - @GET - @Produces(APPLICATION_JSON) - List getTopicClients(@PathParam("topic") String topic); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/BlacklistEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/BlacklistEndpoint.java deleted file mode 100644 index 10bd4f4315..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/BlacklistEndpoint.java +++ /dev/null @@ -1,40 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.BlacklistStatus; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("blacklist") -public interface BlacklistEndpoint { - - @POST - @Produces(APPLICATION_JSON) - @Consumes(APPLICATION_JSON) - @Path("/topics") - Response blacklistTopics(List qualifiedTopicNames); - - @DELETE - @Produces(APPLICATION_JSON) - @Path("/topics/{topicName}") - Response unblacklistTopic(@PathParam("topicName") String qualifiedTopicName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/topics/{topicName}") - BlacklistStatus isTopicBlacklisted(@PathParam("topicName") String qualifiedTopicName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/topics") - List topicsBlacklist(); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ConsistencyEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ConsistencyEndpoint.java deleted file mode 100644 index 8e1c694e3f..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ConsistencyEndpoint.java +++ /dev/null @@ -1,25 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("consistency") -public interface ConsistencyEndpoint { - - @GET - @Produces({APPLICATION_JSON}) - @Path("/inconsistencies/groups") - Response listInconsistentGroups(@QueryParam("groupNames") List groupNames); - - @GET - @Produces({APPLICATION_JSON}) - @Path("/groups") - Response listAllGroups(); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/FilterEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/FilterEndpoint.java deleted file mode 100644 index b17d3f7df0..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/FilterEndpoint.java +++ /dev/null @@ -1,23 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import pl.allegro.tech.hermes.api.MessageFiltersVerificationInput; -import pl.allegro.tech.hermes.api.MessageFiltersVerificationResult; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("filters") -public interface FilterEndpoint { - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - MessageFiltersVerificationResult verify(@PathParam("topicName") String qualifiedTopicName, - MessageFiltersVerificationInput input); - -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/GroupEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/GroupEndpoint.java deleted file mode 100644 index 3f52307b91..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/GroupEndpoint.java +++ /dev/null @@ -1,47 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.Group; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("groups") -public interface GroupEndpoint { - - @GET - @Produces(APPLICATION_JSON) - List list(); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{groupName}") - Group get(@PathParam("groupName") String groupName); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - Response create(Group group); - - @PUT - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{groupName}") - Response update(@PathParam("groupName") String groupName, Group group); - - @DELETE - @Path("/{groupName}") - Response delete(@PathParam("groupName") String groupName); - -} - - diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ModeEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ModeEndpoint.java deleted file mode 100644 index 62a125b88e..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ModeEndpoint.java +++ /dev/null @@ -1,23 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; - -@Path("mode") -public interface ModeEndpoint { - - @GET - @Produces(TEXT_PLAIN) - String getMode(); - - @POST - @Produces(APPLICATION_JSON) - Response setMode(@QueryParam("mode") String mode); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OAuthProviderEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OAuthProviderEndpoint.java deleted file mode 100644 index 0eea82013d..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OAuthProviderEndpoint.java +++ /dev/null @@ -1,46 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.OAuthProvider; -import pl.allegro.tech.hermes.api.PatchData; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("oauth/providers") -public interface OAuthProviderEndpoint { - - @GET - @Produces(APPLICATION_JSON) - List list(); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - Response create(OAuthProvider oAuthProvider); - - @PUT - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{oAuthProviderName}") - Response update(@PathParam("oAuthProviderName") String name, PatchData patch); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{oAuthProviderName}") - OAuthProvider get(@PathParam("oAuthProviderName") String oAuthProviderName); - - @DELETE - @Produces(APPLICATION_JSON) - @Path("/{oAuthProviderName}") - Response remove(@PathParam("oAuthProviderName") String oAuthProviderName); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OfflineRetransmissionEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OfflineRetransmissionEndpoint.java deleted file mode 100644 index 0f09a7f1be..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OfflineRetransmissionEndpoint.java +++ /dev/null @@ -1,31 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.OfflineRetransmissionRequest; -import pl.allegro.tech.hermes.api.OfflineRetransmissionTask; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("offline-retransmission/tasks") -public interface OfflineRetransmissionEndpoint { - @POST - @Consumes(APPLICATION_JSON) - Response createRetransmissionTask(OfflineRetransmissionRequest request); - - @GET - @Produces(APPLICATION_JSON) - List getAllTasks(); - - @DELETE - @Path("/{taskId}") - Response deleteRetransmissionTask(@PathParam("taskId") String taskId); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OwnerEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OwnerEndpoint.java deleted file mode 100644 index aa8bd545fb..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/OwnerEndpoint.java +++ /dev/null @@ -1,31 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import pl.allegro.tech.hermes.api.Owner; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("owners") -public interface OwnerEndpoint { - - @GET - @Produces(APPLICATION_JSON) - @Path("/sources/{source}") - List search(@PathParam("source") String source, @QueryParam("search") String searchString); - - @GET - @Produces(APPLICATION_JSON) - @Path("/sources/{source}/{id}") - Owner get(@PathParam("source") String source, @PathParam("id") String id); - - @GET - @Path("/sources") - @Produces(APPLICATION_JSON) - List listSources(); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/QueryEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/QueryEndpoint.java deleted file mode 100644 index 87c9b7a817..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/QueryEndpoint.java +++ /dev/null @@ -1,51 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import pl.allegro.tech.hermes.api.Group; -import pl.allegro.tech.hermes.api.Subscription; -import pl.allegro.tech.hermes.api.SubscriptionNameWithMetrics; -import pl.allegro.tech.hermes.api.Topic; -import pl.allegro.tech.hermes.api.TopicNameWithMetrics; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("query") -public interface QueryEndpoint { - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/groups") - List queryGroups(String query); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/topics") - List queryTopics(String query); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/subscriptions") - List querySubscriptions(String query); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/topics/metrics") - List queryTopicsMetrics(String query); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/subscriptions/metrics") - List querySubscriptionsMetrics(String query); -} - - diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ReadinessEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ReadinessEndpoint.java deleted file mode 100644 index b89bb7a5d1..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/ReadinessEndpoint.java +++ /dev/null @@ -1,29 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.DatacenterReadiness; -import pl.allegro.tech.hermes.api.Readiness; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("readiness/datacenters") -public interface ReadinessEndpoint { - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{datacenter}") - Response setReadiness(@PathParam("datacenter") String datacenter, Readiness readiness); - - @GET - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - List getReadiness(); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SchemaEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SchemaEndpoint.java deleted file mode 100644 index 0c4e00e80a..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SchemaEndpoint.java +++ /dev/null @@ -1,42 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("topics/{topicName}/schema") -public interface SchemaEndpoint { - - @GET - @Produces(APPLICATION_JSON) - Response get(@PathParam("topicName") String qualifiedTopicName); - - @GET - @Path("versions/{version}") - @Produces(APPLICATION_JSON) - Response getByVersion(@PathParam("topicName") String qualifiedTopicName, @PathParam("version") int version); - - @GET - @Path("ids/{id}") - @Produces(APPLICATION_JSON) - Response getById(@PathParam("topicName") String qualifiedTopicName, @PathParam("id") int id); - - @POST - @Consumes(APPLICATION_JSON) - Response save(@PathParam("topicName") String qualifiedTopicName, String schema); - - @POST - @Consumes(APPLICATION_JSON) - Response save(@PathParam("topicName") String qualifiedTopicName, @QueryParam(value = "validate") boolean validate, String schema); - - @DELETE - Response delete(@PathParam("topicName") String qualifiedTopicName); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/StatsEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/StatsEndpoint.java deleted file mode 100644 index 6d063c5ca2..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/StatsEndpoint.java +++ /dev/null @@ -1,15 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import pl.allegro.tech.hermes.api.Stats; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("stats") -public interface StatsEndpoint { - @GET - @Produces(APPLICATION_JSON) - Stats getStats(); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionEndpoint.java deleted file mode 100644 index d7806bca7e..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionEndpoint.java +++ /dev/null @@ -1,127 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.DefaultValue; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.ConsumerGroup; -import pl.allegro.tech.hermes.api.OffsetRetransmissionDate; -import pl.allegro.tech.hermes.api.PatchData; -import pl.allegro.tech.hermes.api.Subscription; -import pl.allegro.tech.hermes.api.SubscriptionHealth; -import pl.allegro.tech.hermes.api.SubscriptionMetrics; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("topics/{topicName}/subscriptions") -public interface SubscriptionEndpoint { - @GET - @Produces(APPLICATION_JSON) - List list(@PathParam("topicName") String qualifiedTopicName, - @DefaultValue("false") @QueryParam("tracked") boolean tracked); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/query") - List queryList(@PathParam("topicName") String qualifiedTopicName, - String query); - - @POST - @Consumes(APPLICATION_JSON) - Response create(@PathParam("topicName") String qualifiedTopicName, - Subscription subscription); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}") - Subscription get(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/state") - Subscription.State getState(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/undelivered/last") - Response getLatestUndeliveredMessage(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/undelivered") - Response getLatestUndeliveredMessages(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/metrics") - SubscriptionMetrics getMetrics(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/health") - SubscriptionHealth getHealth(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @PUT - @Consumes(APPLICATION_JSON) - @Path("/{subscriptionName}/state") - Response updateState(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName, - Subscription.State state); - - @DELETE - @Path("/{subscriptionName}") - Response remove(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @PUT - @Consumes(APPLICATION_JSON) - @Path("/{subscriptionName}") - Response update(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName, - PatchData patch); - - @PUT - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/retransmission") - Response retransmit(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName, - @DefaultValue("false") @QueryParam("dryRun") boolean dryRun, - OffsetRetransmissionDate offsetRetransmissionDate); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/moveOffsetsToTheEnd") - Response moveOffsetsToTheEnd(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/events/{messageId}/trace") - Response getMessageTrace(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName, - @PathParam("messageId") String messageId); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{subscriptionName}/consumer-groups") - List describeConsumerGroups(@PathParam("topicName") String qualifiedTopicName, - @PathParam("subscriptionName") String subscriptionName); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionOwnershipEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionOwnershipEndpoint.java deleted file mode 100644 index a610eac1b6..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/SubscriptionOwnershipEndpoint.java +++ /dev/null @@ -1,20 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import pl.allegro.tech.hermes.api.Subscription; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - -@Path("subscriptions/owner") -public interface SubscriptionOwnershipEndpoint { - @GET - @Produces(APPLICATION_JSON) - @Path("/{ownerSourceName}/{ownerId}") - List listForOwner(@PathParam("ownerSourceName") String ownerSourceName, - @PathParam("ownerId") String ownerId); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/TopicEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/TopicEndpoint.java deleted file mode 100644 index 23bc974c6d..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/TopicEndpoint.java +++ /dev/null @@ -1,97 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.DefaultValue; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; -import pl.allegro.tech.hermes.api.MessageTextPreview; -import pl.allegro.tech.hermes.api.PatchData; -import pl.allegro.tech.hermes.api.Topic; -import pl.allegro.tech.hermes.api.TopicMetrics; -import pl.allegro.tech.hermes.api.TopicWithSchema; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static pl.allegro.tech.hermes.api.AvroMediaType.AVRO_BINARY; - -@Path("topics") -public interface TopicEndpoint { - @GET - @Produces(APPLICATION_JSON) - List list( - @DefaultValue("") @QueryParam("groupName") String groupName, - @DefaultValue("false") @QueryParam("tracked") boolean tracked); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/query") - List queryList( - @DefaultValue("") @QueryParam("groupName") String groupName, - String query); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - Response create(TopicWithSchema topic); - - @DELETE - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - Response remove(@PathParam("topicName") String qualifiedTopicName); - - @PUT - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - Response update(@PathParam("topicName") String qualifiedTopicName, PatchData patch); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - TopicWithSchema get(@PathParam("topicName") String qualifiedTopicName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{topicName}/metrics") - TopicMetrics getMetrics(@PathParam("topicName") String qualifiedTopicName); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{topicName}/preview") - List getPreview(@PathParam("topicName") String qualifiedTopicName); - - @POST - @Consumes(APPLICATION_JSON) - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - Response publishMessage(@PathParam("topicName") String qualifiedTopicName, String message); - - @POST - @Consumes(AVRO_BINARY) - @Produces(APPLICATION_JSON) - @Path("/{topicName}") - Response publishMessage(@PathParam("topicName") String qualifiedTopicName, byte[] message); - - @GET - @Produces(APPLICATION_JSON) - @Path("/{topicName}/preview/cluster/{brokersClusterName}/partition/{partition}/offset/{offset}") - String preview(@PathParam("topicName") String qualifiedTopicName, - @PathParam("brokersClusterName") String brokersClusterName, - @PathParam("partition") Integer partition, - @PathParam("offset") Long offset); - - @GET - @Produces(APPLICATION_JSON) - @Path("/owner/{ownerSourceName}/{ownerId}") - List listForOwner(@PathParam("ownerSourceName") String ownerSourceName, @PathParam("ownerId") String ownerId); - -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/UnhealthyEndpoint.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/UnhealthyEndpoint.java deleted file mode 100644 index 6c2178870e..0000000000 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/endpoints/UnhealthyEndpoint.java +++ /dev/null @@ -1,25 +0,0 @@ -package pl.allegro.tech.hermes.api.endpoints; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Response; - -import java.util.List; - -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; - -@Path("unhealthy") -public interface UnhealthyEndpoint { - - @GET - @Produces({APPLICATION_JSON, TEXT_PLAIN}) - @Path("/") - Response listUnhealthy(@QueryParam("ownerSourceName") String ownerSourceName, - @QueryParam("ownerId") String id, - @QueryParam("respectMonitoringSeverity") boolean respectMonitoringSeverity, - @QueryParam("subscriptionNames") List subscriptionNames, - @QueryParam("qualifiedTopicNames") List qualifiedTopicNames); -} diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Patch.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Patch.java index 92b9363cf5..91c57594e8 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Patch.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Patch.java @@ -1,45 +1,44 @@ package pl.allegro.tech.hermes.api.helpers; +import static com.google.common.base.Preconditions.checkNotNull; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import pl.allegro.tech.hermes.api.PatchData; - import java.util.HashMap; import java.util.Map; - -import static com.google.common.base.Preconditions.checkNotNull; +import pl.allegro.tech.hermes.api.PatchData; public class Patch { - private static final ObjectMapper MAPPER = new ObjectMapper() - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .disable(SerializationFeature.WRITE_NULL_MAP_VALUES) - .registerModule(new JavaTimeModule()); - - @SuppressWarnings("unchecked") - public static T apply(T object, PatchData patch) { - checkNotNull(object); - checkNotNull(patch); - Map objectMap = MAPPER.convertValue(object, Map.class); - return (T) MAPPER.convertValue(merge(objectMap, patch.getPatch()), object.getClass()); + private static final ObjectMapper MAPPER = + new ObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .disable(SerializationFeature.WRITE_NULL_MAP_VALUES) + .registerModule(new JavaTimeModule()); + + @SuppressWarnings("unchecked") + public static T apply(T object, PatchData patch) { + checkNotNull(object); + checkNotNull(patch); + Map objectMap = MAPPER.convertValue(object, Map.class); + return (T) MAPPER.convertValue(merge(objectMap, patch.getPatch()), object.getClass()); + } + + @SuppressWarnings("unchecked") + private static Map merge(Map left, Map right) { + Map merged = new HashMap<>(left); + for (Map.Entry entry : right.entrySet()) { + if (entry.getValue() instanceof Map && merged.containsKey(entry.getKey())) { + Map nested = (Map) merged.get(entry.getKey()); + nested.putAll(merge(nested, (Map) entry.getValue())); + } else { + merged.put(entry.getKey(), entry.getValue()); + } } - - @SuppressWarnings("unchecked") - private static Map merge(Map left, Map right) { - Map merged = new HashMap<>(left); - for (Map.Entry entry : right.entrySet()) { - if (entry.getValue() instanceof Map && merged.containsKey(entry.getKey())) { - Map nested = (Map) merged.get(entry.getKey()); - nested.putAll(merge(nested, (Map) entry.getValue())); - } else { - merged.put(entry.getKey(), entry.getValue()); - } - } - return merged; - } - + return merged; + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Replacer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Replacer.java index 099b0e1466..48365bdb07 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Replacer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/helpers/Replacer.java @@ -6,7 +6,9 @@ public final class Replacer { - public static List replaceInAll(String regex, String replacement, String... strings) { - return Arrays.stream(strings).map(s -> s.replaceAll(regex, replacement)).collect(Collectors.toList()); - } + public static List replaceInAll(String regex, String replacement, String... strings) { + return Arrays.stream(strings) + .map(s -> s.replaceAll(regex, replacement)) + .collect(Collectors.toList()); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressDeserializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressDeserializer.java index 78e919cc2d..946c0ac6af 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressDeserializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressDeserializer.java @@ -5,15 +5,15 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; -import pl.allegro.tech.hermes.api.EndpointAddress; - import java.io.IOException; +import pl.allegro.tech.hermes.api.EndpointAddress; public class EndpointAddressDeserializer extends JsonDeserializer { - @Override - public EndpointAddress deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - JsonNode node = jp.getCodec().readTree(jp); - return new EndpointAddress(node.asText()); - } + @Override + public EndpointAddress deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + return new EndpointAddress(node.asText()); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressSerializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressSerializer.java index dda407497e..ce8bf658cb 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressSerializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/EndpointAddressSerializer.java @@ -3,13 +3,13 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import pl.allegro.tech.hermes.api.EndpointAddress; - import java.io.IOException; +import pl.allegro.tech.hermes.api.EndpointAddress; public class EndpointAddressSerializer extends JsonSerializer { - @Override - public void serialize(EndpointAddress value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(value.getRawEndpoint()); - } + @Override + public void serialize(EndpointAddress value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(value.getRawEndpoint()); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/InstantIsoSerializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/InstantIsoSerializer.java index 3b8cd3f2c1..20bf9bcdce 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/InstantIsoSerializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/InstantIsoSerializer.java @@ -1,18 +1,18 @@ package pl.allegro.tech.hermes.api.jackson; +import static java.time.format.DateTimeFormatter.ISO_INSTANT; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; - import java.io.IOException; import java.time.Instant; -import static java.time.format.DateTimeFormatter.ISO_INSTANT; - public class InstantIsoSerializer extends JsonSerializer { - @Override - public void serialize(Instant value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(ISO_INSTANT.format(value)); - } + @Override + public void serialize(Instant value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(ISO_INSTANT.format(value)); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/OffsetDateTimeSerializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/OffsetDateTimeSerializer.java index dcabc9b10b..d82a36e324 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/OffsetDateTimeSerializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/OffsetDateTimeSerializer.java @@ -1,18 +1,18 @@ package pl.allegro.tech.hermes.api.jackson; +import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; + import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; - import java.io.IOException; import java.time.OffsetDateTime; -import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; - public class OffsetDateTimeSerializer extends JsonSerializer { - @Override - public void serialize(OffsetDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeString(ISO_OFFSET_DATE_TIME.format(value)); - } + @Override + public void serialize(OffsetDateTime value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeString(ISO_OFFSET_DATE_TIME.format(value)); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataDeserializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataDeserializer.java index e1775303f9..305b3cd76e 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataDeserializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataDeserializer.java @@ -4,16 +4,16 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; -import pl.allegro.tech.hermes.api.PatchData; - import java.io.IOException; import java.util.Map; +import pl.allegro.tech.hermes.api.PatchData; public class PatchDataDeserializer extends JsonDeserializer { - @Override - public PatchData deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - Map patch = p.getCodec().readValue(p, new TypeReference>() {}); - return new PatchData(patch); - } + @Override + public PatchData deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Map patch = + p.getCodec().readValue(p, new TypeReference>() {}); + return new PatchData(patch); + } } diff --git a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataSerializer.java b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataSerializer.java index 38ca0100f3..3580f35d55 100644 --- a/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataSerializer.java +++ b/hermes-api/src/main/java/pl/allegro/tech/hermes/api/jackson/PatchDataSerializer.java @@ -4,20 +4,20 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; -import pl.allegro.tech.hermes.api.PatchData; - import java.io.IOException; import java.util.Map; +import pl.allegro.tech.hermes.api.PatchData; public class PatchDataSerializer extends JsonSerializer { - @Override - public void serialize(PatchData value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { - gen.writeStartObject(); + @Override + public void serialize(PatchData value, JsonGenerator gen, SerializerProvider serializers) + throws IOException, JsonProcessingException { + gen.writeStartObject(); - for (Map.Entry entry : value.getPatch().entrySet()) { - gen.writeObjectField(entry.getKey(), entry.getValue()); - } - - gen.writeEndObject(); + for (Map.Entry entry : value.getPatch().entrySet()) { + gen.writeObjectField(entry.getKey(), entry.getValue()); } + + gen.writeEndObject(); + } } diff --git a/hermes-api/src/test/groovy/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidatorTest.groovy b/hermes-api/src/test/groovy/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidatorTest.groovy new file mode 100644 index 0000000000..e7aa7a42c3 --- /dev/null +++ b/hermes-api/src/test/groovy/pl/allegro/tech/hermes/api/constraints/OneSourceRetransmissionValidatorTest.groovy @@ -0,0 +1,37 @@ +package pl.allegro.tech.hermes.api.constraints + + +import jakarta.validation.ConstraintValidatorContext +import pl.allegro.tech.hermes.api.OfflineRetransmissionRequest +import spock.lang.Specification + +class OneSourceRetransmissionValidatorTest extends Specification { + + OneSourceRetransmissionValidator validator = new OneSourceRetransmissionValidator() + ConstraintValidatorContext mockContext = Mock() + + def "Validator should validate retransmission request when sourceViewPath is '#sourceViewPath' and sourceTopic is '#sourceTopic'"() { + given: + def request = new OfflineRetransmissionRequest( + sourceViewPath, + sourceTopic, + "someTargetTopic", + "2024-07-08T12:00:00", + "2024-07-08T13:00:00" + ) + expect: + validator.isValid(request, mockContext) == isValid + + where: + sourceViewPath | sourceTopic | isValid + null | "testTopic" | true + "testView" | null | true + null | null | false + "testView" | "testTopic" | false + "" | "" | false + " " | " " | false + "" | "testTopic" | false + "testView" | " " | false + } + +} diff --git a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/SubscriptionTest.java b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/SubscriptionTest.java index fc5030e018..a3392ace04 100644 --- a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/SubscriptionTest.java +++ b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/SubscriptionTest.java @@ -1,9 +1,5 @@ package pl.allegro.tech.hermes.api; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; -import pl.allegro.tech.hermes.api.helpers.Patch; - import static org.assertj.core.api.Assertions.assertThat; import static pl.allegro.tech.hermes.api.PatchData.patchData; import static pl.allegro.tech.hermes.api.SubscriptionOAuthPolicy.GrantType.CLIENT_CREDENTIALS; @@ -11,109 +7,119 @@ import static pl.allegro.tech.hermes.api.SubscriptionPolicy.Builder.subscriptionPolicy; import static pl.allegro.tech.hermes.test.helper.builder.SubscriptionBuilder.subscription; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import pl.allegro.tech.hermes.api.helpers.Patch; + public class SubscriptionTest { - private final ObjectMapper mapper = new ObjectMapper(); + private final ObjectMapper mapper = new ObjectMapper(); - @Test - public void shouldDeserializeSubscription() throws Exception { - // given - String json = "{" + @Test + public void shouldDeserializeSubscription() throws Exception { + // given + String json = + "{" + "\"name\": \"test\", " + "\"topicName\": \"g1.t1\", " + "\"endpoint\": \"http://localhost:8888\"" + "}"; - // when - Subscription subscription = mapper.readValue(json, Subscription.class); - - // then - assertThat(subscription.getName()).isEqualTo("test"); - assertThat(subscription.getEndpoint().getEndpoint()).isEqualTo("http://localhost:8888"); - } - - @Test - public void shouldDeserializeSubscriptionWithoutTopicName() throws Exception { - // given - String json = "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\"}"; - - // when - Subscription subscription = mapper.readValue(json, Subscription.class); - - // then - assertThat(subscription.getName()).isEqualTo("test"); - assertThat(subscription.getEndpoint().getEndpoint()).isEqualTo("http://localhost:8888"); - } - - @Test - public void shouldDeserializeSubscriptionWithoutBackoff() throws Exception { - // given - String json = "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\", \"subscriptionPolicy\": {\"messageTtl\": 100}}"; - - // when - Subscription subscription = mapper.readValue(json, Subscription.class); - - // then - assertThat(subscription.getSerialSubscriptionPolicy().getMessageBackoff()).isEqualTo(100); - } - - @Test - public void shouldDeserializeSubscriptionWithDefaultTracking() throws Exception { - // given - String json = "{" + // when + Subscription subscription = mapper.readValue(json, Subscription.class); + + // then + assertThat(subscription.getName()).isEqualTo("test"); + assertThat(subscription.getEndpoint().getEndpoint()).isEqualTo("http://localhost:8888"); + } + + @Test + public void shouldDeserializeSubscriptionWithoutTopicName() throws Exception { + // given + String json = "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\"}"; + + // when + Subscription subscription = mapper.readValue(json, Subscription.class); + + // then + assertThat(subscription.getName()).isEqualTo("test"); + assertThat(subscription.getEndpoint().getEndpoint()).isEqualTo("http://localhost:8888"); + } + + @Test + public void shouldDeserializeSubscriptionWithoutBackoff() throws Exception { + // given + String json = + "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\", \"subscriptionPolicy\": {\"messageTtl\": 100}}"; + + // when + Subscription subscription = mapper.readValue(json, Subscription.class); + + // then + assertThat(subscription.getSerialSubscriptionPolicy().getMessageBackoff()).isEqualTo(100); + } + + @Test + public void shouldDeserializeSubscriptionWithDefaultTracking() throws Exception { + // given + String json = + "{" + "\"name\": \"test\", " + "\"topicName\": \"g1.t1\", " + "\"endpoint\": \"http://localhost:8888\"" + "}"; - // when - Subscription subscription = mapper.readValue(json, Subscription.class); + // when + Subscription subscription = mapper.readValue(json, Subscription.class); - // then - assertThat(subscription.isTrackingEnabled()).isFalse(); - assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACKING_OFF); - } + // then + assertThat(subscription.isTrackingEnabled()).isFalse(); + assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACKING_OFF); + } - @Test - public void shouldDeserializeSubscriptionWithTrackAllMode() throws Exception { - // given - String json = "{" + @Test + public void shouldDeserializeSubscriptionWithTrackAllMode() throws Exception { + // given + String json = + "{" + "\"name\": \"test\", " + "\"topicName\": \"g1.t1\", " + "\"endpoint\": \"http://localhost:8888\", " + "\"trackingMode\": \"trackingAll\"" + "}"; - // when - Subscription subscription = mapper.readValue(json, Subscription.class); + // when + Subscription subscription = mapper.readValue(json, Subscription.class); - // then - assertThat(subscription.isTrackingEnabled()).isTrue(); - assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_ALL); - } + // then + assertThat(subscription.isTrackingEnabled()).isTrue(); + assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_ALL); + } - @Test - public void shouldDeserializeSubscriptionWithTrackEnabled() throws Exception { - // given - String json = "{" + @Test + public void shouldDeserializeSubscriptionWithTrackEnabled() throws Exception { + // given + String json = + "{" + "\"name\": \"test\", " + "\"topicName\": \"g1.t1\", " + "\"endpoint\": \"http://localhost:8888\", " + "\"trackingEnabled\": \"true\"" + "}"; - // when - Subscription subscription = mapper.readValue(json, Subscription.class); + // when + Subscription subscription = mapper.readValue(json, Subscription.class); - // then - assertThat(subscription.isTrackingEnabled()).isTrue(); - assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_ALL); - } + // then + assertThat(subscription.isTrackingEnabled()).isTrue(); + assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_ALL); + } - @Test - public void shouldDeserializeSubscriptionWithTrackEnabledAndTrackMode() throws Exception { - // given - String json = "{" + @Test + public void shouldDeserializeSubscriptionWithTrackEnabledAndTrackMode() throws Exception { + // given + String json = + "{" + "\"name\": \"test\", " + "\"topicName\": \"g1.t1\", " + "\"endpoint\": \"http://localhost:8888\", " @@ -121,70 +127,74 @@ public void shouldDeserializeSubscriptionWithTrackEnabledAndTrackMode() throws E + "\"trackingMode\": \"discardedOnly\"" + "}"; - // when - Subscription subscription = mapper.readValue(json, Subscription.class); - - // then - assertThat(subscription.isTrackingEnabled()).isTrue(); - assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_DISCARDED_ONLY); - } - - @Test - public void shouldApplyPatchToSubscriptionPolicy() { - //given - PatchData patch = patchData().set("rate", 8).build(); - - //when - SubscriptionPolicy subscription = subscriptionPolicy() - .withRate(1) - .applyPatch(patch).build(); - - //then - assertThat(subscription.getRate()).isEqualTo(8); - } - - @Test - public void shouldAnonymizePassword() { - // given - Subscription subscription = subscription("group.topic", "subscription").withEndpoint("http://user:password@service/path").build(); - - // when & then - assertThat(subscription.anonymize().getEndpoint()).isEqualTo(new EndpointAddress("http://user:*****@service/path")); - } - - @Test - public void shouldApplyPatchChangingSubscriptionOAuthPolicyGrantType() { - // given - Subscription subscription = subscription("group.topic", "subscription") - .withOAuthPolicy(new SubscriptionOAuthPolicy(CLIENT_CREDENTIALS, "myProvider", "repo", null, null)) - .build(); - PatchData oAuthPolicyPatchData = patchData() - .set("grantType", SubscriptionOAuthPolicy.GrantType.USERNAME_PASSWORD.getName()) - .set("username", "user1") - .set("password", "abc123") - .build(); - PatchData patch = patchData() - .set("oAuthPolicy", oAuthPolicyPatchData) - .build(); - - // when - Subscription updated = Patch.apply(subscription, patch); - - // then - SubscriptionOAuthPolicy updatedPolicy = updated.getOAuthPolicy(); - assertThat(updatedPolicy.getGrantType()).isEqualTo(USERNAME_PASSWORD); - assertThat(updatedPolicy.getUsername()).isEqualTo("user1"); - } - - @Test - public void shouldReadIntBackoffMultiplier() throws Exception { - // given - String json = "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\", \"subscriptionPolicy\": {\"messageBackoff\": 1000, \"backoffMultiplier\": 3}}"; - - // when - Subscription subscription = mapper.readValue(json, Subscription.class); - - // then - assertThat(subscription.getSerialSubscriptionPolicy().getBackoffMultiplier()).isEqualTo(3); - } + // when + Subscription subscription = mapper.readValue(json, Subscription.class); + + // then + assertThat(subscription.isTrackingEnabled()).isTrue(); + assertThat(subscription.getTrackingMode()).isEqualTo(TrackingMode.TRACK_DISCARDED_ONLY); + } + + @Test + public void shouldApplyPatchToSubscriptionPolicy() { + // given + PatchData patch = patchData().set("rate", 8).build(); + + // when + SubscriptionPolicy subscription = subscriptionPolicy().withRate(1).applyPatch(patch).build(); + + // then + assertThat(subscription.getRate()).isEqualTo(8); + } + + @Test + public void shouldAnonymizePassword() { + // given + Subscription subscription = + subscription("group.topic", "subscription") + .withEndpoint("http://user:password@service/path") + .build(); + + // when & then + assertThat(subscription.anonymize().getEndpoint()) + .isEqualTo(new EndpointAddress("http://user:*****@service/path")); + } + + @Test + public void shouldApplyPatchChangingSubscriptionOAuthPolicyGrantType() { + // given + Subscription subscription = + subscription("group.topic", "subscription") + .withOAuthPolicy( + new SubscriptionOAuthPolicy(CLIENT_CREDENTIALS, "myProvider", "repo", null, null)) + .build(); + PatchData oAuthPolicyPatchData = + patchData() + .set("grantType", SubscriptionOAuthPolicy.GrantType.USERNAME_PASSWORD.getName()) + .set("username", "user1") + .set("password", "abc123") + .build(); + PatchData patch = patchData().set("oAuthPolicy", oAuthPolicyPatchData).build(); + + // when + Subscription updated = Patch.apply(subscription, patch); + + // then + SubscriptionOAuthPolicy updatedPolicy = updated.getOAuthPolicy(); + assertThat(updatedPolicy.getGrantType()).isEqualTo(USERNAME_PASSWORD); + assertThat(updatedPolicy.getUsername()).isEqualTo("user1"); + } + + @Test + public void shouldReadIntBackoffMultiplier() throws Exception { + // given + String json = + "{\"name\": \"test\", \"endpoint\": \"http://localhost:8888\", \"subscriptionPolicy\": {\"messageBackoff\": 1000, \"backoffMultiplier\": 3}}"; + + // when + Subscription subscription = mapper.readValue(json, Subscription.class); + + // then + assertThat(subscription.getSerialSubscriptionPolicy().getBackoffMultiplier()).isEqualTo(3); + } } diff --git a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicNameTest.java b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicNameTest.java index fcaaddb968..ddf6ddb833 100644 --- a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicNameTest.java +++ b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicNameTest.java @@ -1,40 +1,39 @@ package pl.allegro.tech.hermes.api; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; -public class TopicNameTest { +import org.junit.Test; - @Test - public void shouldConvertQualifiedName() { - // given - TopicName topicName = new TopicName("group1", "topic1"); - - // when & then - assertThat(TopicName.fromQualifiedName(topicName.qualifiedName())).isEqualTo(topicName); - } - - @Test - public void shouldHandleDottedGroupName() { - // given - TopicName topicName = TopicName.fromQualifiedName("group.topic"); - - // when & then - assertThat(topicName.getGroupName()).isEqualTo("group"); - assertThat(topicName.getName()).isEqualTo("topic"); - } - - @Test(expected = IllegalArgumentException.class) - public void shouldThrowExceptionWhenInvalidQualifiedNameProvided() { - // when & then - TopicName.fromQualifiedName("invalidQualifiedName"); - } - - @Test - public void shouldBeNullSafe() { - // when & then - assertThat(TopicName.fromQualifiedName(null)).isNull(); - } +public class TopicNameTest { + @Test + public void shouldConvertQualifiedName() { + // given + TopicName topicName = new TopicName("group1", "topic1"); + + // when & then + assertThat(TopicName.fromQualifiedName(topicName.qualifiedName())).isEqualTo(topicName); + } + + @Test + public void shouldHandleDottedGroupName() { + // given + TopicName topicName = TopicName.fromQualifiedName("group.topic"); + + // when & then + assertThat(topicName.getGroupName()).isEqualTo("group"); + assertThat(topicName.getName()).isEqualTo("topic"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenInvalidQualifiedNameProvided() { + // when & then + TopicName.fromQualifiedName("invalidQualifiedName"); + } + + @Test + public void shouldBeNullSafe() { + // when & then + assertThat(TopicName.fromQualifiedName(null)).isNull(); + } } diff --git a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicTest.java b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicTest.java index 2c74da0365..b983210192 100644 --- a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicTest.java +++ b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/TopicTest.java @@ -1,78 +1,114 @@ package pl.allegro.tech.hermes.api; +import static org.assertj.core.api.Assertions.assertThat; + import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; -import static org.assertj.core.api.Assertions.assertThat; - public class TopicTest { - private final ObjectMapper objectMapper = createObjectMapper(); - - @Test - public void shouldDeserializeTopicWithDefaults() throws Exception { - // given - String json = "{\"name\":\"foo.bar\", \"description\": \"description\"}"; - - // when - Topic topic = objectMapper.readValue(json, Topic.class); - - // then - assertThat(topic.getName().getName()).isEqualTo("bar"); - assertThat(topic.getName().getGroupName()).isEqualTo("foo"); - assertThat(topic.getDescription()).isEqualTo("description"); - assertThat(topic.isSchemaIdAwareSerializationEnabled()).isEqualTo(true); - } - - @Test - public void shouldDeserializeTopic() throws Exception { - // given - String json = "{\"name\":\"foo.bar\", \"description\": \"description\", \"schemaIdAwareSerializationEnabled\": \"false\"}"; - - // when - Topic topic = objectMapper.readValue(json, Topic.class); - - // then - assertThat(topic.getName().getName()).isEqualTo("bar"); - assertThat(topic.getName().getGroupName()).isEqualTo("foo"); - assertThat(topic.getDescription()).isEqualTo("description"); - assertThat(topic.isSchemaIdAwareSerializationEnabled()).isEqualTo(false); - } - - @Test - public void shouldSetDefaultAckIfNotPresentInJson() throws Exception { - // given - String json = "{\"name\":\"foo.bar\", \"description\": \"description\"}"; - - // when - Topic topic = objectMapper.readValue(json, Topic.class); - - // then - assertThat(topic.isReplicationConfirmRequired()).isEqualTo(false); - } - - @Test - public void shouldSkippedDeserializedOldSchemaVersionId() throws Exception { - // given - String json = "{\"name\":\"foo.bar\", \"description\": \"description\", \"schemaVersionAwareSerializationEnabled\": false}"; - - // when - Topic topic = objectMapper.readValue(json, Topic.class); - - // then - assertThat(topic.getName().getName()).isEqualTo("bar"); - } - - private ObjectMapper createObjectMapper() { - ObjectMapper mapper = new ObjectMapper(); - - final InjectableValues defaultSchemaIdAwareSerializationEnabled = new InjectableValues - .Std().addValue(Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, true); - - mapper.setInjectableValues(defaultSchemaIdAwareSerializationEnabled); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - return mapper; - } + private final ObjectMapper objectMapper = createObjectMapper(false); + + @Test + public void shouldDeserializeTopicWithDefaults() throws Exception { + // given + String json = "{\"name\":\"foo.bar\", \"description\": \"description\"}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.getName().getName()).isEqualTo("bar"); + assertThat(topic.getName().getGroupName()).isEqualTo("foo"); + assertThat(topic.getDescription()).isEqualTo("description"); + assertThat(topic.isSchemaIdAwareSerializationEnabled()).isEqualTo(true); + } + + @Test + public void shouldDeserializeTopic() throws Exception { + // given + String json = + "{\"name\":\"foo.bar\", \"description\": \"description\", \"schemaIdAwareSerializationEnabled\": \"false\"}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.getName().getName()).isEqualTo("bar"); + assertThat(topic.getName().getGroupName()).isEqualTo("foo"); + assertThat(topic.getDescription()).isEqualTo("description"); + assertThat(topic.isSchemaIdAwareSerializationEnabled()).isEqualTo(false); + } + + @Test + public void shouldSetDefaultAckIfNotPresentInJson() throws Exception { + // given + String json = "{\"name\":\"foo.bar\", \"description\": \"description\"}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.isReplicationConfirmRequired()).isEqualTo(false); + } + + @Test + public void shouldSkippedDeserializedOldSchemaVersionId() throws Exception { + // given + String json = + "{\"name\":\"foo.bar\", \"description\": \"description\", \"schemaVersionAwareSerializationEnabled\": false}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.getName().getName()).isEqualTo("bar"); + } + + @Test + public void shouldDeserializeFallbackToRemoteDatacenterWithDefaults() throws Exception { + // given + String json = "{\"name\":\"foo.bar\", \"description\": \"description\"}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.isFallbackToRemoteDatacenterEnabled()).isEqualTo(false); + + // and when + Topic topic2 = createObjectMapper(true).readValue(json, Topic.class); + + // then + assertThat(topic2.isFallbackToRemoteDatacenterEnabled()).isEqualTo(true); + } + + @Test + public void shouldDeserializeFallbackToRemoteDatacenter() throws Exception { + // given + String json = + "{\"name\":\"foo.bar\", \"description\": \"description\", \"fallbackToRemoteDatacenterEnabled\": true}"; + + // when + Topic topic = objectMapper.readValue(json, Topic.class); + + // then + assertThat(topic.isFallbackToRemoteDatacenterEnabled()).isEqualTo(true); + } + + private ObjectMapper createObjectMapper(boolean fallbackToRemoteDatacenterEnabled) { + ObjectMapper mapper = new ObjectMapper(); + + final InjectableValues defaultSchemaIdAwareSerializationEnabled = + new InjectableValues.Std() + .addValue(Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, true) + .addValue( + Topic.DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY, fallbackToRemoteDatacenterEnabled); + + mapper.setInjectableValues(defaultSchemaIdAwareSerializationEnabled); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + return mapper; + } } diff --git a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/helper/PatchTest.java b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/helper/PatchTest.java index 2e5315ba50..afb0ae8a8b 100644 --- a/hermes-api/src/test/java/pl/allegro/tech/hermes/api/helper/PatchTest.java +++ b/hermes-api/src/test/java/pl/allegro/tech/hermes/api/helper/PatchTest.java @@ -1,5 +1,12 @@ package pl.allegro.tech.hermes.api.helper; +import static org.assertj.core.api.Assertions.assertThat; +import static pl.allegro.tech.hermes.api.PatchData.patchData; +import static pl.allegro.tech.hermes.api.SubscriptionPolicy.Builder.subscriptionPolicy; +import static pl.allegro.tech.hermes.test.helper.builder.SubscriptionBuilder.subscription; +import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; + +import java.util.HashMap; import org.junit.Test; import pl.allegro.tech.hermes.api.PatchData; import pl.allegro.tech.hermes.api.Subscription; @@ -7,79 +14,74 @@ import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.api.helpers.Patch; -import java.util.HashMap; - -import static org.assertj.core.api.Assertions.assertThat; -import static pl.allegro.tech.hermes.api.PatchData.patchData; -import static pl.allegro.tech.hermes.api.SubscriptionPolicy.Builder.subscriptionPolicy; -import static pl.allegro.tech.hermes.test.helper.builder.SubscriptionBuilder.subscription; -import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; - public class PatchTest { - @Test - public void shouldApplyPatch() { - // given - SubscriptionPolicy policy = SubscriptionPolicy.create(new HashMap<>()); - PatchData patch = patchData().set("rate", 10).set("messageTtl", 30).build(); - - // when - SubscriptionPolicy patched = Patch.apply(policy, patch); - - // when & then - assertThat(patched.getRate()).isEqualTo(10); - assertThat(patched.getMessageTtl()).isEqualTo(30); - } - - @Test(expected = NullPointerException.class) - public void shouldThrowExceptionForNullValues() { - // when - Patch.apply(null, null); - - // then - // exception is thrown - } - - @Test - public void shouldIgnoreUnknownFields() { - //given - SubscriptionPolicy policy = subscriptionPolicy().withRate(10).build(); - PatchData patch = patchData().set("unknown", 10).set("messageTtl", 30).build(); - - // when - SubscriptionPolicy patched = Patch.apply(policy, patch); - - // then - assertThat(patched.getMessageTtl()).isEqualTo(30); - } - - @Test - public void shouldPatchNestedObjects() { - // given - Subscription subscription = subscription("group.topic", "sub").build(); - PatchData patch = patchData().set( - "subscriptionPolicy", patchData().set("rate", 200).set("messageTtl", 8).build().getPatch() - ).build(); - - // when - SubscriptionPolicy result = Patch.apply(subscription, patch).getSerialSubscriptionPolicy(); - - // then - assertThat(result.getMessageTtl()).isEqualTo(8); - assertThat(result.getRate()).isEqualTo(200); - } - - @Test - public void shouldNotResetPrimitiveFields() { - // given - Topic topic = topic("group.topic").withTrackingEnabled(true).build(); - PatchData patch = patchData().set("schemaIdAwareSerializationEnabled", true).build(); - - // when - Topic patched = Patch.apply(topic, patch); - - // then - assertThat(patched.isTrackingEnabled()).isTrue(); - assertThat(patched.isSchemaIdAwareSerializationEnabled()).isTrue(); - } + @Test + public void shouldApplyPatch() { + // given + SubscriptionPolicy policy = SubscriptionPolicy.create(new HashMap<>()); + PatchData patch = patchData().set("rate", 10).set("messageTtl", 30).build(); + + // when + SubscriptionPolicy patched = Patch.apply(policy, patch); + + // when & then + assertThat(patched.getRate()).isEqualTo(10); + assertThat(patched.getMessageTtl()).isEqualTo(30); + } + + @Test(expected = NullPointerException.class) + public void shouldThrowExceptionForNullValues() { + // when + Patch.apply(null, null); + + // then + // exception is thrown + } + + @Test + public void shouldIgnoreUnknownFields() { + // given + SubscriptionPolicy policy = subscriptionPolicy().withRate(10).build(); + PatchData patch = patchData().set("unknown", 10).set("messageTtl", 30).build(); + + // when + SubscriptionPolicy patched = Patch.apply(policy, patch); + + // then + assertThat(patched.getMessageTtl()).isEqualTo(30); + } + + @Test + public void shouldPatchNestedObjects() { + // given + Subscription subscription = subscription("group.topic", "sub").build(); + PatchData patch = + patchData() + .set( + "subscriptionPolicy", + patchData().set("rate", 200).set("messageTtl", 8).build().getPatch()) + .build(); + + // when + SubscriptionPolicy result = Patch.apply(subscription, patch).getSerialSubscriptionPolicy(); + + // then + assertThat(result.getMessageTtl()).isEqualTo(8); + assertThat(result.getRate()).isEqualTo(200); + } + + @Test + public void shouldNotResetPrimitiveFields() { + // given + Topic topic = topic("group.topic").withTrackingEnabled(true).build(); + PatchData patch = patchData().set("schemaIdAwareSerializationEnabled", true).build(); + + // when + Topic patched = Patch.apply(topic, patch); + + // then + assertThat(patched.isTrackingEnabled()).isTrue(); + assertThat(patched.isSchemaIdAwareSerializationEnabled()).isTrue(); + } } diff --git a/hermes-api/src/test/resources/allure.properties b/hermes-api/src/test/resources/allure.properties new file mode 100644 index 0000000000..8c70baec64 --- /dev/null +++ b/hermes-api/src/test/resources/allure.properties @@ -0,0 +1 @@ +allure.results.directory=../build/allure-results diff --git a/hermes-benchmark/build.gradle b/hermes-benchmark/build.gradle index abd76e7967..6262b38a8a 100644 --- a/hermes-benchmark/build.gradle +++ b/hermes-benchmark/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'me.champeau.jmh' version '0.6.8' + id 'me.champeau.jmh' version '0.7.2' } configurations { @@ -26,10 +26,9 @@ jmh { } dependencies { - jmh group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.12' - jmh group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.12' - jmh group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.1' - jmh group: 'org.spf4j', name: 'spf4j-jmh', version: '8.0.3' + jmh group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.37' + jmh group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.37' + jmh group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.5' jmh project(':hermes-frontend') jmh project(':hermes-test-helper') jmh project(':hermes-common') diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/HermesServerBenchmark.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/HermesServerBenchmark.java index 90bf020797..562e435ec3 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/HermesServerBenchmark.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/HermesServerBenchmark.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.benchmark; +import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Mode; @@ -13,40 +14,38 @@ import org.openjdk.jmh.runner.options.TimeValue; import pl.allegro.tech.hermes.benchmark.environment.HermesServerEnvironment; -import java.util.concurrent.TimeUnit; - @State(Scope.Benchmark) public class HermesServerBenchmark { - @Benchmark - @BenchmarkMode(Mode.Throughput) - @OutputTimeUnit(TimeUnit.SECONDS) - public int benchmarkPublishingThroughput(HermesServerEnvironment hermesServerEnvironment) { - return hermesServerEnvironment.publisher().publish(); - } - - @Benchmark - @BenchmarkMode(Mode.SampleTime) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - public int benchmarkPublishingLatency(HermesServerEnvironment hermesServerEnvironment) { - return hermesServerEnvironment.publisher().publish(); - } - - public static void main(String[] args) throws RunnerException { - Options opt = new OptionsBuilder() - .include(".*" + HermesServerBenchmark.class.getSimpleName() + ".*") - .warmupIterations(4) - .measurementIterations(4) - //.addProfiler(JmhFlightRecorderProfiler.class) - //.jvmArgs("-XX:+UnlockCommercialFeatures") - .measurementTime(TimeValue.seconds(60)) - .warmupTime(TimeValue.seconds(40)) - .forks(1) - .threads(2) - .syncIterations(false) - .build(); - - new Runner(opt).run(); - } - + @Benchmark + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + public int benchmarkPublishingThroughput(HermesServerEnvironment hermesServerEnvironment) { + return hermesServerEnvironment.publisher().publish(); + } + + @Benchmark + @BenchmarkMode(Mode.SampleTime) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public int benchmarkPublishingLatency(HermesServerEnvironment hermesServerEnvironment) { + return hermesServerEnvironment.publisher().publish(); + } + + public static void main(String[] args) throws RunnerException { + Options opt = + new OptionsBuilder() + .include(".*" + HermesServerBenchmark.class.getSimpleName() + ".*") + .warmupIterations(4) + .measurementIterations(4) + // .addProfiler(JmhFlightRecorderProfiler.class) + // .jvmArgs("-XX:+UnlockCommercialFeatures") + .measurementTime(TimeValue.seconds(60)) + .warmupTime(TimeValue.seconds(40)) + .forks(1) + .threads(2) + .syncIterations(false) + .build(); + + new Runner(opt).run(); + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/MessageRepositoryBenchmark.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/MessageRepositoryBenchmark.java index eec1a2b04b..6c1e0900d0 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/MessageRepositoryBenchmark.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/MessageRepositoryBenchmark.java @@ -1,5 +1,14 @@ package pl.allegro.tech.hermes.benchmark; +import static java.util.Collections.emptyMap; +import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; import net.openhft.chronicle.map.ChronicleMap; import net.openhft.chronicle.map.ChronicleMapBuilder; import org.openjdk.jmh.annotations.Benchmark; @@ -23,15 +32,6 @@ import pl.allegro.tech.hermes.frontend.publishing.message.Message; import pl.allegro.tech.hermes.frontend.publishing.message.MessageIdGenerator; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; - @Fork(1) @Warmup(iterations = 5) @Measurement(iterations = 5) @@ -40,92 +40,98 @@ @OutputTimeUnit(TimeUnit.NANOSECONDS) public class MessageRepositoryBenchmark { - @State(Scope.Benchmark) - public static class Repositories { - private static final int ENTRIES = 100; - private static final int AVERAGE_MESSAGE_SIZE = 600; + @State(Scope.Benchmark) + public static class Repositories { + private static final int ENTRIES = 100; + private static final int AVERAGE_MESSAGE_SIZE = 600; - MessageRepository hermesImplMessageRepository; - MessageRepository baselineMessageRepository; + MessageRepository hermesImplMessageRepository; + MessageRepository baselineMessageRepository; - Message message; - Topic topic; + Message message; + Topic topic; - @Setup - public void setup() throws IOException { - message = generateMessage(); - topic = topic("groupName.topic").build(); + @Setup + public void setup() throws IOException { + message = generateMessage(); + topic = topic("groupName.topic").build(); - hermesImplMessageRepository = new ChronicleMapMessageRepository(prepareFile(), ENTRIES, AVERAGE_MESSAGE_SIZE); - baselineMessageRepository = new BaselineChronicleMapMessageRepository(prepareFile(), ENTRIES, AVERAGE_MESSAGE_SIZE); - } + hermesImplMessageRepository = + new ChronicleMapMessageRepository(prepareFile(), ENTRIES, AVERAGE_MESSAGE_SIZE); + baselineMessageRepository = + new BaselineChronicleMapMessageRepository(prepareFile(), ENTRIES, AVERAGE_MESSAGE_SIZE); + } + + private Message generateMessage() { + byte[] messageContent = UUID.randomUUID().toString().getBytes(); + String id = MessageIdGenerator.generate(); + return new JsonMessage( + id, messageContent, System.currentTimeMillis(), "partition-key", emptyMap()); + } - private Message generateMessage() { - byte[] messageContent = UUID.randomUUID().toString().getBytes(); - String id = MessageIdGenerator.generate(); - return new JsonMessage(id, messageContent, System.currentTimeMillis(), "partition-key"); - } + private File prepareFile() throws IOException { - private File prepareFile() throws IOException { + String baseDir = Files.createTempDirectory(null).toFile().getAbsolutePath(); + return new File(baseDir, "messages.dat"); + } + } + + @Benchmark + public void hermesImplSave(Repositories repositories) { + repositories.hermesImplMessageRepository.save(repositories.message, repositories.topic); + } + + @Benchmark + public void baselineSave(Repositories repositories) { + repositories.baselineMessageRepository.save(repositories.message, repositories.topic); + } + + public static class BaselineChronicleMapMessageRepository implements MessageRepository { + private static final boolean SAME_BUILDER_CONFIG = false; + + private final ChronicleMap map; + + public BaselineChronicleMapMessageRepository(File file, int entries, int averageMessageSize) { + try { + map = + ChronicleMapBuilder.of(String.class, ChronicleMapEntryValue.class) + .constantKeySizeBySample(MessageIdGenerator.generate()) + .averageValueSize(averageMessageSize) + .entries(entries) + .sparseFile(true) + .createOrRecoverPersistedTo(file, SAME_BUILDER_CONFIG); + } catch (IOException e) { + throw new ChronicleMapCreationException(e); + } + } - String baseDir = Files.createTempDirectory(null).toFile().getAbsolutePath(); - return new File(baseDir, "messages.dat"); - } + @Override + public void save(Message message, Topic topic) { + ChronicleMapEntryValue entryValue = + new ChronicleMapEntryValue( + message.getData(), + message.getTimestamp(), + topic.getQualifiedName(), + message.getPartitionKey(), + null, + null, + emptyMap()); + map.put(message.getId(), entryValue); } - @Benchmark - public void hermesImplSave(Repositories repositories) { - repositories.hermesImplMessageRepository.save(repositories.message, repositories.topic); + @Override + public void delete(String messageId) { + throw new UnsupportedOperationException(); } - @Benchmark - public void baselineSave(Repositories repositories) { - repositories.baselineMessageRepository.save(repositories.message, repositories.topic); + @Override + public List findAll() { + throw new UnsupportedOperationException(); } - public static class BaselineChronicleMapMessageRepository implements MessageRepository { - private static final boolean SAME_BUILDER_CONFIG = false; - - private final ChronicleMap map; - - public BaselineChronicleMapMessageRepository(File file, int entries, int averageMessageSize) { - try { - map = ChronicleMapBuilder.of(String.class, ChronicleMapEntryValue.class) - .constantKeySizeBySample(MessageIdGenerator.generate()) - .averageValueSize(averageMessageSize) - .entries(entries) - .sparseFile(true) - .createOrRecoverPersistedTo(file, SAME_BUILDER_CONFIG); - } catch (IOException e) { - throw new ChronicleMapCreationException(e); - } - } - - @Override - public void save(Message message, Topic topic) { - ChronicleMapEntryValue entryValue = new ChronicleMapEntryValue( - message.getData(), - message.getTimestamp(), - topic.getQualifiedName(), - message.getPartitionKey(), - null, - null); - map.put(message.getId(), entryValue); - } - - @Override - public void delete(String messageId) { - throw new UnsupportedOperationException(); - } - - @Override - public List findAll() { - throw new UnsupportedOperationException(); - } - - @Override - public void close() { - throw new UnsupportedOperationException(); - } + @Override + public void close() { + throw new UnsupportedOperationException(); } + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/BenchmarkMessageContentWrapper.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/BenchmarkMessageContentWrapper.java index 39bf8afc61..81801e35c7 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/BenchmarkMessageContentWrapper.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/BenchmarkMessageContentWrapper.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.benchmark.environment; +import java.util.Map; import org.apache.avro.Schema; import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.common.message.wrapper.AvroMessageContentWrapper; @@ -8,35 +9,44 @@ import pl.allegro.tech.hermes.common.message.wrapper.UnwrappedMessageContent; import pl.allegro.tech.hermes.schema.CompiledSchema; -import java.util.Map; - public class BenchmarkMessageContentWrapper implements MessageContentWrapper { - private final AvroMessageContentWrapper avroMessageContentWrapper; - - public BenchmarkMessageContentWrapper(AvroMessageContentWrapper avroMessageContentWrapper) { - this.avroMessageContentWrapper = avroMessageContentWrapper; - } - - @Override - public UnwrappedMessageContent unwrapAvro(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - throw new UnsupportedOperationException(); - } - - @Override - public UnwrappedMessageContent unwrapJson(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] wrapAvro(byte[] data, String id, long timestamp, Topic topic, CompiledSchema schema, - Map externalMetadata) { - byte[] wrapped = avroMessageContentWrapper.wrapContent(data, id, timestamp, schema.getSchema(), externalMetadata); - return topic.isSchemaIdAwareSerializationEnabled() ? SchemaAwareSerDe.serialize(schema.getId(), wrapped) : wrapped; - } - - @Override - public byte[] wrapJson(byte[] data, String id, long timestamp, Map externalMetadata) { - throw new UnsupportedOperationException(); - } + private final AvroMessageContentWrapper avroMessageContentWrapper; + + public BenchmarkMessageContentWrapper(AvroMessageContentWrapper avroMessageContentWrapper) { + this.avroMessageContentWrapper = avroMessageContentWrapper; + } + + @Override + public UnwrappedMessageContent unwrapAvro( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + throw new UnsupportedOperationException(); + } + + @Override + public UnwrappedMessageContent unwrapJson(byte[] data) { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] wrapAvro( + byte[] data, + String id, + long timestamp, + Topic topic, + CompiledSchema schema, + Map externalMetadata) { + byte[] wrapped = + avroMessageContentWrapper.wrapContent( + data, id, timestamp, schema.getSchema(), externalMetadata); + return topic.isSchemaIdAwareSerializationEnabled() + ? SchemaAwareSerDe.serialize(schema.getId(), wrapped) + : wrapped; + } + + @Override + public byte[] wrapJson( + byte[] data, String id, long timestamp, Map externalMetadata) { + throw new UnsupportedOperationException(); + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/DisabledReadinessChecker.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/DisabledReadinessChecker.java index 4216c1cbe0..433cf53746 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/DisabledReadinessChecker.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/DisabledReadinessChecker.java @@ -1,27 +1,27 @@ package pl.allegro.tech.hermes.benchmark.environment; -import pl.allegro.tech.hermes.frontend.server.ReadinessChecker; +import pl.allegro.tech.hermes.frontend.readiness.ReadinessChecker; class DisabledReadinessChecker implements ReadinessChecker { - private boolean isReady; + private boolean isReady; - public DisabledReadinessChecker(boolean isReady) { - this.isReady = isReady; - } + public DisabledReadinessChecker(boolean isReady) { + this.isReady = isReady; + } - @Override - public boolean isReady() { - return this.isReady; - } + @Override + public boolean isReady() { + return this.isReady; + } - @Override - public void start() { - this.isReady = true; - } + @Override + public void start() { + this.isReady = true; + } - @Override - public void stop() { - this.isReady = false; - } + @Override + public void stop() { + this.isReady = false; + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesPublisher.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesPublisher.java index 610eafea81..41633f4ff6 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesPublisher.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesPublisher.java @@ -1,6 +1,10 @@ package pl.allegro.tech.hermes.benchmark.environment; -import com.codahale.metrics.MetricRegistry; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.config.CookieSpecs; @@ -16,72 +20,65 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - public class HermesPublisher { - private static final int CONNECT_TIMEOUT = 3000; - private static final int SOCKET_TIMEOUT = 3000; - private static final Logger logger = LoggerFactory.getLogger(HermesPublisher.class); + private static final int CONNECT_TIMEOUT = 3000; + private static final int SOCKET_TIMEOUT = 3000; + private static final Logger logger = LoggerFactory.getLogger(HermesPublisher.class); - private final CloseableHttpAsyncClient httpClient; - private final MetricRegistry metricRegistry; - private final URI targetUrl; - private final HttpEntity body; + private final CloseableHttpAsyncClient httpClient; + private final URI targetUrl; + private final HttpEntity body; - public HermesPublisher(int maxConnectionsPerRoute, String targetUrl, String body, MetricRegistry metricRegistry) - throws IOReactorException, UnsupportedEncodingException { - this.targetUrl = URI.create(targetUrl); - this.metricRegistry = metricRegistry; + public HermesPublisher(int maxConnectionsPerRoute, String targetUrl, String body) + throws IOReactorException, UnsupportedEncodingException { + this.targetUrl = URI.create(targetUrl); - RequestConfig requestConfig = RequestConfig.custom() - .setCookieSpec(CookieSpecs.IGNORE_COOKIES) - .setAuthenticationEnabled(false) - .build(); + RequestConfig requestConfig = + RequestConfig.custom() + .setCookieSpec(CookieSpecs.IGNORE_COOKIES) + .setAuthenticationEnabled(false) + .build(); - IOReactorConfig ioReactorConfig = IOReactorConfig.custom() - .setIoThreadCount(Runtime.getRuntime().availableProcessors()) - .setConnectTimeout(CONNECT_TIMEOUT) - .setSoTimeout(SOCKET_TIMEOUT) - .build(); + IOReactorConfig ioReactorConfig = + IOReactorConfig.custom() + .setIoThreadCount(Runtime.getRuntime().availableProcessors()) + .setConnectTimeout(CONNECT_TIMEOUT) + .setSoTimeout(SOCKET_TIMEOUT) + .build(); - PoolingNHttpClientConnectionManager connectionManager = new PoolingNHttpClientConnectionManager( - new DefaultConnectingIOReactor(ioReactorConfig)); - connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); + PoolingNHttpClientConnectionManager connectionManager = + new PoolingNHttpClientConnectionManager(new DefaultConnectingIOReactor(ioReactorConfig)); + connectionManager.setDefaultMaxPerRoute(maxConnectionsPerRoute); - httpClient = HttpAsyncClients.custom() - .setConnectionManager(connectionManager) - .setDefaultRequestConfig(requestConfig) - .build(); + httpClient = + HttpAsyncClients.custom() + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .build(); - httpClient.start(); + httpClient.start(); - this.body = new StringEntity(body); - } + this.body = new StringEntity(body); + } - public int publish() { - int response = 0; - HttpPost httpPost = new HttpPost(targetUrl); - httpPost.setEntity(body); - httpPost.setHeader("Content-Type", "application/json"); + public int publish() { + int response = 0; + HttpPost httpPost = new HttpPost(targetUrl); + httpPost.setEntity(body); + httpPost.setHeader("Content-Type", "application/json"); - try { - Future future = httpClient.execute(httpPost, null); - response = future.get().getStatusLine().getStatusCode(); - metricRegistry.counter("response." + response).inc(); - } catch (RuntimeException | InterruptedException | ExecutionException exception) { - metricRegistry.counter("client.exceptions").inc(); - logger.error("Client exception", exception); - } - return response; + try { + Future future = httpClient.execute(httpPost, null); + response = future.get().getStatusLine().getStatusCode(); + } catch (RuntimeException | InterruptedException | ExecutionException exception) { + logger.error("Client exception", exception); } + return response; + } - public void stop() throws IOException { - if (httpClient.isRunning()) { - httpClient.close(); - } + public void stop() throws IOException { + if (httpClient.isRunning()) { + httpClient.close(); } + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerEnvironment.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerEnvironment.java index 52aece4222..2862a8ddb9 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerEnvironment.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerEnvironment.java @@ -1,6 +1,7 @@ package pl.allegro.tech.hermes.benchmark.environment; -import com.codahale.metrics.MetricRegistry; +import java.io.IOException; +import java.util.Objects; import org.apache.commons.io.IOUtils; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Scope; @@ -11,61 +12,57 @@ import org.slf4j.LoggerFactory; import pl.allegro.tech.hermes.frontend.server.HermesServer; -import java.io.IOException; -import java.util.Objects; - @State(Scope.Benchmark) public class HermesServerEnvironment { - private static final Logger logger = LoggerFactory.getLogger(HermesServerEnvironment.class); - private static final int MAX_CONNECTIONS_PER_ROUTE = 200; - - public static final String BENCHMARK_TOPIC = "bench.topic"; + private static final Logger logger = LoggerFactory.getLogger(HermesServerEnvironment.class); + private static final int MAX_CONNECTIONS_PER_ROUTE = 200; - private HermesPublisher publisher; - private MetricRegistry metricRegistry; + public static final String BENCHMARK_TOPIC = "bench.topic"; - private HermesServer hermesServer; + private HermesPublisher publisher; - public static void main(String[] args) throws Exception { - new HermesServerEnvironment().setupEnvironment(); - } + private HermesServer hermesServer; - @Setup(Level.Trial) - public void setupEnvironment() throws Exception { - hermesServer = HermesServerFactory.provideHermesServer(); - hermesServer.start(); - } + public static void main(String[] args) throws Exception { + new HermesServerEnvironment().setupEnvironment(); + } - @Setup(Level.Trial) - public void setupPublisher() throws Exception { - metricRegistry = new MetricRegistry(); + @Setup(Level.Trial) + public void setupEnvironment() throws Exception { + hermesServer = HermesServerFactory.provideHermesServer(); + hermesServer.start(); + } - String messageBody = loadMessageResource("completeMessage"); - publisher = new HermesPublisher(MAX_CONNECTIONS_PER_ROUTE, "http://localhost:8080/topics/" + BENCHMARK_TOPIC, messageBody, metricRegistry); - } + @Setup(Level.Trial) + public void setupPublisher() throws Exception { - @TearDown(Level.Trial) - public void shutdownServers() throws Exception { - hermesServer.stop(); - } + String messageBody = loadMessageResource("completeMessage"); + publisher = + new HermesPublisher( + MAX_CONNECTIONS_PER_ROUTE, + "http://localhost:8080/topics/" + BENCHMARK_TOPIC, + messageBody); + } - @TearDown(Level.Trial) - public void shutdownPublisherAndReportMetrics() throws Exception { - reportMetrics(); - publisher.stop(); - } + @TearDown(Level.Trial) + public void shutdownServers() throws Exception { + hermesServer.stop(); + } - public HermesPublisher publisher() { - return publisher; - } + @TearDown(Level.Trial) + public void shutdownPublisherAndReportMetrics() throws Exception { + publisher.stop(); + } - public static String loadMessageResource(String name) throws IOException { - return IOUtils.toString(Objects.requireNonNull(HermesServerEnvironment.class - .getResourceAsStream(String.format("/message/%s.json", name)))); - } + public HermesPublisher publisher() { + return publisher; + } - private void reportMetrics() { - metricRegistry.getCounters().forEach((key, value) -> logger.info(key + ": " + value.getCount())); - } + public static String loadMessageResource(String name) throws IOException { + return IOUtils.toString( + Objects.requireNonNull( + HermesServerEnvironment.class.getResourceAsStream( + String.format("/message/%s.json", name)))); + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerFactory.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerFactory.java index 356fe6afb1..4c76d4bafe 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerFactory.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/HermesServerFactory.java @@ -1,16 +1,22 @@ package pl.allegro.tech.hermes.benchmark.environment; -import com.codahale.metrics.MetricRegistry; +import static pl.allegro.tech.hermes.api.ContentType.AVRO; +import static pl.allegro.tech.hermes.benchmark.environment.HermesServerEnvironment.loadMessageResource; +import static pl.allegro.tech.hermes.frontend.publishing.handlers.ThroughputLimiter.QuotaInsight.quotaConfirmed; +import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; + import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.undertow.server.HttpHandler; +import java.io.IOException; +import java.time.Clock; +import java.util.Collections; import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.common.message.wrapper.AvroMessageContentWrapper; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.frontend.cache.topic.TopicsCache; +import pl.allegro.tech.hermes.frontend.config.HTTPHeadersProperties; import pl.allegro.tech.hermes.frontend.config.HandlersChainProperties; -import pl.allegro.tech.hermes.frontend.config.HeaderPropagationProperties; import pl.allegro.tech.hermes.frontend.config.HermesServerProperties; import pl.allegro.tech.hermes.frontend.config.SchemaProperties; import pl.allegro.tech.hermes.frontend.config.SslProperties; @@ -25,9 +31,9 @@ import pl.allegro.tech.hermes.frontend.publishing.message.MessageContentTypeEnforcer; import pl.allegro.tech.hermes.frontend.publishing.message.MessageFactory; import pl.allegro.tech.hermes.frontend.publishing.metadata.DefaultHeadersPropagator; +import pl.allegro.tech.hermes.frontend.readiness.HealthCheckService; import pl.allegro.tech.hermes.frontend.server.HermesServer; import pl.allegro.tech.hermes.frontend.validator.MessageValidators; -import pl.allegro.tech.hermes.metrics.PathsCompiler; import pl.allegro.tech.hermes.schema.DirectCompiledSchemaRepository; import pl.allegro.tech.hermes.schema.DirectSchemaVersionsRepository; import pl.allegro.tech.hermes.schema.RawSchemaClient; @@ -35,79 +41,79 @@ import pl.allegro.tech.hermes.schema.SchemaRepository; import pl.allegro.tech.hermes.tracker.frontend.Trackers; -import java.io.IOException; -import java.time.Clock; -import java.util.Collections; - -import static pl.allegro.tech.hermes.api.ContentType.AVRO; -import static pl.allegro.tech.hermes.benchmark.environment.HermesServerEnvironment.loadMessageResource; -import static pl.allegro.tech.hermes.frontend.publishing.handlers.ThroughputLimiter.QuotaInsight.quotaConfirmed; -import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; - class HermesServerFactory { - private static final Topic topic = topic(HermesServerEnvironment.BENCHMARK_TOPIC).withContentType(AVRO).build(); - + private static final Topic topic = + topic(HermesServerEnvironment.BENCHMARK_TOPIC).withContentType(AVRO).build(); - static HermesServer provideHermesServer() throws IOException { - ThroughputLimiter throughputLimiter = (exampleTopic, throughput) -> quotaConfirmed(); - HermesMetrics hermesMetrics = new HermesMetrics(new MetricRegistry(), new PathsCompiler("")); - MetricsFacade metricsFacade = new MetricsFacade(new SimpleMeterRegistry(), hermesMetrics); - TopicsCache topicsCache = new InMemoryTopicsCache(metricsFacade, topic); - BrokerMessageProducer brokerMessageProducer = new InMemoryBrokerMessageProducer(); - RawSchemaClient rawSchemaClient = new InMemorySchemaClient(topic.getName(), loadMessageResource("schema"), 1, 1); - Trackers trackers = new Trackers(Collections.emptyList()); - AvroMessageContentWrapper avroMessageContentWrapper = new AvroMessageContentWrapper(Clock.systemDefaultZone()); - HttpHandler httpHandler = provideHttpHandler(throughputLimiter, topicsCache, - brokerMessageProducer, rawSchemaClient, trackers, avroMessageContentWrapper); - SslProperties sslProperties = new SslProperties(); - HermesServerProperties hermesServerProperties = new HermesServerProperties(); - hermesServerProperties.setGracefulShutdownEnabled(false); + static HermesServer provideHermesServer() throws IOException { + ThroughputLimiter throughputLimiter = (exampleTopic, throughput) -> quotaConfirmed(); + MetricsFacade metricsFacade = new MetricsFacade(new SimpleMeterRegistry()); + TopicsCache topicsCache = new InMemoryTopicsCache(metricsFacade, topic); + BrokerMessageProducer brokerMessageProducer = new InMemoryBrokerMessageProducer(); + RawSchemaClient rawSchemaClient = + new InMemorySchemaClient(topic.getName(), loadMessageResource("schema"), 1, 1); + Trackers trackers = new Trackers(Collections.emptyList()); + AvroMessageContentWrapper avroMessageContentWrapper = + new AvroMessageContentWrapper(Clock.systemDefaultZone()); + HttpHandler httpHandler = + provideHttpHandler( + throughputLimiter, + topicsCache, + brokerMessageProducer, + rawSchemaClient, + trackers, + avroMessageContentWrapper); + SslProperties sslProperties = new SslProperties(); + HermesServerProperties hermesServerProperties = new HermesServerProperties(); + hermesServerProperties.setGracefulShutdownEnabled(false); - return new HermesServer( - sslProperties, - hermesServerProperties, - metricsFacade, - httpHandler, - new DisabledReadinessChecker(false), - new NoOpMessagePreviewPersister(), - throughputLimiter, - null, - false, - null, - null); - } + return new HermesServer( + sslProperties, + hermesServerProperties, + metricsFacade, + httpHandler, + new HealthCheckService(), + new DisabledReadinessChecker(false), + new NoOpMessagePreviewPersister(), + throughputLimiter, + null, + null); + } - private static HttpHandler provideHttpHandler(ThroughputLimiter throughputLimiter, - TopicsCache topicsCache, BrokerMessageProducer brokerMessageProducer, - RawSchemaClient rawSchemaClient, Trackers trackers, AvroMessageContentWrapper avroMessageContentWrapper) { - HeaderPropagationProperties headerPropagationProperties = new HeaderPropagationProperties(); - HandlersChainProperties handlersChainProperties = new HandlersChainProperties(); - TrackingHeadersExtractor trackingHeadersExtractor = new DefaultTrackingHeaderExtractor(); - SchemaProperties schemaProperties = new SchemaProperties(); + private static HttpHandler provideHttpHandler( + ThroughputLimiter throughputLimiter, + TopicsCache topicsCache, + BrokerMessageProducer brokerMessageProducer, + RawSchemaClient rawSchemaClient, + Trackers trackers, + AvroMessageContentWrapper avroMessageContentWrapper) { + HTTPHeadersProperties httpHeadersProperties = new HTTPHeadersProperties(); + HandlersChainProperties handlersChainProperties = new HandlersChainProperties(); + TrackingHeadersExtractor trackingHeadersExtractor = new DefaultTrackingHeaderExtractor(); + SchemaProperties schemaProperties = new SchemaProperties(); - return new HandlersChainFactory( - topicsCache, - new MessageErrorProcessor(new ObjectMapper(), trackers, trackingHeadersExtractor), - new MessageEndProcessor(trackers, new BrokerListeners(), trackingHeadersExtractor), - new MessageFactory( - new MessageValidators(Collections.emptyList()), - new MessageContentTypeEnforcer(), - new SchemaRepository( - new DirectSchemaVersionsRepository(rawSchemaClient), - new DirectCompiledSchemaRepository<>(rawSchemaClient, SchemaCompilersFactory.avroSchemaCompiler()) - ), - new DefaultHeadersPropagator(headerPropagationProperties.isEnabled(), headerPropagationProperties.getAllowFilter()), - new BenchmarkMessageContentWrapper(avroMessageContentWrapper), - Clock.systemDefaultZone(), - schemaProperties.isIdHeaderEnabled() - ), - brokerMessageProducer, - null, - throughputLimiter, - null, - false, - handlersChainProperties - ).provide(); - } + return new HandlersChainFactory( + topicsCache, + new MessageErrorProcessor(new ObjectMapper(), trackers, trackingHeadersExtractor), + new MessageEndProcessor(trackers, new BrokerListeners(), trackingHeadersExtractor), + new MessageFactory( + new MessageValidators(Collections.emptyList()), + new MessageContentTypeEnforcer(), + new SchemaRepository( + new DirectSchemaVersionsRepository(rawSchemaClient), + new DirectCompiledSchemaRepository<>( + rawSchemaClient, SchemaCompilersFactory.avroSchemaCompiler())), + new DefaultHeadersPropagator(httpHeadersProperties), + new BenchmarkMessageContentWrapper(avroMessageContentWrapper), + Clock.systemDefaultZone(), + schemaProperties.isIdHeaderEnabled()), + brokerMessageProducer, + null, + throughputLimiter, + null, + false, + handlersChainProperties) + .provide(); + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryBrokerMessageProducer.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryBrokerMessageProducer.java index a7f3a3d832..994b90777b 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryBrokerMessageProducer.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryBrokerMessageProducer.java @@ -7,13 +7,18 @@ public class InMemoryBrokerMessageProducer implements BrokerMessageProducer { - @Override - public void send(Message message, CachedTopic topic, PublishingCallback callback) { - callback.onPublished(message, topic.getTopic()); - } + @Override + public void send(Message message, CachedTopic topic, PublishingCallback callback) { + callback.onPublished(message, topic.getTopic()); + } - @Override - public boolean isTopicAvailable(CachedTopic topic) { - return true; - } + @Override + public boolean areAllTopicsAvailable() { + return true; + } + + @Override + public boolean isTopicAvailable(CachedTopic cachedTopic) { + return true; + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemorySchemaClient.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemorySchemaClient.java index 1c2ec6ec38..f25c6ed395 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemorySchemaClient.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemorySchemaClient.java @@ -1,6 +1,9 @@ package pl.allegro.tech.hermes.benchmark.environment; import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import pl.allegro.tech.hermes.api.RawSchema; import pl.allegro.tech.hermes.api.RawSchemaWithMetadata; import pl.allegro.tech.hermes.api.TopicName; @@ -8,54 +11,50 @@ import pl.allegro.tech.hermes.schema.SchemaId; import pl.allegro.tech.hermes.schema.SchemaVersion; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - public class InMemorySchemaClient implements RawSchemaClient { - private final TopicName schemaTopicName; - private final RawSchemaWithMetadata rawSchemaWithMetadata; - - public InMemorySchemaClient(TopicName schemaTopicName, String schemaSource, int id, int version) { - this.schemaTopicName = schemaTopicName; - rawSchemaWithMetadata = RawSchemaWithMetadata.of(schemaSource, id, version); - } - - @Override - public Optional getRawSchemaWithMetadata(TopicName topic, SchemaVersion version) { - return schemaTopicName.equals(topic) && Objects.equals(rawSchemaWithMetadata.getVersion(), version.value()) - ? Optional.of(rawSchemaWithMetadata) : Optional.empty(); - } - - @Override - public Optional getRawSchemaWithMetadata(TopicName topic, SchemaId schemaId) { - return schemaTopicName.equals(topic) && Objects.equals(rawSchemaWithMetadata.getId(), schemaId.value()) - ? Optional.of(rawSchemaWithMetadata) : Optional.empty(); - } - - @Override - public Optional getLatestRawSchemaWithMetadata(TopicName topic) { - return schemaTopicName.equals(topic) ? Optional.of(rawSchemaWithMetadata) : Optional.empty(); - } - - @Override - public List getVersions(TopicName topic) { - return ImmutableList.of(SchemaVersion.valueOf(rawSchemaWithMetadata.getVersion())); - } - - @Override - public void registerSchema(TopicName topic, RawSchema rawSchema) { - - } - - @Override - public void deleteAllSchemaVersions(TopicName topic) { - - } - - @Override - public void validateSchema(TopicName topic, RawSchema rawSchema) { - - } + private final TopicName schemaTopicName; + private final RawSchemaWithMetadata rawSchemaWithMetadata; + + public InMemorySchemaClient(TopicName schemaTopicName, String schemaSource, int id, int version) { + this.schemaTopicName = schemaTopicName; + rawSchemaWithMetadata = RawSchemaWithMetadata.of(schemaSource, id, version); + } + + @Override + public Optional getRawSchemaWithMetadata( + TopicName topic, SchemaVersion version) { + return schemaTopicName.equals(topic) + && Objects.equals(rawSchemaWithMetadata.getVersion(), version.value()) + ? Optional.of(rawSchemaWithMetadata) + : Optional.empty(); + } + + @Override + public Optional getRawSchemaWithMetadata( + TopicName topic, SchemaId schemaId) { + return schemaTopicName.equals(topic) + && Objects.equals(rawSchemaWithMetadata.getId(), schemaId.value()) + ? Optional.of(rawSchemaWithMetadata) + : Optional.empty(); + } + + @Override + public Optional getLatestRawSchemaWithMetadata(TopicName topic) { + return schemaTopicName.equals(topic) ? Optional.of(rawSchemaWithMetadata) : Optional.empty(); + } + + @Override + public List getVersions(TopicName topic) { + return ImmutableList.of(SchemaVersion.valueOf(rawSchemaWithMetadata.getVersion())); + } + + @Override + public void registerSchema(TopicName topic, RawSchema rawSchema) {} + + @Override + public void deleteAllSchemaVersions(TopicName topic) {} + + @Override + public void validateSchema(TopicName topic, RawSchema rawSchema) {} } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryTopicsCache.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryTopicsCache.java index d673c51187..1c054ac63e 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryTopicsCache.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/InMemoryTopicsCache.java @@ -1,51 +1,49 @@ package pl.allegro.tech.hermes.benchmark.environment; +import com.codahale.metrics.MetricRegistry; +import java.util.List; +import java.util.Optional; import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.common.kafka.KafkaTopic; import pl.allegro.tech.hermes.common.kafka.KafkaTopicName; import pl.allegro.tech.hermes.common.kafka.KafkaTopics; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.frontend.cache.topic.TopicsCache; import pl.allegro.tech.hermes.frontend.metric.CachedTopic; - -import java.util.List; -import java.util.Optional; +import pl.allegro.tech.hermes.frontend.metric.ThroughputRegistry; class InMemoryTopicsCache implements TopicsCache { - private final MetricsFacade metricsFacade; - private final KafkaTopics kafkaTopics; - private final Topic topic; - - - InMemoryTopicsCache(MetricsFacade metricsFacade, Topic topic) { - this.metricsFacade = metricsFacade; - this.topic = topic; - this.kafkaTopics = new KafkaTopics(new KafkaTopic(KafkaTopicName.valueOf(topic.getQualifiedName()), topic.getContentType())); - } - - @Override - public Optional getTopic(String qualifiedTopicName) { - if (qualifiedTopicName.equals(topic.getQualifiedName())) { - return Optional.of( - new CachedTopic( - topic, - metricsFacade, - kafkaTopics - ) - ); - } - return Optional.empty(); - } - - @Override - public List getTopics() { - throw new UnsupportedOperationException(); - } - - @Override - public void start() { - throw new UnsupportedOperationException(); + private final MetricsFacade metricsFacade; + private final KafkaTopics kafkaTopics; + private final Topic topic; + private final ThroughputRegistry throughputRegistry; + + InMemoryTopicsCache(MetricsFacade metricsFacade, Topic topic) { + this.metricsFacade = metricsFacade; + this.topic = topic; + this.kafkaTopics = + new KafkaTopics( + new KafkaTopic( + KafkaTopicName.valueOf(topic.getQualifiedName()), topic.getContentType())); + this.throughputRegistry = new ThroughputRegistry(metricsFacade, new MetricRegistry()); + } + + @Override + public Optional getTopic(String qualifiedTopicName) { + if (qualifiedTopicName.equals(topic.getQualifiedName())) { + return Optional.of(new CachedTopic(topic, metricsFacade, throughputRegistry, kafkaTopics)); } + return Optional.empty(); + } + + @Override + public List getTopics() { + throw new UnsupportedOperationException(); + } + + @Override + public void start() { + throw new UnsupportedOperationException(); + } } diff --git a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/NoOpMessagePreviewPersister.java b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/NoOpMessagePreviewPersister.java index d491102a5e..1a47684d23 100644 --- a/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/NoOpMessagePreviewPersister.java +++ b/hermes-benchmark/src/jmh/java/pl/allegro/tech/hermes/benchmark/environment/NoOpMessagePreviewPersister.java @@ -4,11 +4,9 @@ class NoOpMessagePreviewPersister implements MessagePreviewPersister { - @Override - public void start() { - } + @Override + public void start() {} - @Override - public void shutdown() { - } + @Override + public void shutdown() {} } diff --git a/hermes-client/build.gradle b/hermes-client/build.gradle index 4839a6e378..8e2fa2c039 100644 --- a/hermes-client/build.gradle +++ b/hermes-client/build.gradle @@ -3,7 +3,6 @@ plugins { } dependencies { - compileOnly group: 'io.dropwizard.metrics', name: 'metrics-core', version: versions.dropwizard_metrics compileOnly group: 'io.micrometer', name: 'micrometer-core', version: versions.micrometer_metrics compileOnly group: 'org.glassfish.jersey.core', name: 'jersey-client', version: versions.jersey compileOnly group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: versions.jersey @@ -12,22 +11,21 @@ dependencies { compileOnly group: 'com.squareup.okhttp3', name: 'okhttp', version: versions.okhttp implementation group: 'net.jodah', name: 'failsafe', version: versions.failsafe - api group: 'io.projectreactor', name: 'reactor-core', version: '3.4.25' + api group: 'io.projectreactor', name: 'reactor-core', version: '3.6.5' testImplementation group: 'org.spockframework', name: 'spock-core', version: versions.spock testImplementation group: 'org.spockframework', name: 'spock-junit4', version: versions.spock testImplementation group: 'org.wiremock', name: 'wiremock-standalone', version: versions.wiremock - testImplementation group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' - testImplementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.5.0' + testImplementation group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '6.0.0' + testImplementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0' - testImplementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: versions.dropwizard_metrics testImplementation group: 'io.micrometer', name: 'micrometer-core', version: versions.micrometer_metrics testImplementation group: 'org.glassfish.jersey.core', name: 'jersey-client', version: versions.jersey testImplementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: versions.jersey testImplementation group: 'org.springframework', name: 'spring-web', version: versions.spring_web + testImplementation group: 'org.springframework', name: 'spring-context', version: versions.spring_web testImplementation group: 'org.springframework', name: 'spring-webflux', version: versions.spring_web testImplementation group: 'com.squareup.okhttp3', name: 'okhttp', version: versions.okhttp - testImplementation group: 'io.projectreactor.netty', name: 'reactor-netty', version: '1.0.25' - testImplementation group: 'io.projectreactor', name: 'reactor-test', version: '3.4.25' - + testImplementation group: 'io.projectreactor.netty', name: 'reactor-netty', version: '1.1.18' + testImplementation group: 'io.projectreactor', name: 'reactor-test', version: '3.6.5' } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClient.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClient.java index 76e2b03978..0590e3bf83 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClient.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClient.java @@ -1,11 +1,6 @@ package pl.allegro.tech.hermes.client; -import net.jodah.failsafe.Failsafe; -import net.jodah.failsafe.RetryPolicy; -import net.jodah.failsafe.event.ExecutionAttemptedEvent; -import net.jodah.failsafe.event.ExecutionCompletedEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static pl.allegro.tech.hermes.client.HermesMessage.hermesMessage; import java.net.URI; import java.time.temporal.ChronoUnit; @@ -21,157 +16,182 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; - -import static pl.allegro.tech.hermes.client.HermesMessage.hermesMessage; +import net.jodah.failsafe.Failsafe; +import net.jodah.failsafe.RetryPolicy; +import net.jodah.failsafe.event.ExecutionAttemptedEvent; +import net.jodah.failsafe.event.ExecutionCompletedEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HermesClient { - private static final Logger logger = LoggerFactory.getLogger(HermesClient.class); - - private final HermesSender sender; - private final String uri; - private final Map defaultHeaders; - private final AtomicInteger currentlySending = new AtomicInteger(0); - private final RetryPolicy retryPolicy; - private final ScheduledExecutorService scheduler; - private volatile boolean shutdown = false; - private final List messageDeliveryListeners = new ArrayList<>(); - - HermesClient(HermesSender sender, - URI uri, - Map defaultHeaders, - int retries, - Predicate retryCondition, - long retrySleepInMillis, - long maxRetrySleepInMillis, - ScheduledExecutorService scheduler) { - this.sender = sender; - this.uri = createUri(uri); - this.defaultHeaders = Collections.unmodifiableMap(new HashMap<>(defaultHeaders)); - this.retryPolicy = createRetryPolicy(retries, retryCondition, retrySleepInMillis, maxRetrySleepInMillis); - this.scheduler = scheduler; - } - - private RetryPolicy createRetryPolicy(int retries, Predicate retryCondition, - long retrySleepInMillis, long maxRetrySleepInMillis) { - RetryPolicy retryPolicy = new RetryPolicy() - .withMaxRetries(retries) - .handleIf((resp, cause) -> retryCondition.test(resp)) - .onRetriesExceeded((e) -> handleMaxRetriesExceeded(e)) - .onRetry((e) -> handleFailedAttempt(e)) - .onFailure((e) -> handleFailure(e)) - .onSuccess((e) -> handleSuccessfulRetry(e)); - - if (retrySleepInMillis > 0) { - retryPolicy.withBackoff(retrySleepInMillis, maxRetrySleepInMillis, ChronoUnit.MILLIS); - } - return retryPolicy; - } - - private String createUri(URI uri) { - String uriString = uri.toString(); - return uriString + (uriString.endsWith("/") ? "" : "/") + "topics/"; - } - - public CompletableFuture publishJSON(String topic, byte[] message) { - return publish(hermesMessage(topic, message).json().build()); - } - - public CompletableFuture publishJSON(String topic, String message) { - return publish(hermesMessage(topic, message).json().build()); - } - - public CompletableFuture publishAvro(String topic, int schemaVersion, byte[] message) { - return publish(hermesMessage(topic, message).avro(schemaVersion).build()); - } - - public CompletableFuture publish(String topic, String message) { - return publish(hermesMessage(topic, message).build()); - } - - public CompletableFuture publish(String topic, String contentType, byte[] message) { - return publish(hermesMessage(topic, message).withContentType(contentType).build()); - } - - public CompletableFuture publish(String topic, String contentType, String message) { - return publish(hermesMessage(topic, message).withContentType(contentType).build()); - } - - public CompletableFuture publish(String topic, String contentType, int schemaVersion, byte[] message) { - return publish(hermesMessage(topic, message).withContentType(contentType).withSchemaVersion(schemaVersion).build()); - } - - public CompletableFuture publish(HermesMessage message) { - if (shutdown) { - return completedWithShutdownException(); - } - HermesMessage.appendDefaults(message, defaultHeaders); - return publishWithRetries(message); - } - - public boolean addMessageDeliveryListener(MessageDeliveryListener listener) { - return messageDeliveryListeners.add(listener); - } - - private CompletableFuture publishWithRetries(HermesMessage message) { - currentlySending.incrementAndGet(); - return Failsafe.with(retryPolicy) - .with(scheduler) - .onComplete((e) -> currentlySending.decrementAndGet()) - .getStageAsync(() -> sendOnce(message)); - } - - private CompletableFuture sendOnce(HermesMessage message) { - long startTime = System.nanoTime(); - - return sender.send(URI.create(uri + message.getTopic()), message) - .exceptionally(e -> HermesResponseBuilder.hermesFailureResponse(e, message)) - .whenComplete((resp, cause) -> { - long latency = System.nanoTime() - startTime; - messageDeliveryListeners.forEach(l -> l.onSend(resp, latency)); - }); - } - - private CompletableFuture completedWithShutdownException() { - CompletableFuture alreadyShutdown = new CompletableFuture<>(); - alreadyShutdown.completeExceptionally(new HermesClientShutdownException()); - return alreadyShutdown; - } - - public CompletableFuture closeAsync(long pollInterval) { - shutdown = true; - return new HermesClientTermination(pollInterval) - .observe(() -> currentlySending.get() == 0) - .whenComplete((response, ex) -> scheduler.shutdown()); - } - - public void close(long pollInterval, long timeout) throws InterruptedException, TimeoutException { - try { - closeAsync(pollInterval).get(timeout, TimeUnit.MILLISECONDS); - } catch (ExecutionException e) { - throw (InterruptedException) e.getCause(); - } - } - - private void handleMaxRetriesExceeded(ExecutionCompletedEvent event) { - if (event.getResult().isSuccess()) { - return; - } - - HermesMessage message = event.getResult().getHermesMessage(); - messageDeliveryListeners.forEach(l -> l.onMaxRetriesExceeded(event.getResult(), event.getAttemptCount())); - logger.error("Failed to send message to topic {} after {} attempts", - message.getTopic(), event.getAttemptCount()); - } - - private void handleFailedAttempt(ExecutionAttemptedEvent event) { - messageDeliveryListeners.forEach(l -> l.onFailedRetry(event.getLastResult(), event.getAttemptCount())); - } - - private void handleFailure(ExecutionCompletedEvent event) { - messageDeliveryListeners.forEach(l -> l.onFailure(event.getResult(), event.getAttemptCount())); - } - - private void handleSuccessfulRetry(ExecutionCompletedEvent event) { - messageDeliveryListeners.forEach(l -> l.onSuccessfulRetry(event.getResult(), event.getAttemptCount())); - } + private static final Logger logger = LoggerFactory.getLogger(HermesClient.class); + + private final HermesSender sender; + private final String uri; + private final Map defaultHeaders; + private final AtomicInteger currentlySending = new AtomicInteger(0); + private final RetryPolicy retryPolicy; + private final ScheduledExecutorService scheduler; + private volatile boolean shutdown = false; + private final List messageDeliveryListeners = new ArrayList<>(); + + HermesClient( + HermesSender sender, + URI uri, + Map defaultHeaders, + int retries, + Predicate retryCondition, + long retrySleepInMillis, + long maxRetrySleepInMillis, + ScheduledExecutorService scheduler) { + this.sender = sender; + this.uri = createUri(uri); + this.defaultHeaders = Collections.unmodifiableMap(new HashMap<>(defaultHeaders)); + this.retryPolicy = + createRetryPolicy(retries, retryCondition, retrySleepInMillis, maxRetrySleepInMillis); + this.scheduler = scheduler; + } + + private RetryPolicy createRetryPolicy( + int retries, + Predicate retryCondition, + long retrySleepInMillis, + long maxRetrySleepInMillis) { + RetryPolicy retryPolicy = + new RetryPolicy() + .withMaxRetries(retries) + .handleIf((resp, cause) -> retryCondition.test(resp)) + .onRetriesExceeded((e) -> handleMaxRetriesExceeded(e)) + .onRetry((e) -> handleFailedAttempt(e)) + .onFailure((e) -> handleFailure(e)) + .onSuccess((e) -> handleSuccessfulRetry(e)); + + if (retrySleepInMillis > 0) { + retryPolicy.withBackoff(retrySleepInMillis, maxRetrySleepInMillis, ChronoUnit.MILLIS); + } + return retryPolicy; + } + + private String createUri(URI uri) { + String uriString = uri.toString(); + return uriString + (uriString.endsWith("/") ? "" : "/") + "topics/"; + } + + public CompletableFuture publishJSON(String topic, byte[] message) { + return publish(hermesMessage(topic, message).json().build()); + } + + public CompletableFuture publishJSON(String topic, String message) { + return publish(hermesMessage(topic, message).json().build()); + } + + public CompletableFuture publishAvro( + String topic, int schemaVersion, byte[] message) { + return publish(hermesMessage(topic, message).avro(schemaVersion).build()); + } + + public CompletableFuture publish(String topic, String message) { + return publish(hermesMessage(topic, message).build()); + } + + public CompletableFuture publish( + String topic, String contentType, byte[] message) { + return publish(hermesMessage(topic, message).withContentType(contentType).build()); + } + + public CompletableFuture publish( + String topic, String contentType, String message) { + return publish(hermesMessage(topic, message).withContentType(contentType).build()); + } + + public CompletableFuture publish( + String topic, String contentType, int schemaVersion, byte[] message) { + return publish( + hermesMessage(topic, message) + .withContentType(contentType) + .withSchemaVersion(schemaVersion) + .build()); + } + + public CompletableFuture publish(HermesMessage message) { + if (shutdown) { + return completedWithShutdownException(); + } + HermesMessage.appendDefaults(message, defaultHeaders); + return publishWithRetries(message); + } + + public boolean addMessageDeliveryListener(MessageDeliveryListener listener) { + return messageDeliveryListeners.add(listener); + } + + private CompletableFuture publishWithRetries(HermesMessage message) { + currentlySending.incrementAndGet(); + return Failsafe.with(retryPolicy) + .with(scheduler) + .onComplete((e) -> currentlySending.decrementAndGet()) + .getStageAsync(() -> sendOnce(message)); + } + + private CompletableFuture sendOnce(HermesMessage message) { + long startTime = System.nanoTime(); + + return sender + .send(URI.create(uri + message.getTopic()), message) + .exceptionally(e -> HermesResponseBuilder.hermesFailureResponse(e, message)) + .whenComplete( + (resp, cause) -> { + long latency = System.nanoTime() - startTime; + messageDeliveryListeners.forEach(l -> l.onSend(resp, latency)); + }); + } + + private CompletableFuture completedWithShutdownException() { + CompletableFuture alreadyShutdown = new CompletableFuture<>(); + alreadyShutdown.completeExceptionally(new HermesClientShutdownException()); + return alreadyShutdown; + } + + public CompletableFuture closeAsync(long pollInterval) { + shutdown = true; + return new HermesClientTermination(pollInterval) + .observe(() -> currentlySending.get() == 0) + .whenComplete((response, ex) -> scheduler.shutdown()); + } + + public void close(long pollInterval, long timeout) throws InterruptedException, TimeoutException { + try { + closeAsync(pollInterval).get(timeout, TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + throw (InterruptedException) e.getCause(); + } + } + + private void handleMaxRetriesExceeded(ExecutionCompletedEvent event) { + if (event.getResult().isSuccess()) { + return; + } + + HermesMessage message = event.getResult().getHermesMessage(); + messageDeliveryListeners.forEach( + l -> l.onMaxRetriesExceeded(event.getResult(), event.getAttemptCount())); + logger.error( + "Failed to send message to topic {} after {} attempts", + message.getTopic(), + event.getAttemptCount()); + } + + private void handleFailedAttempt(ExecutionAttemptedEvent event) { + messageDeliveryListeners.forEach( + l -> l.onFailedRetry(event.getLastResult(), event.getAttemptCount())); + } + + private void handleFailure(ExecutionCompletedEvent event) { + messageDeliveryListeners.forEach(l -> l.onFailure(event.getResult(), event.getAttemptCount())); + } + + private void handleSuccessfulRetry(ExecutionCompletedEvent event) { + messageDeliveryListeners.forEach( + l -> l.onSuccessfulRetry(event.getResult(), event.getAttemptCount())); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBasicRetryCondition.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBasicRetryCondition.java index e0cd051370..4e6957b7b2 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBasicRetryCondition.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBasicRetryCondition.java @@ -1,20 +1,21 @@ package pl.allegro.tech.hermes.client; -import java.util.function.Predicate; - import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT; +import java.util.function.Predicate; + public class HermesClientBasicRetryCondition implements Predicate { - @Override - public boolean test(HermesResponse response) { - return response == null || (isClientTimeoutOrServerError(response) || isFailedExceptionally(response)); - } + @Override + public boolean test(HermesResponse response) { + return response == null + || (isClientTimeoutOrServerError(response) || isFailedExceptionally(response)); + } - private boolean isClientTimeoutOrServerError(HermesResponse response) { - return response.getHttpStatus() == HTTP_CLIENT_TIMEOUT || response.getHttpStatus() / 100 == 5; - } + private boolean isClientTimeoutOrServerError(HermesResponse response) { + return response.getHttpStatus() == HTTP_CLIENT_TIMEOUT || response.getHttpStatus() / 100 == 5; + } - private boolean isFailedExceptionally(HermesResponse response) { - return response.isFailure() && response.getFailureCause().isPresent(); - } + private boolean isFailedExceptionally(HermesResponse response) { + return response.isFailure() && response.getFailureCause().isPresent(); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBuilder.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBuilder.java index c318b8e890..6041033150 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBuilder.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientBuilder.java @@ -1,8 +1,5 @@ package pl.allegro.tech.hermes.client; -import pl.allegro.tech.hermes.client.metrics.MetricsMessageDeliveryListener; -import pl.allegro.tech.hermes.client.metrics.MetricsProvider; - import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -11,82 +8,95 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; import java.util.function.Supplier; +import pl.allegro.tech.hermes.client.metrics.MetricsMessageDeliveryListener; +import pl.allegro.tech.hermes.client.metrics.MetricsProvider; public class HermesClientBuilder { - private HermesSender sender; - private URI uri = URI.create("http://localhost:8080"); - private final Map defaultHeaders = new HashMap<>(); - private int retries = 3; - private Predicate retryCondition = new HermesClientBasicRetryCondition(); - private long retrySleepInMillis = 100; - private long maxRetrySleepInMillis = 300; - private Supplier schedulerFactory = Executors::newSingleThreadScheduledExecutor; - private Optional metrics = Optional.empty(); - - public HermesClientBuilder(HermesSender sender) { - this.sender = sender; - this.defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, HermesMessage.APPLICATION_JSON); - } - - public static HermesClientBuilder hermesClient(HermesSender sender) { - return new HermesClientBuilder(sender); - } - - public HermesClient build() { - HermesClient hermesClient = new HermesClient(sender, uri, defaultHeaders, retries, retryCondition, - retrySleepInMillis, maxRetrySleepInMillis, schedulerFactory.get()); - - metrics.ifPresent((metricsProvider) -> { - hermesClient.addMessageDeliveryListener(new MetricsMessageDeliveryListener(metricsProvider)); + private HermesSender sender; + private URI uri = URI.create("http://localhost:8080"); + private final Map defaultHeaders = new HashMap<>(); + private int retries = 3; + private Predicate retryCondition = new HermesClientBasicRetryCondition(); + private long retrySleepInMillis = 100; + private long maxRetrySleepInMillis = 300; + private Supplier schedulerFactory = + Executors::newSingleThreadScheduledExecutor; + private Optional metrics = Optional.empty(); + + public HermesClientBuilder(HermesSender sender) { + this.sender = sender; + this.defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, HermesMessage.APPLICATION_JSON); + } + + public static HermesClientBuilder hermesClient(HermesSender sender) { + return new HermesClientBuilder(sender); + } + + public HermesClient build() { + HermesClient hermesClient = + new HermesClient( + sender, + uri, + defaultHeaders, + retries, + retryCondition, + retrySleepInMillis, + maxRetrySleepInMillis, + schedulerFactory.get()); + + metrics.ifPresent( + (metricsProvider) -> { + hermesClient.addMessageDeliveryListener( + new MetricsMessageDeliveryListener(metricsProvider)); }); - return hermesClient; - } - - public HermesClientBuilder withURI(URI uri) { - this.uri = uri; - return this; - } - - public HermesClientBuilder withMetrics(MetricsProvider metrics) { - this.metrics = Optional.of(metrics); - return this; - } - - public HermesClientBuilder withDefaultContentType(String defaultContentType) { - defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, defaultContentType); - return this; - } - - public HermesClientBuilder withDefaultHeaderValue(String header, String value) { - defaultHeaders.put(header, value); - return this; - } - - public HermesClientBuilder withRetries(int retries) { - this.retries = retries; - return this; - } - - public HermesClientBuilder withRetries(int retries, Predicate retryCondition) { - this.retryCondition = retryCondition; - return withRetries(retries); - } - - public HermesClientBuilder withRetrySleep(long retrySleepInMillis) { - this.retrySleepInMillis = retrySleepInMillis; - return this; - } - - public HermesClientBuilder withRetrySleep(long retrySleepInMillis, long maxRetrySleepInMillis) { - this.retrySleepInMillis = retrySleepInMillis; - this.maxRetrySleepInMillis = maxRetrySleepInMillis; - return this; - } - - public HermesClientBuilder withScheduler(ScheduledExecutorService scheduler) { - this.schedulerFactory = () -> scheduler; - return this; - } + return hermesClient; + } + + public HermesClientBuilder withURI(URI uri) { + this.uri = uri; + return this; + } + + public HermesClientBuilder withMetrics(MetricsProvider metrics) { + this.metrics = Optional.of(metrics); + return this; + } + + public HermesClientBuilder withDefaultContentType(String defaultContentType) { + defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, defaultContentType); + return this; + } + + public HermesClientBuilder withDefaultHeaderValue(String header, String value) { + defaultHeaders.put(header, value); + return this; + } + + public HermesClientBuilder withRetries(int retries) { + this.retries = retries; + return this; + } + + public HermesClientBuilder withRetries(int retries, Predicate retryCondition) { + this.retryCondition = retryCondition; + return withRetries(retries); + } + + public HermesClientBuilder withRetrySleep(long retrySleepInMillis) { + this.retrySleepInMillis = retrySleepInMillis; + return this; + } + + public HermesClientBuilder withRetrySleep(long retrySleepInMillis, long maxRetrySleepInMillis) { + this.retrySleepInMillis = retrySleepInMillis; + this.maxRetrySleepInMillis = maxRetrySleepInMillis; + return this; + } + + public HermesClientBuilder withScheduler(ScheduledExecutorService scheduler) { + this.schedulerFactory = () -> scheduler; + return this; + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientShutdownException.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientShutdownException.java index 1b50292abd..12b4a0a23a 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientShutdownException.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientShutdownException.java @@ -2,11 +2,11 @@ public class HermesClientShutdownException extends RuntimeException { - public HermesClientShutdownException() { - this("Hermes client is already shutdown"); - } + public HermesClientShutdownException() { + this("Hermes client is already shutdown"); + } - public HermesClientShutdownException(String message) { - super(message); - } + public HermesClientShutdownException(String message) { + super(message); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientTermination.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientTermination.java index 897e21eb8a..f131f15081 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientTermination.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesClientTermination.java @@ -7,30 +7,31 @@ class HermesClientTermination { - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - private final long pollInterval; + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private final long pollInterval; - HermesClientTermination(long pollInterval) { - this.pollInterval = pollInterval; - } + HermesClientTermination(long pollInterval) { + this.pollInterval = pollInterval; + } - CompletableFuture observe(BooleanSupplier condition) { - final CompletableFuture result = new CompletableFuture<>(); + CompletableFuture observe(BooleanSupplier condition) { + final CompletableFuture result = new CompletableFuture<>(); - executorService.submit(() -> { - try { - while (!condition.getAsBoolean()) { - Thread.sleep(pollInterval); - } - result.complete(null); - } catch (InterruptedException e) { - result.completeExceptionally(e); - Thread.currentThread().interrupt(); - } finally { - executorService.shutdown(); + executorService.submit( + () -> { + try { + while (!condition.getAsBoolean()) { + Thread.sleep(pollInterval); } + result.complete(null); + } catch (InterruptedException e) { + result.completeExceptionally(e); + Thread.currentThread().interrupt(); + } finally { + executorService.shutdown(); + } }); - return result; - } -} \ No newline at end of file + return result; + } +} diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesMessage.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesMessage.java index 7b7040e007..4bd2fe0fb0 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesMessage.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesMessage.java @@ -6,171 +6,163 @@ import java.util.Map; import java.util.function.BiConsumer; -/** - * All information Hermes needs to send a message. - */ +/** All information Hermes needs to send a message. */ public class HermesMessage { - public static final String SCHEMA_VERSION_HEADER = "Schema-Version"; - - static final String APPLICATION_JSON = "application/json;charset=UTF-8"; - static final String CONTENT_TYPE_HEADER = "Content-Type"; - static final String AVRO_BINARY = "avro/binary"; + public static final String SCHEMA_VERSION_HEADER = "Schema-Version"; + + static final String APPLICATION_JSON = "application/json;charset=UTF-8"; + static final String CONTENT_TYPE_HEADER = "Content-Type"; + static final String AVRO_BINARY = "avro/binary"; + + private final String topic; + private final byte[] body; + private final Map headers; + + private HermesMessage(String topic, Map headers, byte[] body) { + this.topic = topic; + this.headers = headers; + this.body = body; + } + + /** Use builder via: HermesMessage#hermesMessage instead. */ + @Deprecated + public HermesMessage(String topic, String contentType, int schemaVersion, byte[] body) { + Map headers = new HashMap<>(); + headers.put(CONTENT_TYPE_HEADER, contentType); + headers.put(SCHEMA_VERSION_HEADER, Integer.toString(schemaVersion)); + + this.topic = topic; + this.headers = headers; + this.body = body; + } + + /** Use builder via: HermesMessage#hermesMessage instead. */ + @Deprecated + public HermesMessage(String topic, String contentType, byte[] body) { + this(topic, contentType, -1, body); + } + + /** + * Message on given topic with given MIME Content Type. + * + *

Use builder via: HermesMessage#hermesMessage instead. + * + * @param topic topic name + * @param contentType MIME content type + * @param body body which will be translated to byte[] using UTF-8 charset + */ + @Deprecated + public HermesMessage(String topic, String contentType, String body) { + this(topic, contentType, body.getBytes(StandardCharsets.UTF_8)); + } + + /** Use builder via: HermesMessage#hermesMessage instead. */ + @Deprecated + public HermesMessage(String topic, String body) { + this(topic, null, body); + } + + public static Builder hermesMessage(String topic, byte[] content) { + return new Builder(topic, content); + } + + public static Builder hermesMessage(String topic, String content) { + return new Builder(topic, content); + } + + /** + * This method modifies the state of HermesMessage in order to avoid additional allocation when + * appending default values. Using same HermesMessage in multiple HermesClient objects with + * different defaults is very unlikely, as is sending the same message in multiple threads (which + * could cause issues with concurrent modification of map). + */ + static HermesMessage appendDefaults(HermesMessage message, Map headers) { + for (Map.Entry entry : headers.entrySet()) { + if (!message.headers.containsKey(entry.getKey())) { + message.headers.put(entry.getKey(), entry.getValue()); + } + } + return message; + } + + public String getTopic() { + return topic; + } + + public byte[] getBody() { + return body; + } + + public String getContentType() { + return headers.get(CONTENT_TYPE_HEADER); + } + + public int getSchemaVersion() { + String schemaVersion = headers.get(SCHEMA_VERSION_HEADER); + return schemaVersion != null ? Integer.parseInt(schemaVersion) : -1; + } + + public boolean schemaVersionDefined() { + return headers.containsKey(SCHEMA_VERSION_HEADER); + } + + public Map getHeaders() { + return Collections.unmodifiableMap(headers); + } + + public void consumeHeaders(BiConsumer consumer) { + headers.forEach(consumer); + } + + @Override + public String toString() { + return new String(getBody(), StandardCharsets.UTF_8); + } + + public static class Builder { private final String topic; private final byte[] body; - private final Map headers; - - private HermesMessage(String topic, Map headers, byte[] body) { - this.topic = topic; - this.headers = headers; - this.body = body; - } - - /** - * Use builder via: HermesMessage#hermesMessage instead. - */ - @Deprecated - public HermesMessage(String topic, String contentType, int schemaVersion, byte[] body) { - Map headers = new HashMap<>(); - headers.put(CONTENT_TYPE_HEADER, contentType); - headers.put(SCHEMA_VERSION_HEADER, Integer.toString(schemaVersion)); - - this.topic = topic; - this.headers = headers; - this.body = body; - } - - /** - * Use builder via: HermesMessage#hermesMessage instead. - */ - @Deprecated - public HermesMessage(String topic, String contentType, byte[] body) { - this(topic, contentType, -1, body); - } - - /** - *

Message on given topic with given MIME Content Type.

- *

Use builder via: HermesMessage#hermesMessage instead.

- * - * @param topic topic name - * @param contentType MIME content type - * @param body body which will be translated to byte[] using UTF-8 charset - */ - @Deprecated - public HermesMessage(String topic, String contentType, String body) { - this(topic, contentType, body.getBytes(StandardCharsets.UTF_8)); - } - - /** - * Use builder via: HermesMessage#hermesMessage instead. - */ - @Deprecated - public HermesMessage(String topic, String body) { - this(topic, null, body); - } - - public static Builder hermesMessage(String topic, byte[] content) { - return new Builder(topic, content); - } - - public static Builder hermesMessage(String topic, String content) { - return new Builder(topic, content); - } - - /** - * This method modifies the state of HermesMessage in order to avoid additional - * allocation when appending default values. Using same HermesMessage in multiple - * HermesClient objects with different defaults is very unlikely, as is sending the same - * message in multiple threads (which could cause issues with concurrent modification of - * map). - */ - static HermesMessage appendDefaults(HermesMessage message, Map headers) { - for (Map.Entry entry : headers.entrySet()) { - if (!message.headers.containsKey(entry.getKey())) { - message.headers.put(entry.getKey(), entry.getValue()); - } - } - return message; - } - - public String getTopic() { - return topic; - } + private final Map headers = new HashMap<>(); - public byte[] getBody() { - return body; + private Builder(String topic, byte[] body) { + this.topic = topic; + this.body = body; } - public String getContentType() { - return headers.get(CONTENT_TYPE_HEADER); + private Builder(String topic, String body) { + this(topic, body.getBytes(StandardCharsets.UTF_8)); } - public int getSchemaVersion() { - String schemaVersion = headers.get(SCHEMA_VERSION_HEADER); - return schemaVersion != null ? Integer.parseInt(schemaVersion) : -1; + public HermesMessage build() { + return new HermesMessage(topic, headers, body); } - public boolean schemaVersionDefined() { - return headers.containsKey(SCHEMA_VERSION_HEADER); + public Builder json() { + this.withContentType(APPLICATION_JSON); + return this; } - public Map getHeaders() { - return Collections.unmodifiableMap(headers); + public Builder avro(int schemaVersion) { + this.withContentType(AVRO_BINARY); + this.withSchemaVersion(schemaVersion); + return this; } - public void consumeHeaders(BiConsumer consumer) { - headers.forEach(consumer); + public Builder withContentType(String contentType) { + this.headers.put(CONTENT_TYPE_HEADER, contentType); + return this; } - @Override - public String toString() { - return new String(getBody(), StandardCharsets.UTF_8); + public Builder withSchemaVersion(int schemaVersion) { + this.headers.put(SCHEMA_VERSION_HEADER, Integer.toString(schemaVersion)); + return this; } - public static class Builder { - - private final String topic; - private final byte[] body; - private final Map headers = new HashMap<>(); - - private Builder(String topic, byte[] body) { - this.topic = topic; - this.body = body; - } - - private Builder(String topic, String body) { - this(topic, body.getBytes(StandardCharsets.UTF_8)); - } - - public HermesMessage build() { - return new HermesMessage(topic, headers, body); - } - - public Builder json() { - this.withContentType(APPLICATION_JSON); - return this; - } - - public Builder avro(int schemaVersion) { - this.withContentType(AVRO_BINARY); - this.withSchemaVersion(schemaVersion); - return this; - } - - public Builder withContentType(String contentType) { - this.headers.put(CONTENT_TYPE_HEADER, contentType); - return this; - } - - public Builder withSchemaVersion(int schemaVersion) { - this.headers.put(SCHEMA_VERSION_HEADER, Integer.toString(schemaVersion)); - return this; - } - - public Builder withHeader(String header, String value) { - this.headers.put(header, value); - return this; - } + public Builder withHeader(String header, String value) { + this.headers.put(header, value); + return this; } + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponse.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponse.java index 84c6e97c1b..54cdcf3a78 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponse.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponse.java @@ -1,80 +1,78 @@ package pl.allegro.tech.hermes.client; -import java.util.Optional; - import static java.net.HttpURLConnection.HTTP_ACCEPTED; import static java.net.HttpURLConnection.HTTP_CREATED; +import java.util.Optional; + public interface HermesResponse { - String MESSAGE_ID = "Hermes-Message-Id"; - String HTTP_1_1 = "http/1.1"; - - int getHttpStatus(); - - HermesMessage getHermesMessage(); - - @Deprecated - default boolean wasPublished() { - return getHttpStatus() == HTTP_CREATED; - } - - @Deprecated - default boolean wasAccepted() { - return wasPublished() || getHttpStatus() == HTTP_ACCEPTED; - } - - default boolean isSuccess() { - return getHttpStatus() == HTTP_CREATED || getHttpStatus() == HTTP_ACCEPTED; - } - - default boolean isFailure() { - return !isSuccess(); - } - - default Optional getFailureCause() { - return Optional.empty(); - } - - /** - * Retrieves failed HermesMessage. - * - * @deprecated as of Hermes 1.2.4, in favor of {@link #getHermesMessage()} - */ - @Deprecated - default Optional getFailedMessage() { - return Optional.empty(); - } - - default String getBody() { - return ""; - } - - default String getHeader(String header) { - return ""; - } - - default String getMessageId() { - return getHeader(MESSAGE_ID); - } - - default String getProtocol() { - return HTTP_1_1; - } - - default String getDebugLog() { - StringBuilder builder = new StringBuilder("Sending message ") - .append(getMessageId()) - .append(" to Hermes ") - .append(isSuccess() ? "succeeded" : "failed") - .append(", response code: ") - .append(getHttpStatus()) - .append(", body: ") - .append(getBody()); - getFailureCause().ifPresent(ex -> - builder.append(", exception: ") - .append(ex.getMessage()) - ); - return builder.toString(); - } + String MESSAGE_ID = "Hermes-Message-Id"; + String HTTP_1_1 = "http/1.1"; + + int getHttpStatus(); + + HermesMessage getHermesMessage(); + + @Deprecated + default boolean wasPublished() { + return getHttpStatus() == HTTP_CREATED; + } + + @Deprecated + default boolean wasAccepted() { + return wasPublished() || getHttpStatus() == HTTP_ACCEPTED; + } + + default boolean isSuccess() { + return getHttpStatus() == HTTP_CREATED || getHttpStatus() == HTTP_ACCEPTED; + } + + default boolean isFailure() { + return !isSuccess(); + } + + default Optional getFailureCause() { + return Optional.empty(); + } + + /** + * Retrieves failed HermesMessage. + * + * @deprecated as of Hermes 1.2.4, in favor of {@link #getHermesMessage()} + */ + @Deprecated + default Optional getFailedMessage() { + return Optional.empty(); + } + + default String getBody() { + return ""; + } + + default String getHeader(String header) { + return ""; + } + + default String getMessageId() { + return getHeader(MESSAGE_ID); + } + + default String getProtocol() { + return HTTP_1_1; + } + + default String getDebugLog() { + StringBuilder builder = + new StringBuilder("Sending message ") + .append(getMessageId()) + .append(" to Hermes ") + .append(isSuccess() ? "succeeded" : "failed") + .append(", response code: ") + .append(getHttpStatus()) + .append(", body: ") + .append(getBody()); + getFailureCause().ifPresent(ex -> builder.append(", exception: ").append(ex.getMessage())); + return builder.toString(); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponseBuilder.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponseBuilder.java index 44a6752606..14b203eb66 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponseBuilder.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesResponseBuilder.java @@ -5,90 +5,89 @@ public class HermesResponseBuilder { - private int statusCode = -1; - private String body = ""; - private String protocol = "http/1.1"; - private Throwable failureCause; - private Function headerSupplier = (header) -> null; - private HermesMessage hermesMessage; - - public static HermesResponseBuilder hermesResponse(HermesMessage hermesMessage) { - return new HermesResponseBuilder().withHermesMessage(hermesMessage); - } - - public static HermesResponse hermesFailureResponse(Throwable exception, HermesMessage hermesMessage) { - return hermesResponse(hermesMessage) - .withFailureCause(exception) - .build(); - } - - public HermesResponseBuilder withHttpStatus(int statusCode) { - this.statusCode = statusCode; - return this; - } - - public HermesResponseBuilder withBody(String body) { - this.body = body; - return this; - } - - private HermesResponseBuilder withFailureCause(Throwable exception) { - this.failureCause = exception; - return this; - } - - private HermesResponseBuilder withHermesMessage(HermesMessage hermesMessage) { - this.hermesMessage = hermesMessage; - return this; - } - - public HermesResponseBuilder withHeaderSupplier(Function headerSupplier) { - this.headerSupplier = headerSupplier; - return this; - } - - public HermesResponseBuilder withProtocol(String protocol) { - this.protocol = protocol; - return this; - } - - public HermesResponse build() { - return new HermesResponse() { - - @Override - public int getHttpStatus() { - return statusCode; - } - - @Override - public HermesMessage getHermesMessage() { - return hermesMessage; - } - - @Override - public Optional getFailureCause() { - return Optional.ofNullable(failureCause); - } - - @Override - public Optional getFailedMessage() { - return Optional.ofNullable(hermesMessage); - } - - @Override - public String getBody() { - return body; - } - - @Override - public String getHeader(String header) { - return headerSupplier.apply(header); - } - - @Override - public String getProtocol() { - return protocol; - } - }; - } + private int statusCode = -1; + private String body = ""; + private String protocol = "http/1.1"; + private Throwable failureCause; + private Function headerSupplier = (header) -> null; + private HermesMessage hermesMessage; + + public static HermesResponseBuilder hermesResponse(HermesMessage hermesMessage) { + return new HermesResponseBuilder().withHermesMessage(hermesMessage); + } + + public static HermesResponse hermesFailureResponse( + Throwable exception, HermesMessage hermesMessage) { + return hermesResponse(hermesMessage).withFailureCause(exception).build(); + } + + public HermesResponseBuilder withHttpStatus(int statusCode) { + this.statusCode = statusCode; + return this; + } + + public HermesResponseBuilder withBody(String body) { + this.body = body; + return this; + } + + private HermesResponseBuilder withFailureCause(Throwable exception) { + this.failureCause = exception; + return this; + } + + private HermesResponseBuilder withHermesMessage(HermesMessage hermesMessage) { + this.hermesMessage = hermesMessage; + return this; + } + + public HermesResponseBuilder withHeaderSupplier(Function headerSupplier) { + this.headerSupplier = headerSupplier; + return this; + } + + public HermesResponseBuilder withProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + public HermesResponse build() { + return new HermesResponse() { + + @Override + public int getHttpStatus() { + return statusCode; + } + + @Override + public HermesMessage getHermesMessage() { + return hermesMessage; + } + + @Override + public Optional getFailureCause() { + return Optional.ofNullable(failureCause); + } + + @Override + public Optional getFailedMessage() { + return Optional.ofNullable(hermesMessage); + } + + @Override + public String getBody() { + return body; + } + + @Override + public String getHeader(String header) { + return headerSupplier.apply(header); + } + + @Override + public String getProtocol() { + return protocol; + } + }; + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesSender.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesSender.java index a6b096dbc2..fe7fc2710f 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesSender.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/HermesSender.java @@ -6,5 +6,5 @@ @FunctionalInterface public interface HermesSender { - CompletableFuture send(URI uri, HermesMessage message); + CompletableFuture send(URI uri, HermesMessage message); } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/MessageDeliveryListener.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/MessageDeliveryListener.java index bd5a1dcb82..139b706134 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/MessageDeliveryListener.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/MessageDeliveryListener.java @@ -2,13 +2,13 @@ public interface MessageDeliveryListener { - void onSend(HermesResponse response, long latency); + void onSend(HermesResponse response, long latency); - void onFailure(HermesResponse message, int attemptCount); + void onFailure(HermesResponse message, int attemptCount); - void onFailedRetry(HermesResponse message, int attemptCount); + void onFailedRetry(HermesResponse message, int attemptCount); - void onSuccessfulRetry(HermesResponse message, int attemptCount); + void onSuccessfulRetry(HermesResponse message, int attemptCount); - void onMaxRetriesExceeded(HermesResponse message, int attemptCount); + void onMaxRetriesExceeded(HermesResponse message, int attemptCount); } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClient.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClient.java index 0702ecc198..f6ea952c6f 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClient.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClient.java @@ -1,10 +1,8 @@ package pl.allegro.tech.hermes.client; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; -import reactor.util.retry.Retry; +import static pl.allegro.tech.hermes.client.HermesMessage.hermesMessage; +import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesFailureResponse; +import static reactor.core.Exceptions.isRetryExhausted; import java.net.URI; import java.time.Duration; @@ -16,313 +14,330 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; - -import static pl.allegro.tech.hermes.client.HermesMessage.hermesMessage; -import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesFailureResponse; -import static reactor.core.Exceptions.isRetryExhausted; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.util.retry.Retry; public class ReactiveHermesClient { - private static final Logger logger = LoggerFactory.getLogger(ReactiveHermesClient.class); - private static final String RETRY_CONTEXT_KEY = "hermes-retry-context-key"; - - private final ReactiveHermesSender sender; - private final String uri; - private final Map defaultHeaders; - private final AtomicInteger currentlySending = new AtomicInteger(0); - private final int maxRetries; - private final Predicate retryCondition; - private final Duration retrySleep; - private final Duration maxRetrySleep; - private final Double jitterFactor; - private final Scheduler scheduler; - private volatile boolean shutdown = false; - private final List messageDeliveryListeners; - - ReactiveHermesClient(ReactiveHermesSender sender, - URI uri, - Map defaultHeaders, - int maxRetries, - Predicate retryCondition, - long retrySleepInMillis, - long maxRetrySleepInMillis, - double jitterFactor, - Scheduler scheduler) { - this.sender = sender; - this.uri = createUri(uri); - this.defaultHeaders = Collections.unmodifiableMap(new HashMap<>(defaultHeaders)); - this.maxRetries = maxRetries; - this.retryCondition = retryCondition; - this.retrySleep = Duration.ofMillis(retrySleepInMillis); - this.maxRetrySleep = Duration.ofMillis(maxRetrySleepInMillis); - this.jitterFactor = jitterFactor; - this.scheduler = scheduler; - this.messageDeliveryListeners = new ArrayList<>(); - } - - private String createUri(URI uri) { - String uriString = uri.toString(); - return uriString + (uriString.endsWith("/") ? "" : "/") + "topics/"; - } - - public Mono publishJSON(String topic, byte[] message) { - return publish(hermesMessage(topic, message).json().build()); - } - - public Mono publishJSON(String topic, String message) { - return publish(hermesMessage(topic, message).json().build()); - } - - public Mono publishAvro(String topic, int schemaVersion, byte[] message) { - return publish(hermesMessage(topic, message).avro(schemaVersion).build()); - } - - public Mono publish(String topic, String message) { - return publish(hermesMessage(topic, message).build()); - } - - public Mono publish(String topic, String contentType, byte[] message) { - return publish(hermesMessage(topic, message).withContentType(contentType).build()); - } - - public Mono publish(String topic, String contentType, String message) { - return publish(hermesMessage(topic, message).withContentType(contentType).build()); - } - - public Mono publish(String topic, String contentType, int schemaVersion, byte[] message) { - return publish(hermesMessage(topic, message).withContentType(contentType).withSchemaVersion(schemaVersion).build()); - } - - public Mono publish(HermesMessage message) { - if (shutdown) { - return completedWithShutdownException(); - } - HermesMessage.appendDefaults(message, defaultHeaders); - return publishWithRetries(message); - } + private static final Logger logger = LoggerFactory.getLogger(ReactiveHermesClient.class); + private static final String RETRY_CONTEXT_KEY = "hermes-retry-context-key"; + + private final ReactiveHermesSender sender; + private final String uri; + private final Map defaultHeaders; + private final AtomicInteger currentlySending = new AtomicInteger(0); + private final int maxRetries; + private final Predicate retryCondition; + private final Duration retrySleep; + private final Duration maxRetrySleep; + private final Double jitterFactor; + private final Scheduler scheduler; + private volatile boolean shutdown = false; + private final List messageDeliveryListeners; + + ReactiveHermesClient( + ReactiveHermesSender sender, + URI uri, + Map defaultHeaders, + int maxRetries, + Predicate retryCondition, + long retrySleepInMillis, + long maxRetrySleepInMillis, + double jitterFactor, + Scheduler scheduler) { + this.sender = sender; + this.uri = createUri(uri); + this.defaultHeaders = Collections.unmodifiableMap(new HashMap<>(defaultHeaders)); + this.maxRetries = maxRetries; + this.retryCondition = retryCondition; + this.retrySleep = Duration.ofMillis(retrySleepInMillis); + this.maxRetrySleep = Duration.ofMillis(maxRetrySleepInMillis); + this.jitterFactor = jitterFactor; + this.scheduler = scheduler; + this.messageDeliveryListeners = new ArrayList<>(); + } + + private String createUri(URI uri) { + String uriString = uri.toString(); + return uriString + (uriString.endsWith("/") ? "" : "/") + "topics/"; + } + + public Mono publishJSON(String topic, byte[] message) { + return publish(hermesMessage(topic, message).json().build()); + } + + public Mono publishJSON(String topic, String message) { + return publish(hermesMessage(topic, message).json().build()); + } + + public Mono publishAvro(String topic, int schemaVersion, byte[] message) { + return publish(hermesMessage(topic, message).avro(schemaVersion).build()); + } + + public Mono publish(String topic, String message) { + return publish(hermesMessage(topic, message).build()); + } + + public Mono publish(String topic, String contentType, byte[] message) { + return publish(hermesMessage(topic, message).withContentType(contentType).build()); + } + + public Mono publish(String topic, String contentType, String message) { + return publish(hermesMessage(topic, message).withContentType(contentType).build()); + } + + public Mono publish( + String topic, String contentType, int schemaVersion, byte[] message) { + return publish( + hermesMessage(topic, message) + .withContentType(contentType) + .withSchemaVersion(schemaVersion) + .build()); + } + + public Mono publish(HermesMessage message) { + if (shutdown) { + return completedWithShutdownException(); + } + HermesMessage.appendDefaults(message, defaultHeaders); + return publishWithRetries(message); + } + + public boolean addMessageDeliveryListener(MessageDeliveryListener listener) { + return messageDeliveryListeners.add(listener); + } + + private Mono publishWithRetries(HermesMessage message) { + currentlySending.incrementAndGet(); + Mono sendOnceResult = + sendOnce(message) + .flatMap(this::testRetryCondition) + .onErrorResume(exception -> mapExceptionToFailedAttempt(exception, message)) + .subscribeOn(scheduler); + + return retry(sendOnceResult) + .doOnSuccess( + hr -> { + if (hr.response.isSuccess() || !hr.matchesRetryPolicy) { + handleSuccessfulRetry(hr.response, hr.attempt); + } else { + handleFailure(hr.response, hr.attempt); + } + }) + .map(result -> result.response) + .doFinally(s -> currentlySending.decrementAndGet()); + } + + private Mono retry(Mono sendOnceResult) { + Retry retrySpec = prepareRetrySpec(); + return sendOnceResult + .flatMap(this::unwrapFailedAttemptAsException) + .retryWhen(retrySpec) + .contextWrite(ctx -> ctx.put(RETRY_CONTEXT_KEY, HermesRetryContext.emptyRetryContext())) + .onErrorResume(Exception.class, this::unwrapRetryExhaustedException); + } + + private Mono unwrapRetryExhaustedException(Exception exception) { + if (isRetryExhausted(exception)) { + RetryFailedException rfe = (RetryFailedException) (exception.getCause()); + Failed failedAttempt = rfe.failed; + HermesResponse hermesResponse = failedAttempt.hermesResponse; + handleMaxRetriesExceeded(hermesResponse, failedAttempt.attempt); + Throwable cause = rfe.getCause(); + if (cause instanceof ShouldRetryException) { + ShouldRetryException sre = (ShouldRetryException) cause; + return Mono.just((Attempt) Result.attempt(sre.hermesResponse, failedAttempt.attempt, true)); + } + if (hermesResponse.isFailure()) { + handleFailure(hermesResponse, failedAttempt.attempt); + } + } + return Mono.error(exception); + } + + private Mono unwrapFailedAttemptAsException(Result result) { + if (result instanceof Failed) { + Failed failed = (Failed) result; + return Mono.error(new RetryFailedException(failed)); + } else { + return Mono.just((Attempt) result); + } + } + + private Mono mapExceptionToFailedAttempt( + Throwable throwable, HermesMessage hermesMessage) { + return getNextAttempt() + .map(attempt -> Result.failureByException(throwable, hermesMessage, attempt)); + } + + private Mono testRetryCondition(HermesResponse response) { + return getNextAttempt() + .map( + attempt -> { + if (retryCondition.test(response)) { + return Result.retryableFailure(response, attempt); + } else { + return Result.attempt(response, attempt, false); + } + }); + } + + private Retry prepareRetrySpec() { + if (retrySleep.isZero()) { + return Retry.max(maxRetries).doAfterRetry(this::handleRetryAttempt); + } else { + return Retry.backoff(maxRetries, retrySleep) + .maxBackoff(maxRetrySleep) + .jitter(jitterFactor) + .doAfterRetry(this::handleRetryAttempt); + } + } + + private void handleRetryAttempt(Retry.RetrySignal retrySignal) { + RetryFailedException failedException = (RetryFailedException) retrySignal.failure(); + handleFailedAttempt(failedException.failed.hermesResponse, retrySignal.totalRetries() + 1); + } + + private Mono getNextAttempt() { + return Mono.deferContextual(Mono::just) + .map( + ctx -> + ctx.getOrDefault(RETRY_CONTEXT_KEY, HermesRetryContext.emptyRetryContext()) + .getAndIncrementAttempt()); + } + + private Mono sendOnce(HermesMessage message) { + return Mono.defer( + () -> { + long startTime = System.nanoTime(); + try { + return sender + .sendReactively(URI.create(uri + message.getTopic()), message) + .onErrorResume(e -> Mono.just(hermesFailureResponse(e, message))) + .doOnNext( + resp -> { + long latency = System.nanoTime() - startTime; + messageDeliveryListeners.forEach(l -> l.onSend(resp, latency)); + }); + + } catch (Exception e) { + return Mono.error(e); + } + }); + } + + private Mono completedWithShutdownException() { + return Mono.error(new HermesClientShutdownException()); + } + + public Mono closeAsync(long pollInterval) { + shutdown = true; + CompletableFuture voidCompletableFuture = + new HermesClientTermination(pollInterval) + .observe(() -> currentlySending.get() == 0) + .whenComplete((response, ex) -> scheduler.dispose()); + return Mono.fromFuture(voidCompletableFuture); + } + + public void close(long pollInterval, long timeout) { + closeAsync(pollInterval).block(Duration.ofMillis(timeout)); + } + + private void handleMaxRetriesExceeded(HermesResponse response, int attemptCount) { + messageDeliveryListeners.forEach(l -> l.onMaxRetriesExceeded(response, attemptCount)); + logger.error( + "Failed to send message to topic {} after {} attempts", + response.getHermesMessage().getTopic(), + attemptCount); + } + + private void handleFailedAttempt(HermesResponse response, long attemptCount) { + messageDeliveryListeners.forEach(l -> l.onFailedRetry(response, (int) attemptCount)); + } + + private void handleFailure(HermesResponse response, long attemptCount) { + messageDeliveryListeners.forEach(l -> l.onFailure(response, (int) attemptCount)); + } + + private void handleSuccessfulRetry(HermesResponse response, long attemptCount) { + messageDeliveryListeners.forEach(l -> l.onSuccessfulRetry(response, (int) attemptCount)); + } + + private static class ShouldRetryException extends Exception { + private final HermesResponse hermesResponse; + + public ShouldRetryException(HermesResponse hermesResponse) { + this.hermesResponse = hermesResponse; + } + + public HermesResponse getHermesResponse() { + return hermesResponse; + } + } + + private static class RetryFailedException extends Exception { + private final Failed failed; - public boolean addMessageDeliveryListener(MessageDeliveryListener listener) { - return messageDeliveryListeners.add(listener); + public RetryFailedException(Failed failed) { + super(failed.cause); + this.failed = failed; } + } - private Mono publishWithRetries(HermesMessage message) { - currentlySending.incrementAndGet(); - Mono sendOnceResult = sendOnce(message) - .flatMap(this::testRetryCondition) - .onErrorResume(exception -> mapExceptionToFailedAttempt(exception, message)) - .subscribeOn(scheduler); - - return retry(sendOnceResult) - .doOnSuccess(hr -> { - if (hr.response.isSuccess() || !hr.matchesRetryPolicy) { - handleSuccessfulRetry(hr.response, hr.attempt); - } else { - handleFailure(hr.response, hr.attempt); - } - }) - .map(result -> result.response) - .doFinally(s -> currentlySending.decrementAndGet()); + private interface Result { + static Result attempt(HermesResponse response, int attempt, boolean qualifiedForRetry) { + return new Attempt(response, attempt, qualifiedForRetry); } - private Mono retry(Mono sendOnceResult) { - Retry retrySpec = prepareRetrySpec(); - return sendOnceResult - .flatMap(this::unwrapFailedAttemptAsException) - .retryWhen(retrySpec) - .contextWrite(ctx -> ctx.put(RETRY_CONTEXT_KEY, HermesRetryContext.emptyRetryContext())) - .onErrorResume(Exception.class, this::unwrapRetryExhaustedException); + static Result retryableFailure(HermesResponse response, int attempt) { + return new Failed(response, attempt, new ShouldRetryException(response)); } - private Mono unwrapRetryExhaustedException(Exception exception) { - if (isRetryExhausted(exception)) { - RetryFailedException rfe = (RetryFailedException) (exception.getCause()); - Failed failedAttempt = rfe.failed; - HermesResponse hermesResponse = failedAttempt.hermesResponse; - handleMaxRetriesExceeded(hermesResponse, failedAttempt.attempt); - Throwable cause = rfe.getCause(); - if (cause instanceof ShouldRetryException) { - ShouldRetryException sre = (ShouldRetryException) cause; - return Mono.just((Attempt) Result.attempt(sre.hermesResponse, failedAttempt.attempt, true)); - } - if (hermesResponse.isFailure()) { - handleFailure(hermesResponse, failedAttempt.attempt); - } - } - return Mono.error(exception); + static Result failureByException( + Throwable throwable, HermesMessage hermesMessage, int attempt) { + return new Failed(hermesFailureResponse(throwable, hermesMessage), attempt, throwable); } + } - private Mono unwrapFailedAttemptAsException(Result result) { - if (result instanceof Failed) { - Failed failed = (Failed) result; - return Mono.error(new RetryFailedException(failed)); - } else { - return Mono.just((Attempt) result); - } - } - - private Mono mapExceptionToFailedAttempt(Throwable throwable, HermesMessage hermesMessage) { - return getNextAttempt().map(attempt -> Result.failureByException(throwable, hermesMessage, attempt)); - } - - private Mono testRetryCondition(HermesResponse response) { - return getNextAttempt() - .map(attempt -> { - if (retryCondition.test(response)) { - return Result.retryableFailure(response, attempt); - } else { - return Result.attempt(response, attempt, false); - } - }); - } + private static class Attempt implements Result { + private final HermesResponse response; + private final int attempt; + private final boolean matchesRetryPolicy; - private Retry prepareRetrySpec() { - if (retrySleep.isZero()) { - return Retry.max(maxRetries) - .doAfterRetry(this::handleRetryAttempt); - } else { - return Retry.backoff(maxRetries, retrySleep) - .maxBackoff(maxRetrySleep) - .jitter(jitterFactor) - .doAfterRetry(this::handleRetryAttempt); - } + private Attempt(HermesResponse response, int attempt, boolean matchesRetryPolicy) { + this.response = response; + this.attempt = attempt; + this.matchesRetryPolicy = matchesRetryPolicy; } + } - private void handleRetryAttempt(Retry.RetrySignal retrySignal) { - RetryFailedException failedException = (RetryFailedException) retrySignal.failure(); - handleFailedAttempt(failedException.failed.hermesResponse, retrySignal.totalRetries() + 1); - } + private static class Failed implements Result { + private final HermesResponse hermesResponse; + private final int attempt; + private final Throwable cause; - private Mono getNextAttempt() { - return Mono.deferContextual(Mono::just) - .map(ctx -> ctx.getOrDefault(RETRY_CONTEXT_KEY, HermesRetryContext.emptyRetryContext()) - .getAndIncrementAttempt() - ); + private Failed(HermesResponse hermesResponse, int attempt, Throwable cause) { + this.attempt = attempt; + this.cause = cause; + this.hermesResponse = hermesResponse; } + } - private Mono sendOnce(HermesMessage message) { - return Mono.defer(() -> { - long startTime = System.nanoTime(); - try { - return sender.sendReactively(URI.create(uri + message.getTopic()), message) - .onErrorResume(e -> Mono.just(hermesFailureResponse(e, message))) - .doOnNext(resp -> { - long latency = System.nanoTime() - startTime; - messageDeliveryListeners.forEach(l -> l.onSend(resp, latency)); - }); - - } catch (Exception e) { - return Mono.error(e); - } - } - ); + private static class HermesRetryContext { + static HermesRetryContext emptyRetryContext() { + return new HermesRetryContext(); } - private Mono completedWithShutdownException() { - return Mono.error(new HermesClientShutdownException()); - } + private int attempt; - public Mono closeAsync(long pollInterval) { - shutdown = true; - CompletableFuture voidCompletableFuture = new HermesClientTermination(pollInterval) - .observe(() -> currentlySending.get() == 0) - .whenComplete((response, ex) -> scheduler.dispose()); - return Mono.fromFuture(voidCompletableFuture); + HermesRetryContext() { + attempt = 1; } - public void close(long pollInterval, long timeout) { - closeAsync(pollInterval).block(Duration.ofMillis(timeout)); - } - - private void handleMaxRetriesExceeded(HermesResponse response, int attemptCount) { - messageDeliveryListeners.forEach(l -> l.onMaxRetriesExceeded(response, attemptCount)); - logger.error("Failed to send message to topic {} after {} attempts", - response.getHermesMessage().getTopic(), attemptCount); - } - - private void handleFailedAttempt(HermesResponse response, long attemptCount) { - messageDeliveryListeners.forEach(l -> l.onFailedRetry(response, (int) attemptCount)); - } - - private void handleFailure(HermesResponse response, long attemptCount) { - messageDeliveryListeners.forEach(l -> l.onFailure(response, (int) attemptCount)); - } - - private void handleSuccessfulRetry(HermesResponse response, long attemptCount) { - messageDeliveryListeners.forEach(l -> l.onSuccessfulRetry(response, (int) attemptCount)); - } - - private static class ShouldRetryException extends Exception { - private final HermesResponse hermesResponse; - - public ShouldRetryException(HermesResponse hermesResponse) { - this.hermesResponse = hermesResponse; - } - - public HermesResponse getHermesResponse() { - return hermesResponse; - } - } - - private static class RetryFailedException extends Exception { - private final Failed failed; - - public RetryFailedException(Failed failed) { - super(failed.cause); - this.failed = failed; - } - } - - private interface Result { - static Result attempt(HermesResponse response, int attempt, boolean qualifiedForRetry) { - return new Attempt(response, attempt, qualifiedForRetry); - } - - static Result retryableFailure(HermesResponse response, int attempt) { - return new Failed(response, attempt, new ShouldRetryException(response)); - } - - static Result failureByException(Throwable throwable, HermesMessage hermesMessage, int attempt) { - return new Failed(hermesFailureResponse(throwable, hermesMessage), attempt, throwable); - } - } - - private static class Attempt implements Result { - private final HermesResponse response; - private final int attempt; - private final boolean matchesRetryPolicy; - - private Attempt(HermesResponse response, int attempt, boolean matchesRetryPolicy) { - this.response = response; - this.attempt = attempt; - this.matchesRetryPolicy = matchesRetryPolicy; - } - } - - private static class Failed implements Result { - private final HermesResponse hermesResponse; - private final int attempt; - private final Throwable cause; - - private Failed(HermesResponse hermesResponse, int attempt, Throwable cause) { - this.attempt = attempt; - this.cause = cause; - this.hermesResponse = hermesResponse; - } - } - - private static class HermesRetryContext { - static HermesRetryContext emptyRetryContext() { - return new HermesRetryContext(); - } - - private int attempt; - - HermesRetryContext() { - attempt = 1; - } - - int getAndIncrementAttempt() { - return attempt++; - } + int getAndIncrementAttempt() { + return attempt++; } + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClientBuilder.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClientBuilder.java index 1303acd49a..c25da59ab0 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClientBuilder.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesClientBuilder.java @@ -1,10 +1,5 @@ package pl.allegro.tech.hermes.client; -import pl.allegro.tech.hermes.client.metrics.MetricsMessageDeliveryListener; -import pl.allegro.tech.hermes.client.metrics.MetricsProvider; -import reactor.core.scheduler.Scheduler; -import reactor.core.scheduler.Schedulers; - import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -12,88 +7,106 @@ import java.util.concurrent.Executors; import java.util.function.Predicate; import java.util.function.Supplier; +import pl.allegro.tech.hermes.client.metrics.MetricsMessageDeliveryListener; +import pl.allegro.tech.hermes.client.metrics.MetricsProvider; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; public class ReactiveHermesClientBuilder { - private final ReactiveHermesSender sender; - private URI uri = URI.create("http://localhost:8080"); - private final Map defaultHeaders = new HashMap<>(); - private int retries = 3; - private Predicate retryCondition = new HermesClientBasicRetryCondition(); - private long retrySleepInMillis = 100; - private long maxRetrySleepInMillis = 300; - private double jitterFactor = 0.5d; - private Supplier schedulerFactory = () -> Schedulers.fromExecutor(Executors.newSingleThreadScheduledExecutor()); - private Optional metrics = Optional.empty(); - - public ReactiveHermesClientBuilder(ReactiveHermesSender sender) { - this.sender = sender; - this.defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, HermesMessage.APPLICATION_JSON); - } - - public static ReactiveHermesClientBuilder hermesClient(ReactiveHermesSender sender) { - return new ReactiveHermesClientBuilder(sender); - } - - public ReactiveHermesClient build() { - ReactiveHermesClient hermesClient = new ReactiveHermesClient(sender, uri, defaultHeaders, retries, retryCondition, - retrySleepInMillis, maxRetrySleepInMillis, jitterFactor, schedulerFactory.get()); - - metrics.ifPresent((metricsProvider) -> { - hermesClient.addMessageDeliveryListener(new MetricsMessageDeliveryListener(metricsProvider)); + private final ReactiveHermesSender sender; + private URI uri = URI.create("http://localhost:8080"); + private final Map defaultHeaders = new HashMap<>(); + private int retries = 3; + private Predicate retryCondition = new HermesClientBasicRetryCondition(); + private long retrySleepInMillis = 100; + private long maxRetrySleepInMillis = 300; + private double jitterFactor = 0.5d; + private Supplier schedulerFactory = + () -> Schedulers.fromExecutor(Executors.newSingleThreadScheduledExecutor()); + private Optional metrics = Optional.empty(); + + public ReactiveHermesClientBuilder(ReactiveHermesSender sender) { + this.sender = sender; + this.defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, HermesMessage.APPLICATION_JSON); + } + + public static ReactiveHermesClientBuilder hermesClient(ReactiveHermesSender sender) { + return new ReactiveHermesClientBuilder(sender); + } + + public ReactiveHermesClient build() { + ReactiveHermesClient hermesClient = + new ReactiveHermesClient( + sender, + uri, + defaultHeaders, + retries, + retryCondition, + retrySleepInMillis, + maxRetrySleepInMillis, + jitterFactor, + schedulerFactory.get()); + + metrics.ifPresent( + (metricsProvider) -> { + hermesClient.addMessageDeliveryListener( + new MetricsMessageDeliveryListener(metricsProvider)); }); - return hermesClient; - } - - public ReactiveHermesClientBuilder withURI(URI uri) { - this.uri = uri; - return this; - } - - public ReactiveHermesClientBuilder withMetrics(MetricsProvider metrics) { - this.metrics = Optional.of(metrics); - return this; - } - - public ReactiveHermesClientBuilder withDefaultContentType(String defaultContentType) { - defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, defaultContentType); - return this; - } - - public ReactiveHermesClientBuilder withDefaultHeaderValue(String header, String value) { - defaultHeaders.put(header, value); - return this; - } - - public ReactiveHermesClientBuilder withRetries(int retries) { - this.retries = retries; - return this; - } - - public ReactiveHermesClientBuilder withRetries(int retries, Predicate retryCondition) { - this.retryCondition = retryCondition; - return withRetries(retries); - } - - public ReactiveHermesClientBuilder withRetrySleep(long retrySleepInMillis) { - this.retrySleepInMillis = retrySleepInMillis; - return this; - } - - public ReactiveHermesClientBuilder withRetrySleep(long retrySleepInMillis, long maxRetrySleepInMillis) { - this.retrySleepInMillis = retrySleepInMillis; - this.maxRetrySleepInMillis = maxRetrySleepInMillis; - return this; - } - - public ReactiveHermesClientBuilder withScheduler(Scheduler scheduler) { - this.schedulerFactory = () -> scheduler; - return this; - } - - public ReactiveHermesClientBuilder withJitter(Double jitterFactor) { - this.jitterFactor = jitterFactor; - return this; - } + return hermesClient; + } + + public ReactiveHermesClientBuilder withURI(URI uri) { + this.uri = uri; + return this; + } + + public ReactiveHermesClientBuilder withMetrics(MetricsProvider metrics) { + this.metrics = Optional.of(metrics); + return this; + } + + public ReactiveHermesClientBuilder withDefaultContentType(String defaultContentType) { + defaultHeaders.put(HermesMessage.CONTENT_TYPE_HEADER, defaultContentType); + return this; + } + + public ReactiveHermesClientBuilder withDefaultHeaderValue(String header, String value) { + defaultHeaders.put(header, value); + return this; + } + + public ReactiveHermesClientBuilder withRetries(int retries) { + this.retries = retries; + return this; + } + + public ReactiveHermesClientBuilder withRetries( + int retries, Predicate retryCondition) { + this.retryCondition = retryCondition; + return withRetries(retries); + } + + public ReactiveHermesClientBuilder withRetrySleep(long retrySleepInMillis) { + this.retrySleepInMillis = retrySleepInMillis; + return this; + } + + public ReactiveHermesClientBuilder withRetrySleep( + long retrySleepInMillis, long maxRetrySleepInMillis) { + this.retrySleepInMillis = retrySleepInMillis; + this.maxRetrySleepInMillis = maxRetrySleepInMillis; + return this; + } + + public ReactiveHermesClientBuilder withScheduler(Scheduler scheduler) { + this.schedulerFactory = () -> scheduler; + return this; + } + + public ReactiveHermesClientBuilder withJitter(Double jitterFactor) { + this.jitterFactor = jitterFactor; + return this; + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesSender.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesSender.java index b500ed1257..e538458c87 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesSender.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/ReactiveHermesSender.java @@ -1,11 +1,10 @@ package pl.allegro.tech.hermes.client; -import reactor.core.publisher.Mono; - import java.net.URI; +import reactor.core.publisher.Mono; @FunctionalInterface public interface ReactiveHermesSender { - Mono sendReactively(URI uri, HermesMessage message); + Mono sendReactively(URI uri, HermesMessage message); } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/jersey/JerseyHermesSender.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/jersey/JerseyHermesSender.java index 3d59d58548..7b21121f57 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/jersey/JerseyHermesSender.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/jersey/JerseyHermesSender.java @@ -1,52 +1,53 @@ package pl.allegro.tech.hermes.client.jersey; +import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; + import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.Entity; import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.client.InvocationCallback; import jakarta.ws.rs.core.Response; +import java.net.URI; +import java.util.concurrent.CompletableFuture; import pl.allegro.tech.hermes.client.HermesMessage; import pl.allegro.tech.hermes.client.HermesResponse; import pl.allegro.tech.hermes.client.HermesSender; -import java.net.URI; -import java.util.concurrent.CompletableFuture; - -import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; - public class JerseyHermesSender implements HermesSender { - private final Client client; - - public JerseyHermesSender(Client client) { - this.client = client; - } - - @Override - public CompletableFuture send(URI uri, HermesMessage message) { - CompletableFuture future = new CompletableFuture<>(); - Invocation.Builder builder = client.target(uri).request(); - message.consumeHeaders(builder::header); - builder.async() - .post(Entity.entity(message.getBody(), message.getContentType()), - new InvocationCallback() { - @Override - public void completed(Response response) { - future.complete(fromJerseyResponse(message, response)); - } - - @Override - public void failed(Throwable exception) { - future.completeExceptionally(exception); - } - }); - return future; - } - - private HermesResponse fromJerseyResponse(HermesMessage message, Response response) { - return hermesResponse(message) - .withHttpStatus(response.getStatus()) - .withBody(response.readEntity(String.class)) - .withHeaderSupplier(response::getHeaderString) - .build(); - } + private final Client client; + + public JerseyHermesSender(Client client) { + this.client = client; + } + + @Override + public CompletableFuture send(URI uri, HermesMessage message) { + CompletableFuture future = new CompletableFuture<>(); + Invocation.Builder builder = client.target(uri).request(); + message.consumeHeaders(builder::header); + builder + .async() + .post( + Entity.entity(message.getBody(), message.getContentType()), + new InvocationCallback() { + @Override + public void completed(Response response) { + future.complete(fromJerseyResponse(message, response)); + } + + @Override + public void failed(Throwable exception) { + future.completeExceptionally(exception); + } + }); + return future; + } + + private HermesResponse fromJerseyResponse(HermesMessage message, Response response) { + return hermesResponse(message) + .withHttpStatus(response.getStatus()) + .withBody(response.readEntity(String.class)) + .withHeaderSupplier(response::getHeaderString) + .build(); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/DropwizardMetricsProvider.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/DropwizardMetricsProvider.java deleted file mode 100644 index ded184d90e..0000000000 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/DropwizardMetricsProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -package pl.allegro.tech.hermes.client.metrics; - -import com.codahale.metrics.MetricRegistry; - -import java.util.Map; -import java.util.concurrent.TimeUnit; - -public class DropwizardMetricsProvider implements MetricsProvider { - - private final MetricRegistry metrics; - - - public DropwizardMetricsProvider(MetricRegistry metrics) { - this.metrics = metrics; - } - - @Override - public void counterIncrement(String topic, String key) { - metrics.counter(prefix + topic + "." + key).inc(); - } - - @Override - public void counterIncrement(String topic, String key, Map tags) { - counterIncrement(topic, buildCounterName(key, tags)); - } - - @Override - public void timerRecord(String topic, String key, long duration, TimeUnit unit) { - metrics.timer(prefix + topic + "." + key).update(duration, unit); - } - - @Override - public void histogramUpdate(String topic, String key, int value) { - metrics.histogram(prefix + topic + "." + key).update(value); - } - - private String buildCounterName(String key, Map tags) { - return key + "." + String.join(".", tags.values()); - } -} diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsMessageDeliveryListener.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsMessageDeliveryListener.java index 7f72a5ce1f..5fedec10dd 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsMessageDeliveryListener.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsMessageDeliveryListener.java @@ -1,77 +1,76 @@ package pl.allegro.tech.hermes.client.metrics; -import pl.allegro.tech.hermes.client.HermesMessage; -import pl.allegro.tech.hermes.client.HermesResponse; -import pl.allegro.tech.hermes.client.MessageDeliveryListener; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import java.util.HashMap; import java.util.Map; - -import static java.util.concurrent.TimeUnit.NANOSECONDS; +import pl.allegro.tech.hermes.client.HermesMessage; +import pl.allegro.tech.hermes.client.HermesResponse; +import pl.allegro.tech.hermes.client.MessageDeliveryListener; public class MetricsMessageDeliveryListener implements MessageDeliveryListener { - private final MetricsProvider metrics; - - public MetricsMessageDeliveryListener(MetricsProvider metrics) { - this.metrics = metrics; - } - - @Override - public void onSend(HermesResponse response, long latency) { - HermesMessage message = response.getHermesMessage(); - String topic = MetricsUtils.sanitizeTopic(message.getTopic()); - - metrics.timerRecord(topic, "latency", latency, NANOSECONDS); - Map tags = new HashMap<>(); - tags.put("code", String.valueOf(response.getHttpStatus())); - metrics.counterIncrement(topic, "status", tags); - - counterIncrementIf(topic, "publish.failure", response.isFailure()); - } - - @Override - public void onFailure(HermesResponse response, int attemptCount) { - String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); - metrics.counterIncrement(topic, "failure"); - } - - @Override - public void onFailedRetry(HermesResponse response, int attemptCount) { - String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); - metrics.counterIncrement(topic, "retries.count"); - metrics.counterIncrement(topic, "failure"); - - metrics.counterIncrement(topic, "publish.retry.failure"); - } - - @Override - public void onSuccessfulRetry(HermesResponse response, int attemptCount) { - String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); - metrics.counterIncrement(topic, "retries.success"); - metrics.histogramUpdate(topic, "retries.attempts", attemptCount - 1); - - boolean wasRetried = attemptCount > 1; - - metrics.counterIncrement(topic, "publish.attempt"); - counterIncrementIf(topic, "publish.retry.success", response.isSuccess() && wasRetried); - counterIncrementIf(topic, "publish.finally.success", response.isSuccess()); - counterIncrementIf(topic, "publish.retry.failure", response.isFailure() && wasRetried); - counterIncrementIf(topic, "publish.finally.failure", response.isFailure()); - counterIncrementIf(topic, "publish.retry.attempt", wasRetried); - } - - @Override - public void onMaxRetriesExceeded(HermesResponse response, int attemptCount) { - String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); - metrics.counterIncrement(topic, "retries.exhausted"); - metrics.counterIncrement(topic, "publish.finally.failure"); - metrics.counterIncrement(topic, "publish.attempt"); - metrics.counterIncrement(topic, "publish.retry.attempt"); - } - - private void counterIncrementIf(String topic, String name, boolean condition) { - if (condition) { - metrics.counterIncrement(topic, name); - } + private final MetricsProvider metrics; + + public MetricsMessageDeliveryListener(MetricsProvider metrics) { + this.metrics = metrics; + } + + @Override + public void onSend(HermesResponse response, long latency) { + HermesMessage message = response.getHermesMessage(); + String topic = MetricsUtils.sanitizeTopic(message.getTopic()); + + metrics.timerRecord(topic, "latency", latency, NANOSECONDS); + Map tags = new HashMap<>(); + tags.put("code", String.valueOf(response.getHttpStatus())); + metrics.counterIncrement(topic, "status", tags); + + counterIncrementIf(topic, "publish.failure", response.isFailure()); + } + + @Override + public void onFailure(HermesResponse response, int attemptCount) { + String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); + metrics.counterIncrement(topic, "failure"); + } + + @Override + public void onFailedRetry(HermesResponse response, int attemptCount) { + String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); + metrics.counterIncrement(topic, "retries.count"); + metrics.counterIncrement(topic, "failure"); + + metrics.counterIncrement(topic, "publish.retry.failure"); + } + + @Override + public void onSuccessfulRetry(HermesResponse response, int attemptCount) { + String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); + metrics.counterIncrement(topic, "retries.success"); + metrics.histogramUpdate(topic, "retries.attempts", attemptCount - 1); + + boolean wasRetried = attemptCount > 1; + + metrics.counterIncrement(topic, "publish.attempt"); + counterIncrementIf(topic, "publish.retry.success", response.isSuccess() && wasRetried); + counterIncrementIf(topic, "publish.finally.success", response.isSuccess()); + counterIncrementIf(topic, "publish.retry.failure", response.isFailure() && wasRetried); + counterIncrementIf(topic, "publish.finally.failure", response.isFailure()); + counterIncrementIf(topic, "publish.retry.attempt", wasRetried); + } + + @Override + public void onMaxRetriesExceeded(HermesResponse response, int attemptCount) { + String topic = MetricsUtils.sanitizeTopic(response.getHermesMessage().getTopic()); + metrics.counterIncrement(topic, "retries.exhausted"); + metrics.counterIncrement(topic, "publish.finally.failure"); + metrics.counterIncrement(topic, "publish.attempt"); + metrics.counterIncrement(topic, "publish.retry.attempt"); + } + + private void counterIncrementIf(String topic, String name, boolean condition) { + if (condition) { + metrics.counterIncrement(topic, name); } + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsProvider.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsProvider.java index 4f077e66d7..52e997ece7 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsProvider.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsProvider.java @@ -5,13 +5,13 @@ public interface MetricsProvider { - String prefix = "hermes-client."; + String prefix = "hermes-client."; - void counterIncrement(String topic, String key); + void counterIncrement(String topic, String key); - void counterIncrement(String topic, String key, Map tags); + void counterIncrement(String topic, String key, Map tags); - void timerRecord(String topic, String key, long duration, TimeUnit unit); + void timerRecord(String topic, String key, long duration, TimeUnit unit); - void histogramUpdate(String topic, String key, int value); + void histogramUpdate(String topic, String key, int value); } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsUtils.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsUtils.java index cfb1da88b9..cb2744d01b 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsUtils.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MetricsUtils.java @@ -2,10 +2,10 @@ class MetricsUtils { - static String sanitizeTopic(String topic) { - int lastDot = topic.lastIndexOf("."); - char[] sanitized = topic.replaceAll("\\.", "_").toCharArray(); - sanitized[lastDot] = '.'; - return String.valueOf(sanitized); - } + static String sanitizeTopic(String topic) { + int lastDot = topic.lastIndexOf("."); + char[] sanitized = topic.replaceAll("\\.", "_").toCharArray(); + sanitized[lastDot] = '.'; + return String.valueOf(sanitized); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MicrometerTaggedMetricsProvider.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MicrometerTaggedMetricsProvider.java index f25ddee79f..a3dfdc2d78 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MicrometerTaggedMetricsProvider.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/metrics/MicrometerTaggedMetricsProvider.java @@ -3,7 +3,6 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; - import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -11,47 +10,59 @@ public class MicrometerTaggedMetricsProvider implements MetricsProvider { - private final MeterRegistry metrics; - - public MicrometerTaggedMetricsProvider(MeterRegistry metrics) { - this.metrics = metrics; - } - - @Override - public void counterIncrement(String topic, String key) { - counterIncrement(topic, key, new HashMap<>()); - } - - @Override - public void counterIncrement(String topic, String key, Map tags) { - tags.put("topic", topic); - metrics.counter(buildCounterName(key), Tags.of(tags.entrySet().stream() - .map(e -> Tag.of(e.getKey(), e.getValue())) - .collect(Collectors.toSet()))) - .increment(); - } - - @Override - public void timerRecord(String topic, String key, long duration, TimeUnit unit) { - Map tags = new HashMap<>(); - tags.put("topic", topic); - metrics.timer(buildCounterName(key), Tags.of(tags.entrySet().stream() - .map(e -> Tag.of(e.getKey(), e.getValue())) - .collect(Collectors.toSet()))) - .record(duration, unit); - } - - @Override - public void histogramUpdate(String topic, String key, int value) { - Map tags = new HashMap<>(); - tags.put("topic", topic); - metrics.summary(buildCounterName(key), Tags.of(tags.entrySet().stream() - .map(e -> Tag.of(e.getKey(), e.getValue())) - .collect(Collectors.toSet()))) - .record(value); - } - - private String buildCounterName(String key) { - return prefix + key; - } + private final MeterRegistry metrics; + + public MicrometerTaggedMetricsProvider(MeterRegistry metrics) { + this.metrics = metrics; + } + + @Override + public void counterIncrement(String topic, String key) { + counterIncrement(topic, key, new HashMap<>()); + } + + @Override + public void counterIncrement(String topic, String key, Map tags) { + tags.put("topic", topic); + metrics + .counter( + buildCounterName(key), + Tags.of( + tags.entrySet().stream() + .map(e -> Tag.of(e.getKey(), e.getValue())) + .collect(Collectors.toSet()))) + .increment(); + } + + @Override + public void timerRecord(String topic, String key, long duration, TimeUnit unit) { + Map tags = new HashMap<>(); + tags.put("topic", topic); + metrics + .timer( + buildCounterName(key), + Tags.of( + tags.entrySet().stream() + .map(e -> Tag.of(e.getKey(), e.getValue())) + .collect(Collectors.toSet()))) + .record(duration, unit); + } + + @Override + public void histogramUpdate(String topic, String key, int value) { + Map tags = new HashMap<>(); + tags.put("topic", topic); + metrics + .summary( + buildCounterName(key), + Tags.of( + tags.entrySet().stream() + .map(e -> Tag.of(e.getKey(), e.getValue())) + .collect(Collectors.toSet()))) + .record(value); + } + + private String buildCounterName(String key) { + return prefix + key; + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/okhttp/OkHttpHermesSender.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/okhttp/OkHttpHermesSender.java index aef4dc7272..c549bcba39 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/okhttp/OkHttpHermesSender.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/okhttp/OkHttpHermesSender.java @@ -1,5 +1,10 @@ package pl.allegro.tech.hermes.client.okhttp; +import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CompletableFuture; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; @@ -11,53 +16,48 @@ import pl.allegro.tech.hermes.client.HermesResponse; import pl.allegro.tech.hermes.client.HermesSender; -import java.io.IOException; -import java.net.URI; -import java.util.concurrent.CompletableFuture; - -import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; - public class OkHttpHermesSender implements HermesSender { - private final OkHttpClient client; + private final OkHttpClient client; - public OkHttpHermesSender(OkHttpClient client) { - this.client = client; - } + public OkHttpHermesSender(OkHttpClient client) { + this.client = client; + } - @Override - public CompletableFuture send(URI uri, HermesMessage message) { - CompletableFuture future = new CompletableFuture<>(); + @Override + public CompletableFuture send(URI uri, HermesMessage message) { + CompletableFuture future = new CompletableFuture<>(); - RequestBody body = RequestBody.create(MediaType.parse(message.getContentType()), message.getBody()); - Request.Builder builder = new Request.Builder(); - message.consumeHeaders(builder::addHeader); - Request request = builder - .post(body) - .url(uri.toString()) - .build(); + RequestBody body = + RequestBody.create(MediaType.parse(message.getContentType()), message.getBody()); + Request.Builder builder = new Request.Builder(); + message.consumeHeaders(builder::addHeader); + Request request = builder.post(body).url(uri.toString()).build(); - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { + client + .newCall(request) + .enqueue( + new Callback() { + @Override + public void onFailure(Call call, IOException e) { future.completeExceptionally(e); - } + } - @Override - public void onResponse(Call call, Response response) throws IOException { + @Override + public void onResponse(Call call, Response response) throws IOException { future.complete(fromOkHttpResponse(message, response)); - } - }); - - return future; - } - - HermesResponse fromOkHttpResponse(HermesMessage message, Response response) throws IOException { - return hermesResponse(message) - .withHeaderSupplier(response::header) - .withHttpStatus(response.code()) - .withBody(response.body().string()) - .withProtocol(response.protocol().toString()) - .build(); - } + } + }); + + return future; + } + + HermesResponse fromOkHttpResponse(HermesMessage message, Response response) throws IOException { + return hermesResponse(message) + .withHeaderSupplier(response::header) + .withHttpStatus(response.code()) + .withBody(response.body().string()) + .withProtocol(response.protocol().toString()) + .build(); + } } diff --git a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/webclient/WebClientHermesSender.java b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/webclient/WebClientHermesSender.java index a258aec589..3998f90704 100644 --- a/hermes-client/src/main/java/pl/allegro/tech/hermes/client/webclient/WebClientHermesSender.java +++ b/hermes-client/src/main/java/pl/allegro/tech/hermes/client/webclient/WebClientHermesSender.java @@ -1,5 +1,12 @@ package pl.allegro.tech.hermes.client.webclient; +import static java.util.stream.Collectors.toMap; +import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; + +import java.net.URI; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; import org.springframework.web.reactive.function.client.WebClient; import pl.allegro.tech.hermes.client.HermesMessage; import pl.allegro.tech.hermes.client.HermesResponse; @@ -7,53 +14,56 @@ import pl.allegro.tech.hermes.client.ReactiveHermesSender; import reactor.core.publisher.Mono; -import java.net.URI; -import java.util.Map; -import java.util.TreeMap; -import java.util.concurrent.CompletableFuture; +public class WebClientHermesSender implements HermesSender, ReactiveHermesSender { -import static java.util.stream.Collectors.toMap; -import static pl.allegro.tech.hermes.client.HermesResponseBuilder.hermesResponse; + private static final Mono NO_BODY = Mono.just(""); + private final WebClient webClient; -public class WebClientHermesSender implements HermesSender, ReactiveHermesSender { + public WebClientHermesSender(WebClient webClient) { + this.webClient = webClient; + } - private static final Mono NO_BODY = Mono.just(""); - private final WebClient webClient; - - public WebClientHermesSender(WebClient webClient) { - this.webClient = webClient; - } - - @Override - public Mono sendReactively(URI uri, HermesMessage message) { - return webClient.post() - .uri(uri) - .syncBody(message.getBody()) - .headers(httpHeaders -> httpHeaders.setAll(message.getHeaders())) - .exchange() - .flatMap(response -> response - .bodyToMono(String.class) - .switchIfEmpty(NO_BODY) - .map(body -> hermesResponse(message) + @Override + public Mono sendReactively(URI uri, HermesMessage message) { + return webClient + .post() + .uri(uri) + .syncBody(message.getBody()) + .headers(httpHeaders -> httpHeaders.setAll(message.getHeaders())) + .exchange() + .flatMap( + response -> + response + .bodyToMono(String.class) + .switchIfEmpty(NO_BODY) + .map( + body -> + hermesResponse(message) .withBody(body) .withHttpStatus(response.statusCode().value()) - .withHeaderSupplier(header -> - convertToCaseInsensitiveMap(response.headers().asHttpHeaders().toSingleValueMap()).get(header)) + .withHeaderSupplier( + header -> + convertToCaseInsensitiveMap( + response + .headers() + .asHttpHeaders() + .toSingleValueMap()) + .get(header)) .build())); - } - - @Override - public CompletableFuture send(URI uri, HermesMessage message) { - return sendReactively(uri, message).toFuture(); - } - - private TreeMap convertToCaseInsensitiveMap(Map hashMap) { - return hashMap.entrySet().stream() - .collect(toMap( - Map.Entry::getKey, - Map.Entry::getValue, - (oldVal, newVal) -> newVal, - () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER) - )); - } + } + + @Override + public CompletableFuture send(URI uri, HermesMessage message) { + return sendReactively(uri, message).toFuture(); + } + + private TreeMap convertToCaseInsensitiveMap(Map hashMap) { + return hashMap.entrySet().stream() + .collect( + toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (oldVal, newVal) -> newVal, + () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER))); + } } diff --git a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMetricsTest.groovy b/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMetricsTest.groovy deleted file mode 100644 index c608c1c5cc..0000000000 --- a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMetricsTest.groovy +++ /dev/null @@ -1,207 +0,0 @@ -package pl.allegro.tech.hermes.client - -import com.codahale.metrics.MetricRegistry -import pl.allegro.tech.hermes.client.metrics.DropwizardMetricsProvider -import pl.allegro.tech.hermes.client.metrics.MetricsProvider -import spock.lang.Specification - -import java.time.Duration -import java.time.temporal.ChronoUnit -import java.util.concurrent.CompletableFuture - -import static java.util.concurrent.CompletableFuture.completedFuture -import static pl.allegro.tech.hermes.client.HermesClientBuilder.hermesClient - -class HermesClientMetricsTest extends Specification { - - private MetricRegistry metrics = new MetricRegistry() - private MetricsProvider metricsProvider = new DropwizardMetricsProvider(metrics) - - def "should measure publish latency"() { - given: - HermesClient client = hermesClient(delayedHermesSender(Duration.ofMillis(100))) - .withRetrySleep(0) - .withMetrics(metricsProvider).build() - - when: - client.publish("com.group.topic", "123").join() - - then: - metrics.counter("hermes-client.com_group.topic.status.201").count == 1 - metrics.timer("hermes-client.com_group.topic.latency").getSnapshot().getMax() >= Duration.ofMillis(100).get(ChronoUnit.NANOS) - } - - def "should close timer on exceptional completion and log failure metric"() { - given: - HermesClient client = hermesClient({uri, msg -> failingFuture(new RuntimeException())}) - .withRetrySleep(0) - .withRetries(3) - .withMetrics(metricsProvider).build() - - when: - silence({ client.publish("com.group.topic", "123").join() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 4 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 3 - } - - def "should update max retries exceeded metric"() { - given: - HermesClient client = hermesClient({uri, msg -> failingFuture(new RuntimeException())}) - .withRetrySleep(0) - .withRetries(3) - .withMetrics(metricsProvider).build() - - when: - silence({ client.publish("com.group.topic", "123").join() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.retries.count").count == 3 - metrics.counter("hermes-client.com_group.topic.retries.exhausted").count == 1 - metrics.counter("hermes-client.com_group.topic.retries.success").count == 0 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().size() == 0 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 1 - } - - def "should update retries metrics"() { - given: - HermesClient client1 = hermesClient(failingHermesSender(2)) - .withRetrySleep(0) - .withRetries(4) - .withMetrics(metricsProvider).build() - - HermesClient client2 = hermesClient(failingHermesSender(5)) - .withRetrySleep(0) - .withRetries(6) - .withMetrics(metricsProvider).build() - - HermesClient client3 = hermesClient(failingHermesSender(3)) - .withRetrySleep(0) - .withRetries(2) - .withMetrics(metricsProvider).build() - - when: - silence({ client1.publish("com.group.topic", "123").join() }) - silence({ client2.publish("com.group.topic", "456").join() }) - silence({ client3.publish("com.group.topic", "789").join() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 10 - metrics.counter("hermes-client.com_group.topic.retries.exhausted").count == 1 - metrics.counter("hermes-client.com_group.topic.retries.success").count == 2 - metrics.counter("hermes-client.com_group.topic.retries.count").count == 9 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().getMin() == 2 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().getMax() == 5 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 2 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 10 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 2 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 9 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 3 - } - - def "should update failure metrics when there is an application-level error"() { - given: - HermesClient client1 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(4) - .withMetrics(metricsProvider).build() - - HermesClient client2 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(6) - .withMetrics(metricsProvider).build() - - HermesClient client3 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(2) - .withMetrics(metricsProvider).build() - - when: - silence({ client1.publish("com.group.topic", "123").join() }) - silence({ client2.publish("com.group.topic", "456").join() }) - silence({ client3.publish("com.group.topic", "789").join() }) - - then: - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 0 - } - - private CompletableFuture successFuture(HermesMessage message) { - return completedFuture(HermesResponseBuilder.hermesResponse(message).withHttpStatus(201).build()) - } - - private CompletableFuture badRequestFuture(HermesMessage message) { - return completedFuture(HermesResponseBuilder.hermesResponse(message).withHttpStatus(400).build()) - } - - private CompletableFuture failingFuture(Throwable throwable) { - CompletableFuture future = new CompletableFuture<>() - future.completeExceptionally(throwable) - return future - } - - private HermesSender failingHermesSender(int errorNo) { - new HermesSender() { - int i = 0 - @Override - CompletableFuture send(URI uri, HermesMessage message) { - i++ - if (i <= errorNo) { - return failingFuture(new RuntimeException()) - } - return successFuture(message) - } - } - } - - private HermesSender delayedHermesSender(Duration sendLatencyMs) { - new HermesSender() { - @Override - CompletableFuture send(URI uri, HermesMessage message) { - Thread.sleep(sendLatencyMs.toMillis()) - return successFuture(message) - } - } - } - - private HermesSender badRequestHermesSender() { - new HermesSender() { - @Override - CompletableFuture send(URI uri, HermesMessage message) { - return badRequestFuture(message) - } - } - } - - private void silence(Runnable runnable) { - try { - runnable.run() - } catch (Exception ex) { - // do nothing - } - } - -} diff --git a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMicrometerTaggedMetricsTest.groovy b/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMicrometerTaggedMetricsTest.groovy index d7175066ec..05b760eea9 100644 --- a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMicrometerTaggedMetricsTest.groovy +++ b/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/HermesClientMicrometerTaggedMetricsTest.groovy @@ -7,7 +7,6 @@ import pl.allegro.tech.hermes.client.metrics.MicrometerTaggedMetricsProvider import spock.lang.Specification import java.time.Duration -import java.time.temporal.ChronoUnit import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -31,8 +30,8 @@ class HermesClientMicrometerTaggedMetricsTest extends Specification { then: metrics.counter("hermes-client.status", "code", String.valueOf(201), "topic", "com_group.topic").count() == 1 def timer = metrics.timer("hermes-client.latency", "topic", "com_group.topic") - timer.totalTime(TimeUnit.NANOSECONDS) >= Duration.ofMillis(100).get(ChronoUnit.NANOS) - timer.totalTime(TimeUnit.NANOSECONDS) < Duration.ofMillis(300).get(ChronoUnit.NANOS) + timer.totalTime(TimeUnit.NANOSECONDS) >= Duration.ofMillis(100).toNanos() + timer.totalTime(TimeUnit.NANOSECONDS) < Duration.ofMillis(1000).toNanos() } def "should close timer on exceptional completion and log failure metric"() { diff --git a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/ReactiveHermesClientMetricsTest.groovy b/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/ReactiveHermesClientMetricsTest.groovy deleted file mode 100644 index c1993857dd..0000000000 --- a/hermes-client/src/test/groovy/pl/allegro/tech/hermes/client/ReactiveHermesClientMetricsTest.groovy +++ /dev/null @@ -1,205 +0,0 @@ -package pl.allegro.tech.hermes.client - -import com.codahale.metrics.MetricRegistry -import pl.allegro.tech.hermes.client.metrics.DropwizardMetricsProvider -import pl.allegro.tech.hermes.client.metrics.MetricsProvider -import reactor.core.publisher.Mono -import spock.lang.Specification - -import java.time.Duration -import java.time.temporal.ChronoUnit - -import static pl.allegro.tech.hermes.client.ReactiveHermesClientBuilder.hermesClient - -class ReactiveHermesClientMetricsTest extends Specification { - - private MetricRegistry metrics = new MetricRegistry() - private MetricsProvider metricsProvider = new DropwizardMetricsProvider(metrics) - - def "should measure publish latency"() { - given: - ReactiveHermesClient client = hermesClient(delayedHermesSender(Duration.ofMillis(100))) - .withRetrySleep(0) - .withMetrics(metricsProvider).build() - - when: - client.publish("com.group.topic", "123").block() - - then: - metrics.counter("hermes-client.com_group.topic.status.201").count == 1 - metrics.timer("hermes-client.com_group.topic.latency").getSnapshot().getMax() >= Duration.ofMillis(100).get(ChronoUnit.NANOS) - metrics.timer("hermes-client.com_group.topic.latency").getSnapshot().getMax() < Duration.ofMillis(300).get(ChronoUnit.NANOS) - } - - def "should close timer on exceptional completion and log failure metric"() { - given: - ReactiveHermesClient client = hermesClient({uri, msg -> failingMono(new RuntimeException())}) - .withRetrySleep(0) - .withRetries(3) - .withMetrics(metricsProvider).build() - - when: - silence({ client.publish("com.group.topic", "123").block() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 4 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 3 - } - - def "should update max retries exceeded metric"() { - given: - ReactiveHermesClient client = hermesClient({uri, msg -> failingMono(new RuntimeException())}) - .withRetrySleep(0) - .withRetries(3) - .withMetrics(metricsProvider).build() - - when: - silence({ client.publish("com.group.topic", "123").block() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.retries.count").count == 3 - metrics.counter("hermes-client.com_group.topic.retries.exhausted").count == 1 - metrics.counter("hermes-client.com_group.topic.retries.success").count == 0 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().size() == 0 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 4 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 1 - } - - def "should update retries metrics"() { - given: - ReactiveHermesClient client1 = hermesClient(failingHermesSender(2)) - .withRetrySleep(0) - .withRetries(4) - .withMetrics(metricsProvider).build() - - ReactiveHermesClient client2 = hermesClient(failingHermesSender(5)) - .withRetrySleep(0) - .withRetries(6) - .withMetrics(metricsProvider).build() - - ReactiveHermesClient client3 = hermesClient(failingHermesSender(3)) - .withRetrySleep(0) - .withRetries(2) - .withMetrics(metricsProvider).build() - - when: - silence({ client1.publish("com.group.topic", "123").block() }) - silence({ client2.publish("com.group.topic", "456").block() }) - silence({ client3.publish("com.group.topic", "789").block() }) - - then: - metrics.counter("hermes-client.com_group.topic.failure").count == 10 - metrics.counter("hermes-client.com_group.topic.retries.exhausted").count == 1 - metrics.counter("hermes-client.com_group.topic.retries.success").count == 2 - metrics.counter("hermes-client.com_group.topic.retries.count").count == 9 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().getMin() == 2 - metrics.histogram("hermes-client.com_group.topic.retries.attempts").getSnapshot().getMax() == 5 - metrics.timers.containsKey("hermes-client.com_group.topic.latency") - - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 2 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 1 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 10 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 2 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 9 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 3 - } - - def "should update failure metrics when there is an application-level error"() { - given: - ReactiveHermesClient client1 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(4) - .withMetrics(metricsProvider).build() - - ReactiveHermesClient client2 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(6) - .withMetrics(metricsProvider).build() - - ReactiveHermesClient client3 = hermesClient(badRequestHermesSender()) - .withRetrySleep(0) - .withRetries(2) - .withMetrics(metricsProvider).build() - - when: - silence({ client1.publish("com.group.topic", "123").block() }) - silence({ client2.publish("com.group.topic", "456").block() }) - silence({ client3.publish("com.group.topic", "789").block() }) - - then: - metrics.counter("hermes-client.com_group.topic.publish.finally.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.finally.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.failure").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.attempt").count == 3 - metrics.counter("hermes-client.com_group.topic.publish.retry.success").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.failure").count == 0 - metrics.counter("hermes-client.com_group.topic.publish.retry.attempt").count == 0 - } - - private Mono successMono(HermesMessage message) { - return Mono.just(HermesResponseBuilder.hermesResponse(message).withHttpStatus(201).build()) - } - - private Mono badRequestMono(HermesMessage message) { - return Mono.just(HermesResponseBuilder.hermesResponse(message).withHttpStatus(400).build()) - } - - private Mono failingMono(Throwable throwable) { - return Mono.error(throwable) - } - - private ReactiveHermesSender failingHermesSender(int errorNo) { - new ReactiveHermesSender() { - int i = 0 - @Override - Mono sendReactively(URI uri, HermesMessage message) { - i++ - if (i <= errorNo) { - return failingMono(new RuntimeException()) - } - return successMono(message) - } - } - } - - private ReactiveHermesSender delayedHermesSender(Duration sendLatencyMs) { - new ReactiveHermesSender() { - @Override - Mono sendReactively(URI uri, HermesMessage message) { - Thread.sleep(sendLatencyMs.toMillis()) - return successMono(message) - } - } - } - - private ReactiveHermesSender badRequestHermesSender() { - new ReactiveHermesSender() { - @Override - Mono sendReactively(URI uri, HermesMessage message) { - return badRequestMono(message) - } - } - } - - private void silence(Runnable runnable) { - try { - runnable.run() - } catch (Exception ex) { - // do nothing - } - } - -} diff --git a/hermes-client/src/test/resources/allure.properties b/hermes-client/src/test/resources/allure.properties new file mode 100644 index 0000000000..8c70baec64 --- /dev/null +++ b/hermes-client/src/test/resources/allure.properties @@ -0,0 +1 @@ +allure.results.directory=../build/allure-results diff --git a/hermes-common/build.gradle b/hermes-common/build.gradle index ccf959276b..d1e850d4d6 100644 --- a/hermes-common/build.gradle +++ b/hermes-common/build.gradle @@ -25,23 +25,21 @@ dependencies { api group: 'tech.allegro.schema.json2avro', name: 'converter', version: versions.json2avro api group: 'org.apache.commons', name: 'commons-collections4', version: '4.4' - implementation group: 'commons-codec', name: 'commons-codec', version: '1.9' + implementation group: 'commons-codec', name: 'commons-codec', version: '1.16.1' implementation group: 'com.google.guava', name: 'guava', version: versions.guava api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: versions.jackson api group: 'org.apache.avro', name: 'avro', version: versions.avro - api group: 'com.jayway.jsonpath', name: 'json-path', version: '2.5.0' + api group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0' - implementation group: 'io.dropwizard.metrics', name: 'metrics-graphite', version: versions.dropwizard_metrics - implementation group: 'io.dropwizard.metrics', name: 'metrics-jvm', version: versions.dropwizard_metrics - implementation group: 'org.mpierce.metrics.reservoir', name: 'hdrhistogram-metrics-reservoir', version: '1.1.0' + implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: versions.dropwizard_metrics - implementation group: 'com.google.code.findbugs', name: 'annotations', version: '3.0.0' + implementation group: 'com.google.code.findbugs', name: 'annotations', version: '3.0.1' api group: 'io.micrometer', name: 'micrometer-core', version: versions.micrometer_metrics api group: 'io.micrometer', name: 'micrometer-registry-prometheus', version: versions.micrometer_metrics - implementation group: 'org.slf4j', name: 'log4j-over-slf4j', version: '2.0.4' - implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.7' + implementation group: 'org.slf4j', name: 'log4j-over-slf4j', version: '2.0.13' + implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.14' api(group: 'org.apache.kafka', name: 'kafka-clients', version: versions.kafka) { exclude group: 'net.sf.jopt-simple' } @@ -50,12 +48,10 @@ dependencies { testImplementation project(':hermes-test-helper') - testImplementation group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + testImplementation group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '6.0.0' testImplementation group: 'org.spockframework', name: 'spock-core', version: versions.spock testImplementation group: 'org.spockframework', name: 'spock-junit4', version: versions.spock - testImplementation (group: 'com.jayway.awaitility', name: 'awaitility-groovy', version: '1.7.0') { - exclude group: 'org.codehaus.groovy', module: 'groovy-all' - } + testImplementation group: 'org.awaitility', name: 'awaitility-groovy', version: '4.2.1' testRuntimeOnly group: 'org.junit.vintage', name: 'junit-vintage-engine', version: versions.junit_jupiter } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminOperationsCallback.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminOperationsCallback.java index 3a8c08760f..0f418e9c0c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminOperationsCallback.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminOperationsCallback.java @@ -3,5 +3,5 @@ import pl.allegro.tech.hermes.api.SubscriptionName; public interface AdminOperationsCallback { - void onRetransmissionStarts(SubscriptionName subscription) throws Exception; + void onRetransmissionStarts(SubscriptionName subscription) throws Exception; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminTool.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminTool.java index 033e58ca3f..7120793822 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminTool.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminTool.java @@ -4,9 +4,9 @@ public interface AdminTool { - void retransmit(SubscriptionName subscriptionName); + void retransmit(SubscriptionName subscriptionName); - enum Operations { - RETRANSMIT - } + enum Operations { + RETRANSMIT + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminToolStartupException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminToolStartupException.java index 4a053eb2b4..d3b257cfc2 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminToolStartupException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/AdminToolStartupException.java @@ -2,8 +2,7 @@ public class AdminToolStartupException extends RuntimeException { - public AdminToolStartupException(Throwable cause) { - super(cause); - } - + public AdminToolStartupException(Throwable cause) { + super(cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminCache.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminCache.java index 0ea419fe50..9bab864f80 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminCache.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminCache.java @@ -1,6 +1,11 @@ package pl.allegro.tech.hermes.common.admin.zookeeper; +import static pl.allegro.tech.hermes.common.admin.AdminTool.Operations.RETRANSMIT; + import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Clock; +import java.util.ArrayList; +import java.util.List; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; @@ -9,56 +14,52 @@ import pl.allegro.tech.hermes.common.admin.AdminOperationsCallback; import pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperPaths; -import java.time.Clock; -import java.util.ArrayList; -import java.util.List; - -import static pl.allegro.tech.hermes.common.admin.AdminTool.Operations.RETRANSMIT; - public class ZookeeperAdminCache extends PathChildrenCache implements PathChildrenCacheListener { - private final ObjectMapper objectMapper; - private final List adminCallbacks = new ArrayList<>(); + private final ObjectMapper objectMapper; + private final List adminCallbacks = new ArrayList<>(); - private final long initializationTime; + private final long initializationTime; - public ZookeeperAdminCache(ZookeeperPaths zookeeperPaths, - CuratorFramework client, - ObjectMapper objectMapper, - Clock clock) { - super(client, zookeeperPaths.adminPath(), true); - this.objectMapper = objectMapper; - this.initializationTime = clock.millis(); - getListenable().addListener(this); - } + public ZookeeperAdminCache( + ZookeeperPaths zookeeperPaths, + CuratorFramework client, + ObjectMapper objectMapper, + Clock clock) { + super(client, zookeeperPaths.adminPath(), true); + this.objectMapper = objectMapper; + this.initializationTime = clock.millis(); + getListenable().addListener(this); + } - @Override - public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { - switch (event.getType()) { - case CHILD_UPDATED: - case CHILD_ADDED: - if (event.getData().getPath().contains(RETRANSMIT.name()) && isYoungerThanThisNode(event)) { - retransmit(event); - } - break; - default: - break; + @Override + public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { + switch (event.getType()) { + case CHILD_UPDATED: + case CHILD_ADDED: + if (event.getData().getPath().contains(RETRANSMIT.name()) && isYoungerThanThisNode(event)) { + retransmit(event); } + break; + default: + break; } + } - private boolean isYoungerThanThisNode(PathChildrenCacheEvent event) { - return event.getData().getStat().getMtime() > initializationTime; - } + private boolean isYoungerThanThisNode(PathChildrenCacheEvent event) { + return event.getData().getStat().getMtime() > initializationTime; + } - private void retransmit(PathChildrenCacheEvent event) throws Exception { - SubscriptionName subscriptionName = objectMapper.readValue(event.getData().getData(), SubscriptionName.class); + private void retransmit(PathChildrenCacheEvent event) throws Exception { + SubscriptionName subscriptionName = + objectMapper.readValue(event.getData().getData(), SubscriptionName.class); - for (AdminOperationsCallback adminCallback : adminCallbacks) { - adminCallback.onRetransmissionStarts(subscriptionName); - } + for (AdminOperationsCallback adminCallback : adminCallbacks) { + adminCallback.onRetransmissionStarts(subscriptionName); } + } - public void addCallback(AdminOperationsCallback callback) { - adminCallbacks.add(callback); - } + public void addCallback(AdminOperationsCallback callback) { + adminCallbacks.add(callback); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminTool.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminTool.java index b387c5e2f6..aa2aca00a9 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminTool.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/admin/zookeeper/ZookeeperAdminTool.java @@ -1,5 +1,7 @@ package pl.allegro.tech.hermes.common.admin.zookeeper; +import static pl.allegro.tech.hermes.common.admin.AdminTool.Operations.RETRANSMIT; + import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.curator.framework.CuratorFramework; import org.apache.zookeeper.CreateMode; @@ -8,35 +10,35 @@ import pl.allegro.tech.hermes.common.exception.RetransmissionException; import pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperPaths; -import static pl.allegro.tech.hermes.common.admin.AdminTool.Operations.RETRANSMIT; - public class ZookeeperAdminTool implements AdminTool { - private final ZookeeperPaths zookeeperPaths; - private final CuratorFramework curatorFramework; - private final ObjectMapper objectMapper; - - public ZookeeperAdminTool(ZookeeperPaths zookeeperPaths, CuratorFramework curatorFramework, - ObjectMapper objectMapper) { - this.zookeeperPaths = zookeeperPaths; - this.curatorFramework = curatorFramework; - this.objectMapper = objectMapper; - } - - @Override - public void retransmit(SubscriptionName subscriptionName) { - try { - executeAdminOperation(subscriptionName, RETRANSMIT.name()); - } catch (Exception e) { - throw new RetransmissionException(e); - } + private final ZookeeperPaths zookeeperPaths; + private final CuratorFramework curatorFramework; + private final ObjectMapper objectMapper; + + public ZookeeperAdminTool( + ZookeeperPaths zookeeperPaths, CuratorFramework curatorFramework, ObjectMapper objectMapper) { + this.zookeeperPaths = zookeeperPaths; + this.curatorFramework = curatorFramework; + this.objectMapper = objectMapper; + } + + @Override + public void retransmit(SubscriptionName subscriptionName) { + try { + executeAdminOperation(subscriptionName, RETRANSMIT.name()); + } catch (Exception e) { + throw new RetransmissionException(e); } + } - private void executeAdminOperation(SubscriptionName subscriptionName, String name) throws Exception { - String path = zookeeperPaths.adminOperationPath(name); + private void executeAdminOperation(SubscriptionName subscriptionName, String name) + throws Exception { + String path = zookeeperPaths.adminOperationPath(name); - curatorFramework.create() - .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) - .forPath(path, objectMapper.writeValueAsBytes(subscriptionName)); - } + curatorFramework + .create() + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) + .forPath(path, objectMapper.writeValueAsBytes(subscriptionName)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerDetails.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerDetails.java index 6a2444f78e..c1d393ee2f 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerDetails.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerDetails.java @@ -2,19 +2,19 @@ public class BrokerDetails { - private String host; - private int port; + private String host; + private int port; - public BrokerDetails(String host, int port) { - this.host = host; - this.port = port; - } + public BrokerDetails(String host, int port) { + this.host = host; + this.port = port; + } - public String getHost() { - return host; - } + public String getHost() { + return host; + } - public int getPort() { - return port; - } + public int getPort() { + return port; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerStorage.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerStorage.java index fb636dd956..9051919ca4 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerStorage.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/BrokerStorage.java @@ -1,12 +1,11 @@ package pl.allegro.tech.hermes.common.broker; -import org.apache.kafka.common.TopicPartition; - import java.util.List; +import org.apache.kafka.common.TopicPartition; public interface BrokerStorage { - int readLeaderForPartition(TopicPartition topicAndPartition); + int readLeaderForPartition(TopicPartition topicAndPartition); - List readPartitionsIds(String topicName); + List readPartitionsIds(String topicName); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/KafkaBrokerStorage.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/KafkaBrokerStorage.java index e051d64ec6..e75a225593 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/KafkaBrokerStorage.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/broker/KafkaBrokerStorage.java @@ -1,6 +1,9 @@ package pl.allegro.tech.hermes.common.broker; import com.google.common.collect.Ordering; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import org.apache.kafka.clients.admin.AdminClient; import org.apache.kafka.clients.admin.TopicDescription; import org.apache.kafka.common.KafkaFuture; @@ -9,50 +12,50 @@ import pl.allegro.tech.hermes.common.exception.BrokerNotFoundForPartitionException; import pl.allegro.tech.hermes.common.exception.PartitionsNotFoundForGivenTopicException; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - public class KafkaBrokerStorage implements BrokerStorage { - private final AdminClient kafkaAdminClient; - - public KafkaBrokerStorage(AdminClient kafkaAdminClient) { - this.kafkaAdminClient = kafkaAdminClient; - } - - @Override - public int readLeaderForPartition(TopicPartition topicAndPartition) { - try { - return describeTopic(topicAndPartition.topic()) - .thenApply(description -> description.partitions().get(topicAndPartition.partition()).leader().id()) - .get(); - } catch (Exception exception) { - throw new BrokerNotFoundForPartitionException(topicAndPartition.topic(), topicAndPartition.partition(), exception); - } + private final AdminClient kafkaAdminClient; + + public KafkaBrokerStorage(AdminClient kafkaAdminClient) { + this.kafkaAdminClient = kafkaAdminClient; + } + + @Override + public int readLeaderForPartition(TopicPartition topicAndPartition) { + try { + return describeTopic(topicAndPartition.topic()) + .thenApply( + description -> + description.partitions().get(topicAndPartition.partition()).leader().id()) + .get(); + } catch (Exception exception) { + throw new BrokerNotFoundForPartitionException( + topicAndPartition.topic(), topicAndPartition.partition(), exception); } + } - @Override - public List readPartitionsIds(String topicName) { - try { - List partitions = describeTopic(topicName) - .thenApply(this::resolvePartitionIds) - .get(); - - return Ordering.natural().sortedCopy(partitions); - } catch (Exception exception) { - throw new PartitionsNotFoundForGivenTopicException(topicName, exception); - } - } - - private KafkaFuture describeTopic(String topic) { - return kafkaAdminClient.describeTopics(Collections.singletonList(topic)).all() - .thenApply(topicsMap -> topicsMap.get(topic)); - } + @Override + public List readPartitionsIds(String topicName) { + try { + List partitions = + describeTopic(topicName).thenApply(this::resolvePartitionIds).get(); - private List resolvePartitionIds(TopicDescription topicDescription) { - return topicDescription.partitions().stream() - .map(TopicPartitionInfo::partition) - .collect(Collectors.toList()); + return Ordering.natural().sortedCopy(partitions); + } catch (Exception exception) { + throw new PartitionsNotFoundForGivenTopicException(topicName, exception); } + } + + private KafkaFuture describeTopic(String topic) { + return kafkaAdminClient + .describeTopics(Collections.singletonList(topic)) + .all() + .thenApply(topicsMap -> topicsMap.get(topic)); + } + + private List resolvePartitionIds(TopicDescription topicDescription) { + return topicDescription.partitions().stream() + .map(TopicPartitionInfo::partition) + .collect(Collectors.toList()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/cache/queue/LinkedHashSetBlockingQueue.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/cache/queue/LinkedHashSetBlockingQueue.java index b061eed052..eaa90f8d51 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/cache/queue/LinkedHashSetBlockingQueue.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/cache/queue/LinkedHashSetBlockingQueue.java @@ -1,23 +1,19 @@ package pl.allegro.tech.hermes.common.cache.queue; /** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://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 + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT 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 java.util.AbstractQueue; import java.util.Collection; import java.util.Iterator; @@ -32,391 +28,357 @@ * A blocking queue implementation backed by a linked hash set for predictable iteration order and * constant time addition, removal and contains operations. * - * Author: Sebastian Schaffert - * Project: apache.marmotta + *

Author: Sebastian Schaffert Project: apache.marmotta */ public class LinkedHashSetBlockingQueue extends AbstractQueue implements BlockingQueue { - private int capacity = Integer.MAX_VALUE; - - /** Current number of elements */ - private final AtomicInteger count = new AtomicInteger(0); - - /** Lock held by take, poll, etc */ - private final ReentrantLock takeLock = new ReentrantLock(); - - /** Wait queue for waiting takes */ - private final Condition notEmpty = takeLock.newCondition(); - - /** Lock held by put, offer, etc */ - private final ReentrantLock putLock = new ReentrantLock(); - - /** Wait queue for waiting puts */ - private final Condition notFull = putLock.newCondition(); - - private final LinkedHashSet delegate; - - public LinkedHashSetBlockingQueue() { - delegate = new LinkedHashSet(); + private int capacity = Integer.MAX_VALUE; + + /** Current number of elements */ + private final AtomicInteger count = new AtomicInteger(0); + + /** Lock held by take, poll, etc */ + private final ReentrantLock takeLock = new ReentrantLock(); + + /** Wait queue for waiting takes */ + private final Condition notEmpty = takeLock.newCondition(); + + /** Lock held by put, offer, etc */ + private final ReentrantLock putLock = new ReentrantLock(); + + /** Wait queue for waiting puts */ + private final Condition notFull = putLock.newCondition(); + + private final LinkedHashSet delegate; + + public LinkedHashSetBlockingQueue() { + delegate = new LinkedHashSet(); + } + + public LinkedHashSetBlockingQueue(int capacity) { + this.delegate = new LinkedHashSet(capacity); + this.capacity = capacity; + } + + @Override + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + final AtomicInteger count = this.count; + if (count.get() == capacity) return false; + int c = -1; + final ReentrantLock putLock = this.putLock; + putLock.lock(); + try { + if (count.get() < capacity) { + final boolean wasAdded = enqueue(e); + c = wasAdded ? count.getAndIncrement() : count.get(); + if (c + 1 < capacity) notFull.signal(); + } + } finally { + putLock.unlock(); } - - public LinkedHashSetBlockingQueue(int capacity) { - this.delegate = new LinkedHashSet(capacity); - this.capacity = capacity; + if (c == 0) signalNotEmpty(); + return c >= 0; + } + + @Override + public void put(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); + + int c = -1; + final ReentrantLock putLock = this.putLock; + final AtomicInteger count = this.count; + putLock.lockInterruptibly(); + try { + while (count.get() == capacity) { + notFull.await(); + } + final boolean wasAdded = enqueue(e); + c = wasAdded ? count.getAndIncrement() : count.get(); + if (c + 1 < capacity) notFull.signal(); + } finally { + putLock.unlock(); } - - @Override - public boolean offer(E e) { - if (e == null) throw new NullPointerException(); - final AtomicInteger count = this.count; - if (count.get() == capacity) - return false; - int c = -1; - final ReentrantLock putLock = this.putLock; - putLock.lock(); - try { - if (count.get() < capacity) { - final boolean wasAdded = enqueue(e); - c = wasAdded?count.getAndIncrement():count.get(); - if (c + 1 < capacity) - notFull.signal(); - } - } finally { - putLock.unlock(); - } - if (c == 0) - signalNotEmpty(); - return c >= 0; + if (c == 0) signalNotEmpty(); + } + + @Override + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + if (e == null) throw new NullPointerException(); + long nanos = unit.toNanos(timeout); + int c = -1; + final ReentrantLock putLock = this.putLock; + final AtomicInteger count = this.count; + putLock.lockInterruptibly(); + try { + while (count.get() == capacity) { + + if (nanos <= 0) return false; + nanos = notFull.awaitNanos(nanos); + } + final boolean wasAdded = enqueue(e); + c = wasAdded ? count.getAndIncrement() : count.get(); + if (c + 1 < capacity) notFull.signal(); + } finally { + putLock.unlock(); } - - @Override - public void put(E e) throws InterruptedException { - if (e == null) throw new NullPointerException(); - - int c = -1; - final ReentrantLock putLock = this.putLock; - final AtomicInteger count = this.count; - putLock.lockInterruptibly(); - try { - while (count.get() == capacity) { - notFull.await(); - } - final boolean wasAdded = enqueue(e); - c = wasAdded?count.getAndIncrement():count.get(); - if (c + 1 < capacity) - notFull.signal(); - } finally { - putLock.unlock(); - } - if (c == 0) - signalNotEmpty(); + if (c == 0) signalNotEmpty(); + return true; + } + + @Override + public E take() throws InterruptedException { + E x; + int c = -1; + final AtomicInteger count = this.count; + final ReentrantLock takeLock = this.takeLock; + takeLock.lockInterruptibly(); + try { + while (count.get() == 0) { + notEmpty.await(); + } + x = dequeue(); + c = count.getAndDecrement(); + if (c > 1) notEmpty.signal(); + } finally { + takeLock.unlock(); } - - @Override - public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (e == null) throw new NullPointerException(); - long nanos = unit.toNanos(timeout); - int c = -1; - final ReentrantLock putLock = this.putLock; - final AtomicInteger count = this.count; - putLock.lockInterruptibly(); - try { - while (count.get() == capacity) { - - if (nanos <= 0) - return false; - nanos = notFull.awaitNanos(nanos); - } - final boolean wasAdded = enqueue(e); - c = wasAdded?count.getAndIncrement():count.get(); - if (c + 1 < capacity) - notFull.signal(); - } finally { - putLock.unlock(); - } - if (c == 0) - signalNotEmpty(); - return true; + if (c == capacity) signalNotFull(); + return x; + } + + @Override + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + E x = null; + int c = -1; + long nanos = unit.toNanos(timeout); + final AtomicInteger count = this.count; + final ReentrantLock takeLock = this.takeLock; + takeLock.lockInterruptibly(); + try { + while (count.get() == 0) { + if (nanos <= 0) return null; + nanos = notEmpty.awaitNanos(nanos); + } + x = dequeue(); + c = count.getAndDecrement(); + if (c > 1) notEmpty.signal(); + } finally { + takeLock.unlock(); } - - @Override - public E take() throws InterruptedException { - E x; - int c = -1; - final AtomicInteger count = this.count; - final ReentrantLock takeLock = this.takeLock; - takeLock.lockInterruptibly(); - try { - while (count.get() == 0) { - notEmpty.await(); - } - x = dequeue(); - c = count.getAndDecrement(); - if (c > 1) - notEmpty.signal(); - } finally { - takeLock.unlock(); - } - if (c == capacity) - signalNotFull(); - return x; + if (c == capacity) signalNotFull(); + return x; + } + + @Override + public int remainingCapacity() { + return Integer.MAX_VALUE - size(); + } + + @Override + public int drainTo(Collection c) { + return drainTo(c, Integer.MAX_VALUE); + } + + @Override + public int drainTo(Collection c, int maxElements) { + if (c == null) throw new NullPointerException(); + if (c == this) throw new IllegalArgumentException(); + boolean signalNotFull = false; + final ReentrantLock takeLock = this.takeLock; + takeLock.lock(); + try { + int n = Math.min(maxElements, count.get()); + Iterator it = delegate.iterator(); + for (int i = 0; i < n && it.hasNext(); i++) { + E x = it.next(); + c.add(x); + } + count.getAndAdd(-n); + return n; + } finally { + takeLock.unlock(); + if (signalNotFull) signalNotFull(); } - - @Override - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - E x = null; - int c = -1; - long nanos = unit.toNanos(timeout); - final AtomicInteger count = this.count; - final ReentrantLock takeLock = this.takeLock; - takeLock.lockInterruptibly(); - try { - while (count.get() == 0) { - if (nanos <= 0) - return null; - nanos = notEmpty.awaitNanos(nanos); - } - x = dequeue(); - c = count.getAndDecrement(); - if (c > 1) - notEmpty.signal(); - } finally { - takeLock.unlock(); - } - if (c == capacity) - signalNotFull(); - return x; + } + + @Override + public E poll() { + final AtomicInteger count = this.count; + if (count.get() == 0) return null; + E x = null; + int c = -1; + final ReentrantLock takeLock = this.takeLock; + takeLock.lock(); + try { + if (count.get() > 0) { + x = dequeue(); + c = count.getAndDecrement(); + if (c > 1) notEmpty.signal(); + } + } finally { + takeLock.unlock(); } - - @Override - public int remainingCapacity() { - return Integer.MAX_VALUE - size(); + if (c == capacity) signalNotFull(); + return x; + } + + @Override + public E peek() { + if (count.get() == 0) return null; + final ReentrantLock takeLock = this.takeLock; + takeLock.lock(); + try { + Iterator it = delegate.iterator(); + if (it.hasNext()) { + return it.next(); + } else { + return null; + } + } finally { + takeLock.unlock(); } - - @Override - public int drainTo(Collection c) { - return drainTo(c,Integer.MAX_VALUE); + } + + /** + * Creates a node and links it at end of queue. + * + * @param x the item + * @return true if this set did not already contain x + */ + private boolean enqueue(E x) { + synchronized (delegate) { + return delegate.add(x); } - - @Override - public int drainTo(Collection c, int maxElements) { - if (c == null) - throw new NullPointerException(); - if (c == this) - throw new IllegalArgumentException(); - boolean signalNotFull = false; - final ReentrantLock takeLock = this.takeLock; - takeLock.lock(); - try { - int n = Math.min(maxElements, count.get()); - Iterator it = delegate.iterator(); - for(int i=0; i it = delegate.iterator(); + E x = it.next(); + it.remove(); + return x; } - - @Override - public E poll() { - final AtomicInteger count = this.count; - if (count.get() == 0) - return null; - E x = null; - int c = -1; - final ReentrantLock takeLock = this.takeLock; - takeLock.lock(); - try { - if (count.get() > 0) { - x = dequeue(); - c = count.getAndDecrement(); - if (c > 1) - notEmpty.signal(); - } - } finally { - takeLock.unlock(); - } - if (c == capacity) - signalNotFull(); - return x; + } + + /** Lock to prevent both puts and takes. */ + void fullyLock() { + putLock.lock(); + takeLock.lock(); + } + + /** Unlock to allow both puts and takes. */ + void fullyUnlock() { + takeLock.unlock(); + putLock.unlock(); + } + + /** + * Signals a waiting take. Called only from put/offer (which do not otherwise ordinarily lock + * takeLock.) + */ + private void signalNotEmpty() { + final ReentrantLock takeLock = this.takeLock; + takeLock.lock(); + try { + notEmpty.signal(); + } finally { + takeLock.unlock(); } - - - @Override - public E peek() { - if (count.get() == 0) - return null; - final ReentrantLock takeLock = this.takeLock; - takeLock.lock(); - try { - Iterator it = delegate.iterator(); - if(it.hasNext()) { - return it.next(); - } else { - return null; - } - } finally { - takeLock.unlock(); - } - } - - - /** - * Creates a node and links it at end of queue. - * @param x the item - * @return true if this set did not already contain x - */ - private boolean enqueue(E x) { - synchronized (delegate) { - return delegate.add(x); - } - } - - /** - * Removes a node from head of queue. - * @return the node - */ - private E dequeue() { - synchronized (delegate) { - Iterator it = delegate.iterator(); - E x = it.next(); - it.remove(); - return x; - } - } - - - - /** - * Lock to prevent both puts and takes. - */ - void fullyLock() { - putLock.lock(); - takeLock.lock(); - } - - /** - * Unlock to allow both puts and takes. - */ - void fullyUnlock() { - takeLock.unlock(); - putLock.unlock(); - } - - /** - * Signals a waiting take. Called only from put/offer (which do not - * otherwise ordinarily lock takeLock.) - */ - private void signalNotEmpty() { - final ReentrantLock takeLock = this.takeLock; - takeLock.lock(); - try { - notEmpty.signal(); - } finally { - takeLock.unlock(); - } + } + + /** Signals a waiting put. Called only from take/poll. */ + private void signalNotFull() { + final ReentrantLock putLock = this.putLock; + putLock.lock(); + try { + notFull.signal(); + } finally { + putLock.unlock(); } - - /** - * Signals a waiting put. Called only from take/poll. - */ - private void signalNotFull() { - final ReentrantLock putLock = this.putLock; - putLock.lock(); + } + + /** Tells whether both locks are held by current thread. */ + boolean isFullyLocked() { + return (putLock.isHeldByCurrentThread() && takeLock.isHeldByCurrentThread()); + } + + @Override + public Iterator iterator() { + final Iterator it = delegate.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + fullyLock(); try { - notFull.signal(); + return it.hasNext(); } finally { - putLock.unlock(); + fullyUnlock(); } - } - - /** - * Tells whether both locks are held by current thread. - */ - boolean isFullyLocked() { - return (putLock.isHeldByCurrentThread() && - takeLock.isHeldByCurrentThread()); - } - - @Override - public Iterator iterator() { - final Iterator it = delegate.iterator(); - return new Iterator() { - @Override - public boolean hasNext() { - fullyLock(); - try { - return it.hasNext(); - } finally { - fullyUnlock(); - } - } - - @Override - public E next() { - fullyLock(); - try { - return it.next(); - } finally { - fullyUnlock(); - } - } - - @Override - public void remove() { - fullyLock(); - try { - it.remove(); - - // remove counter - count.getAndDecrement(); - } finally { - fullyUnlock(); - } - } - }; - } - - @Override - public int size() { - return count.get(); - } - - @Override - public boolean remove(Object o) { - if (o == null) return false; + } + @Override + public E next() { fullyLock(); try { - if(delegate.remove(o)) { - if(count.getAndDecrement() == capacity) { - notFull.signal(); - } - return true; - } + return it.next(); } finally { - fullyUnlock(); + fullyUnlock(); } + } - return false; - } - - @Override - public void clear() { + @Override + public void remove() { fullyLock(); try { - delegate.clear(); - count.set(0); + it.remove(); + + // remove counter + count.getAndDecrement(); } finally { - fullyUnlock(); + fullyUnlock(); + } + } + }; + } + + @Override + public int size() { + return count.get(); + } + + @Override + public boolean remove(Object o) { + if (o == null) return false; + + fullyLock(); + try { + if (delegate.remove(o)) { + if (count.getAndDecrement() == capacity) { + notFull.signal(); } + return true; + } + } finally { + fullyUnlock(); } - + return false; + } + + @Override + public void clear() { + fullyLock(); + try { + delegate.clear(); + count.set(0); + } finally { + fullyUnlock(); + } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/clock/ClockFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/clock/ClockFactory.java index 583717df37..15484df856 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/clock/ClockFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/clock/ClockFactory.java @@ -4,7 +4,7 @@ public class ClockFactory { - public Clock provide() { - return Clock.systemDefaultZone(); - } + public Clock provide() { + return Clock.systemDefaultZone(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/DefaultExecutorServiceFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/DefaultExecutorServiceFactory.java index cb490375ce..ff420dda03 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/DefaultExecutorServiceFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/DefaultExecutorServiceFactory.java @@ -1,16 +1,14 @@ package pl.allegro.tech.hermes.common.concurrent; import com.google.common.util.concurrent.ThreadFactoryBuilder; - import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; public class DefaultExecutorServiceFactory implements ExecutorServiceFactory { - @Override - public ScheduledExecutorService createSingleThreadScheduledExecutor(String nameFormat) { - return Executors.newSingleThreadScheduledExecutor( - new ThreadFactoryBuilder().setNameFormat(nameFormat).build() - ); - } + @Override + public ScheduledExecutorService createSingleThreadScheduledExecutor(String nameFormat) { + return Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryBuilder().setNameFormat(nameFormat).build()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/ExecutorServiceFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/ExecutorServiceFactory.java index f9c8837ca8..62ffed8525 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/ExecutorServiceFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/concurrent/ExecutorServiceFactory.java @@ -4,5 +4,5 @@ public interface ExecutorServiceFactory { - ScheduledExecutorService createSingleThreadScheduledExecutor(String nameFormat); + ScheduledExecutorService createSingleThreadScheduledExecutor(String nameFormat); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthenticationProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthenticationProperties.java new file mode 100644 index 0000000000..658db7741d --- /dev/null +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthenticationProperties.java @@ -0,0 +1,78 @@ +package pl.allegro.tech.hermes.common.config; + +public class KafkaAuthenticationProperties { + + private boolean enabled = false; + private String mechanism = "PLAIN"; + private String protocol = "SASL_PLAINTEXT"; + private String username = "username"; + private String password = "password"; + private String loginModule = "org.apache.kafka.common.security.plain.PlainLoginModule"; + private String jaasConfig; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getMechanism() { + return mechanism; + } + + public void setMechanism(String mechanism) { + this.mechanism = mechanism; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getLoginModule() { + return loginModule; + } + + public void setLoginModule(String loginModule) { + this.loginModule = loginModule; + } + + public String getJaasConfig() { + if (jaasConfig != null) { + return jaasConfig; + } + return loginModule + + " required\n" + + "username=\"" + + username + + "\"\n" + + "password=\"" + + password + + "\";"; + } + + public void setJaasConfig(String jaasConfig) { + this.jaasConfig = jaasConfig; + } +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthorizationProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthorizationProperties.java deleted file mode 100644 index def9455d2c..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/KafkaAuthorizationProperties.java +++ /dev/null @@ -1,50 +0,0 @@ -package pl.allegro.tech.hermes.common.config; - -public class KafkaAuthorizationProperties { - - private boolean enabled = false; - private String mechanism = "PLAIN"; - private String protocol = "SASL_PLAINTEXT"; - private String username = "username"; - private String password = "password"; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getMechanism() { - return mechanism; - } - - public void setMechanism(String mechanism) { - this.mechanism = mechanism; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaCacheProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaCacheProperties.java index 84181c41f9..35a7a51cbe 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaCacheProperties.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaCacheProperties.java @@ -1,76 +1,75 @@ package pl.allegro.tech.hermes.common.config; -import pl.allegro.tech.hermes.common.schema.SchemaVersionRepositoryParameters; - import java.time.Duration; +import pl.allegro.tech.hermes.common.schema.SchemaVersionRepositoryParameters; public class SchemaCacheProperties implements SchemaVersionRepositoryParameters { - private Duration refreshAfterWrite = Duration.ofMinutes(10); + private Duration refreshAfterWrite = Duration.ofMinutes(10); - private Duration expireAfterWrite = Duration.ofHours(24); + private Duration expireAfterWrite = Duration.ofHours(24); - private Duration compiledExpireAfterAccess = Duration.ofHours(40); + private Duration compiledExpireAfterAccess = Duration.ofHours(40); - private int reloadThreadPoolSize = 2; + private int reloadThreadPoolSize = 2; - private boolean enabled = true; + private boolean enabled = true; - private int compiledMaximumSize = 2000; + private int compiledMaximumSize = 2000; - @Override - public boolean isCacheEnabled() { - return enabled; - } + @Override + public boolean isCacheEnabled() { + return enabled; + } - @Override - public Duration getRefreshAfterWrite() { - return refreshAfterWrite; - } + @Override + public Duration getRefreshAfterWrite() { + return refreshAfterWrite; + } - public void setRefreshAfterWrite(Duration refreshAfterWrite) { - this.refreshAfterWrite = refreshAfterWrite; - } + public void setRefreshAfterWrite(Duration refreshAfterWrite) { + this.refreshAfterWrite = refreshAfterWrite; + } - @Override - public Duration getExpireAfterWrite() { - return expireAfterWrite; - } + @Override + public Duration getExpireAfterWrite() { + return expireAfterWrite; + } - public void setExpireAfterWrite(Duration expireAfterWrite) { - this.expireAfterWrite = expireAfterWrite; - } + public void setExpireAfterWrite(Duration expireAfterWrite) { + this.expireAfterWrite = expireAfterWrite; + } - public Duration getCompiledExpireAfterAccess() { - return compiledExpireAfterAccess; - } + public Duration getCompiledExpireAfterAccess() { + return compiledExpireAfterAccess; + } - public void setCompiledExpireAfterAccess(Duration compiledExpireAfterAccess) { - this.compiledExpireAfterAccess = compiledExpireAfterAccess; - } + public void setCompiledExpireAfterAccess(Duration compiledExpireAfterAccess) { + this.compiledExpireAfterAccess = compiledExpireAfterAccess; + } - @Override - public int getReloadThreadPoolSize() { - return reloadThreadPoolSize; - } + @Override + public int getReloadThreadPoolSize() { + return reloadThreadPoolSize; + } - public void setReloadThreadPoolSize(int reloadThreadPoolSize) { - this.reloadThreadPoolSize = reloadThreadPoolSize; - } + public void setReloadThreadPoolSize(int reloadThreadPoolSize) { + this.reloadThreadPoolSize = reloadThreadPoolSize; + } - public boolean isEnabled() { - return enabled; - } + public boolean isEnabled() { + return enabled; + } - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } - public int getCompiledMaximumSize() { - return compiledMaximumSize; - } + public int getCompiledMaximumSize() { + return compiledMaximumSize; + } - public void setCompiledMaximumSize(int compiledMaximumSize) { - this.compiledMaximumSize = compiledMaximumSize; - } + public void setCompiledMaximumSize(int compiledMaximumSize) { + this.compiledMaximumSize = compiledMaximumSize; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaRepositoryProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaRepositoryProperties.java index a9746a764f..544c5be382 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaRepositoryProperties.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/config/SchemaRepositoryProperties.java @@ -4,73 +4,73 @@ public class SchemaRepositoryProperties { - private String serverUrl = "http://localhost:8888/"; + private String serverUrl = "http://localhost:8888/"; - private Duration httpReadTimeout = Duration.ofSeconds(2); + private Duration httpReadTimeout = Duration.ofSeconds(2); - private Duration httpConnectTimeout = Duration.ofSeconds(2000); + private Duration httpConnectTimeout = Duration.ofSeconds(2000); - private double onlineCheckPermitsPerSecond = 100.0; + private double onlineCheckPermitsPerSecond = 100.0; - private Duration onlineCheckAcquireWait = Duration.ofMillis(500); + private Duration onlineCheckAcquireWait = Duration.ofMillis(500); - private boolean subjectSuffixEnabled = false; + private boolean subjectSuffixEnabled = false; - private boolean subjectNamespaceEnabled = false; + private boolean subjectNamespaceEnabled = false; - public String getServerUrl() { - return serverUrl; - } + public String getServerUrl() { + return serverUrl; + } - public void setServerUrl(String serverUrl) { - this.serverUrl = serverUrl; - } + public void setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + } - public Duration getHttpReadTimeout() { - return httpReadTimeout; - } + public Duration getHttpReadTimeout() { + return httpReadTimeout; + } - public void setHttpReadTimeout(Duration httpReadTimeout) { - this.httpReadTimeout = httpReadTimeout; - } + public void setHttpReadTimeout(Duration httpReadTimeout) { + this.httpReadTimeout = httpReadTimeout; + } - public Duration getHttpConnectTimeout() { - return httpConnectTimeout; - } + public Duration getHttpConnectTimeout() { + return httpConnectTimeout; + } - public void setHttpConnectTimeout(Duration httpConnectTimeout) { - this.httpConnectTimeout = httpConnectTimeout; - } + public void setHttpConnectTimeout(Duration httpConnectTimeout) { + this.httpConnectTimeout = httpConnectTimeout; + } - public double getOnlineCheckPermitsPerSecond() { - return onlineCheckPermitsPerSecond; - } + public double getOnlineCheckPermitsPerSecond() { + return onlineCheckPermitsPerSecond; + } - public void setOnlineCheckPermitsPerSecond(double onlineCheckPermitsPerSecond) { - this.onlineCheckPermitsPerSecond = onlineCheckPermitsPerSecond; - } + public void setOnlineCheckPermitsPerSecond(double onlineCheckPermitsPerSecond) { + this.onlineCheckPermitsPerSecond = onlineCheckPermitsPerSecond; + } - public Duration getOnlineCheckAcquireWait() { - return onlineCheckAcquireWait; - } + public Duration getOnlineCheckAcquireWait() { + return onlineCheckAcquireWait; + } - public void setOnlineCheckAcquireWait(Duration onlineCheckAcquireWait) { - this.onlineCheckAcquireWait = onlineCheckAcquireWait; - } + public void setOnlineCheckAcquireWait(Duration onlineCheckAcquireWait) { + this.onlineCheckAcquireWait = onlineCheckAcquireWait; + } - public boolean isSubjectSuffixEnabled() { - return subjectSuffixEnabled; - } + public boolean isSubjectSuffixEnabled() { + return subjectSuffixEnabled; + } - public void setSubjectSuffixEnabled(boolean subjectSuffixEnabled) { - this.subjectSuffixEnabled = subjectSuffixEnabled; - } + public void setSubjectSuffixEnabled(boolean subjectSuffixEnabled) { + this.subjectSuffixEnabled = subjectSuffixEnabled; + } - public boolean isSubjectNamespaceEnabled() { - return subjectNamespaceEnabled; - } + public boolean isSubjectNamespaceEnabled() { + return subjectNamespaceEnabled; + } - public void setSubjectNamespaceEnabled(boolean subjectNamespaceEnabled) { - this.subjectNamespaceEnabled = subjectNamespaceEnabled; - } + public void setSubjectNamespaceEnabled(boolean subjectNamespaceEnabled) { + this.subjectNamespaceEnabled = subjectNamespaceEnabled; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/CuratorClientFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/CuratorClientFactory.java index 351fb66e87..59951d29ac 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/CuratorClientFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/CuratorClientFactory.java @@ -1,6 +1,9 @@ package pl.allegro.tech.hermes.common.di.factories; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadFactory; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; @@ -11,88 +14,88 @@ import org.slf4j.LoggerFactory; import pl.allegro.tech.hermes.common.exception.InternalProcessingException; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.ThreadFactory; - public class CuratorClientFactory { - public static class ZookeeperAuthorization { - private final String scheme; - private final String user; - private final String password; + public static class ZookeeperAuthorization { + private final String scheme; + private final String user; + private final String password; - public ZookeeperAuthorization(String scheme, String user, String password) { - this.scheme = scheme; - this.user = user; - this.password = password; - } + public ZookeeperAuthorization(String scheme, String user, String password) { + this.scheme = scheme; + this.user = user; + this.password = password; + } - byte[] getAuth() { - return String.join(":", user, password).getBytes(); - } + byte[] getAuth() { + return String.join(":", user, password).getBytes(); } + } - private static final Logger logger = LoggerFactory.getLogger(CuratorClientFactory.class); - private final ZookeeperParameters zookeeperParameters; + private static final Logger logger = LoggerFactory.getLogger(CuratorClientFactory.class); + private final ZookeeperParameters zookeeperParameters; - public CuratorClientFactory(ZookeeperParameters zookeeperParameters) { - this.zookeeperParameters = zookeeperParameters; - } + public CuratorClientFactory(ZookeeperParameters zookeeperParameters) { + this.zookeeperParameters = zookeeperParameters; + } - public CuratorFramework provide(String connectString) { - return provide(connectString, Optional.empty()); - } + public CuratorFramework provide(String connectString) { + return provide(connectString, Optional.empty()); + } - public CuratorFramework provide(String connectString, Optional zookeeperAuthorization) { - ThreadFactory threadFactory = new ThreadFactoryBuilder() - .setNameFormat("hermes-curator-%d") - .setUncaughtExceptionHandler((t, e) -> - logger.error("Exception from curator with name {}", t.getName(), e)).build(); - CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() - .threadFactory(threadFactory) - .connectString(connectString) - .sessionTimeoutMs((int) zookeeperParameters.getSessionTimeout().toMillis()) - .connectionTimeoutMs((int) zookeeperParameters.getConnectionTimeout().toMillis()) - .retryPolicy( - new ExponentialBackoffRetry( - (int) zookeeperParameters.getBaseSleepTime().toMillis(), - zookeeperParameters.getMaxRetries(), - (int) zookeeperParameters.getMaxSleepTime().toMillis() - ) - ); + public CuratorFramework provide( + String connectString, Optional zookeeperAuthorization) { + ThreadFactory threadFactory = + new ThreadFactoryBuilder() + .setNameFormat("hermes-curator-%d") + .setUncaughtExceptionHandler( + (t, e) -> logger.error("Exception from curator with name {}", t.getName(), e)) + .build(); + CuratorFrameworkFactory.Builder builder = + CuratorFrameworkFactory.builder() + .threadFactory(threadFactory) + .connectString(connectString) + .sessionTimeoutMs((int) zookeeperParameters.getSessionTimeout().toMillis()) + .connectionTimeoutMs((int) zookeeperParameters.getConnectionTimeout().toMillis()) + .retryPolicy( + new ExponentialBackoffRetry( + (int) zookeeperParameters.getBaseSleepTime().toMillis(), + zookeeperParameters.getMaxRetries(), + (int) zookeeperParameters.getMaxSleepTime().toMillis())); - zookeeperAuthorization.ifPresent(it -> { - builder.authorization(it.scheme, it.getAuth()); - builder.aclProvider( - new ACLProvider() { - @Override - public List getDefaultAcl() { - return ZooDefs.Ids.CREATOR_ALL_ACL; - } + zookeeperAuthorization.ifPresent( + it -> { + builder.authorization(it.scheme, it.getAuth()); + builder.aclProvider( + new ACLProvider() { + @Override + public List getDefaultAcl() { + return ZooDefs.Ids.CREATOR_ALL_ACL; + } - @Override - public List getAclForPath(String path) { - return ZooDefs.Ids.CREATOR_ALL_ACL; - } - } - ); + @Override + public List getAclForPath(String path) { + return ZooDefs.Ids.CREATOR_ALL_ACL; + } + }); }); - CuratorFramework curatorClient = builder.build(); - startAndWaitForConnection(curatorClient); + CuratorFramework curatorClient = builder.build(); + startAndWaitForConnection(curatorClient); - return curatorClient; - } + return curatorClient; + } - private void startAndWaitForConnection(CuratorFramework curator) { - curator.start(); - try { - curator.blockUntilConnected(); - } catch (InterruptedException interruptedException) { - RuntimeException exception = new InternalProcessingException("Could not start Zookeeper Curator", interruptedException); - logger.error(exception.getMessage(), interruptedException); - throw exception; - } + private void startAndWaitForConnection(CuratorFramework curator) { + curator.start(); + try { + curator.blockUntilConnected(); + } catch (InterruptedException interruptedException) { + RuntimeException exception = + new InternalProcessingException( + "Could not start Zookeeper Curator", interruptedException); + logger.error(exception.getMessage(), interruptedException); + throw exception; } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/GraphiteParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/GraphiteParameters.java deleted file mode 100644 index 84314096c4..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/GraphiteParameters.java +++ /dev/null @@ -1,10 +0,0 @@ -package pl.allegro.tech.hermes.common.di.factories; - -public interface GraphiteParameters { - - String getPrefix(); - - String getHost(); - - int getPort(); -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/HermesCuratorClientFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/HermesCuratorClientFactory.java index 2817083d2b..6152c55f9c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/HermesCuratorClientFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/HermesCuratorClientFactory.java @@ -1,32 +1,33 @@ package pl.allegro.tech.hermes.common.di.factories; -import org.apache.curator.framework.CuratorFramework; - import java.util.Optional; +import org.apache.curator.framework.CuratorFramework; public class HermesCuratorClientFactory { - private final ZookeeperParameters zookeeperParameters; - private final CuratorClientFactory curatorClientFactory; + private final ZookeeperParameters zookeeperParameters; + private final CuratorClientFactory curatorClientFactory; - public HermesCuratorClientFactory(ZookeeperParameters zookeeperParameters, CuratorClientFactory curatorClientFactory) { - this.zookeeperParameters = zookeeperParameters; - this.curatorClientFactory = curatorClientFactory; - } - - public CuratorFramework provide() { - String connectString = zookeeperParameters.getConnectionString(); + public HermesCuratorClientFactory( + ZookeeperParameters zookeeperParameters, CuratorClientFactory curatorClientFactory) { + this.zookeeperParameters = zookeeperParameters; + this.curatorClientFactory = curatorClientFactory; + } - Optional authorization = Optional.empty(); + public CuratorFramework provide() { + String connectString = zookeeperParameters.getConnectionString(); - if (zookeeperParameters.isAuthorizationEnabled()) { - authorization = Optional.of(new CuratorClientFactory.ZookeeperAuthorization( - zookeeperParameters.getScheme(), - zookeeperParameters.getUser(), - zookeeperParameters.getPassword()) - ); - } + Optional authorization = Optional.empty(); - return curatorClientFactory.provide(connectString, authorization); + if (zookeeperParameters.isAuthorizationEnabled()) { + authorization = + Optional.of( + new CuratorClientFactory.ZookeeperAuthorization( + zookeeperParameters.getScheme(), + zookeeperParameters.getUser(), + zookeeperParameters.getPassword())); } + + return curatorClientFactory.provide(connectString, authorization); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryFactory.java deleted file mode 100644 index 1d1de61579..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryFactory.java +++ /dev/null @@ -1,107 +0,0 @@ -package pl.allegro.tech.hermes.common.di.factories; - -import com.codahale.metrics.ConsoleReporter; -import com.codahale.metrics.Metric; -import com.codahale.metrics.MetricAttribute; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.MetricSet; -import com.codahale.metrics.graphite.Graphite; -import com.codahale.metrics.graphite.GraphiteReporter; -import com.codahale.metrics.jvm.FileDescriptorRatioGauge; -import com.codahale.metrics.jvm.GarbageCollectorMetricSet; -import com.codahale.metrics.jvm.MemoryUsageGaugeSet; -import com.google.common.base.Joiner; -import com.google.common.collect.Sets; -import jakarta.inject.Named; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; -import pl.allegro.tech.hermes.common.metric.MetricRegistryWithHdrHistogramReservoir; -import pl.allegro.tech.hermes.common.util.InstanceIdResolver; - -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -public class MetricRegistryFactory { - - private static final Logger logger = LoggerFactory.getLogger(MetricRegistryFactory.class); - private final MetricRegistryParameters metricRegistryParameters; - private final GraphiteParameters graphiteParameters; - private final InstanceIdResolver instanceIdResolver; - private final String moduleName; - - public MetricRegistryFactory(MetricRegistryParameters metricRegistryParameters, - GraphiteParameters graphiteParameters, - InstanceIdResolver instanceIdResolver, - @Named("moduleName") String moduleName) { - this.metricRegistryParameters = metricRegistryParameters; - this.graphiteParameters = graphiteParameters; - this.instanceIdResolver = instanceIdResolver; - this.moduleName = moduleName; - } - - public MetricRegistry provide() { - MetricRegistry registry = new MetricRegistryWithHdrHistogramReservoir(); - - if (metricRegistryParameters.isGraphiteReporterEnabled()) { - String prefix = Joiner.on(".").join( - graphiteParameters.getPrefix(), - moduleName, - instanceIdResolver.resolve().replaceAll("\\.", HermesMetrics.REPLACEMENT_CHAR)); - - GraphiteReporter - .forRegistry(registry) - .prefixedWith(prefix) - .disabledMetricAttributes(getDisabledAttributesFromConfig()) - .build(new Graphite(new InetSocketAddress( - graphiteParameters.getHost(), - graphiteParameters.getPort() - ))) - .start(metricRegistryParameters.getReportPeriod().toSeconds(), TimeUnit.SECONDS); - } - if (metricRegistryParameters.isConsoleReporterEnabled()) { - ConsoleReporter.forRegistry(registry).build().start( - metricRegistryParameters.getReportPeriod().toSeconds(), TimeUnit.SECONDS - ); - } - registerJvmMetrics(registry); - - return registry; - } - - private void registerJvmMetrics(MetricRegistry metricRegistry) { - registerAll("jvm.gc", new GarbageCollectorMetricSet(), metricRegistry); - registerAll("jvm.memory", new MemoryUsageGaugeSet(), metricRegistry); - metricRegistry.register("jvm.descriptors", new FileDescriptorRatioGauge()); - } - - private void registerAll(String prefix, MetricSet metricSet, MetricRegistry registry) { - for (Map.Entry entry : metricSet.getMetrics().entrySet()) { - if (entry.getValue() instanceof MetricSet) { - registerAll(prefix + "." + entry.getKey(), (MetricSet) entry.getValue(), registry); - } else { - registry.register(prefix + "." + entry.getKey(), entry.getValue()); - } - } - } - - private Set getDisabledAttributesFromConfig() { - Set disabledAttributes = Sets.newHashSet(); - String disabledAttributesFromConfig = metricRegistryParameters.getDisabledAttributes(); - List disabledAttributesList = Arrays.asList(disabledAttributesFromConfig.split("\\s*,\\s*")); - - disabledAttributesList.forEach(singleAttribute -> { - try { - disabledAttributes.add(MetricAttribute.valueOf(singleAttribute)); - } catch (IllegalArgumentException e) { - logger.warn("Failed to add disabled attribute from config: {}", e.getMessage()); - } - }); - - return disabledAttributes; - } -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryParameters.java deleted file mode 100644 index d112cea526..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MetricRegistryParameters.java +++ /dev/null @@ -1,16 +0,0 @@ -package pl.allegro.tech.hermes.common.di.factories; - -import java.time.Duration; - -public interface MetricRegistryParameters { - - boolean isZookeeperReporterEnabled(); - - boolean isGraphiteReporterEnabled(); - - boolean isConsoleReporterEnabled(); - - String getDisabledAttributes(); - - Duration getReportPeriod(); -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MicrometerRegistryParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MicrometerRegistryParameters.java index 48a74a84ea..0c599b13b2 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MicrometerRegistryParameters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/MicrometerRegistryParameters.java @@ -4,9 +4,9 @@ import java.util.List; public interface MicrometerRegistryParameters { - List getPercentiles(); + List getPercentiles(); - boolean zookeeperReporterEnabled(); + boolean zookeeperReporterEnabled(); - Duration zookeeperReportPeriod(); + Duration zookeeperReportPeriod(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ModelAwareZookeeperNotifyingCacheFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ModelAwareZookeeperNotifyingCacheFactory.java index cbd1f1998e..2bc4d9bd3c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ModelAwareZookeeperNotifyingCacheFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ModelAwareZookeeperNotifyingCacheFactory.java @@ -1,29 +1,58 @@ package pl.allegro.tech.hermes.common.di.factories; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.apache.curator.framework.CuratorFramework; +import pl.allegro.tech.hermes.common.cache.queue.LinkedHashSetBlockingQueue; +import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.infrastructure.zookeeper.cache.ModelAwareZookeeperNotifyingCache; public class ModelAwareZookeeperNotifyingCacheFactory { - private final CuratorFramework curator; + private final CuratorFramework curator; - private final ZookeeperParameters zookeeperParameters; + private final MetricsFacade metricsFacade; - public ModelAwareZookeeperNotifyingCacheFactory(CuratorFramework curator, ZookeeperParameters zookeeperParameters) { - this.curator = curator; - this.zookeeperParameters = zookeeperParameters; - } + private final ZookeeperParameters zookeeperParameters; + + public ModelAwareZookeeperNotifyingCacheFactory( + CuratorFramework curator, + MetricsFacade metricaFacade, + ZookeeperParameters zookeeperParameters) { + this.curator = curator; + this.metricsFacade = metricaFacade; + this.zookeeperParameters = zookeeperParameters; + } - public ModelAwareZookeeperNotifyingCache provide() { - String rootPath = zookeeperParameters.getRoot(); - ModelAwareZookeeperNotifyingCache cache = new ModelAwareZookeeperNotifyingCache( - curator, rootPath, zookeeperParameters.getProcessingThreadPoolSize() - ); - try { - cache.start(); - } catch (Exception e) { - throw new IllegalStateException("Unable to start Zookeeper cache for root path " + rootPath, e); - } - return cache; + public ModelAwareZookeeperNotifyingCache provide() { + String rootPath = zookeeperParameters.getRoot(); + ExecutorService executor = + createExecutor(rootPath, zookeeperParameters.getProcessingThreadPoolSize()); + ModelAwareZookeeperNotifyingCache cache = + new ModelAwareZookeeperNotifyingCache(curator, executor, rootPath); + try { + cache.start(); + } catch (Exception e) { + throw new IllegalStateException( + "Unable to start Zookeeper cache for root path " + rootPath, e); } + return cache; + } + + private ExecutorService createExecutor(String rootPath, int processingThreadPoolSize) { + ThreadFactory threadFactory = + new ThreadFactoryBuilder().setNameFormat(rootPath + "-zk-cache-%d").build(); + ExecutorService executor = + new ThreadPoolExecutor( + 1, + processingThreadPoolSize, + Integer.MAX_VALUE, + TimeUnit.SECONDS, + new LinkedHashSetBlockingQueue<>(), + threadFactory); + return metricsFacade.executor().monitor(executor, rootPath + "zk-cache"); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ObjectMapperFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ObjectMapperFactory.java index 368e8cf0bd..8ac12fee47 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ObjectMapperFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ObjectMapperFactory.java @@ -10,23 +10,30 @@ public class ObjectMapperFactory { - private final boolean schemaIdSerializationEnabled; + private final boolean schemaIdSerializationEnabled; + private final boolean fallbackToRemoteDatacenterEnabled; - public ObjectMapperFactory(boolean schemaIdSerializationEnabled) { - this.schemaIdSerializationEnabled = schemaIdSerializationEnabled; - } + public ObjectMapperFactory( + boolean schemaIdSerializationEnabled, boolean fallbackToRemoteDatacenterEnabled) { + this.schemaIdSerializationEnabled = schemaIdSerializationEnabled; + this.fallbackToRemoteDatacenterEnabled = fallbackToRemoteDatacenterEnabled; + } - public ObjectMapper provide() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - objectMapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES); - objectMapper.registerModule(new JavaTimeModule()); + public ObjectMapper provide() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES); + objectMapper.registerModule(new JavaTimeModule()); - final InjectableValues defaultSchemaIdAwareSerializationEnabled = new InjectableValues - .Std().addValue(Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, schemaIdSerializationEnabled); - objectMapper.setInjectableValues(defaultSchemaIdAwareSerializationEnabled); + final InjectableValues defaultSchemaIdAwareSerializationEnabled = + new InjectableValues.Std() + .addValue( + Topic.DEFAULT_SCHEMA_ID_SERIALIZATION_ENABLED_KEY, schemaIdSerializationEnabled) + .addValue( + Topic.DEFAULT_FALLBACK_TO_REMOTE_DATACENTER_KEY, fallbackToRemoteDatacenterEnabled); + objectMapper.setInjectableValues(defaultSchemaIdAwareSerializationEnabled); - return objectMapper; - } + return objectMapper; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/PrometheusMeterRegistryFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/PrometheusMeterRegistryFactory.java index d2849d9cb1..7d46f2bba2 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/PrometheusMeterRegistryFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/PrometheusMeterRegistryFactory.java @@ -9,61 +9,69 @@ import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; +import java.util.concurrent.TimeUnit; import pl.allegro.tech.hermes.common.metric.counter.CounterStorage; import pl.allegro.tech.hermes.common.metric.counter.zookeeper.ZookeeperCounterReporter; -import java.util.concurrent.TimeUnit; - public class PrometheusMeterRegistryFactory { - private final MicrometerRegistryParameters parameters; - private final PrometheusConfig prometheusConfig; - private final CounterStorage counterStorage; - private final String prefix; + private final MicrometerRegistryParameters parameters; + private final PrometheusConfig prometheusConfig; + private final CounterStorage counterStorage; + private final String prefix; - public PrometheusMeterRegistryFactory(MicrometerRegistryParameters parameters, - PrometheusConfig prometheusConfig, - CounterStorage counterStorage, String prefix) { - this.parameters = parameters; - this.prometheusConfig = prometheusConfig; - this.counterStorage = counterStorage; - this.prefix = prefix + "_"; - } + public PrometheusMeterRegistryFactory( + MicrometerRegistryParameters parameters, + PrometheusConfig prometheusConfig, + CounterStorage counterStorage, + String prefix) { + this.parameters = parameters; + this.prometheusConfig = prometheusConfig; + this.counterStorage = counterStorage; + this.prefix = prefix + "_"; + } - public PrometheusMeterRegistry provide() { - PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry(prometheusConfig); - applyFilters(meterRegistry); - if (parameters.zookeeperReporterEnabled()) { - registerZookeeperReporter(meterRegistry); - } - registerJvmMetrics(meterRegistry); - return meterRegistry; + public PrometheusMeterRegistry provide() { + PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry(prometheusConfig); + applyFilters(meterRegistry); + if (parameters.zookeeperReporterEnabled()) { + registerZookeeperReporter(meterRegistry); } + registerJvmMetrics(meterRegistry); + return meterRegistry; + } - private void applyFilters(PrometheusMeterRegistry meterRegistry) { - meterRegistry.config().meterFilter(new MeterFilter() { - @Override - public Meter.Id map(Meter.Id id) { + private void applyFilters(PrometheusMeterRegistry meterRegistry) { + meterRegistry + .config() + .meterFilter( + new MeterFilter() { + @Override + public Meter.Id map(Meter.Id id) { return id.withName(prefix + id.getName()); - } + } - @Override - public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) { + @Override + public DistributionStatisticConfig configure( + Meter.Id id, DistributionStatisticConfig config) { return DistributionStatisticConfig.builder() - .percentiles(parameters.getPercentiles() - .stream().mapToDouble(Double::doubleValue).toArray() - ).build().merge(config); - } - }); - } + .percentiles( + parameters.getPercentiles().stream() + .mapToDouble(Double::doubleValue) + .toArray()) + .build() + .merge(config); + } + }); + } - private void registerZookeeperReporter(PrometheusMeterRegistry meterRegistry) { - new ZookeeperCounterReporter(meterRegistry, counterStorage, prefix) - .start(parameters.zookeeperReportPeriod().toSeconds(), TimeUnit.SECONDS); - } + private void registerZookeeperReporter(PrometheusMeterRegistry meterRegistry) { + new ZookeeperCounterReporter(meterRegistry, counterStorage, prefix) + .start(parameters.zookeeperReportPeriod().toSeconds(), TimeUnit.SECONDS); + } - private void registerJvmMetrics(MeterRegistry meterRegistry) { - new JvmMemoryMetrics().bindTo(meterRegistry); - new JvmGcMetrics().bindTo(meterRegistry); - new JvmThreadMetrics().bindTo(meterRegistry); - } + private void registerJvmMetrics(MeterRegistry meterRegistry) { + new JvmMemoryMetrics().bindTo(meterRegistry); + new JvmGcMetrics().bindTo(meterRegistry); + new JvmThreadMetrics().bindTo(meterRegistry); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ZookeeperParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ZookeeperParameters.java index 7c8a7b36ee..6123d4926e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ZookeeperParameters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/di/factories/ZookeeperParameters.java @@ -4,27 +4,27 @@ public interface ZookeeperParameters { - String getConnectionString(); + String getConnectionString(); - Duration getBaseSleepTime(); + Duration getBaseSleepTime(); - Duration getMaxSleepTime(); + Duration getMaxSleepTime(); - int getMaxRetries(); + int getMaxRetries(); - Duration getConnectionTimeout(); + Duration getConnectionTimeout(); - Duration getSessionTimeout(); + Duration getSessionTimeout(); - String getRoot(); + String getRoot(); - int getProcessingThreadPoolSize(); + int getProcessingThreadPoolSize(); - boolean isAuthorizationEnabled(); + boolean isAuthorizationEnabled(); - String getScheme(); + String getScheme(); - String getUser(); + String getUser(); - String getPassword(); + String getPassword(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerInfoNotAvailableException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerInfoNotAvailableException.java index 5b1d033b4a..9e99906700 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerInfoNotAvailableException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerInfoNotAvailableException.java @@ -2,7 +2,7 @@ public class BrokerInfoNotAvailableException extends InternalProcessingException { - public BrokerInfoNotAvailableException(Integer brokerId, Throwable cause) { - super("Could not find or read info about broker with id " + brokerId, cause); - } + public BrokerInfoNotAvailableException(Integer brokerId, Throwable cause) { + super("Could not find or read info about broker with id " + brokerId, cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerNotFoundForPartitionException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerNotFoundForPartitionException.java index 8eff999a1d..15604905ee 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerNotFoundForPartitionException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/BrokerNotFoundForPartitionException.java @@ -3,7 +3,7 @@ @SuppressWarnings("serial") public class BrokerNotFoundForPartitionException extends InternalProcessingException { - public BrokerNotFoundForPartitionException(String topic, int partition, Throwable cause) { - super(String.format("Broker not found for topic %s and partition %d", topic, partition), cause); - } + public BrokerNotFoundForPartitionException(String topic, int partition, Throwable cause) { + super(String.format("Broker not found for topic %s and partition %d", topic, partition), cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/EndpointProtocolNotSupportedException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/EndpointProtocolNotSupportedException.java index 5b8c150684..a6d00e08ef 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/EndpointProtocolNotSupportedException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/EndpointProtocolNotSupportedException.java @@ -5,12 +5,15 @@ public class EndpointProtocolNotSupportedException extends HermesException { - public EndpointProtocolNotSupportedException(EndpointAddress endpoint) { - super(String.format("Protocol %s not supported in endpoint %s", endpoint.getProtocol(), endpoint.toString())); - } + public EndpointProtocolNotSupportedException(EndpointAddress endpoint) { + super( + String.format( + "Protocol %s not supported in endpoint %s", + endpoint.getProtocol(), endpoint.toString())); + } - @Override - public ErrorCode getCode() { - return ErrorCode.VALIDATION_ERROR; - } + @Override + public ErrorCode getCode() { + return ErrorCode.VALIDATION_ERROR; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/HermesException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/HermesException.java index 7d21902938..f0153a52bb 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/HermesException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/HermesException.java @@ -2,19 +2,18 @@ import pl.allegro.tech.hermes.api.ErrorCode; - public abstract class HermesException extends RuntimeException { - public HermesException(Throwable t) { - super(t); - } + public HermesException(Throwable t) { + super(t); + } - public HermesException(String message) { - super(message); - } + public HermesException(String message) { + super(message); + } - public HermesException(String message, Throwable cause) { - super(message, cause); - } + public HermesException(String message, Throwable cause) { + super(message, cause); + } - public abstract ErrorCode getCode(); + public abstract ErrorCode getCode(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/InternalProcessingException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/InternalProcessingException.java index 7983ae724d..5e3ec2d1b9 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/InternalProcessingException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/InternalProcessingException.java @@ -1,15 +1,15 @@ package pl.allegro.tech.hermes.common.exception; public class InternalProcessingException extends RuntimeException { - public InternalProcessingException(Throwable throwable) { - super(throwable); - } + public InternalProcessingException(Throwable throwable) { + super(throwable); + } - public InternalProcessingException(String message) { - super(message); - } + public InternalProcessingException(String message) { + super(message); + } - public InternalProcessingException(String message, Throwable throwable) { - super(message, throwable); - } + public InternalProcessingException(String message, Throwable throwable) { + super(message, throwable); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/PartitionsNotFoundForGivenTopicException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/PartitionsNotFoundForGivenTopicException.java index 7e290ffb5a..b3481d2fdf 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/PartitionsNotFoundForGivenTopicException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/PartitionsNotFoundForGivenTopicException.java @@ -5,12 +5,12 @@ @SuppressWarnings("serial") public class PartitionsNotFoundForGivenTopicException extends HermesException { - public PartitionsNotFoundForGivenTopicException(String topicName, Throwable cause) { - super(String.format("Partitions not found for topic: %s", topicName), cause); - } + public PartitionsNotFoundForGivenTopicException(String topicName, Throwable cause) { + super(String.format("Partitions not found for topic: %s", topicName), cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.PARTITIONS_NOT_FOUND_FOR_TOPIC; - } + @Override + public ErrorCode getCode() { + return ErrorCode.PARTITIONS_NOT_FOUND_FOR_TOPIC; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RepositoryNotAvailableException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RepositoryNotAvailableException.java index dd2a0bbaf0..fef3a94e5b 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RepositoryNotAvailableException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RepositoryNotAvailableException.java @@ -2,8 +2,7 @@ public class RepositoryNotAvailableException extends InternalProcessingException { - public RepositoryNotAvailableException(String message) { - super(message); - } - + public RepositoryNotAvailableException(String message) { + super(message); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RetransmissionException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RetransmissionException.java index 5d0b43be90..44d13ba790 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RetransmissionException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/RetransmissionException.java @@ -4,16 +4,16 @@ public class RetransmissionException extends HermesException { - public RetransmissionException(String message) { - super(message); - } + public RetransmissionException(String message) { + super(message); + } - public RetransmissionException(Throwable cause) { - super("Error during retransmitting messages.", cause); - } + public RetransmissionException(Throwable cause) { + super("Error during retransmitting messages.", cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.RETRANSMISSION_EXCEPTION; - } + @Override + public ErrorCode getCode() { + return ErrorCode.RETRANSMISSION_EXCEPTION; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/SubscriptionEndpointAddressChangeException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/SubscriptionEndpointAddressChangeException.java index a62d93e822..e2c6b7d421 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/SubscriptionEndpointAddressChangeException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/SubscriptionEndpointAddressChangeException.java @@ -3,12 +3,12 @@ import pl.allegro.tech.hermes.api.ErrorCode; public class SubscriptionEndpointAddressChangeException extends HermesException { - public SubscriptionEndpointAddressChangeException(Throwable cause) { - super("Error during change topic endpoint address.", cause); - } + public SubscriptionEndpointAddressChangeException(Throwable cause) { + super("Error during change topic endpoint address.", cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.SUBSCRIPTION_ENDPOINT_ADDRESS_CHANGE_EXCEPTION; - } + @Override + public ErrorCode getCode() { + return ErrorCode.SUBSCRIPTION_ENDPOINT_ADDRESS_CHANGE_EXCEPTION; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/UnavailableRateException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/UnavailableRateException.java index 1c671f8415..4b5123b8b5 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/UnavailableRateException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/exception/UnavailableRateException.java @@ -6,23 +6,24 @@ @SuppressWarnings("serial") public class UnavailableRateException extends HermesException { - public UnavailableRateException(TopicName topicName, String subscriptionName, Throwable cause) { - super(String.format( - "Rate for %s subscription on %s topic in group %s is unavailable.", - subscriptionName, topicName.getName(), topicName.getGroupName()), cause - ); - } + public UnavailableRateException(TopicName topicName, String subscriptionName, Throwable cause) { + super( + String.format( + "Rate for %s subscription on %s topic in group %s is unavailable.", + subscriptionName, topicName.getName(), topicName.getGroupName()), + cause); + } - public UnavailableRateException(TopicName topicName, Throwable cause) { - super(String.format( - "Rate for %s topic in group %s is unavailable", - topicName.getName(), topicName.getGroupName()), - cause - ); - } + public UnavailableRateException(TopicName topicName, Throwable cause) { + super( + String.format( + "Rate for %s topic in group %s is unavailable", + topicName.getName(), topicName.getGroupName()), + cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.UNAVAILABLE_RATE; - } + @Override + public ErrorCode getCode() { + return ErrorCode.UNAVAILABLE_RATE; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/ExtraRequestHeadersCollector.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/ExtraRequestHeadersCollector.java index d334c8b0e2..6f321ebbd8 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/ExtraRequestHeadersCollector.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/ExtraRequestHeadersCollector.java @@ -9,46 +9,46 @@ import java.util.function.Supplier; import java.util.stream.Collector; -public class ExtraRequestHeadersCollector implements Collector, StringBuilder, String> { - private ExtraRequestHeadersCollector() { - } - - public static ExtraRequestHeadersCollector extraRequestHeadersCollector() { - return new ExtraRequestHeadersCollector(); - } - - @Override - public Supplier supplier() { - return StringBuilder::new; - } - - @Override - public BiConsumer> accumulator() { - return (StringBuilder accumulator, Map.Entry entry) -> { - accumulator.append(entry.getKey()); - accumulator.append(": "); - accumulator.append(entry.getValue()); - accumulator.append('\n'); - }; - } - - @Override - public BinaryOperator combiner() { - return StringBuilder::append; - } - - @Override - public Function finisher() { - return (StringBuilder acc) -> { - if (acc.length() > 0) { - acc.setLength(acc.length() - 1); - } - return acc.toString(); - }; - } - - @Override - public Set characteristics() { - return Collections.emptySet(); - } +public class ExtraRequestHeadersCollector + implements Collector, StringBuilder, String> { + private ExtraRequestHeadersCollector() {} + + public static ExtraRequestHeadersCollector extraRequestHeadersCollector() { + return new ExtraRequestHeadersCollector(); + } + + @Override + public Supplier supplier() { + return StringBuilder::new; + } + + @Override + public BiConsumer> accumulator() { + return (StringBuilder accumulator, Map.Entry entry) -> { + accumulator.append(entry.getKey()); + accumulator.append(": "); + accumulator.append(entry.getValue()); + accumulator.append('\n'); + }; + } + + @Override + public BinaryOperator combiner() { + return StringBuilder::append; + } + + @Override + public Function finisher() { + return (StringBuilder acc) -> { + if (acc.length() > 0) { + acc.setLength(acc.length() - 1); + } + return acc.toString(); + }; + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/MessageMetadataHeaders.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/MessageMetadataHeaders.java index d48f8002ce..943502748f 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/MessageMetadataHeaders.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/http/MessageMetadataHeaders.java @@ -1,27 +1,26 @@ package pl.allegro.tech.hermes.common.http; public enum MessageMetadataHeaders { + MESSAGE_ID("Hermes-Message-Id"), + BATCH_ID("Hermes-Batch-Id"), + TOPIC_NAME("Hermes-Topic-Name"), + SUBSCRIPTION_NAME("Hermes-Subscription-Name"), + RETRY_COUNT("Hermes-Retry-Count"), + SCHEMA_VERSION("Schema-Version"), + SCHEMA_ID("Schema-Id"), + PARTITION_KEY("Partition-Key"); - MESSAGE_ID("Hermes-Message-Id"), - BATCH_ID("Hermes-Batch-Id"), - TOPIC_NAME("Hermes-Topic-Name"), - SUBSCRIPTION_NAME("Hermes-Subscription-Name"), - RETRY_COUNT("Hermes-Retry-Count"), - SCHEMA_VERSION("Schema-Version"), - SCHEMA_ID("Schema-Id"), - PARTITION_KEY("Partition-Key"); + private final String headerName; - private final String headerName; + MessageMetadataHeaders(String headerName) { + this.headerName = headerName; + } - MessageMetadataHeaders(String headerName) { - this.headerName = headerName; - } + public String getName() { + return this.headerName; + } - public String getName() { - return this.headerName; - } - - public String getCamelCaseName() { - return this.headerName.replace("-", ""); - } + public String getCamelCaseName() { + return this.headerName.replace("-", ""); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/ConsumerGroupId.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/ConsumerGroupId.java index aaff0c6110..b554fc6854 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/ConsumerGroupId.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/ConsumerGroupId.java @@ -1,44 +1,44 @@ package pl.allegro.tech.hermes.common.kafka; -import java.util.Objects; - import static com.google.common.base.Preconditions.checkNotNull; -public class ConsumerGroupId { +import java.util.Objects; - private final String value; +public class ConsumerGroupId { - private ConsumerGroupId(String value) { - this.value = checkNotNull(value); - } + private final String value; - public static ConsumerGroupId valueOf(String value) { - return new ConsumerGroupId(value); - } + private ConsumerGroupId(String value) { + this.value = checkNotNull(value); + } - public String asString() { - return value; - } + public static ConsumerGroupId valueOf(String value) { + return new ConsumerGroupId(value); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConsumerGroupId that = (ConsumerGroupId) o; - return Objects.equals(value, that.value); - } + public String asString() { + return value; + } - @Override - public int hashCode() { - return Objects.hash(value); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public String toString() { - return "ConsumerGroupId(" + value + ")"; + if (o == null || getClass() != o.getClass()) { + return false; } + ConsumerGroupId that = (ConsumerGroupId) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "ConsumerGroupId(" + value + ")"; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/HTTPHeadersPropagationAsKafkaHeadersProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/HTTPHeadersPropagationAsKafkaHeadersProperties.java new file mode 100644 index 0000000000..a28d0e951a --- /dev/null +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/HTTPHeadersPropagationAsKafkaHeadersProperties.java @@ -0,0 +1,7 @@ +package pl.allegro.tech.hermes.common.kafka; + +public interface HTTPHeadersPropagationAsKafkaHeadersProperties { + boolean isEnabled(); + + String getPrefix(); +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/JsonToAvroMigrationKafkaNamesMapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/JsonToAvroMigrationKafkaNamesMapper.java index 92ceaf4834..bfe177419c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/JsonToAvroMigrationKafkaNamesMapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/JsonToAvroMigrationKafkaNamesMapper.java @@ -1,37 +1,45 @@ package pl.allegro.tech.hermes.common.kafka; +import java.util.function.Function; import pl.allegro.tech.hermes.api.ContentType; import pl.allegro.tech.hermes.api.Topic; -import java.util.function.Function; - public class JsonToAvroMigrationKafkaNamesMapper extends NamespaceKafkaNamesMapper { - public JsonToAvroMigrationKafkaNamesMapper(String namespace, String namespaceSeparator) { - super(namespace, namespaceSeparator); - } - - public KafkaTopics toKafkaTopics(Topic topic) { - KafkaTopic primary = mapToKafkaTopic.andThen(appendNamespace).andThen(appendContentTypeSuffix).apply(topic); - - if (topic.wasMigratedFromJsonType()) { - KafkaTopic secondary = mapToJsonKafkaTopic.andThen(appendNamespace).andThen(appendContentTypeSuffix).apply(topic); - return new KafkaTopics(primary, secondary); - } - return new KafkaTopics(primary); + public JsonToAvroMigrationKafkaNamesMapper(String namespace, String namespaceSeparator) { + super(namespace, namespaceSeparator); + } + + public KafkaTopics toKafkaTopics(Topic topic) { + KafkaTopic primary = + mapToKafkaTopic.andThen(appendNamespace).andThen(appendContentTypeSuffix).apply(topic); + + if (topic.wasMigratedFromJsonType()) { + KafkaTopic secondary = + mapToJsonKafkaTopic + .andThen(appendNamespace) + .andThen(appendContentTypeSuffix) + .apply(topic); + return new KafkaTopics(primary, secondary); } + return new KafkaTopics(primary); + } - private final Function mapToJsonKafkaTopic = it -> - new KafkaTopic(KafkaTopicName.valueOf(it.getQualifiedName()), ContentType.JSON); + private final Function mapToJsonKafkaTopic = + it -> new KafkaTopic(KafkaTopicName.valueOf(it.getQualifiedName()), ContentType.JSON); - private final Function appendContentTypeSuffix = kafkaTopic -> { + private final Function appendContentTypeSuffix = + kafkaTopic -> { switch (kafkaTopic.contentType()) { - case JSON: - return kafkaTopic; - case AVRO: - return new KafkaTopic(KafkaTopicName.valueOf(kafkaTopic.name().asString() + "_avro"), kafkaTopic.contentType()); - default: - throw new IllegalStateException(String.format("Unknown content type '%s'", kafkaTopic.contentType())); + case JSON: + return kafkaTopic; + case AVRO: + return new KafkaTopic( + KafkaTopicName.valueOf(kafkaTopic.name().asString() + "_avro"), + kafkaTopic.contentType()); + default: + throw new IllegalStateException( + String.format("Unknown content type '%s'", kafkaTopic.contentType())); } - }; + }; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPool.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPool.java index 3014885869..f22fc0b05b 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPool.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPool.java @@ -1,20 +1,5 @@ package pl.allegro.tech.hermes.common.kafka; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; -import com.google.common.util.concurrent.UncheckedExecutionException; -import org.apache.kafka.clients.consumer.KafkaConsumer; -import org.apache.kafka.common.TopicPartition; -import pl.allegro.tech.hermes.common.broker.BrokerStorage; -import pl.allegro.tech.hermes.common.exception.BrokerNotFoundForPartitionException; - -import java.util.Properties; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - import static org.apache.kafka.clients.CommonClientConfigs.SECURITY_PROTOCOL_CONFIG; import static org.apache.kafka.clients.consumer.ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG; import static org.apache.kafka.clients.consumer.ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG; @@ -25,87 +10,111 @@ import static org.apache.kafka.common.config.SaslConfigs.SASL_JAAS_CONFIG; import static org.apache.kafka.common.config.SaslConfigs.SASL_MECHANISM; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; +import com.google.common.cache.RemovalNotification; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.TopicPartition; +import pl.allegro.tech.hermes.common.broker.BrokerStorage; +import pl.allegro.tech.hermes.common.exception.BrokerNotFoundForPartitionException; /** - * This class help us to avoid unnecessarily creating new kafka consumers for the same broker instance, mainly in case of - * retransmission and migrating topic from json to avro. We map topic and its partitions to specific kafka broker which - * without caching would lead to creating exactly the same consumer multiple times in short period of time. - * Additionaly consumers created here are rarely used, so we don't bother about concurrency and thread safety. + * This class help us to avoid unnecessarily creating new kafka consumers for the same broker + * instance, mainly in case of retransmission and migrating topic from json to avro. We map topic + * and its partitions to specific kafka broker which without caching would lead to creating exactly + * the same consumer multiple times in short period of time. Additionaly consumers created here are + * rarely used, so we don't bother about concurrency and thread safety. */ public class KafkaConsumerPool { - private final LoadingCache> kafkaConsumers; - private final BrokerStorage brokerStorage; - - public KafkaConsumerPool(KafkaConsumerPoolConfig poolConfig, BrokerStorage brokerStorage, String configuredBootstrapServers) { - this.brokerStorage = brokerStorage; - this.kafkaConsumers = CacheBuilder.newBuilder() - .expireAfterAccess(poolConfig.getCacheExpirationSeconds(), TimeUnit.SECONDS) - .removalListener(new KafkaConsumerRemoveListener()) - .build(new KafkaConsumerSupplier(poolConfig, configuredBootstrapServers)); + private final LoadingCache> kafkaConsumers; + private final BrokerStorage brokerStorage; + + public KafkaConsumerPool( + KafkaConsumerPoolConfig poolConfig, + BrokerStorage brokerStorage, + String configuredBootstrapServers) { + this.brokerStorage = brokerStorage; + this.kafkaConsumers = + CacheBuilder.newBuilder() + .expireAfterAccess(poolConfig.getCacheExpirationSeconds(), TimeUnit.SECONDS) + .removalListener(new KafkaConsumerRemoveListener()) + .build(new KafkaConsumerSupplier(poolConfig, configuredBootstrapServers)); + } + + public KafkaConsumer get(KafkaTopic topic, int partition) { + return get(topic.name().asString(), partition); + } + + public KafkaConsumer get(String topicName, int partition) { + try { + int leaderId = brokerStorage.readLeaderForPartition(new TopicPartition(topicName, partition)); + return kafkaConsumers.get(leaderId); + + } catch (ExecutionException e) { + String message = + String.format( + "Cannot get KafkaConsumer for topic %s and partition %d", topicName, partition); + throw new KafkaConsumerPoolException(message, e); + } catch (UncheckedExecutionException e) { + if (e.getCause() instanceof BrokerNotFoundForPartitionException) { + throw (BrokerNotFoundForPartitionException) e.getCause(); + } + throw e; } + } - public KafkaConsumer get(KafkaTopic topic, int partition) { - return get(topic.name().asString(), partition); - } + private static class KafkaConsumerSupplier + extends CacheLoader> { + + private final KafkaConsumerPoolConfig poolConfig; + private final String configuredBootstrapServers; - public KafkaConsumer get(String topicName, int partition) { - try { - int leaderId = brokerStorage.readLeaderForPartition(new TopicPartition(topicName, partition)); - return kafkaConsumers.get(leaderId); - - } catch (ExecutionException e) { - String message = String.format("Cannot get KafkaConsumer for topic %s and partition %d", topicName, partition); - throw new KafkaConsumerPoolException(message, e); - } catch (UncheckedExecutionException e) { - if (e.getCause() instanceof BrokerNotFoundForPartitionException) { - throw (BrokerNotFoundForPartitionException) e.getCause(); - } - throw e; - } + KafkaConsumerSupplier(KafkaConsumerPoolConfig poolConfig, String configuredBootstrapServers) { + this.poolConfig = poolConfig; + this.configuredBootstrapServers = configuredBootstrapServers; } - private static class KafkaConsumerSupplier extends CacheLoader> { - - private final KafkaConsumerPoolConfig poolConfig; - private final String configuredBootstrapServers; - - KafkaConsumerSupplier(KafkaConsumerPoolConfig poolConfig, String configuredBootstrapServers) { - this.poolConfig = poolConfig; - this.configuredBootstrapServers = configuredBootstrapServers; - } - - @Override - public KafkaConsumer load(Integer leaderId) throws Exception { - return createKafkaConsumer(); - } - - private KafkaConsumer createKafkaConsumer() { - - Properties props = new Properties(); - props.put(BOOTSTRAP_SERVERS_CONFIG, configuredBootstrapServers); - props.put(GROUP_ID_CONFIG, poolConfig.getIdPrefix() + "_" + poolConfig.getConsumerGroupName()); - props.put(RECEIVE_BUFFER_CONFIG, poolConfig.getBufferSizeBytes()); - props.put(ENABLE_AUTO_COMMIT_CONFIG, false); - props.put(FETCH_MAX_WAIT_MS_CONFIG, poolConfig.getFetchMaxWaitMillis()); - props.put(FETCH_MIN_BYTES_CONFIG, poolConfig.getFetchMinBytes()); - props.put("key.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer"); - props.put("value.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer"); - - if (poolConfig.isSaslEnabled()) { - props.put(SASL_MECHANISM, poolConfig.getSecurityMechanism()); - props.put(SECURITY_PROTOCOL_CONFIG, poolConfig.getSecurityProtocol()); - props.put(SASL_JAAS_CONFIG, poolConfig.getSaslJaasConfig()); - } - return new KafkaConsumer<>(props); - } + @Override + public KafkaConsumer load(Integer leaderId) throws Exception { + return createKafkaConsumer(); } - private static class KafkaConsumerRemoveListener implements RemovalListener> { - @Override - public void onRemoval(RemovalNotification> notification) { - notification.getValue().close(); - } + private KafkaConsumer createKafkaConsumer() { + + Properties props = new Properties(); + props.put(BOOTSTRAP_SERVERS_CONFIG, configuredBootstrapServers); + props.put( + GROUP_ID_CONFIG, poolConfig.getIdPrefix() + "_" + poolConfig.getConsumerGroupName()); + props.put(RECEIVE_BUFFER_CONFIG, poolConfig.getBufferSizeBytes()); + props.put(ENABLE_AUTO_COMMIT_CONFIG, false); + props.put(FETCH_MAX_WAIT_MS_CONFIG, poolConfig.getFetchMaxWaitMillis()); + props.put(FETCH_MIN_BYTES_CONFIG, poolConfig.getFetchMinBytes()); + props.put("key.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + props.put( + "value.deserializer", "org.apache.kafka.common.serialization.ByteArrayDeserializer"); + + if (poolConfig.isSaslEnabled()) { + props.put(SASL_MECHANISM, poolConfig.getSecurityMechanism()); + props.put(SECURITY_PROTOCOL_CONFIG, poolConfig.getSecurityProtocol()); + props.put(SASL_JAAS_CONFIG, poolConfig.getSaslJaasConfig()); + } + return new KafkaConsumer<>(props); } + } + + private static class KafkaConsumerRemoveListener + implements RemovalListener> { + @Override + public void onRemoval( + RemovalNotification> notification) { + notification.getValue().close(); + } + } } - diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolConfig.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolConfig.java index 772306fffa..38212a9093 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolConfig.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolConfig.java @@ -2,69 +2,77 @@ public class KafkaConsumerPoolConfig { - private final int cacheExpirationSeconds; - private final int bufferSizeBytes; - private final int fetchMaxWaitMillis; - private final int fetchMinBytes; - private final String idPrefix; - private final String consumerGroupName; - private final boolean isSaslEnabled; - private final String securityMechanism; - private final String securityProtocol; - private final String saslJaasConfig; + private final int cacheExpirationSeconds; + private final int bufferSizeBytes; + private final int fetchMaxWaitMillis; + private final int fetchMinBytes; + private final String idPrefix; + private final String consumerGroupName; + private final boolean isSaslEnabled; + private final String securityMechanism; + private final String securityProtocol; + private final String saslJaasConfig; - public KafkaConsumerPoolConfig(int cacheExpirationSeconds, int bufferSize, int fetchMaxWaitMillis, - int fetchMinBytes, String idPrefix, String consumerGroupName, - boolean isSaslEnabled, String securityMechanism, String securityProtocol, String saslJaasConfig) { - this.cacheExpirationSeconds = cacheExpirationSeconds; - this.bufferSizeBytes = bufferSize; - this.fetchMaxWaitMillis = fetchMaxWaitMillis; - this.fetchMinBytes = fetchMinBytes; - this.idPrefix = idPrefix; - this.consumerGroupName = consumerGroupName; - this.isSaslEnabled = isSaslEnabled; - this.securityMechanism = securityMechanism; - this.securityProtocol = securityProtocol; - this.saslJaasConfig = saslJaasConfig; - } + public KafkaConsumerPoolConfig( + int cacheExpirationSeconds, + int bufferSize, + int fetchMaxWaitMillis, + int fetchMinBytes, + String idPrefix, + String consumerGroupName, + boolean isSaslEnabled, + String securityMechanism, + String securityProtocol, + String saslJaasConfig) { + this.cacheExpirationSeconds = cacheExpirationSeconds; + this.bufferSizeBytes = bufferSize; + this.fetchMaxWaitMillis = fetchMaxWaitMillis; + this.fetchMinBytes = fetchMinBytes; + this.idPrefix = idPrefix; + this.consumerGroupName = consumerGroupName; + this.isSaslEnabled = isSaslEnabled; + this.securityMechanism = securityMechanism; + this.securityProtocol = securityProtocol; + this.saslJaasConfig = saslJaasConfig; + } - public int getCacheExpirationSeconds() { - return cacheExpirationSeconds; - } + public int getCacheExpirationSeconds() { + return cacheExpirationSeconds; + } - public int getBufferSizeBytes() { - return bufferSizeBytes; - } + public int getBufferSizeBytes() { + return bufferSizeBytes; + } - public String getIdPrefix() { - return idPrefix; - } + public String getIdPrefix() { + return idPrefix; + } - public String getConsumerGroupName() { - return consumerGroupName; - } + public String getConsumerGroupName() { + return consumerGroupName; + } - public int getFetchMaxWaitMillis() { - return fetchMaxWaitMillis; - } + public int getFetchMaxWaitMillis() { + return fetchMaxWaitMillis; + } - public int getFetchMinBytes() { - return fetchMinBytes; - } + public int getFetchMinBytes() { + return fetchMinBytes; + } - public boolean isSaslEnabled() { - return isSaslEnabled; - } + public boolean isSaslEnabled() { + return isSaslEnabled; + } - public String getSecurityMechanism() { - return securityMechanism; - } + public String getSecurityMechanism() { + return securityMechanism; + } - public String getSecurityProtocol() { - return securityProtocol; - } + public String getSecurityProtocol() { + return securityProtocol; + } - public String getSaslJaasConfig() { - return saslJaasConfig; - } + public String getSaslJaasConfig() { + return saslJaasConfig; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolException.java index 150031e460..bc55b8cc6d 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaConsumerPoolException.java @@ -5,13 +5,12 @@ public class KafkaConsumerPoolException extends HermesException { - public KafkaConsumerPoolException(String message, Throwable t) { - super(message, t); - } - - @Override - public ErrorCode getCode() { - return ErrorCode.SIMPLE_CONSUMER_POOL_EXCEPTION; - } + public KafkaConsumerPoolException(String message, Throwable t) { + super(message, t); + } + @Override + public ErrorCode getCode() { + return ErrorCode.SIMPLE_CONSUMER_POOL_EXCEPTION; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaHeaderNameParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaHeaderNameParameters.java index da9048b89b..bf6f0bbc57 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaHeaderNameParameters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaHeaderNameParameters.java @@ -2,12 +2,9 @@ public interface KafkaHeaderNameParameters { - String getSchemaVersion(); + String getSchemaVersion(); - String getSchemaId(); - - String getMessageId(); - - String getTimestamp(); + String getSchemaId(); + String getMessageId(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaNamesMapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaNamesMapper.java index d926d3f9aa..1613f222ed 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaNamesMapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaNamesMapper.java @@ -5,7 +5,7 @@ public interface KafkaNamesMapper { - ConsumerGroupId toConsumerGroupId(SubscriptionName subscription); + ConsumerGroupId toConsumerGroupId(SubscriptionName subscription); - KafkaTopics toKafkaTopics(Topic topic); + KafkaTopics toKafkaTopics(Topic topic); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaParameters.java index 3e50065dfd..113ad8b6d0 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaParameters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaParameters.java @@ -2,15 +2,15 @@ public interface KafkaParameters { - boolean isEnabled(); + String getDatacenter(); - String getMechanism(); + boolean isAuthenticationEnabled(); - String getProtocol(); + String getAuthenticationMechanism(); - String getUsername(); + String getAuthenticationProtocol(); - String getPassword(); + String getBrokerList(); - String getBrokerList(); + String getJaasConfig(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopic.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopic.java index e6155a4273..63a786bb66 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopic.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopic.java @@ -1,53 +1,51 @@ package pl.allegro.tech.hermes.common.kafka; -import com.google.common.base.MoreObjects; -import pl.allegro.tech.hermes.api.ContentType; +import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.MoreObjects; import java.util.Objects; - -import static com.google.common.base.Preconditions.checkNotNull; +import pl.allegro.tech.hermes.api.ContentType; public class KafkaTopic { - private final KafkaTopicName name; - private final ContentType contentType; + private final KafkaTopicName name; + private final ContentType contentType; - public KafkaTopic(KafkaTopicName name, ContentType contentType) { - this.name = checkNotNull(name); - this.contentType = checkNotNull(contentType); - } - - public KafkaTopicName name() { - return name; - } + public KafkaTopic(KafkaTopicName name, ContentType contentType) { + this.name = checkNotNull(name); + this.contentType = checkNotNull(contentType); + } - public ContentType contentType() { - return contentType; - } + public KafkaTopicName name() { + return name; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - KafkaTopic that = (KafkaTopic) o; - return Objects.equals(name, that.name) - && Objects.equals(contentType, that.contentType); - } + public ContentType contentType() { + return contentType; + } - @Override - public int hashCode() { - return Objects.hash(name, contentType); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("name", name) - .add("contentType", contentType) - .toString(); + if (o == null || getClass() != o.getClass()) { + return false; } + KafkaTopic that = (KafkaTopic) o; + return Objects.equals(name, that.name) && Objects.equals(contentType, that.contentType); + } + + @Override + public int hashCode() { + return Objects.hash(name, contentType); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("contentType", contentType) + .toString(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopicName.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopicName.java index 58f7f438ef..8c2ab7dea6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopicName.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopicName.java @@ -1,50 +1,49 @@ package pl.allegro.tech.hermes.common.kafka; +import static com.google.common.base.Preconditions.checkNotNull; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Objects; -import static com.google.common.base.Preconditions.checkNotNull; - public class KafkaTopicName { - private final String value; + private final String value; - private KafkaTopicName(String value) { - this.value = checkNotNull(value); - } + private KafkaTopicName(String value) { + this.value = checkNotNull(value); + } - @JsonCreator - public static KafkaTopicName valueOf(@JsonProperty("value") String value) { - return new KafkaTopicName(value); - } + @JsonCreator + public static KafkaTopicName valueOf(@JsonProperty("value") String value) { + return new KafkaTopicName(value); + } - @JsonGetter(value = "value") - public String asString() { - return value; - } + @JsonGetter(value = "value") + public String asString() { + return value; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - KafkaTopicName that = (KafkaTopicName) o; - return Objects.equals(value, that.value); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public String toString() { - return "KafkaTopicName(" + value + ")"; + if (o == null || getClass() != o.getClass()) { + return false; } + KafkaTopicName that = (KafkaTopicName) o; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "KafkaTopicName(" + value + ")"; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopics.java index dde42b8944..4093a28bd0 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/KafkaTopics.java @@ -1,45 +1,45 @@ package pl.allegro.tech.hermes.common.kafka; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; -import static com.google.common.base.Preconditions.checkNotNull; - public class KafkaTopics { - private final KafkaTopic primary; - private final Optional secondary; + private final KafkaTopic primary; + private final Optional secondary; - public KafkaTopics(KafkaTopic primary) { - this.primary = checkNotNull(primary); - this.secondary = Optional.empty(); - } + public KafkaTopics(KafkaTopic primary) { + this.primary = checkNotNull(primary); + this.secondary = Optional.empty(); + } - public KafkaTopics(KafkaTopic primary, KafkaTopic secondary) { - this.primary = checkNotNull(primary); - this.secondary = Optional.of(secondary); - } + public KafkaTopics(KafkaTopic primary, KafkaTopic secondary) { + this.primary = checkNotNull(primary); + this.secondary = Optional.of(secondary); + } - public KafkaTopic getPrimary() { - return primary; - } + public KafkaTopic getPrimary() { + return primary; + } - public Optional getSecondary() { - return secondary; - } + public Optional getSecondary() { + return secondary; + } - public void forEach(Consumer consumer) { - consumer.accept(primary); - secondary.ifPresent(consumer); - } + public void forEach(Consumer consumer) { + consumer.accept(primary); + secondary.ifPresent(consumer); + } - public boolean allMatch(Function matcher) { - return matcher.apply(primary) && secondary.map(matcher).orElse(true); - } + public boolean allMatch(Function matcher) { + return matcher.apply(primary) && secondary.map(matcher).orElse(true); + } - public Stream stream() { - return secondary.map(secondary -> Stream.of(primary, secondary)).orElse(Stream.of(primary)); - } + public Stream stream() { + return secondary.map(secondary -> Stream.of(primary, secondary)).orElse(Stream.of(primary)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/NamespaceKafkaNamesMapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/NamespaceKafkaNamesMapper.java index d0577e3f46..18a1414f78 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/NamespaceKafkaNamesMapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/NamespaceKafkaNamesMapper.java @@ -1,52 +1,54 @@ package pl.allegro.tech.hermes.common.kafka; +import static pl.allegro.tech.hermes.api.helpers.Replacer.replaceInAll; + import com.google.common.base.Joiner; +import java.util.function.Function; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.api.Topic; -import java.util.function.Function; - -import static pl.allegro.tech.hermes.api.helpers.Replacer.replaceInAll; - public class NamespaceKafkaNamesMapper implements KafkaNamesMapper { - private final String namespace; - private final String namespaceSeparator; + private final String namespace; + private final String namespaceSeparator; - public NamespaceKafkaNamesMapper(String namespace, String namespaceSeparator) { - this.namespace = namespace; - this.namespaceSeparator = namespaceSeparator; - } + public NamespaceKafkaNamesMapper(String namespace, String namespaceSeparator) { + this.namespace = namespace; + this.namespaceSeparator = namespaceSeparator; + } - @Override - public ConsumerGroupId toConsumerGroupId(SubscriptionName subscriptionName) { - return ConsumerGroupId.valueOf( - appendNamespace(subscriptionNameToConsumerId(subscriptionName)) - ); - } + @Override + public ConsumerGroupId toConsumerGroupId(SubscriptionName subscriptionName) { + return ConsumerGroupId.valueOf(appendNamespace(subscriptionNameToConsumerId(subscriptionName))); + } - @Override - public KafkaTopics toKafkaTopics(Topic topic) { - return mapToKafkaTopic.andThen(appendNamespace).andThen(mapToKafkaTopics).apply(topic); - } + @Override + public KafkaTopics toKafkaTopics(Topic topic) { + return mapToKafkaTopic.andThen(appendNamespace).andThen(mapToKafkaTopics).apply(topic); + } - protected Function mapToKafkaTopic = it -> - new KafkaTopic(KafkaTopicName.valueOf(it.getQualifiedName()), it.getContentType()); + protected Function mapToKafkaTopic = + it -> new KafkaTopic(KafkaTopicName.valueOf(it.getQualifiedName()), it.getContentType()); - protected Function appendNamespace = it -> - new KafkaTopic(KafkaTopicName.valueOf(appendNamespace(it.name().asString())), it.contentType()); + protected Function appendNamespace = + it -> + new KafkaTopic( + KafkaTopicName.valueOf(appendNamespace(it.name().asString())), it.contentType()); - protected Function mapToKafkaTopics = KafkaTopics::new; + protected Function mapToKafkaTopics = KafkaTopics::new; - private String subscriptionNameToConsumerId(SubscriptionName subscriptionName) { - return Joiner.on("_").join(replaceInAll("_", "__", + private String subscriptionNameToConsumerId(SubscriptionName subscriptionName) { + return Joiner.on("_") + .join( + replaceInAll( + "_", + "__", subscriptionName.getTopicName().getGroupName(), subscriptionName.getTopicName().getName(), - subscriptionName.getName()) - ); - } + subscriptionName.getName())); + } - private String appendNamespace(String name) { - return namespace.isEmpty() ? name : namespace + namespaceSeparator + name; - } + private String appendNamespace(String name) { + return namespace.isEmpty() ? name : namespace + namespaceSeparator + name; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffset.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffset.java index cc390200ae..459770e508 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffset.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffset.java @@ -2,55 +2,54 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import pl.allegro.tech.hermes.common.kafka.KafkaTopicName; - import java.util.Objects; +import pl.allegro.tech.hermes.common.kafka.KafkaTopicName; public class PartitionOffset { - private final KafkaTopicName topic; - private final int partition; - private final long offset; - - @JsonCreator - public PartitionOffset(@JsonProperty("topic") KafkaTopicName topic, - @JsonProperty("offset") long offset, - @JsonProperty("partition") int partition) { - this.topic = topic; - this.offset = offset; - this.partition = partition; + private final KafkaTopicName topic; + private final int partition; + private final long offset; + + @JsonCreator + public PartitionOffset( + @JsonProperty("topic") KafkaTopicName topic, + @JsonProperty("offset") long offset, + @JsonProperty("partition") int partition) { + this.topic = topic; + this.offset = offset; + this.partition = partition; + } + + public KafkaTopicName getTopic() { + return topic; + } + + public int getPartition() { + return partition; + } + + public long getOffset() { + return offset; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - public KafkaTopicName getTopic() { - return topic; + if (obj == null || getClass() != obj.getClass()) { + return false; } - - public int getPartition() { - return partition; - } - - public long getOffset() { - return offset; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final PartitionOffset that = (PartitionOffset) obj; - - return Objects.equals(this.topic, that.topic) - && Objects.equals(this.partition, that.partition) - && Objects.equals(this.offset, that.offset); - } - - @Override - public int hashCode() { - return Objects.hash(topic, partition, offset); - } - -} \ No newline at end of file + final PartitionOffset that = (PartitionOffset) obj; + + return Objects.equals(this.topic, that.topic) + && Objects.equals(this.partition, that.partition) + && Objects.equals(this.offset, that.offset); + } + + @Override + public int hashCode() { + return Objects.hash(topic, partition, offset); + } +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffsets.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffsets.java index a0f2023064..c0c5b1ef30 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffsets.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/PartitionOffsets.java @@ -6,20 +6,20 @@ public class PartitionOffsets implements Iterable { - private final List offsets = new ArrayList<>(); + private final List offsets = new ArrayList<>(); - public PartitionOffsets add(PartitionOffset offset) { - offsets.add(offset); - return this; - } + public PartitionOffsets add(PartitionOffset offset) { + offsets.add(offset); + return this; + } - public PartitionOffsets addAll(PartitionOffsets offsets) { - offsets.forEach(this::add); - return this; - } + public PartitionOffsets addAll(PartitionOffsets offsets) { + offsets.forEach(this::add); + return this; + } - @Override - public Iterator iterator() { - return offsets.iterator(); - } + @Override + public Iterator iterator() { + return offsets.iterator(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/SubscriptionOffsetChangeIndicator.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/SubscriptionOffsetChangeIndicator.java index 095b3746a8..863c8a8801 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/SubscriptionOffsetChangeIndicator.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/kafka/offset/SubscriptionOffsetChangeIndicator.java @@ -1,21 +1,32 @@ package pl.allegro.tech.hermes.common.kafka.offset; +import java.util.List; import pl.allegro.tech.hermes.api.TopicName; import pl.allegro.tech.hermes.common.kafka.KafkaTopic; import pl.allegro.tech.hermes.common.kafka.KafkaTopicName; -import java.util.List; - public interface SubscriptionOffsetChangeIndicator { - void setSubscriptionOffset(TopicName topicName, String subscriptionName, String brokersClusterName, PartitionOffset partitionOffset); - - PartitionOffsets getSubscriptionOffsets(TopicName topic, String subscriptionName, String brokersClusterName); - - boolean areOffsetsMoved(TopicName topicName, String subscriptionName, String brokersClusterName, - KafkaTopic kafkaTopic, List partitionIds); - - - void removeOffset(TopicName topicName, String subscriptionName, String brokersClusterName, - KafkaTopicName kafkaTopicName, int partitionId); + void setSubscriptionOffset( + TopicName topicName, + String subscriptionName, + String brokersClusterName, + PartitionOffset partitionOffset); + + PartitionOffsets getSubscriptionOffsets( + TopicName topic, String subscriptionName, String brokersClusterName); + + boolean areOffsetsMoved( + TopicName topicName, + String subscriptionName, + String brokersClusterName, + KafkaTopic kafkaTopic, + List partitionIds); + + void removeOffset( + TopicName topicName, + String subscriptionName, + String brokersClusterName, + KafkaTopicName kafkaTopicName, + int partitionId); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroBinaryDecoders.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroBinaryDecoders.java index c77be92b29..4c7d397651 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroBinaryDecoders.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroBinaryDecoders.java @@ -1,46 +1,51 @@ package pl.allegro.tech.hermes.common.message.converter; +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.InputStream; import org.apache.avro.Schema; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.BinaryDecoder; import org.apache.avro.io.DecoderFactory; -import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import tech.allegro.schema.json2avro.converter.AvroConversionException; -import java.io.ByteArrayInputStream; -import java.io.Closeable; -import java.io.InputStream; - public class AvroBinaryDecoders { - private static ThreadLocal threadLocalEmptyInputStream = - ThreadLocal.withInitial(() -> new ByteArrayInputStream(new byte[0])); - - private static ThreadLocal threadLocalBinaryDecoder = - ThreadLocal.withInitial(() -> DecoderFactory.get().binaryDecoder(threadLocalEmptyInputStream.get(), null)); - - static GenericRecord decodeReusingThreadLocalBinaryDecoder(byte[] message, Schema schema) { - try (FlushableBinaryDecoderHolder holder = new FlushableBinaryDecoderHolder()) { - BinaryDecoder binaryDecoder = DecoderFactory.get().binaryDecoder(message, holder.getBinaryDecoder()); - return new GenericDatumReader(schema).read(null, binaryDecoder); - } catch (Exception e) { - String reason = e.getMessage() == null ? ExceptionUtils.getRootCauseMessage(e) : e.getMessage(); - throw new AvroConversionException(String.format("Could not deserialize Avro message with provided schema, reason: %s", reason)); - } + private static ThreadLocal threadLocalEmptyInputStream = + ThreadLocal.withInitial(() -> new ByteArrayInputStream(new byte[0])); + + private static ThreadLocal threadLocalBinaryDecoder = + ThreadLocal.withInitial( + () -> DecoderFactory.get().binaryDecoder(threadLocalEmptyInputStream.get(), null)); + + static GenericRecord decodeReusingThreadLocalBinaryDecoder(byte[] message, Schema schema) { + try (FlushableBinaryDecoderHolder holder = new FlushableBinaryDecoderHolder()) { + BinaryDecoder binaryDecoder = + DecoderFactory.get().binaryDecoder(message, holder.getBinaryDecoder()); + return new GenericDatumReader(schema).read(null, binaryDecoder); + } catch (Exception e) { + String reason = + e.getMessage() == null ? ExceptionUtils.getRootCauseMessage(e) : e.getMessage(); + throw new AvroConversionException( + String.format( + "Could not deserialize Avro message with provided schema, reason: %s", reason)); } + } - static class FlushableBinaryDecoderHolder implements Closeable { + static class FlushableBinaryDecoderHolder implements Closeable { - final BinaryDecoder binaryDecoder = threadLocalBinaryDecoder.get(); + final BinaryDecoder binaryDecoder = threadLocalBinaryDecoder.get(); - BinaryDecoder getBinaryDecoder() { - return binaryDecoder; - } + BinaryDecoder getBinaryDecoder() { + return binaryDecoder; + } - @Override - public void close() { - DecoderFactory.get().binaryDecoder(threadLocalEmptyInputStream.get(), threadLocalBinaryDecoder.get()); - } + @Override + public void close() { + DecoderFactory.get() + .binaryDecoder(threadLocalEmptyInputStream.get(), threadLocalBinaryDecoder.get()); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroRecordToBytesConverter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroRecordToBytesConverter.java index ea4f6fe347..64762fcccc 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroRecordToBytesConverter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/converter/AvroRecordToBytesConverter.java @@ -1,26 +1,25 @@ package pl.allegro.tech.hermes.common.message.converter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import org.apache.avro.Schema; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.BinaryEncoder; import org.apache.avro.io.EncoderFactory; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - public interface AvroRecordToBytesConverter { - static GenericRecord bytesToRecord(byte[] data, Schema schema) { - return AvroBinaryDecoders.decodeReusingThreadLocalBinaryDecoder(data, schema); - } + static GenericRecord bytesToRecord(byte[] data, Schema schema) { + return AvroBinaryDecoders.decodeReusingThreadLocalBinaryDecoder(data, schema); + } - static byte [] recordToBytes(GenericRecord genericRecord, Schema schema) throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BinaryEncoder binaryEncoder = EncoderFactory.get().binaryEncoder(outputStream, null); - new GenericDatumWriter<>(schema).write(genericRecord, binaryEncoder); - binaryEncoder.flush(); + static byte[] recordToBytes(GenericRecord genericRecord, Schema schema) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BinaryEncoder binaryEncoder = EncoderFactory.get().binaryEncoder(outputStream, null); + new GenericDatumWriter<>(schema).write(genericRecord, binaryEncoder); + binaryEncoder.flush(); - return outputStream.toByteArray(); - } + return outputStream.toByteArray(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/LastUndeliveredMessageReader.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/LastUndeliveredMessageReader.java index 326060eefc..8cef6b8052 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/LastUndeliveredMessageReader.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/LastUndeliveredMessageReader.java @@ -1,11 +1,10 @@ package pl.allegro.tech.hermes.common.message.undelivered; +import java.util.Optional; import pl.allegro.tech.hermes.api.SentMessageTrace; import pl.allegro.tech.hermes.api.TopicName; -import java.util.Optional; - public interface LastUndeliveredMessageReader { - Optional last(TopicName topicName, String subscriptionName); + Optional last(TopicName topicName, String subscriptionName); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessageLog.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessageLog.java index 6897e76277..c76782e08a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessageLog.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessageLog.java @@ -1,11 +1,10 @@ package pl.allegro.tech.hermes.common.message.undelivered; - import pl.allegro.tech.hermes.api.SentMessageTrace; public interface UndeliveredMessageLog { - void add(SentMessageTrace undeliveredMessage); + void add(SentMessageTrace undeliveredMessage); - void persist(); + void persist(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessagePaths.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessagePaths.java index 54912a787c..b6555312f0 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessagePaths.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/UndeliveredMessagePaths.java @@ -5,15 +5,15 @@ class UndeliveredMessagePaths { - private static final String NODE_NAME = "undelivered"; + private static final String NODE_NAME = "undelivered"; - private final ZookeeperPaths zookeeperPaths; + private final ZookeeperPaths zookeeperPaths; - UndeliveredMessagePaths(ZookeeperPaths zookeeperPaths) { - this.zookeeperPaths = zookeeperPaths; - } + UndeliveredMessagePaths(ZookeeperPaths zookeeperPaths) { + this.zookeeperPaths = zookeeperPaths; + } - String buildPath(TopicName topicName, String subscriptionName) { - return zookeeperPaths.subscriptionPath(topicName, subscriptionName, NODE_NAME); - } + String buildPath(TopicName topicName, String subscriptionName) { + return zookeeperPaths.subscriptionPath(topicName, subscriptionName, NODE_NAME); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperLastUndeliveredMessageReader.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperLastUndeliveredMessageReader.java index f8c2a37ddd..c65cf2bc75 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperLastUndeliveredMessageReader.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperLastUndeliveredMessageReader.java @@ -1,48 +1,48 @@ package pl.allegro.tech.hermes.common.message.undelivered; +import static java.lang.String.format; + import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; import org.apache.curator.framework.CuratorFramework; import pl.allegro.tech.hermes.api.SentMessageTrace; import pl.allegro.tech.hermes.api.TopicName; import pl.allegro.tech.hermes.common.exception.InternalProcessingException; import pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperPaths; -import java.util.Optional; - -import static java.lang.String.format; - public class ZookeeperLastUndeliveredMessageReader implements LastUndeliveredMessageReader { - private final CuratorFramework curator; - private final UndeliveredMessagePaths paths; - private final ObjectMapper mapper; - - public ZookeeperLastUndeliveredMessageReader(CuratorFramework curator, - ZookeeperPaths zookeeperPaths, - ObjectMapper mapper) { - this.curator = curator; - this.paths = new UndeliveredMessagePaths(zookeeperPaths); - this.mapper = mapper; + private final CuratorFramework curator; + private final UndeliveredMessagePaths paths; + private final ObjectMapper mapper; + + public ZookeeperLastUndeliveredMessageReader( + CuratorFramework curator, ZookeeperPaths zookeeperPaths, ObjectMapper mapper) { + this.curator = curator; + this.paths = new UndeliveredMessagePaths(zookeeperPaths); + this.mapper = mapper; + } + + @Override + public Optional last(TopicName topicName, String subscriptionName) { + try { + String path = paths.buildPath(topicName, subscriptionName); + if (exists(path)) { + return Optional.of( + mapper.readValue(curator.getData().forPath(path), SentMessageTrace.class)); + } else { + return Optional.empty(); + } + } catch (Exception e) { + throw new InternalProcessingException( + format( + "Could not read latest undelivered message for topic: %s and subscription: %s .", + topicName.qualifiedName(), subscriptionName), + e); } + } - @Override - public Optional last(TopicName topicName, String subscriptionName) { - try { - String path = paths.buildPath(topicName, subscriptionName); - if (exists(path)) { - return Optional.of(mapper.readValue(curator.getData().forPath(path), SentMessageTrace.class)); - } else { - return Optional.empty(); - } - } catch (Exception e) { - throw new InternalProcessingException( - format("Could not read latest undelivered message for topic: %s and subscription: %s .", - topicName.qualifiedName(), subscriptionName), - e); - } - } - - private boolean exists(String path) throws Exception { - return curator.checkExists().forPath(path) != null; - } + private boolean exists(String path) throws Exception { + return curator.checkExists().forPath(path) != null; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLog.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLog.java index 9baa14c3ea..9f781cd942 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLog.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLog.java @@ -1,6 +1,10 @@ package pl.allegro.tech.hermes.common.message.undelivered; +import static java.lang.String.format; + import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.BackgroundPathAndBytesable; import org.slf4j.Logger; @@ -12,66 +16,66 @@ import pl.allegro.tech.hermes.metrics.HermesCounter; import pl.allegro.tech.hermes.metrics.HermesHistogram; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import static java.lang.String.format; - public class ZookeeperUndeliveredMessageLog implements UndeliveredMessageLog { - private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperUndeliveredMessageLog.class); + private static final Logger LOGGER = + LoggerFactory.getLogger(ZookeeperUndeliveredMessageLog.class); - private final CuratorFramework curator; - private final UndeliveredMessagePaths paths; - private final ObjectMapper mapper; - private final HermesCounter persistedMessagesMeter; - private final HermesHistogram persistedMessageSizeHistogram; + private final CuratorFramework curator; + private final UndeliveredMessagePaths paths; + private final ObjectMapper mapper; + private final HermesCounter persistedMessagesMeter; + private final HermesHistogram persistedMessageSizeHistogram; - private final ConcurrentMap lastUndeliveredMessages = new ConcurrentHashMap<>(); + private final ConcurrentMap lastUndeliveredMessages = + new ConcurrentHashMap<>(); - public ZookeeperUndeliveredMessageLog(CuratorFramework curator, - ZookeeperPaths zookeeperPaths, - ObjectMapper mapper, - MetricsFacade metricsFacade) { - this.curator = curator; - this.paths = new UndeliveredMessagePaths(zookeeperPaths); - this.mapper = mapper; - persistedMessagesMeter = metricsFacade.undeliveredMessages().undeliveredMessagesCounter(); - persistedMessageSizeHistogram = metricsFacade.undeliveredMessages().undeliveredMessagesSizeHistogram(); - } + public ZookeeperUndeliveredMessageLog( + CuratorFramework curator, + ZookeeperPaths zookeeperPaths, + ObjectMapper mapper, + MetricsFacade metricsFacade) { + this.curator = curator; + this.paths = new UndeliveredMessagePaths(zookeeperPaths); + this.mapper = mapper; + persistedMessagesMeter = metricsFacade.undeliveredMessages().undeliveredMessagesCounter(); + persistedMessageSizeHistogram = + metricsFacade.undeliveredMessages().undeliveredMessagesSizeHistogram(); + } - @Override - public void add(SentMessageTrace message) { - lastUndeliveredMessages.put(new SubscriptionName(message.getSubscription(), message.getTopicName()), message); - } + @Override + public void add(SentMessageTrace message) { + lastUndeliveredMessages.put( + new SubscriptionName(message.getSubscription(), message.getTopicName()), message); + } - @Override - public void persist() { - for (SubscriptionName key : lastUndeliveredMessages.keySet()) { - log(lastUndeliveredMessages.remove(key)); - } + @Override + public void persist() { + for (SubscriptionName key : lastUndeliveredMessages.keySet()) { + log(lastUndeliveredMessages.remove(key)); } + } - private void log(SentMessageTrace messageTrace) { - try { - String undeliveredPath = paths.buildPath(messageTrace.getTopicName(), messageTrace.getSubscription()); - BackgroundPathAndBytesable builder = exists(undeliveredPath) ? curator.setData() : curator.create(); - byte[] bytesToPersist = mapper.writeValueAsBytes(messageTrace); - builder.forPath(undeliveredPath, bytesToPersist); - persistedMessagesMeter.increment(); - persistedMessageSizeHistogram.record(bytesToPersist.length); - } catch (Exception exception) { - LOGGER.warn( - format("Could not log undelivered message for topic: %s and subscription: %s", - messageTrace.getQualifiedTopicName(), - messageTrace.getSubscription() - ), - exception - ); - } + private void log(SentMessageTrace messageTrace) { + try { + String undeliveredPath = + paths.buildPath(messageTrace.getTopicName(), messageTrace.getSubscription()); + BackgroundPathAndBytesable builder = + exists(undeliveredPath) ? curator.setData() : curator.create(); + byte[] bytesToPersist = mapper.writeValueAsBytes(messageTrace); + builder.forPath(undeliveredPath, bytesToPersist); + persistedMessagesMeter.increment(); + persistedMessageSizeHistogram.record(bytesToPersist.length); + } catch (Exception exception) { + LOGGER.warn( + format( + "Could not log undelivered message for topic: %s and subscription: %s", + messageTrace.getQualifiedTopicName(), messageTrace.getSubscription()), + exception); } + } - private boolean exists(String path) throws Exception { - return curator.checkExists().forPath(path) != null; - } + private boolean exists(String path) throws Exception { + return curator.checkExists().forPath(path) != null; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroInvalidMetadataException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroInvalidMetadataException.java index a6dd9d20fe..3e1f7c4d47 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroInvalidMetadataException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroInvalidMetadataException.java @@ -3,7 +3,7 @@ import pl.allegro.tech.hermes.common.exception.InternalProcessingException; public class AvroInvalidMetadataException extends InternalProcessingException { - public AvroInvalidMetadataException(String message, Exception cause) { - super(message, cause); - } + public AvroInvalidMetadataException(String message, Exception cause) { + super(message, cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapper.java index 3f6d6ee30e..88549d34ae 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapper.java @@ -5,7 +5,8 @@ interface AvroMessageContentUnwrapper { - AvroMessageContentUnwrapperResult unwrap(byte[] data, Topic topic, @Nullable Integer headerId, @Nullable Integer headerVersion); + AvroMessageContentUnwrapperResult unwrap( + byte[] data, Topic topic, @Nullable Integer headerId, @Nullable Integer headerVersion); - boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion); + boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapperResult.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapperResult.java index 2849a842b6..8e8eeb7791 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapperResult.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentUnwrapperResult.java @@ -2,32 +2,35 @@ class AvroMessageContentUnwrapperResult { - private final UnwrappedMessageContent content; - private final AvroMessageContentUnwrapperResultStatus status; - - private AvroMessageContentUnwrapperResult(UnwrappedMessageContent content, - AvroMessageContentUnwrapperResultStatus status) { - this.content = content; - this.status = status; - } - - public static AvroMessageContentUnwrapperResult success(UnwrappedMessageContent content) { - return new AvroMessageContentUnwrapperResult(content, AvroMessageContentUnwrapperResultStatus.SUCCESS); - } - - public static AvroMessageContentUnwrapperResult failure() { - return new AvroMessageContentUnwrapperResult(null, AvroMessageContentUnwrapperResultStatus.FAILURE); - } - - public UnwrappedMessageContent getContent() { - return content; - } - - public AvroMessageContentUnwrapperResultStatus getStatus() { - return status; - } - - enum AvroMessageContentUnwrapperResultStatus { - SUCCESS, FAILURE - } + private final UnwrappedMessageContent content; + private final AvroMessageContentUnwrapperResultStatus status; + + private AvroMessageContentUnwrapperResult( + UnwrappedMessageContent content, AvroMessageContentUnwrapperResultStatus status) { + this.content = content; + this.status = status; + } + + public static AvroMessageContentUnwrapperResult success(UnwrappedMessageContent content) { + return new AvroMessageContentUnwrapperResult( + content, AvroMessageContentUnwrapperResultStatus.SUCCESS); + } + + public static AvroMessageContentUnwrapperResult failure() { + return new AvroMessageContentUnwrapperResult( + null, AvroMessageContentUnwrapperResultStatus.FAILURE); + } + + public UnwrappedMessageContent getContent() { + return content; + } + + public AvroMessageContentUnwrapperResultStatus getStatus() { + return status; + } + + enum AvroMessageContentUnwrapperResultStatus { + SUCCESS, + FAILURE + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapper.java index 9b34fe141e..e0244a4f2c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapper.java @@ -1,10 +1,10 @@ package pl.allegro.tech.hermes.common.message.wrapper; -import org.apache.avro.AvroRuntimeException; -import org.apache.avro.Schema; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.util.Utf8; -import pl.allegro.tech.hermes.schema.CompiledSchema; +import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; +import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.recordToBytes; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MARKER; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MESSAGE_ID_KEY; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_TIMESTAMP_KEY; import java.time.Clock; import java.util.Collections; @@ -12,95 +12,102 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import org.apache.avro.AvroRuntimeException; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.util.Utf8; +import pl.allegro.tech.hermes.schema.CompiledSchema; -import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; -import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.recordToBytes; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MARKER; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MESSAGE_ID_KEY; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_TIMESTAMP_KEY; - -/** This class deals only with wrapping and unwrapping messages. - * It does not generate any Hermes ID for message in case it is missing. - * Missing Hermes ID will be left as empty. +/** + * This class deals only with wrapping and unwrapping messages. It does not generate any Hermes ID + * for message in case it is missing. Missing Hermes ID will be left as empty. */ public class AvroMessageContentWrapper { - private final Clock clock; + private final Clock clock; - public AvroMessageContentWrapper(Clock clock) { - this.clock = clock; - } + public AvroMessageContentWrapper(Clock clock) { + this.clock = clock; + } - @SuppressWarnings("unchecked") - UnwrappedMessageContent unwrapContent(byte[] data, CompiledSchema schema) { - try { - Map metadata = (Map) bytesToRecord(data, schema.getSchema()).get(METADATA_MARKER); - MessageMetadata messageMetadata = getMetadata(metadata); + @SuppressWarnings("unchecked") + UnwrappedMessageContent unwrapContent(byte[] data, CompiledSchema schema) { + try { + Map metadata = + (Map) bytesToRecord(data, schema.getSchema()).get(METADATA_MARKER); + MessageMetadata messageMetadata = getMetadata(metadata); - return new UnwrappedMessageContent(messageMetadata, data, schema); - } catch (Exception exception) { - throw new UnwrappingException("Could not read avro message", exception); - } + return new UnwrappedMessageContent(messageMetadata, data, schema); + } catch (Exception exception) { + throw new UnwrappingException("Could not read avro message", exception); } - - @SuppressWarnings("unchecked") - private MessageMetadata getMetadata(Map metadata) { - if (metadata == null) { - long timestamp = clock.millis(); - return new MessageMetadata(timestamp, Collections.EMPTY_MAP); - } else { - long timestamp = metadata.containsKey(METADATA_TIMESTAMP_KEY) ? timestampFromMetadata(metadata) : - clock.millis(); - Map extractedMetadata = extractMetadata(metadata); - - return metadata.containsKey(METADATA_MESSAGE_ID_KEY) - ? new MessageMetadata(timestamp, messageIdFromMetadata(metadata), extractedMetadata) - : new MessageMetadata(timestamp, extractedMetadata); - } + } + + @SuppressWarnings("unchecked") + private MessageMetadata getMetadata(Map metadata) { + if (metadata == null) { + long timestamp = clock.millis(); + return new MessageMetadata(timestamp, Collections.EMPTY_MAP); + } else { + long timestamp = + metadata.containsKey(METADATA_TIMESTAMP_KEY) + ? timestampFromMetadata(metadata) + : clock.millis(); + Map extractedMetadata = extractMetadata(metadata); + + return metadata.containsKey(METADATA_MESSAGE_ID_KEY) + ? new MessageMetadata(timestamp, messageIdFromMetadata(metadata), extractedMetadata) + : new MessageMetadata(timestamp, extractedMetadata); } - - public byte[] wrapContent(byte[] message, String id, long timestamp, Schema schema, Map externalMetadata) { - if (schema.getField(METADATA_MARKER) != null) { - GenericRecord genericRecord = bytesToRecord(message, schema); - try { - genericRecord.put(METADATA_MARKER, metadataMap(id, timestamp, externalMetadata)); - return recordToBytes(genericRecord, schema); - } catch (Exception e) { - if (e instanceof AvroRuntimeException && e.getMessage().equals("Not a valid schema field: __metadata")) { - throw new AvroInvalidMetadataException( - "Schema does not contain mandatory __metadata field for Hermes internal metadata. Please fix topic schema.", e); - } - throw new WrappingException("Could not wrap avro message", e); - } - } else { - return message; + } + + public byte[] wrapContent( + byte[] message, + String id, + long timestamp, + Schema schema, + Map externalMetadata) { + if (schema.getField(METADATA_MARKER) != null) { + GenericRecord genericRecord = bytesToRecord(message, schema); + try { + genericRecord.put(METADATA_MARKER, metadataMap(id, timestamp, externalMetadata)); + return recordToBytes(genericRecord, schema); + } catch (Exception e) { + if (e instanceof AvroRuntimeException + && e.getMessage().equals("Not a valid schema field: __metadata")) { + throw new AvroInvalidMetadataException( + "Schema does not contain mandatory __metadata field for Hermes internal metadata. Please fix topic schema.", + e); } + throw new WrappingException("Could not wrap avro message", e); + } + } else { + return message; } - - private Map metadataMap(String id, long timestamp, Map externalMetadata) { - Map metadata = new HashMap<>(); - metadata.put(METADATA_MESSAGE_ID_KEY, new Utf8(id)); - metadata.put(METADATA_TIMESTAMP_KEY, new Utf8(Long.toString(timestamp))); - - externalMetadata.forEach((key, value) -> metadata.put(new Utf8(key), new Utf8(value))); - return metadata; - } - - private long timestampFromMetadata(Map metadata) { - return Long.parseLong(metadata.remove(METADATA_TIMESTAMP_KEY).toString()); - } - - private String messageIdFromMetadata(Map metadata) { - return metadata.remove(METADATA_MESSAGE_ID_KEY).toString(); - } - - private Map extractMetadata(Map metadata) { - return Optional.ofNullable(metadata).orElse(Collections.emptyMap()) - .entrySet() - .stream() - .collect(Collectors.toMap( - entry -> entry.getKey().toString(), - entry -> entry.getValue().toString() - )); - } + } + + private Map metadataMap( + String id, long timestamp, Map externalMetadata) { + Map metadata = new HashMap<>(); + metadata.put(METADATA_MESSAGE_ID_KEY, new Utf8(id)); + metadata.put(METADATA_TIMESTAMP_KEY, new Utf8(Long.toString(timestamp))); + + externalMetadata.forEach((key, value) -> metadata.put(new Utf8(key), new Utf8(value))); + return metadata; + } + + private long timestampFromMetadata(Map metadata) { + return Long.parseLong(metadata.remove(METADATA_TIMESTAMP_KEY).toString()); + } + + private String messageIdFromMetadata(Map metadata) { + return metadata.remove(METADATA_MESSAGE_ID_KEY).toString(); + } + + private Map extractMetadata(Map metadata) { + return Optional.ofNullable(metadata).orElse(Collections.emptyMap()).entrySet().stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toString(), entry -> entry.getValue().toString())); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaIdContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaIdContentWrapper.java index 1a87be5b84..5299a5f3b6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaIdContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaIdContentWrapper.java @@ -12,47 +12,55 @@ public class AvroMessageHeaderSchemaIdContentWrapper implements AvroMessageContentUnwrapper { - private static final Logger logger = LoggerFactory.getLogger(AvroMessageHeaderSchemaIdContentWrapper.class); + private static final Logger logger = + LoggerFactory.getLogger(AvroMessageHeaderSchemaIdContentWrapper.class); - private final SchemaRepository schemaRepository; - private final AvroMessageContentWrapper avroMessageContentWrapper; + private final SchemaRepository schemaRepository; + private final AvroMessageContentWrapper avroMessageContentWrapper; - private final HermesCounter deserializationWithErrorsUsingHeaderSchemaId; - private final HermesCounter deserializationUsingHeaderSchemaId; - private final boolean schemaIdHeaderEnabled; + private final HermesCounter deserializationWithErrorsUsingHeaderSchemaId; + private final HermesCounter deserializationUsingHeaderSchemaId; + private final boolean schemaIdHeaderEnabled; - public AvroMessageHeaderSchemaIdContentWrapper(SchemaRepository schemaRepository, - AvroMessageContentWrapper avroMessageContentWrapper, - MetricsFacade metrics, - boolean schemaIdHeaderEnabled) { - this.schemaRepository = schemaRepository; - this.avroMessageContentWrapper = avroMessageContentWrapper; + public AvroMessageHeaderSchemaIdContentWrapper( + SchemaRepository schemaRepository, + AvroMessageContentWrapper avroMessageContentWrapper, + MetricsFacade metrics, + boolean schemaIdHeaderEnabled) { + this.schemaRepository = schemaRepository; + this.avroMessageContentWrapper = avroMessageContentWrapper; - this.deserializationWithErrorsUsingHeaderSchemaId = metrics.deserialization().errorsForHeaderSchemaId(); - this.deserializationUsingHeaderSchemaId = metrics.deserialization().usingHeaderSchemaId(); - this.schemaIdHeaderEnabled = schemaIdHeaderEnabled; - } + this.deserializationWithErrorsUsingHeaderSchemaId = + metrics.deserialization().errorsForHeaderSchemaId(); + this.deserializationUsingHeaderSchemaId = metrics.deserialization().usingHeaderSchemaId(); + this.schemaIdHeaderEnabled = schemaIdHeaderEnabled; + } - @Override - public AvroMessageContentUnwrapperResult unwrap(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - try { - deserializationUsingHeaderSchemaId.increment(); - CompiledSchema avroSchema = schemaRepository.getAvroSchema(topic, SchemaId.valueOf(schemaId)); + @Override + public AvroMessageContentUnwrapperResult unwrap( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + try { + deserializationUsingHeaderSchemaId.increment(); + CompiledSchema avroSchema = + schemaRepository.getAvroSchema(topic, SchemaId.valueOf(schemaId)); - return AvroMessageContentUnwrapperResult.success(avroMessageContentWrapper.unwrapContent(data, avroSchema)); - } catch (Exception ex) { - logger.warn( - "Could not unwrap content for topic [{}] using schema id provided in header [{}] - falling back", - topic.getQualifiedName(), schemaVersion, ex); + return AvroMessageContentUnwrapperResult.success( + avroMessageContentWrapper.unwrapContent(data, avroSchema)); + } catch (Exception ex) { + logger.warn( + "Could not unwrap content for topic [{}] using schema id provided in header [{}] - falling back", + topic.getQualifiedName(), + schemaVersion, + ex); - deserializationWithErrorsUsingHeaderSchemaId.increment(); + deserializationWithErrorsUsingHeaderSchemaId.increment(); - return AvroMessageContentUnwrapperResult.failure(); - } + return AvroMessageContentUnwrapperResult.failure(); } + } - @Override - public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - return schemaIdHeaderEnabled && schemaId != null; - } + @Override + public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + return schemaIdHeaderEnabled && schemaId != null; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaVersionContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaVersionContentWrapper.java index 6d1d5b803a..4aa622a7b6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaVersionContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageHeaderSchemaVersionContentWrapper.java @@ -12,44 +12,53 @@ public class AvroMessageHeaderSchemaVersionContentWrapper implements AvroMessageContentUnwrapper { - private static final Logger logger = LoggerFactory.getLogger(AvroMessageHeaderSchemaVersionContentWrapper.class); + private static final Logger logger = + LoggerFactory.getLogger(AvroMessageHeaderSchemaVersionContentWrapper.class); - private final SchemaRepository schemaRepository; - private final AvroMessageContentWrapper avroMessageContentWrapper; + private final SchemaRepository schemaRepository; + private final AvroMessageContentWrapper avroMessageContentWrapper; - private final HermesCounter deserializationWithErrorsUsingHeaderSchemaVersion; - private final HermesCounter deserializationUsingHeaderSchemaVersion; + private final HermesCounter deserializationWithErrorsUsingHeaderSchemaVersion; + private final HermesCounter deserializationUsingHeaderSchemaVersion; - public AvroMessageHeaderSchemaVersionContentWrapper(SchemaRepository schemaRepository, - AvroMessageContentWrapper avroMessageContentWrapper, - MetricsFacade metrics) { - this.schemaRepository = schemaRepository; - this.avroMessageContentWrapper = avroMessageContentWrapper; + public AvroMessageHeaderSchemaVersionContentWrapper( + SchemaRepository schemaRepository, + AvroMessageContentWrapper avroMessageContentWrapper, + MetricsFacade metrics) { + this.schemaRepository = schemaRepository; + this.avroMessageContentWrapper = avroMessageContentWrapper; - this.deserializationWithErrorsUsingHeaderSchemaVersion = metrics.deserialization().errorsForHeaderSchemaVersion(); - this.deserializationUsingHeaderSchemaVersion = metrics.deserialization().usingHeaderSchemaVersion(); - } + this.deserializationWithErrorsUsingHeaderSchemaVersion = + metrics.deserialization().errorsForHeaderSchemaVersion(); + this.deserializationUsingHeaderSchemaVersion = + metrics.deserialization().usingHeaderSchemaVersion(); + } - @Override - public AvroMessageContentUnwrapperResult unwrap(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - try { - deserializationUsingHeaderSchemaVersion.increment(); - CompiledSchema avroSchema = schemaRepository.getAvroSchema(topic, SchemaVersion.valueOf(schemaVersion)); + @Override + public AvroMessageContentUnwrapperResult unwrap( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + try { + deserializationUsingHeaderSchemaVersion.increment(); + CompiledSchema avroSchema = + schemaRepository.getAvroSchema(topic, SchemaVersion.valueOf(schemaVersion)); - return AvroMessageContentUnwrapperResult.success(avroMessageContentWrapper.unwrapContent(data, avroSchema)); - } catch (Exception ex) { - logger.warn( - "Could not unwrap content for topic [{}] using schema version provided in header [{}] - falling back", - topic.getQualifiedName(), schemaVersion, ex); + return AvroMessageContentUnwrapperResult.success( + avroMessageContentWrapper.unwrapContent(data, avroSchema)); + } catch (Exception ex) { + logger.warn( + "Could not unwrap content for topic [{}] using schema version provided in header [{}] - falling back", + topic.getQualifiedName(), + schemaVersion, + ex); - deserializationWithErrorsUsingHeaderSchemaVersion.increment(); + deserializationWithErrorsUsingHeaderSchemaVersion.increment(); - return AvroMessageContentUnwrapperResult.failure(); - } + return AvroMessageContentUnwrapperResult.failure(); } + } - @Override - public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - return schemaVersion != null; - } + @Override + public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + return schemaVersion != null; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaIdAwareContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaIdAwareContentWrapper.java index 79e28d67a0..45719da8e6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaIdAwareContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaIdAwareContentWrapper.java @@ -11,58 +11,68 @@ public class AvroMessageSchemaIdAwareContentWrapper implements AvroMessageContentUnwrapper { - private static final Logger logger = LoggerFactory.getLogger(AvroMessageSchemaIdAwareContentWrapper.class); - - private final SchemaRepository schemaRepository; - private final AvroMessageContentWrapper avroMessageContentWrapper; - - private final HermesCounter deserializationUsingSchemaIdAware; - private final HermesCounter deserializationErrorsForSchemaIdAwarePayload; - private final HermesCounter deserializationWithMissingSchemaIdInPayload; - - public AvroMessageSchemaIdAwareContentWrapper(SchemaRepository schemaRepository, - AvroMessageContentWrapper avroMessageContentWrapper, - MetricsFacade metrics) { - this.schemaRepository = schemaRepository; - this.avroMessageContentWrapper = avroMessageContentWrapper; - - this.deserializationErrorsForSchemaIdAwarePayload = metrics.deserialization().errorsForSchemaIdAwarePayload(); - this.deserializationWithMissingSchemaIdInPayload = metrics.deserialization().missingSchemaIdInPayload(); - this.deserializationUsingSchemaIdAware = metrics.deserialization().usingSchemaIdAware(); + private static final Logger logger = + LoggerFactory.getLogger(AvroMessageSchemaIdAwareContentWrapper.class); + + private final SchemaRepository schemaRepository; + private final AvroMessageContentWrapper avroMessageContentWrapper; + + private final HermesCounter deserializationUsingSchemaIdAware; + private final HermesCounter deserializationErrorsForSchemaIdAwarePayload; + private final HermesCounter deserializationWithMissingSchemaIdInPayload; + + public AvroMessageSchemaIdAwareContentWrapper( + SchemaRepository schemaRepository, + AvroMessageContentWrapper avroMessageContentWrapper, + MetricsFacade metrics) { + this.schemaRepository = schemaRepository; + this.avroMessageContentWrapper = avroMessageContentWrapper; + + this.deserializationErrorsForSchemaIdAwarePayload = + metrics.deserialization().errorsForSchemaIdAwarePayload(); + this.deserializationWithMissingSchemaIdInPayload = + metrics.deserialization().missingSchemaIdInPayload(); + this.deserializationUsingSchemaIdAware = metrics.deserialization().usingSchemaIdAware(); + } + + @Override + public AvroMessageContentUnwrapperResult unwrap( + byte[] data, Topic topic, Integer schemaIdFromHeader, Integer schemaVersionFromHeader) { + try { + deserializationUsingSchemaIdAware.increment(); + + SchemaAwarePayload payload = SchemaAwareSerDe.deserialize(data); + CompiledSchema avroSchema = + schemaRepository.getAvroSchema(topic, payload.getSchemaId()); + + return AvroMessageContentUnwrapperResult.success( + avroMessageContentWrapper.unwrapContent(payload.getPayload(), avroSchema)); + } catch (Exception ex) { + logger.warn( + "Could not deserialize schema id aware payload for topic [{}] - falling back", + topic.getQualifiedName(), + ex); + + deserializationErrorsForSchemaIdAwarePayload.increment(); + + return AvroMessageContentUnwrapperResult.failure(); } + } - @Override - public AvroMessageContentUnwrapperResult unwrap(byte[] data, Topic topic, Integer schemaIdFromHeader, Integer schemaVersionFromHeader) { - try { - deserializationUsingSchemaIdAware.increment(); - - SchemaAwarePayload payload = SchemaAwareSerDe.deserialize(data); - CompiledSchema avroSchema = schemaRepository.getAvroSchema(topic, payload.getSchemaId()); - - return AvroMessageContentUnwrapperResult.success(avroMessageContentWrapper.unwrapContent(payload.getPayload(), avroSchema)); - } catch (Exception ex) { - logger.warn("Could not deserialize schema id aware payload for topic [{}] - falling back", topic.getQualifiedName(), ex); + @Override + public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + return isPayloadAwareOfSchemaId(data, topic); + } - deserializationErrorsForSchemaIdAwarePayload.increment(); + private boolean isPayloadAwareOfSchemaId(byte[] data, Topic topic) { + if (topic.isSchemaIdAwareSerializationEnabled()) { + if (SchemaAwareSerDe.startsWithMagicByte(data)) { + return true; + } - return AvroMessageContentUnwrapperResult.failure(); - } + deserializationWithMissingSchemaIdInPayload.increment(); } - @Override - public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - return isPayloadAwareOfSchemaId(data, topic); - } - - private boolean isPayloadAwareOfSchemaId(byte[] data, Topic topic) { - if (topic.isSchemaIdAwareSerializationEnabled()) { - if (SchemaAwareSerDe.startsWithMagicByte(data)) { - return true; - } - - deserializationWithMissingSchemaIdInPayload.increment(); - } - - return false; - } + return false; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaVersionTruncationContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaVersionTruncationContentWrapper.java index c4f39e6aa6..b84dcafdb7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaVersionTruncationContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageSchemaVersionTruncationContentWrapper.java @@ -10,57 +10,66 @@ import pl.allegro.tech.hermes.schema.SchemaRepository; import pl.allegro.tech.hermes.schema.SchemaVersion; -public class AvroMessageSchemaVersionTruncationContentWrapper implements AvroMessageContentUnwrapper { +public class AvroMessageSchemaVersionTruncationContentWrapper + implements AvroMessageContentUnwrapper { - private static final Logger logger = LoggerFactory.getLogger(AvroMessageSchemaVersionTruncationContentWrapper.class); + private static final Logger logger = + LoggerFactory.getLogger(AvroMessageSchemaVersionTruncationContentWrapper.class); - private final SchemaRepository schemaRepository; - private final AvroMessageContentWrapper avroMessageContentWrapper; - private final boolean magicByteTruncationEnabled; + private final SchemaRepository schemaRepository; + private final AvroMessageContentWrapper avroMessageContentWrapper; + private final boolean magicByteTruncationEnabled; - private final HermesCounter deserializationWithSchemaVersionTruncation; - private final HermesCounter deserializationErrorsWithSchemaVersionTruncation; + private final HermesCounter deserializationWithSchemaVersionTruncation; + private final HermesCounter deserializationErrorsWithSchemaVersionTruncation; - public AvroMessageSchemaVersionTruncationContentWrapper(SchemaRepository schemaRepository, - AvroMessageContentWrapper avroMessageContentWrapper, - MetricsFacade metrics, - boolean schemaVersionTruncationEnabled) { - this.schemaRepository = schemaRepository; - this.avroMessageContentWrapper = avroMessageContentWrapper; - this.magicByteTruncationEnabled = schemaVersionTruncationEnabled; - - this.deserializationErrorsWithSchemaVersionTruncation = metrics.deserialization().errorsForSchemaVersionTruncation(); - this.deserializationWithSchemaVersionTruncation = metrics.deserialization().usingSchemaVersionTruncation(); - } + public AvroMessageSchemaVersionTruncationContentWrapper( + SchemaRepository schemaRepository, + AvroMessageContentWrapper avroMessageContentWrapper, + MetricsFacade metrics, + boolean schemaVersionTruncationEnabled) { + this.schemaRepository = schemaRepository; + this.avroMessageContentWrapper = avroMessageContentWrapper; + this.magicByteTruncationEnabled = schemaVersionTruncationEnabled; + this.deserializationErrorsWithSchemaVersionTruncation = + metrics.deserialization().errorsForSchemaVersionTruncation(); + this.deserializationWithSchemaVersionTruncation = + metrics.deserialization().usingSchemaVersionTruncation(); + } - @Override - public AvroMessageContentUnwrapperResult unwrap(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - try { - deserializationWithSchemaVersionTruncation.increment(); + @Override + public AvroMessageContentUnwrapperResult unwrap( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + try { + deserializationWithSchemaVersionTruncation.increment(); - byte[] dataWithoutMagicByteAndSchemaId = SchemaAwareSerDe.trimMagicByteAndSchemaVersion(data); - CompiledSchema avroSchema = schemaRepository.getAvroSchema(topic, SchemaVersion.valueOf(schemaVersion)); + byte[] dataWithoutMagicByteAndSchemaId = SchemaAwareSerDe.trimMagicByteAndSchemaVersion(data); + CompiledSchema avroSchema = + schemaRepository.getAvroSchema(topic, SchemaVersion.valueOf(schemaVersion)); - return AvroMessageContentUnwrapperResult.success( - avroMessageContentWrapper.unwrapContent(dataWithoutMagicByteAndSchemaId, avroSchema)); - } catch (Exception e) { - logger.warn( - "Could not unwrap content for topic [{}] using schema id provided in header [{}] - falling back", - topic.getQualifiedName(), schemaVersion, e); + return AvroMessageContentUnwrapperResult.success( + avroMessageContentWrapper.unwrapContent(dataWithoutMagicByteAndSchemaId, avroSchema)); + } catch (Exception e) { + logger.warn( + "Could not unwrap content for topic [{}] using schema id provided in header [{}] - falling back", + topic.getQualifiedName(), + schemaVersion, + e); - deserializationErrorsWithSchemaVersionTruncation.increment(); + deserializationErrorsWithSchemaVersionTruncation.increment(); - return AvroMessageContentUnwrapperResult.failure(); - } + return AvroMessageContentUnwrapperResult.failure(); } + } - @Override - public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - return magicByteTruncationEnabled && containsSchemaVersionInMagicByteAndInHeader(data, schemaVersion); - } + @Override + public boolean isApplicable(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + return magicByteTruncationEnabled + && containsSchemaVersionInMagicByteAndInHeader(data, schemaVersion); + } - private boolean containsSchemaVersionInMagicByteAndInHeader(byte[] data, Integer schemaVersion) { - return SchemaAwareSerDe.startsWithMagicByte(data) && schemaVersion != null; - } + private boolean containsSchemaVersionInMagicByteAndInHeader(byte[] data, Integer schemaVersion) { + return SchemaAwareSerDe.startsWithMagicByte(data) && schemaVersion != null; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMetadataMarker.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMetadataMarker.java index c860040606..c67291753b 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMetadataMarker.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMetadataMarker.java @@ -3,7 +3,7 @@ import org.apache.avro.util.Utf8; public interface AvroMetadataMarker { - String METADATA_MARKER = "__metadata"; - Utf8 METADATA_TIMESTAMP_KEY = new Utf8("timestamp"); - Utf8 METADATA_MESSAGE_ID_KEY = new Utf8("messageId"); + String METADATA_MARKER = "__metadata"; + Utf8 METADATA_TIMESTAMP_KEY = new Utf8("timestamp"); + Utf8 METADATA_MESSAGE_ID_KEY = new Utf8("messageId"); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/CompositeMessageContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/CompositeMessageContentWrapper.java index 53de748f1b..69d424df4a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/CompositeMessageContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/CompositeMessageContentWrapper.java @@ -1,72 +1,83 @@ package pl.allegro.tech.hermes.common.message.wrapper; +import static java.util.Arrays.asList; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMessageContentUnwrapperResult.AvroMessageContentUnwrapperResultStatus.SUCCESS; + +import java.util.Collection; +import java.util.Map; import org.apache.avro.Schema; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.schema.CompiledSchema; -import java.util.Collection; -import java.util.Map; - -import static java.util.Arrays.asList; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMessageContentUnwrapperResult.AvroMessageContentUnwrapperResultStatus.SUCCESS; - public class CompositeMessageContentWrapper implements MessageContentWrapper { - private static final Logger logger = LoggerFactory.getLogger(CompositeMessageContentWrapper.class); + private static final Logger logger = + LoggerFactory.getLogger(CompositeMessageContentWrapper.class); - private final JsonMessageContentWrapper jsonMessageContentWrapper; - private final AvroMessageContentWrapper avroMessageContentWrapper; - private final Collection avroMessageContentUnwrappers; + private final JsonMessageContentWrapper jsonMessageContentWrapper; + private final AvroMessageContentWrapper avroMessageContentWrapper; + private final Collection avroMessageContentUnwrappers; - public CompositeMessageContentWrapper(JsonMessageContentWrapper jsonMessageContentWrapper, - AvroMessageContentWrapper avroMessageContentWrapper, - AvroMessageSchemaIdAwareContentWrapper schemaIdAwareContentWrapper, - AvroMessageHeaderSchemaVersionContentWrapper headerSchemaVersionContentWrapper, - AvroMessageHeaderSchemaIdContentWrapper headerSchemaIdContentWrapper, - AvroMessageSchemaVersionTruncationContentWrapper schemaVersionTruncationContentWrapper) { + public CompositeMessageContentWrapper( + JsonMessageContentWrapper jsonMessageContentWrapper, + AvroMessageContentWrapper avroMessageContentWrapper, + AvroMessageSchemaIdAwareContentWrapper schemaIdAwareContentWrapper, + AvroMessageHeaderSchemaVersionContentWrapper headerSchemaVersionContentWrapper, + AvroMessageHeaderSchemaIdContentWrapper headerSchemaIdContentWrapper, + AvroMessageSchemaVersionTruncationContentWrapper schemaVersionTruncationContentWrapper) { - this.jsonMessageContentWrapper = jsonMessageContentWrapper; - this.avroMessageContentWrapper = avroMessageContentWrapper; - this.avroMessageContentUnwrappers = asList( - schemaIdAwareContentWrapper, - schemaVersionTruncationContentWrapper, - headerSchemaVersionContentWrapper, - headerSchemaIdContentWrapper); - } + this.jsonMessageContentWrapper = jsonMessageContentWrapper; + this.avroMessageContentWrapper = avroMessageContentWrapper; + this.avroMessageContentUnwrappers = + asList( + schemaIdAwareContentWrapper, + schemaVersionTruncationContentWrapper, + headerSchemaVersionContentWrapper, + headerSchemaIdContentWrapper); + } - public UnwrappedMessageContent unwrapJson(byte[] data) { - return jsonMessageContentWrapper.unwrapContent(data); - } + public UnwrappedMessageContent unwrapJson(byte[] data) { + return jsonMessageContentWrapper.unwrapContent(data); + } - public UnwrappedMessageContent unwrapAvro(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { - for (AvroMessageContentUnwrapper unwrapper : avroMessageContentUnwrappers) { - if (unwrapper.isApplicable(data, topic, schemaId, schemaVersion)) { - AvroMessageContentUnwrapperResult result = unwrapper.unwrap(data, topic, schemaId, schemaVersion); - if (result.getStatus() == SUCCESS) { - return result.getContent(); - } - } + public UnwrappedMessageContent unwrapAvro( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion) { + for (AvroMessageContentUnwrapper unwrapper : avroMessageContentUnwrappers) { + if (unwrapper.isApplicable(data, topic, schemaId, schemaVersion)) { + AvroMessageContentUnwrapperResult result = + unwrapper.unwrap(data, topic, schemaId, schemaVersion); + if (result.getStatus() == SUCCESS) { + return result.getContent(); } - - logger.error("All attempts to unwrap Avro message for topic {} with schema version {} failed", - topic.getQualifiedName(), - schemaVersion); - throw new SchemaMissingException(topic); + } } - public byte[] wrapAvro(byte[] data, - String id, - long timestamp, - Topic topic, - CompiledSchema schema, - Map externalMetadata) { - byte[] wrapped = avroMessageContentWrapper.wrapContent(data, id, timestamp, schema.getSchema(), externalMetadata); - return topic.isSchemaIdAwareSerializationEnabled() ? SchemaAwareSerDe.serialize(schema.getId(), wrapped) : wrapped; - } + logger.error( + "All attempts to unwrap Avro message for topic {} with schema version {} failed", + topic.getQualifiedName(), + schemaVersion); + throw new SchemaMissingException(topic); + } - public byte[] wrapJson(byte[] data, String id, long timestamp, Map externalMetadata) { - return jsonMessageContentWrapper.wrapContent(data, id, timestamp, externalMetadata); - } + public byte[] wrapAvro( + byte[] data, + String id, + long timestamp, + Topic topic, + CompiledSchema schema, + Map externalMetadata) { + byte[] wrapped = + avroMessageContentWrapper.wrapContent( + data, id, timestamp, schema.getSchema(), externalMetadata); + return topic.isSchemaIdAwareSerializationEnabled() + ? SchemaAwareSerDe.serialize(schema.getId(), wrapped) + : wrapped; + } + + public byte[] wrapJson( + byte[] data, String id, long timestamp, Map externalMetadata) { + return jsonMessageContentWrapper.wrapContent(data, id, timestamp, externalMetadata); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/DeserializationException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/DeserializationException.java index 9cc62105f8..c6a167f83e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/DeserializationException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/DeserializationException.java @@ -2,7 +2,7 @@ public class DeserializationException extends RuntimeException { - DeserializationException(String message) { - super(message); - } + DeserializationException(String message) { + super(message); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapper.java index 3014f3073d..f46cb84ade 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapper.java @@ -1,98 +1,103 @@ package pl.allegro.tech.hermes.common.message.wrapper; +import static com.google.common.base.Charsets.UTF_8; +import static com.google.common.primitives.Bytes.indexOf; +import static java.lang.String.format; +import static java.util.Arrays.copyOfRange; + import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; import java.util.UUID; - -import static com.google.common.base.Charsets.UTF_8; -import static com.google.common.primitives.Bytes.indexOf; -import static java.lang.String.format; -import static java.util.Arrays.copyOfRange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class JsonMessageContentWrapper { - private static final Logger LOGGER = LoggerFactory.getLogger(JsonMessageContentWrapper.class); - - private static final byte[] SEPARATOR = ",".getBytes(UTF_8); - private static final byte[] WRAPPED_MARKER = "\"_w\":true".getBytes(UTF_8); - private static final byte JSON_OPEN = (byte) '{'; - private static final byte JSON_CLOSE = (byte) '}'; - private static final int BRACKET_LENGTH = 1; - private final ObjectMapper mapper; - private final byte[] contentRootField; - private final byte[] metadataRootField; - - public JsonMessageContentWrapper(String contentRootName, String metadataRootName, ObjectMapper mapper) { - this.contentRootField = formatNodeKey(contentRootName); - this.metadataRootField = formatNodeKey(metadataRootName); - this.mapper = mapper; + private static final Logger LOGGER = LoggerFactory.getLogger(JsonMessageContentWrapper.class); + + private static final byte[] SEPARATOR = ",".getBytes(UTF_8); + private static final byte[] WRAPPED_MARKER = "\"_w\":true".getBytes(UTF_8); + private static final byte JSON_OPEN = (byte) '{'; + private static final byte JSON_CLOSE = (byte) '}'; + private static final int BRACKET_LENGTH = 1; + private final ObjectMapper mapper; + private final byte[] contentRootField; + private final byte[] metadataRootField; + + public JsonMessageContentWrapper( + String contentRootName, String metadataRootName, ObjectMapper mapper) { + this.contentRootField = formatNodeKey(contentRootName); + this.metadataRootField = formatNodeKey(metadataRootName); + this.mapper = mapper; + } + + byte[] wrapContent(byte[] json, String id, long timestamp, Map externalMetadata) { + try { + return wrapContent( + mapper.writeValueAsBytes(new MessageMetadata(timestamp, id, externalMetadata)), json); + } catch (IOException e) { + throw new WrappingException("Could not wrap json message", e); } - - byte[] wrapContent(byte[] json, String id, long timestamp, Map externalMetadata) { - try { - return wrapContent(mapper.writeValueAsBytes(new MessageMetadata(timestamp, id, externalMetadata)), json); - } catch (IOException e) { - throw new WrappingException("Could not wrap json message", e); - } + } + + private byte[] wrapContent(byte[] attributes, byte[] message) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + stream.write(JSON_OPEN); + stream.write(WRAPPED_MARKER); + stream.write(SEPARATOR); + stream.write(metadataRootField); + stream.write(attributes); + stream.write(SEPARATOR); + stream.write(contentRootField); + stream.write(message); + stream.write(JSON_CLOSE); + return stream.toByteArray(); + } + + public UnwrappedMessageContent unwrapContent(byte[] json) { + if (isWrapped(json)) { + return unwrapMessageContent(json); + } else { + UUID id = UUID.randomUUID(); + LOGGER.warn("Unwrapped message read by consumer (size={}, id={}).", json.length, id); + return new UnwrappedMessageContent( + new MessageMetadata(1L, id.toString(), ImmutableMap.of()), json); } - - private byte[] wrapContent(byte[] attributes, byte[] message) throws IOException { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - stream.write(JSON_OPEN); - stream.write(WRAPPED_MARKER); - stream.write(SEPARATOR); - stream.write(metadataRootField); - stream.write(attributes); - stream.write(SEPARATOR); - stream.write(contentRootField); - stream.write(message); - stream.write(JSON_CLOSE); - return stream.toByteArray(); + } + + private byte[] unwrapContent(byte[] json, int rootIndex) { + return copyOfRange(json, rootIndex + contentRootField.length, json.length - BRACKET_LENGTH); + } + + private UnwrappedMessageContent unwrapMessageContent(byte[] json) { + int rootIndex = indexOf(json, contentRootField); + int metadataIndex = indexOf(json, metadataRootField); + try { + return new UnwrappedMessageContent( + unwrapMesssageMetadata(json, metadataIndex, rootIndex), unwrapContent(json, rootIndex)); + } catch (Exception exception) { + throw new UnwrappingException("Could not unwrap json message", exception); } - - public UnwrappedMessageContent unwrapContent(byte[] json) { - if (isWrapped(json)) { - return unwrapMessageContent(json); - } else { - UUID id = UUID.randomUUID(); - LOGGER.warn("Unwrapped message read by consumer (size={}, id={}).", json.length, id); - return new UnwrappedMessageContent(new MessageMetadata(1L, id.toString(), ImmutableMap.of()), json); - } - } - - private byte[] unwrapContent(byte[] json, int rootIndex) { - return copyOfRange(json, rootIndex + contentRootField.length, json.length - BRACKET_LENGTH); - } - - private UnwrappedMessageContent unwrapMessageContent(byte[] json) { - int rootIndex = indexOf(json, contentRootField); - int metadataIndex = indexOf(json, metadataRootField); - try { - return new UnwrappedMessageContent(unwrapMesssageMetadata(json, metadataIndex, rootIndex), unwrapContent(json, rootIndex)); - } catch (Exception exception) { - throw new UnwrappingException("Could not unwrap json message", exception); - } - } - - private MessageMetadata unwrapMesssageMetadata(byte[] json, int metadataIndexStart, int metadataIndexEnd) throws IOException { - return mapper.readValue(unwrapMetadataBytes(json, metadataIndexStart, metadataIndexEnd), MessageMetadata.class); - } - - private byte[] unwrapMetadataBytes(byte[] json, int metadataIndexStart, int metadataIndexEnd) { - return copyOfRange(json, metadataIndexStart + metadataRootField.length, metadataIndexEnd + BRACKET_LENGTH); - } - - private byte[] formatNodeKey(String keyName) { - return format("\"%s\":", keyName).getBytes(UTF_8); - } - - private boolean isWrapped(byte[] json) { - return indexOf(json, WRAPPED_MARKER) > 0; - } - + } + + private MessageMetadata unwrapMesssageMetadata( + byte[] json, int metadataIndexStart, int metadataIndexEnd) throws IOException { + return mapper.readValue( + unwrapMetadataBytes(json, metadataIndexStart, metadataIndexEnd), MessageMetadata.class); + } + + private byte[] unwrapMetadataBytes(byte[] json, int metadataIndexStart, int metadataIndexEnd) { + return copyOfRange( + json, metadataIndexStart + metadataRootField.length, metadataIndexEnd + BRACKET_LENGTH); + } + + private byte[] formatNodeKey(String keyName) { + return format("\"%s\":", keyName).getBytes(UTF_8); + } + + private boolean isWrapped(byte[] json) { + return indexOf(json, WRAPPED_MARKER) > 0; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapper.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapper.java index ac05f68065..2846279370 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapper.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapper.java @@ -1,23 +1,24 @@ package pl.allegro.tech.hermes.common.message.wrapper; +import java.util.Map; import org.apache.avro.Schema; import pl.allegro.tech.hermes.api.Topic; import pl.allegro.tech.hermes.schema.CompiledSchema; -import java.util.Map; - public interface MessageContentWrapper { - UnwrappedMessageContent unwrapAvro(byte[] data, Topic topic, Integer schemaId, Integer schemaVersion); + UnwrappedMessageContent unwrapAvro( + byte[] data, Topic topic, Integer schemaId, Integer schemaVersion); - UnwrappedMessageContent unwrapJson(byte[] data); + UnwrappedMessageContent unwrapJson(byte[] data); - byte[] wrapAvro(byte[] data, - String id, - long timestamp, - Topic topic, - CompiledSchema schema, - Map externalMetadata); + byte[] wrapAvro( + byte[] data, + String id, + long timestamp, + Topic topic, + CompiledSchema schema, + Map externalMetadata); - byte[] wrapJson(byte[] data, String id, long timestamp, Map externalMetadata); + byte[] wrapJson(byte[] data, String id, long timestamp, Map externalMetadata); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageMetadata.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageMetadata.java index 493e9d09da..3b98829544 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageMetadata.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/MessageMetadata.java @@ -1,62 +1,62 @@ package pl.allegro.tech.hermes.common.message.wrapper; +import static java.util.Optional.ofNullable; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; - import java.util.Map; import java.util.Objects; -import static java.util.Optional.ofNullable; - @JsonIgnoreProperties(ignoreUnknown = true) public class MessageMetadata { - private final long timestamp; - private final String id; - private final Map externalMetadata; - - @JsonCreator - public MessageMetadata(@JsonProperty("timestamp") long timestamp, - @JsonProperty("id") String id, - @JsonProperty("externalMetadata") Map externalMetadata) { - this.id = id; - this.timestamp = timestamp; - this.externalMetadata = ofNullable(externalMetadata).orElseGet(ImmutableMap::of); - } - - public MessageMetadata(long timestamp, Map externalMetadata) { - this(timestamp, "", externalMetadata); - } - - public String getId() { - return id; - } - - public long getTimestamp() { - return timestamp; - } - - public Map getExternalMetadata() { - return ImmutableMap.copyOf(externalMetadata); - } - - @Override - public int hashCode() { - return Objects.hash(id, timestamp); + private final long timestamp; + private final String id; + private final Map externalMetadata; + + @JsonCreator + public MessageMetadata( + @JsonProperty("timestamp") long timestamp, + @JsonProperty("id") String id, + @JsonProperty("externalMetadata") Map externalMetadata) { + this.id = id; + this.timestamp = timestamp; + this.externalMetadata = + ofNullable(externalMetadata).orElseGet(ImmutableMap::of); + } + + public MessageMetadata(long timestamp, Map externalMetadata) { + this(timestamp, "", externalMetadata); + } + + public String getId() { + return id; + } + + public long getTimestamp() { + return timestamp; + } + + public Map getExternalMetadata() { + return ImmutableMap.copyOf(externalMetadata); + } + + @Override + public int hashCode() { + return Objects.hash(id, timestamp); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final MessageMetadata other = (MessageMetadata) obj; - return Objects.equals(this.id, other.id) - && Objects.equals(this.timestamp, other.timestamp); + if (obj == null || getClass() != obj.getClass()) { + return false; } + final MessageMetadata other = (MessageMetadata) obj; + return Objects.equals(this.id, other.id) && Objects.equals(this.timestamp, other.timestamp); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwarePayload.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwarePayload.java index 0c8eab2352..f062b066b7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwarePayload.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwarePayload.java @@ -3,19 +3,19 @@ import pl.allegro.tech.hermes.schema.SchemaId; public class SchemaAwarePayload { - private final byte[] payload; - private final SchemaId schemaId; + private final byte[] payload; + private final SchemaId schemaId; - public SchemaAwarePayload(byte[] payload, SchemaId schemaId) { - this.payload = payload; - this.schemaId = schemaId; - } + public SchemaAwarePayload(byte[] payload, SchemaId schemaId) { + this.payload = payload; + this.schemaId = schemaId; + } - public byte[] getPayload() { - return payload; - } + public byte[] getPayload() { + return payload; + } - public SchemaId getSchemaId() { - return schemaId; - } + public SchemaId getSchemaId() { + return schemaId; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDe.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDe.java index ad7830ca9a..9c4e307a1e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDe.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDe.java @@ -1,50 +1,49 @@ package pl.allegro.tech.hermes.common.message.wrapper; -import pl.allegro.tech.hermes.schema.SchemaId; +import static java.lang.String.format; import java.nio.ByteBuffer; - -import static java.lang.String.format; +import pl.allegro.tech.hermes.schema.SchemaId; public class SchemaAwareSerDe { - private static final byte MAGIC_BYTE_VALUE = 0; - private static final byte MAGIC_BYTE_INDEX = 0; - private static final int HEADER_SIZE = 5; - - private SchemaAwareSerDe() { - } - - public static byte[] serialize(SchemaId schemaId, byte[] binaryAvro) { - ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + binaryAvro.length); - buffer.put(MAGIC_BYTE_VALUE); - buffer.putInt(schemaId.value()); - buffer.put(binaryAvro); - return buffer.array(); - } - - public static SchemaAwarePayload deserialize(byte[] payloadWithHeader) { - ByteBuffer buffer = ByteBuffer.wrap(payloadWithHeader); - assertMagicByte(buffer.get()); - int schemaId = buffer.getInt(); - byte[] payload = new byte[payloadWithHeader.length - HEADER_SIZE]; - buffer.get(payload); - return new SchemaAwarePayload(payload, SchemaId.valueOf(schemaId)); - } - - public static boolean startsWithMagicByte(byte[] payload) { - return payload[MAGIC_BYTE_INDEX] == MAGIC_BYTE_VALUE; - } - - private static void assertMagicByte(byte magicByte) { - if (magicByte != MAGIC_BYTE_VALUE) { - throw new DeserializationException(format("Could not deserialize payload, unknown magic byte: \'%s\'.", magicByte)); - } - } - - public static byte[] trimMagicByteAndSchemaVersion(byte[] data) { - int length = data.length - HEADER_SIZE; - byte[] dataWithoutMagicByteAndSchemaVersion = new byte[length]; - System.arraycopy(data, HEADER_SIZE, dataWithoutMagicByteAndSchemaVersion, 0, length); - return dataWithoutMagicByteAndSchemaVersion; + private static final byte MAGIC_BYTE_VALUE = 0; + private static final byte MAGIC_BYTE_INDEX = 0; + private static final int HEADER_SIZE = 5; + + private SchemaAwareSerDe() {} + + public static byte[] serialize(SchemaId schemaId, byte[] binaryAvro) { + ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + binaryAvro.length); + buffer.put(MAGIC_BYTE_VALUE); + buffer.putInt(schemaId.value()); + buffer.put(binaryAvro); + return buffer.array(); + } + + public static SchemaAwarePayload deserialize(byte[] payloadWithHeader) { + ByteBuffer buffer = ByteBuffer.wrap(payloadWithHeader); + assertMagicByte(buffer.get()); + int schemaId = buffer.getInt(); + byte[] payload = new byte[payloadWithHeader.length - HEADER_SIZE]; + buffer.get(payload); + return new SchemaAwarePayload(payload, SchemaId.valueOf(schemaId)); + } + + public static boolean startsWithMagicByte(byte[] payload) { + return payload[MAGIC_BYTE_INDEX] == MAGIC_BYTE_VALUE; + } + + private static void assertMagicByte(byte magicByte) { + if (magicByte != MAGIC_BYTE_VALUE) { + throw new DeserializationException( + format("Could not deserialize payload, unknown magic byte: \'%s\'.", magicByte)); } + } + + public static byte[] trimMagicByteAndSchemaVersion(byte[] data) { + int length = data.length - HEADER_SIZE; + byte[] dataWithoutMagicByteAndSchemaVersion = new byte[length]; + System.arraycopy(data, HEADER_SIZE, dataWithoutMagicByteAndSchemaVersion, 0, length); + return dataWithoutMagicByteAndSchemaVersion; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaMissingException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaMissingException.java index 6df5563fe1..55677e4f09 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaMissingException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaMissingException.java @@ -6,13 +6,12 @@ public class SchemaMissingException extends HermesException { - SchemaMissingException(Topic topic) { - super("Schema for topic " + topic.getQualifiedName() + " was not available"); - } - - @Override - public ErrorCode getCode() { - return ErrorCode.SCHEMA_COULD_NOT_BE_LOADED; - } + SchemaMissingException(Topic topic) { + super("Schema for topic " + topic.getQualifiedName() + " was not available"); + } + @Override + public ErrorCode getCode() { + return ErrorCode.SCHEMA_COULD_NOT_BE_LOADED; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaOnlineChecksWaitingRateLimiter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaOnlineChecksWaitingRateLimiter.java index e69de29bb2..8b13789179 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaOnlineChecksWaitingRateLimiter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaOnlineChecksWaitingRateLimiter.java @@ -0,0 +1 @@ + diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnsupportedContentTypeException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnsupportedContentTypeException.java index 29788ee711..31956447e5 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnsupportedContentTypeException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnsupportedContentTypeException.java @@ -6,28 +6,24 @@ public class UnsupportedContentTypeException extends InternalProcessingException { - public UnsupportedContentTypeException(Topic topic) { - super(String.format( - "Unsupported content type %s for topic %s", - topic.getContentType(), - topic.getQualifiedName() - )); - } + public UnsupportedContentTypeException(Topic topic) { + super( + String.format( + "Unsupported content type %s for topic %s", + topic.getContentType(), topic.getQualifiedName())); + } - public UnsupportedContentTypeException(Subscription subscription) { - super(String.format( - "Unsupported content type %s for subscription %s", - subscription.getContentType(), - subscription.getQualifiedName() - )); - } - - public UnsupportedContentTypeException(String payloadContentType, Topic topic) { - super(String.format( - "Unsupported payload content type header %s for topic %s", - payloadContentType, - topic.getQualifiedName() - )); - } + public UnsupportedContentTypeException(Subscription subscription) { + super( + String.format( + "Unsupported content type %s for subscription %s", + subscription.getContentType(), subscription.getQualifiedName())); + } + public UnsupportedContentTypeException(String payloadContentType, Topic topic) { + super( + String.format( + "Unsupported payload content type header %s for topic %s", + payloadContentType, topic.getQualifiedName())); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappedMessageContent.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappedMessageContent.java index 1a5185c785..32ffb3f5b8 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappedMessageContent.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappedMessageContent.java @@ -1,39 +1,39 @@ package pl.allegro.tech.hermes.common.message.wrapper; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.util.Optional; import org.apache.avro.Schema; import pl.allegro.tech.hermes.schema.CompiledSchema; -import java.util.Optional; - @SuppressFBWarnings("EI_EXPOSE_REP2") public class UnwrappedMessageContent { - private final MessageMetadata messageMetadata; - private final byte[] content; - private final Optional> schema; - - public UnwrappedMessageContent(MessageMetadata messageMetadata, byte[] content) { - this.messageMetadata = messageMetadata; - this.content = content; - this.schema = Optional.empty(); - } - - public UnwrappedMessageContent(MessageMetadata messageMetadata, byte[] content, CompiledSchema schema) { - this.messageMetadata = messageMetadata; - this.content = content; - this.schema = Optional.of(schema); - } - - public byte[] getContent() { - return content; - } - - public MessageMetadata getMessageMetadata() { - return messageMetadata; - } - - public Optional> getSchema() { - return schema; - } + private final MessageMetadata messageMetadata; + private final byte[] content; + private final Optional> schema; + + public UnwrappedMessageContent(MessageMetadata messageMetadata, byte[] content) { + this.messageMetadata = messageMetadata; + this.content = content; + this.schema = Optional.empty(); + } + + public UnwrappedMessageContent( + MessageMetadata messageMetadata, byte[] content, CompiledSchema schema) { + this.messageMetadata = messageMetadata; + this.content = content; + this.schema = Optional.of(schema); + } + + public byte[] getContent() { + return content; + } + + public MessageMetadata getMessageMetadata() { + return messageMetadata; + } + + public Optional> getSchema() { + return schema; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappingException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappingException.java index 5ec37de19a..ade63a4d4a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappingException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/UnwrappingException.java @@ -3,7 +3,7 @@ import pl.allegro.tech.hermes.common.exception.InternalProcessingException; public class UnwrappingException extends InternalProcessingException { - public UnwrappingException(String message, Exception cause) { - super(message, cause); - } + public UnwrappingException(String message, Exception cause) { + super(message, cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/WrappingException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/WrappingException.java index b335f02fb4..60e6e15ddc 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/WrappingException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/message/wrapper/WrappingException.java @@ -3,7 +3,7 @@ import pl.allegro.tech.hermes.common.exception.InternalProcessingException; public class WrappingException extends InternalProcessingException { - public WrappingException(String message, Exception cause) { - super(message, cause); - } + public WrappingException(String message, Exception cause) { + super(message, cause); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/BrokerMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/BrokerMetrics.java new file mode 100644 index 0000000000..5b9d7b3911 --- /dev/null +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/BrokerMetrics.java @@ -0,0 +1,24 @@ +package pl.allegro.tech.hermes.common.metric; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; +import java.time.Duration; +import pl.allegro.tech.hermes.api.Topic; + +public class BrokerMetrics { + private final MeterRegistry meterRegistry; + + public BrokerMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void recordBrokerLatency(String broker, Topic.Ack ack, Duration duration) { + Timer.builder("broker.latency") + .tag("broker", broker) + .tag("ack", ack.name()) + .publishPercentileHistogram() + .maximumExpectedValue(Duration.ofSeconds(5)) + .register(meterRegistry) + .record(duration); + } +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsistencyMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsistencyMetrics.java new file mode 100644 index 0000000000..c9c3a06bdb --- /dev/null +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsistencyMetrics.java @@ -0,0 +1,17 @@ +package pl.allegro.tech.hermes.common.metric; + +import io.micrometer.core.instrument.MeterRegistry; +import java.util.function.ToDoubleFunction; + +public class ConsistencyMetrics { + private final MeterRegistry meterRegistry; + + ConsistencyMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void registerStorageConsistencyGauge( + T stateObject, ToDoubleFunction valueFunction) { + meterRegistry.gauge("storage.consistency", stateObject, valueFunction); + } +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerMetrics.java index 8ef5a5d086..de757afbde 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerMetrics.java @@ -1,91 +1,71 @@ package pl.allegro.tech.hermes.common.metric; +import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; + import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; +import java.util.function.ToDoubleFunction; import pl.allegro.tech.hermes.api.Subscription; import pl.allegro.tech.hermes.metrics.HermesCounter; import pl.allegro.tech.hermes.metrics.HermesTimer; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import java.util.function.ToDoubleFunction; - -import static pl.allegro.tech.hermes.common.metric.Gauges.BATCH_BUFFER_AVAILABLE_BYTES; -import static pl.allegro.tech.hermes.common.metric.Gauges.BATCH_BUFFER_TOTAL_BYTES; -import static pl.allegro.tech.hermes.common.metric.Gauges.THREADS; -import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; - public class ConsumerMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - private final GaugeRegistrar gaugeRegistrar; - - public ConsumerMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - this.gaugeRegistrar = new GaugeRegistrar(meterRegistry, hermesMetrics); - } - - public void registerQueueUtilizationGauge(T obj, String queueName, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("queue." + queueName + ".utilization", obj, f); - } - - public HermesCounter queueFailuresCounter(String name) { - return HermesCounters.from( - meterRegistry.counter("queue." + name + ".failures"), - hermesMetrics.counter("queue." + name + ".failures") - ); - } - - public void registerConsumerProcessesThreadsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(THREADS, "consumer-processes.threads", obj, f); - } - - public void registerRunningConsumerProcessesGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerRunningConsumerProcessesCountGauge(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("consumer-processes.running", obj, f); - } - - public void registerDyingConsumerProcessesGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerDyingConsumerProcessesCountGauge(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("consumer-processes.dying", obj, f); - } - - public void registerBatchBufferTotalBytesGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(BATCH_BUFFER_TOTAL_BYTES, "batch-buffer.total-bytes", obj, f); - } - - public void registerBatchBufferAvailableBytesGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(BATCH_BUFFER_AVAILABLE_BYTES, "batch-buffer.available-bytes", obj, f); - } - - public HermesCounter oAuthSubscriptionTokenRequestCounter(Subscription subscription, String providerName) { - return HermesCounters.from( - meterRegistry.counter("oauth.token-requests", Tags.concat( - subscriptionTags(subscription.getQualifiedName()), - "provider", providerName - )), - hermesMetrics.oAuthSubscriptionTokenRequestMeter(subscription, providerName) - ); - } - - public HermesTimer oAuthProviderLatencyTimer(String providerName) { - return HermesTimer.from( - meterRegistry.timer("oauth.token-request-latency", Tags.of("provider", providerName)), - hermesMetrics.oAuthProviderLatencyTimer(providerName) - ); - } - - public HermesCounter processedSignalsCounter(String name) { - return HermesCounters.from( - meterRegistry.counter("signals.processed", Tags.of("signal", name)), - hermesMetrics.counter("supervisor.signal." + name) - ); - } - - public HermesCounter droppedSignalsCounter(String name) { - return HermesCounters.from( - meterRegistry.counter("signals.dropped", Tags.of("signal", name)), - hermesMetrics.counter("supervisor.signal.dropped." + name) - ); - } + private final MeterRegistry meterRegistry; + private final GaugeRegistrar gaugeRegistrar; + + public ConsumerMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.gaugeRegistrar = new GaugeRegistrar(meterRegistry); + } + + public void registerQueueUtilizationGauge(T obj, String queueName, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("queue." + queueName + ".utilization", obj, f); + } + + public HermesCounter queueFailuresCounter(String name) { + return HermesCounters.from(meterRegistry.counter("queue." + name + ".failures")); + } + + public void registerConsumerProcessesThreadsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("consumer-processes.threads", obj, f); + } + + public void registerRunningConsumerProcessesGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("consumer-processes.running", obj, f); + } + + public void registerDyingConsumerProcessesGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("consumer-processes.dying", obj, f); + } + + public void registerBatchBufferTotalBytesGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("batch-buffer.total-bytes", obj, f); + } + + public void registerBatchBufferAvailableBytesGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("batch-buffer.available-bytes", obj, f); + } + + public HermesCounter oAuthSubscriptionTokenRequestCounter( + Subscription subscription, String providerName) { + return HermesCounters.from( + meterRegistry.counter( + "oauth.token-requests", + Tags.concat( + subscriptionTags(subscription.getQualifiedName()), "provider", providerName))); + } + + public HermesTimer oAuthProviderLatencyTimer(String providerName) { + return HermesTimer.from( + meterRegistry.timer("oauth.token-request-latency", Tags.of("provider", providerName))); + } + + public HermesCounter processedSignalsCounter(String name) { + return HermesCounters.from(meterRegistry.counter("signals.processed", Tags.of("signal", name))); + } + + public HermesCounter droppedSignalsCounter(String name) { + return HermesCounters.from(meterRegistry.counter("signals.dropped", Tags.of("signal", name))); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerSenderMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerSenderMetrics.java index 76bba6dde7..7821e7af16 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerSenderMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ConsumerSenderMetrics.java @@ -1,9 +1,5 @@ package pl.allegro.tech.hermes.common.metric; -import io.micrometer.core.instrument.MeterRegistry; - -import java.util.function.ToDoubleFunction; - import static pl.allegro.tech.hermes.common.metric.Gauges.CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_ACTIVE_CONNECTIONS; import static pl.allegro.tech.hermes.common.metric.Gauges.CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_IDLE_CONNECTIONS; import static pl.allegro.tech.hermes.common.metric.Gauges.CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_ACTIVE_CONNECTIONS; @@ -11,59 +7,56 @@ import static pl.allegro.tech.hermes.common.metric.Gauges.CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_CONNECTIONS; import static pl.allegro.tech.hermes.common.metric.Gauges.CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_PENDING_CONNECTIONS; +import io.micrometer.core.instrument.MeterRegistry; +import java.util.function.ToDoubleFunction; + public class ConsumerSenderMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - private final GaugeRegistrar gaugeRegistrar; + private final MeterRegistry meterRegistry; + private final GaugeRegistrar gaugeRegistrar; - ConsumerSenderMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - this.gaugeRegistrar = new GaugeRegistrar(meterRegistry, hermesMetrics); - } + ConsumerSenderMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.gaugeRegistrar = new GaugeRegistrar(meterRegistry); + } - public void registerRequestQueueSizeGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerConsumerSenderRequestQueueSize(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("http-clients.request-queue-size", obj, f); - } + public void registerRequestQueueSizeGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("http-clients.request-queue-size", obj, f); + } - public void registerHttp1SerialClientRequestQueueSizeGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerConsumerSenderHttp1SerialClientRequestQueueSize(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("http-clients.serial.http1.request-queue-size", obj, f); - } + public void registerHttp1SerialClientRequestQueueSizeGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("http-clients.serial.http1.request-queue-size", obj, f); + } - public void registerHttp1BatchClientRequestQueueSizeGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerConsumerSenderHttp1BatchClientRequestQueueSize(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("http-clients.batch.http1.request-queue-size", obj, f); - } + public void registerHttp1BatchClientRequestQueueSizeGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("http-clients.batch.http1.request-queue-size", obj, f); + } - public void registerHttp2RequestQueueSizeGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerConsumerSenderHttp2RequestQueueSize(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("http-clients.serial.http2.request-queue-size", obj, f); - } + public void registerHttp2RequestQueueSizeGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge("http-clients.serial.http2.request-queue-size", obj, f); + } - public void registerHttp1SerialClientActiveConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_ACTIVE_CONNECTIONS, obj, f); - } + public void registerHttp1SerialClientActiveConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_ACTIVE_CONNECTIONS, obj, f); + } - public void registerHttp1SerialClientIdleConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_IDLE_CONNECTIONS, obj, f); - } + public void registerHttp1SerialClientIdleConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_IDLE_CONNECTIONS, obj, f); + } - public void registerHttp1BatchClientActiveConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_ACTIVE_CONNECTIONS, obj, f); - } + public void registerHttp1BatchClientActiveConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_ACTIVE_CONNECTIONS, obj, f); + } - public void registerHttp1BatchClientIdleConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_IDLE_CONNECTIONS, obj, f); - } + public void registerHttp1BatchClientIdleConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_IDLE_CONNECTIONS, obj, f); + } - public void registerHttp2SerialClientConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_CONNECTIONS, obj, f); - } + public void registerHttp2SerialClientConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_CONNECTIONS, obj, f); + } - public void registerHttp2SerialClientPendingConnectionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_PENDING_CONNECTIONS, obj, f); - } + public void registerHttp2SerialClientPendingConnectionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_PENDING_CONNECTIONS, obj, f); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Counters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Counters.java deleted file mode 100644 index 2b20ee366a..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Counters.java +++ /dev/null @@ -1,16 +0,0 @@ -package pl.allegro.tech.hermes.common.metric; - -import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; - -public class Counters { - - public static final String PUBLISHED = "published." + GROUP + "." + TOPIC; - public static final String DELIVERED = "delivered." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - public static final String DISCARDED = "discarded." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - public static final String MAXRATE_RATE_HISTORY_FAILURES = - "consumers-rate.max-rate.node." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".history.failures"; - public static final String MAXRATE_FETCH_FAILURES = - "consumers-rate.max-rate.node." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".fetch.failures"; -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/DeserializationMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/DeserializationMetrics.java index e8628afbf8..27d6081d16 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/DeserializationMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/DeserializationMetrics.java @@ -1,93 +1,62 @@ package pl.allegro.tech.hermes.common.metric; +import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import pl.allegro.tech.hermes.metrics.HermesCounter; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import static com.codahale.metrics.MetricRegistry.name; - public class DeserializationMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - private static final String BASE_PATH = "content.avro.deserialization"; - private static final String ERRORS_PATH = BASE_PATH + ".errors"; + private final MeterRegistry meterRegistry; - public DeserializationMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } + private static final String BASE_PATH = "content.avro.deserialization"; + private static final String ERRORS_PATH = BASE_PATH + ".errors"; - public HermesCounter errorsForHeaderSchemaVersion() { - return HermesCounters.from( - deserializationErrorCounter("headerSchemaVersion"), - hermesMetrics.counter(name(ERRORS_PATH, "headerSchemaVersion")) - ); + public DeserializationMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } - } + public HermesCounter errorsForHeaderSchemaVersion() { + return HermesCounters.from(deserializationErrorCounter("headerSchemaVersion")); + } - public HermesCounter errorsForHeaderSchemaId() { - return HermesCounters.from( - deserializationErrorCounter("headerSchemaId"), - hermesMetrics.counter(name(ERRORS_PATH, "headerSchemaId")) - ); - } + public HermesCounter errorsForHeaderSchemaId() { + return HermesCounters.from(deserializationErrorCounter("headerSchemaId")); + } - public HermesCounter errorsForSchemaIdAwarePayload() { - return HermesCounters.from( - deserializationErrorCounter("payloadWithSchemaId"), - hermesMetrics.counter(name(ERRORS_PATH, "payloadWithSchemaId")) - ); - } + public HermesCounter errorsForSchemaIdAwarePayload() { + return HermesCounters.from(deserializationErrorCounter("payloadWithSchemaId")); + } - public HermesCounter errorsForSchemaVersionTruncation() { - return HermesCounters.from( - deserializationErrorCounter("schemaVersionTruncation"), - hermesMetrics.counter(name(ERRORS_PATH, "schemaVersionTruncation")) - ); - } + public HermesCounter errorsForSchemaVersionTruncation() { + return HermesCounters.from(deserializationErrorCounter("schemaVersionTruncation")); + } - private io.micrometer.core.instrument.Counter deserializationErrorCounter(String schemaSource) { - return meterRegistry.counter(ERRORS_PATH, Tags.of("deserialization_type", schemaSource)); - } + private io.micrometer.core.instrument.Counter deserializationErrorCounter(String schemaSource) { + return meterRegistry.counter(ERRORS_PATH, Tags.of("deserialization_type", schemaSource)); + } - public HermesCounter missingSchemaIdInPayload() { - return HermesCounters.from( - meterRegistry.counter(name(BASE_PATH, "missing_schemaIdInPayload")), - hermesMetrics.counter(name(BASE_PATH, "missed", "schemaIdInPayload")) - ); - } + public HermesCounter missingSchemaIdInPayload() { + return HermesCounters.from(meterRegistry.counter(BASE_PATH + ".missing_schemaIdInPayload")); + } - public HermesCounter usingHeaderSchemaVersion() { - return HermesCounters.from( - deserializationAttemptCounter("headerSchemaVersion"), - hermesMetrics.counter(name(BASE_PATH, "using", "headerSchemaVersion")) - ); - } + public HermesCounter usingHeaderSchemaVersion() { + return HermesCounters.from(deserializationAttemptCounter("headerSchemaVersion")); + } - public HermesCounter usingHeaderSchemaId() { - return HermesCounters.from( - deserializationAttemptCounter("headerSchemaId"), - hermesMetrics.counter(name(BASE_PATH, "using", "headerSchemaId")) - ); - } + public HermesCounter usingHeaderSchemaId() { + return HermesCounters.from(deserializationAttemptCounter("headerSchemaId")); + } - public HermesCounter usingSchemaIdAware() { - return HermesCounters.from( - deserializationAttemptCounter("payloadWithSchemaId"), - hermesMetrics.counter(name(BASE_PATH, "using", "schemaIdAware")) - ); - } + public HermesCounter usingSchemaIdAware() { + return HermesCounters.from(deserializationAttemptCounter("payloadWithSchemaId")); + } - public HermesCounter usingSchemaVersionTruncation() { - return HermesCounters.from( - deserializationAttemptCounter("schemaVersionTruncation"), - hermesMetrics.counter(name(BASE_PATH, "using", "schemaVersionTruncation")) - ); - } + public HermesCounter usingSchemaVersionTruncation() { + return HermesCounters.from(deserializationAttemptCounter("schemaVersionTruncation")); + } - private io.micrometer.core.instrument.Counter deserializationAttemptCounter(String deserializationType) { - return meterRegistry.counter(BASE_PATH, Tags.of("deserialization_type", deserializationType)); - } + private Counter deserializationAttemptCounter(String deserializationType) { + return meterRegistry.counter(BASE_PATH, Tags.of("deserialization_type", deserializationType)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ExecutorMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ExecutorMetrics.java index e51acfe8fc..cf59fc909b 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ExecutorMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ExecutorMetrics.java @@ -1,56 +1,23 @@ package pl.allegro.tech.hermes.common.metric; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tags; - -import java.util.function.ToDoubleFunction; +import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; public class ExecutorMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - public ExecutorMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } - - public void registerThreadPoolCapacity(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolCapacity(executorName, () -> (int) f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.capacity", executorName, stateObj, f); - } - - public void registerThreadPoolActiveThreads(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolActiveThreads(executorName, () -> (int) f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.active-threads", executorName, stateObj, f); - } - - public void registerThreadPoolUtilization(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolUtilization(executorName, () -> f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.utilization", executorName, stateObj, f); - } - - public void registerThreadPoolTaskQueueCapacity(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolTaskQueueCapacity(executorName, () -> (int) f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.task-queue-capacity", executorName, stateObj, f); - } - - public void registerThreadPoolTaskQueued(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolTaskQueued(executorName, () -> (int) f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.task-queue-size", executorName, stateObj, f); - } - - public void registerThreadPoolTaskQueueUtilization(String executorName, T stateObj, ToDoubleFunction f) { - hermesMetrics.registerThreadPoolTaskQueueUtilization(executorName, () -> f.applyAsDouble(stateObj)); - registerMicrometerGauge("executors.task-queue-utilization", executorName, stateObj, f); - } + private final MeterRegistry meterRegistry; - public void incrementRequestRejectedCounter(String executorName) { - hermesMetrics.incrementThreadPoolTaskRejectedCount(executorName); - meterRegistry.counter("executors.task-rejected", Tags.of("executor_name", executorName)).increment(); - } + public ExecutorMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + public ExecutorService monitor(ExecutorService executorService, String executorName) { + return ExecutorServiceMetrics.monitor(meterRegistry, executorService, executorName); + } - private void registerMicrometerGauge(String name, String executorName, T stateObj, ToDoubleFunction f) { - meterRegistry.gauge(name, Tags.of("executor_name", executorName), stateObj, f); - } + public ScheduledExecutorService monitor( + ScheduledExecutorService scheduledExecutorService, String executorName) { + return ExecutorServiceMetrics.monitor(meterRegistry, scheduledExecutorService, executorName); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/GaugeRegistrar.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/GaugeRegistrar.java index 3b1acf0486..8e848ed77e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/GaugeRegistrar.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/GaugeRegistrar.java @@ -3,38 +3,21 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; - import java.util.function.ToDoubleFunction; public class GaugeRegistrar { - private final MeterRegistry meterRegistry; - private final HermesMetrics hermesMetrics; - - public GaugeRegistrar(MeterRegistry meterRegistry, HermesMetrics hermesMetrics) { - this.meterRegistry = meterRegistry; - this.hermesMetrics = hermesMetrics; - } + private final MeterRegistry meterRegistry; - public void registerGauge(String graphiteName, - String prometheusName, - T stateObj, - ToDoubleFunction f, - Iterable tags) { - meterRegistry.gauge(prometheusName, tags, stateObj, f); - hermesMetrics.registerGauge(graphiteName, () -> f.applyAsDouble(stateObj)); - } + public GaugeRegistrar(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } - public void registerGauge(String graphiteName, - String prometheusName, - T stateObj, - ToDoubleFunction f) { - registerGauge(graphiteName, prometheusName, stateObj, f, Tags.empty()); - } + public void registerGauge(String name, T stateObj, ToDoubleFunction f) { + registerGauge(name, stateObj, f, Tags.empty()); + } - public void registerGauge(String name, - T stateObj, - ToDoubleFunction f) { - registerGauge(name, name, stateObj, f); - } + public void registerGauge( + String name, T stateObj, ToDoubleFunction f, Iterable tags) { + meterRegistry.gauge(name, tags, stateObj, f); + } } - diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Gauges.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Gauges.java index e7f2f1fa8e..8b70e0b3ae 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Gauges.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Gauges.java @@ -1,46 +1,22 @@ package pl.allegro.tech.hermes.common.metric; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.EXECUTOR_NAME; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; - public class Gauges { - public static final String BATCH_BUFFER_TOTAL_BYTES = "batch-buffer-total-bytes"; - public static final String BATCH_BUFFER_AVAILABLE_BYTES = "batch-buffer-available-bytes"; - - public static final String THREADS = "threads"; - public static final String INFLIGHT_REQUESTS = "inflight-requests"; - public static final String OUTPUT_RATE = "output-rate." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - public static final String BACKUP_STORAGE_SIZE = "backup-storage.size"; - public static final String MAX_RATE_CALCULATION_DURATION = "consumers-rate.max-rate.coordinator.duration"; - public static final String MAX_RATE_VALUE = - "consumers-rate.max-rate.node." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".max-rate"; - public static final String MAX_RATE_ACTUAL_RATE_VALUE = - "consumers-rate.max-rate.node." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".rate"; - public static final String RUNNING_CONSUMER_PROCESSES_COUNT = "consumer-processes.running-consumer-processes.count"; - public static final String DYING_CONSUMER_PROCESSES_COUNT = "consumer-processes.dying-consumer-processes.count"; - public static final String CONSUMER_SENDER_REQUEST_QUEUE_SIZE = "http-clients.request-queue-size"; - public static final String CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_REQUEST_QUEUE_SIZE = "http-clients.serial.http1.request-queue-size"; - public static final String CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_ACTIVE_CONNECTIONS = "http-clients.serial.http1.active-connections"; - public static final String CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_IDLE_CONNECTIONS = "http-clients.serial.http1.idle-connections"; + public static final String INFLIGHT_REQUESTS = "inflight-requests"; + public static final String BACKUP_STORAGE_SIZE = "backup-storage.size"; - public static final String CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_REQUEST_QUEUE_SIZE = "http-clients.batch.http1.request-queue-size"; - public static final String CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_ACTIVE_CONNECTIONS = "http-clients.batch.http1.active-connections"; - public static final String CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_IDLE_CONNECTIONS = "http-clients.batch.http1.idle-connections"; + public static final String CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_ACTIVE_CONNECTIONS = + "http-clients.serial.http1.active-connections"; + public static final String CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_IDLE_CONNECTIONS = + "http-clients.serial.http1.idle-connections"; - public static final String CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_REQUEST_QUEUE_SIZE = "http-clients.serial.http2.request-queue-size"; - public static final String CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_CONNECTIONS = "http-clients.serial.http2.connections"; - public static final String CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_PENDING_CONNECTIONS = "http-clients.serial.http2.pending-connections"; + public static final String CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_ACTIVE_CONNECTIONS = + "http-clients.batch.http1.active-connections"; + public static final String CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_IDLE_CONNECTIONS = + "http-clients.batch.http1.idle-connections"; - public static final String EXECUTORS = "executors."; - public static final String EXECUTOR_ACTIVE_THREADS = EXECUTORS + EXECUTOR_NAME + ".active-threads"; - public static final String EXECUTOR_CAPACITY = EXECUTORS + EXECUTOR_NAME + ".capacity"; - public static final String UTILIZATION = EXECUTORS + EXECUTOR_NAME + ".utilization"; - public static final String TASK_QUEUE_CAPACITY = EXECUTORS + EXECUTOR_NAME + ".task-queue-capacity"; - public static final String TASK_QUEUED = EXECUTORS + EXECUTOR_NAME + ".task-queue-size"; - public static final String TASKS_QUEUE_UTILIZATION = EXECUTORS + EXECUTOR_NAME + ".task-queue-utilization"; - public static final String TASKS_REJECTED_COUNT = EXECUTORS + EXECUTOR_NAME + "task-rejected"; - public static final String INFLIGHT = "inflight." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".count"; + public static final String CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_CONNECTIONS = + "http-clients.serial.http2.connections"; + public static final String CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_PENDING_CONNECTIONS = + "http-clients.serial.http2.pending-connections"; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/HermesMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/HermesMetrics.java deleted file mode 100644 index cb341c0253..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/HermesMetrics.java +++ /dev/null @@ -1,302 +0,0 @@ -package pl.allegro.tech.hermes.common.metric; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.Gauge; -import com.codahale.metrics.Histogram; -import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricFilter; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; -import pl.allegro.tech.hermes.api.Subscription; -import pl.allegro.tech.hermes.api.SubscriptionName; -import pl.allegro.tech.hermes.api.TopicName; -import pl.allegro.tech.hermes.metrics.PathContext; -import pl.allegro.tech.hermes.metrics.PathsCompiler; - -import static pl.allegro.tech.hermes.common.metric.Histograms.INFLIGHT_TIME; -import static pl.allegro.tech.hermes.common.metric.Meters.ERRORS_HTTP_BY_CODE; -import static pl.allegro.tech.hermes.common.metric.Meters.ERRORS_HTTP_BY_FAMILY; -import static pl.allegro.tech.hermes.common.metric.Meters.ERRORS_OTHER; -import static pl.allegro.tech.hermes.common.metric.Meters.ERRORS_TIMEOUTS; -import static pl.allegro.tech.hermes.common.metric.Meters.SUBSCRIPTION_STATUS; -import static pl.allegro.tech.hermes.metrics.PathContext.pathContext; - -public class HermesMetrics { - - public static final String REPLACEMENT_CHAR = "_"; - - private final MetricRegistry metricRegistry; - private final PathsCompiler pathCompiler; - - public HermesMetrics( - MetricRegistry metricRegistry, - PathsCompiler pathCompiler) { - this.metricRegistry = metricRegistry; - this.pathCompiler = pathCompiler; - } - - public static String escapeDots(String value) { - return value.replaceAll("\\.", REPLACEMENT_CHAR); - } - - public Timer timer(String metric) { - return metricRegistry.timer(metricRegistryName(metric)); - } - - public Timer timer(String metric, TopicName topicName) { - return metricRegistry.timer(metricRegistryName(metric, topicName)); - } - - public Timer timer(String metric, TopicName topicName, String name) { - return metricRegistry.timer(metricRegistryName(metric, topicName, name)); - } - - public Meter meter(String metric) { - return metricRegistry.meter(metricRegistryName(metric)); - } - - public Meter meter(String metric, TopicName topicName, String name) { - return metricRegistry.meter(metricRegistryName(metric, topicName, name)); - } - - public Meter meter(String metric, TopicName topicName) { - return metricRegistry.meter(metricRegistryName(metric, topicName)); - } - - public Meter httpStatusCodeMeter(int statusCode) { - return metricRegistry.meter(pathCompiler.compile(Meters.STATUS_CODES, pathContext().withHttpCode(statusCode).build())); - } - - public Meter httpStatusCodeMeter(int statusCode, TopicName topicName) { - return metricRegistry.meter(pathCompiler.compile(Meters.TOPIC_STATUS_CODES, - pathContext().withHttpCode(statusCode).withGroup(topicName.getGroupName()).withTopic(topicName.getName()).build())); - } - - public Histogram histogram(String metric) { - return metricRegistry.histogram(metricRegistryName(metric)); - } - - public Counter counter(String metric) { - return metricRegistry.counter(metricRegistryName(metric)); - } - - public Counter counter(String metric, TopicName topicName) { - return metricRegistry.counter(metricRegistryName(metric, topicName)); - } - - public Counter counter(String metric, TopicName topicName, String name) { - return metricRegistry.counter(metricRegistryName(metric, topicName, name)); - } - - public void registerProducerInflightRequest(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.INFLIGHT_REQUESTS), gauge); - } - - public void registerMessageRepositorySizeGauge(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.BACKUP_STORAGE_SIZE), gauge); - } - - public void registerConsumerSenderRequestQueueSize(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.CONSUMER_SENDER_REQUEST_QUEUE_SIZE), gauge); - } - - public void registerConsumerSenderHttp1SerialClientRequestQueueSize(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.CONSUMER_SENDER_HTTP_1_SERIAL_CLIENT_REQUEST_QUEUE_SIZE), gauge); - } - - public void registerConsumerSenderHttp1BatchClientRequestQueueSize(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.CONSUMER_SENDER_HTTP_1_BATCH_CLIENT_REQUEST_QUEUE_SIZE), gauge); - } - - public void registerConsumerSenderHttp2RequestQueueSize(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.CONSUMER_SENDER_HTTP_2_SERIAL_CLIENT_REQUEST_QUEUE_SIZE), gauge); - } - - public void registerThreadPoolActiveThreads(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.EXECUTOR_ACTIVE_THREADS, executorName, gauge); - } - - public void registerThreadPoolCapacity(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.EXECUTOR_CAPACITY, executorName, gauge); - } - - public void registerThreadPoolUtilization(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.UTILIZATION, executorName, gauge); - } - - public void registerThreadPoolTaskQueueCapacity(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.TASK_QUEUE_CAPACITY, executorName, gauge); - } - - public void registerThreadPoolTaskQueued(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.TASK_QUEUED, executorName, gauge); - } - - public void registerThreadPoolTaskQueueUtilization(String executorName, Gauge gauge) { - registerExecutorGauge(Gauges.TASKS_QUEUE_UTILIZATION, executorName, gauge); - } - - public void incrementThreadPoolTaskRejectedCount(String executorName) { - executorCounter(Gauges.TASKS_REJECTED_COUNT, executorName).inc(); - } - - public void registerInflightGauge(SubscriptionName subscription, Gauge gauge) { - registerGauge(metricRegistryName(Gauges.INFLIGHT, subscription.getTopicName(), subscription.getName()), gauge); - } - - public void unregisterInflightGauge(SubscriptionName subscription) { - unregister(Gauges.INFLIGHT, subscription); - } - - public static void close(Timer.Context... timers) { - for (Timer.Context timer : timers) { - if (timer != null) { - timer.close(); - } - } - } - - public void registerGauge(String name, Gauge gauge) { - String path = pathCompiler.compile(name); - if (!metricRegistry.getGauges().containsKey(name)) { - metricRegistry.register(path, gauge); - } - } - - public void registerGauge(String name, SubscriptionName subscription, Gauge gauge) { - if (!metricRegistry.getGauges().containsKey(name)) { - metricRegistry.register(metricRegistryName(name, subscription.getTopicName(), subscription.getName()), gauge); - } - } - - public void unregister(String metric, SubscriptionName subscription) { - metricRegistry.remove(metricRegistryName(metric, subscription.getTopicName(), subscription.getName())); - } - - public void unregister(String name) { - String path = pathCompiler.compile(name); - metricRegistry.remove(path); - } - - private String metricRegistryName(String metricDisplayName, TopicName topicName, String subscription) { - PathContext pathContext = PathContext.pathContext() - .withGroup(escapeDots(topicName.getGroupName())) - .withTopic(escapeDots(topicName.getName())) - .withSubscription(escapeDots(subscription)) - .build(); - - return pathCompiler.compile(metricDisplayName, pathContext); - } - - private String metricRegistryName(String metricDisplayName, TopicName topicName) { - PathContext pathContext = PathContext.pathContext() - .withGroup(escapeDots(topicName.getGroupName())) - .withTopic(escapeDots(topicName.getName())).build(); - - return pathCompiler.compile(metricDisplayName, pathContext); - } - - private String metricRegistryName(String metricDisplayName) { - return pathCompiler.compile(metricDisplayName); - } - - public Timer schemaTimer(String schemaMetric) { - return metricRegistry.timer(pathCompiler.compile(schemaMetric, pathContext().withSchemaRepoType("schema-registry").build())); - } - - private Gauge registerExecutorGauge(String path, String executorName, Gauge gauge) { - return metricRegistry.register(pathCompiler.compile(path, pathContext().withExecutorName(executorName).build()), gauge); - } - - private Counter executorCounter(String path, String executorName) { - return metricRegistry.counter(pathCompiler.compile(path, pathContext().withExecutorName(executorName).build())); - } - - - public Histogram messageContentSizeHistogram() { - return metricRegistry.histogram(pathCompiler.compile(Histograms.GLOBAL_MESSAGE_SIZE)); - } - - public Histogram messageContentSizeHistogram(TopicName topic) { - return metricRegistry.histogram(pathCompiler.compile(Histograms.MESSAGE_SIZE, pathContext() - .withGroup(escapeDots(topic.getGroupName())) - .withTopic(escapeDots(topic.getName())) - .build())); - } - - public Histogram inflightTimeHistogram(SubscriptionName subscription) { - return metricRegistry.histogram(metricRegistryName(INFLIGHT_TIME, subscription.getTopicName(), subscription.getName())); - } - - public void unregisterInflightTimeHistogram(SubscriptionName subscription) { - unregister(INFLIGHT_TIME, subscription); - } - - public void registerConsumerHttpAnswer(SubscriptionName subscription, int statusCode, long count) { - PathContext pathContext = pathContext() - .withGroup(escapeDots(subscription.getTopicName().getGroupName())) - .withTopic(escapeDots(subscription.getTopicName().getName())) - .withSubscription(escapeDots(subscription.getName())) - .withHttpCode(statusCode) - .withHttpCodeFamily(httpStatusFamily(statusCode)) - .build(); - metricRegistry.meter(pathCompiler.compile(ERRORS_HTTP_BY_FAMILY, pathContext)).mark(count); - metricRegistry.meter(pathCompiler.compile(ERRORS_HTTP_BY_CODE, pathContext)).mark(count); - } - - public void unregisterStatusMeters(SubscriptionName subscription) { - String prefix = metricRegistryName(SUBSCRIPTION_STATUS, subscription.getTopicName(), subscription.getName()); - metricRegistry.removeMatching(MetricFilter.startsWith(prefix)); - } - - private String httpStatusFamily(int statusCode) { - return String.format("%dxx", statusCode / 100); - } - - public Meter consumerErrorsTimeoutMeter(SubscriptionName subscription) { - return metricRegistry.meter(metricRegistryName(ERRORS_TIMEOUTS, subscription.getTopicName(), subscription.getName())); - } - - public void unregisterConsumerErrorsTimeoutMeter(SubscriptionName subscription) { - unregister(ERRORS_TIMEOUTS, subscription); - } - - public Meter consumerErrorsOtherMeter(SubscriptionName subscription) { - return metricRegistry.meter(metricRegistryName(ERRORS_OTHER, subscription.getTopicName(), subscription.getName())); - } - - public void unregisterConsumerErrorsOtherMeter(SubscriptionName subscription) { - unregister(ERRORS_OTHER, subscription); - } - - public Timer consumersWorkloadRebalanceDurationTimer(String kafkaCluster) { - PathContext pathContext = pathContext().withKafkaCluster(kafkaCluster).build(); - return metricRegistry.timer(pathCompiler.compile(Timers.CONSUMER_WORKLOAD_REBALANCE_DURATION, pathContext)); - } - - public Timer oAuthProviderLatencyTimer(String oAuthProviderName) { - PathContext pathContext = pathContext() - .withOAuthProvider(escapeDots(oAuthProviderName)) - .build(); - return metricRegistry.timer(pathCompiler.compile(Timers.OAUTH_PROVIDER_TOKEN_REQUEST_LATENCY, pathContext)); - } - - public Meter oAuthSubscriptionTokenRequestMeter(Subscription subscription, String oAuthProviderName) { - PathContext pathContext = pathContext() - .withGroup(escapeDots(subscription.getTopicName().getGroupName())) - .withTopic(escapeDots(subscription.getTopicName().getName())) - .withSubscription(escapeDots(subscription.getName())) - .withOAuthProvider(escapeDots(oAuthProviderName)) - .build(); - return metricRegistry.meter(pathCompiler.compile(Meters.OAUTH_SUBSCRIPTION_TOKEN_REQUEST, pathContext)); - } - - public void registerRunningConsumerProcessesCountGauge(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.RUNNING_CONSUMER_PROCESSES_COUNT), gauge); - } - - public void registerDyingConsumerProcessesCountGauge(Gauge gauge) { - metricRegistry.register(metricRegistryName(Gauges.DYING_CONSUMER_PROCESSES_COUNT), gauge); - } -} - diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Histograms.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Histograms.java index a12b6f58b5..67a8bad658 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Histograms.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Histograms.java @@ -1,12 +1,6 @@ package pl.allegro.tech.hermes.common.metric; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; - public class Histograms { - public static final String MESSAGE_SIZE = "message-size." + GROUP + "." + TOPIC; - public static final String GLOBAL_MESSAGE_SIZE = "message-size"; - public static final String INFLIGHT_TIME = "inflight." + GROUP + "." + TOPIC + "." + SUBSCRIPTION + ".time"; - public static final String PERSISTED_UNDELIVERED_MESSAGE_SIZE = "undelivered-messages.persisted.message-size"; + public static final String PERSISTED_UNDELIVERED_MESSAGE_SIZE = + "undelivered-messages.persisted.message-size"; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MaxRateMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MaxRateMetrics.java index f1a759308d..5f608a6072 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MaxRateMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MaxRateMetrics.java @@ -1,63 +1,51 @@ package pl.allegro.tech.hermes.common.metric; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; + import io.micrometer.core.instrument.MeterRegistry; +import java.util.List; +import java.util.function.ToDoubleFunction; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.metrics.HermesCounter; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import java.util.List; -import java.util.function.ToDoubleFunction; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static pl.allegro.tech.hermes.common.metric.Counters.MAXRATE_FETCH_FAILURES; -import static pl.allegro.tech.hermes.common.metric.Counters.MAXRATE_RATE_HISTORY_FAILURES; -import static pl.allegro.tech.hermes.common.metric.Gauges.MAX_RATE_ACTUAL_RATE_VALUE; -import static pl.allegro.tech.hermes.common.metric.Gauges.MAX_RATE_CALCULATION_DURATION; -import static pl.allegro.tech.hermes.common.metric.Gauges.MAX_RATE_VALUE; -import static pl.allegro.tech.hermes.common.metric.Gauges.OUTPUT_RATE; -import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; - public class MaxRateMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - MaxRateMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } - - public void registerCalculationDurationInMillisGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerGauge(MAX_RATE_CALCULATION_DURATION, () -> (int) f.applyAsDouble(obj)); - meterRegistry.more().timeGauge("max-rate.calculation.duration", List.of(), obj, MILLISECONDS, f); - } - - public HermesCounter historyUpdateFailuresCounter(SubscriptionName subscription) { - return HermesCounters.from( - meterRegistry.counter("max-rate.history-update.failures", subscriptionTags(subscription)), - hermesMetrics.counter(MAXRATE_RATE_HISTORY_FAILURES, subscription.getTopicName(), subscription.getName()) - ); - } - - public HermesCounter fetchFailuresCounter(SubscriptionName subscription) { - return HermesCounters.from( - meterRegistry.counter("max-rate.fetch.failures", subscriptionTags(subscription)), - hermesMetrics.counter(MAXRATE_FETCH_FAILURES, subscription.getTopicName(), subscription.getName()) - ); - } - - public void registerCalculatedRateGauge(SubscriptionName subscription, T obj, ToDoubleFunction f) { - hermesMetrics.registerGauge(MAX_RATE_VALUE, subscription, () -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("max-rate.calculated-rate", subscriptionTags(subscription), obj, f); - } - - public void registerActualRateGauge(SubscriptionName subscription, T obj, ToDoubleFunction f) { - hermesMetrics.registerGauge(MAX_RATE_ACTUAL_RATE_VALUE, subscription, () -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("max-rate.actual-rate", subscriptionTags(subscription), obj, f); - } - - public void registerOutputRateGauge(SubscriptionName subscription, T obj, ToDoubleFunction f) { - hermesMetrics.registerGauge(OUTPUT_RATE, subscription, () -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge("max-rate.output-rate", subscriptionTags(subscription), obj, f); - } + private final MeterRegistry meterRegistry; + + MaxRateMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void registerCalculationDurationInMillisGauge(T obj, ToDoubleFunction f) { + meterRegistry + .more() + .timeGauge("max-rate.calculation.duration", List.of(), obj, MILLISECONDS, f); + } + + public HermesCounter historyUpdateFailuresCounter(SubscriptionName subscription) { + return HermesCounters.from( + meterRegistry.counter("max-rate.history-update.failures", subscriptionTags(subscription))); + } + + public HermesCounter fetchFailuresCounter(SubscriptionName subscription) { + return HermesCounters.from( + meterRegistry.counter("max-rate.fetch.failures", subscriptionTags(subscription))); + } + + public void registerCalculatedRateGauge( + SubscriptionName subscription, T obj, ToDoubleFunction f) { + meterRegistry.gauge("max-rate.calculated-rate", subscriptionTags(subscription), obj, f); + } + + public void registerActualRateGauge( + SubscriptionName subscription, T obj, ToDoubleFunction f) { + meterRegistry.gauge("max-rate.actual-rate", subscriptionTags(subscription), obj, f); + } + + public void registerOutputRateGauge( + SubscriptionName subscription, T obj, ToDoubleFunction f) { + meterRegistry.gauge("max-rate.output-rate", subscriptionTags(subscription), obj, f); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Meters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Meters.java index c30d031bba..b502e0eea1 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Meters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Meters.java @@ -1,41 +1,13 @@ package pl.allegro.tech.hermes.common.metric; import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.HTTP_CODE; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.HTTP_CODE_FAMILY; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.OAUTH_PROVIDER_NAME; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; public class Meters { - public static final String METER = "meter"; - public static final String TOPIC_METER = METER + "." + GROUP + "." + TOPIC; - public static final String SUBSCRIPTION_METER = TOPIC_METER + "." + SUBSCRIPTION; - public static final String FILTERED_METER = SUBSCRIPTION_METER + ".filtered"; - public static final String SUBSCRIPTION_BATCH_METER = TOPIC_METER + "." + SUBSCRIPTION + ".batch"; - public static final String FAILED_METER = "failed-meter"; - public static final String FAILED_TOPIC_METER = FAILED_METER + "." + GROUP + "." + TOPIC; - public static final String FAILED_METER_SUBSCRIPTION = FAILED_TOPIC_METER + "." + SUBSCRIPTION; - public static final String THROUGHPUT_BYTES = "throughput"; - public static final String TOPIC_THROUGHPUT_BYTES = THROUGHPUT_BYTES + "." + GROUP + "." + TOPIC; - public static final String SUBSCRIPTION_THROUGHPUT_BYTES = TOPIC_THROUGHPUT_BYTES + "." + SUBSCRIPTION; - public static final String STATUS_CODES = "http-status-codes.code" + HTTP_CODE; - public static final String TOPIC_STATUS_CODES = "http-status-codes." + GROUP + "." + TOPIC + ".code" + HTTP_CODE; - public static final String SUBSCRIPTION_STATUS = "status." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - public static final String ERRORS_TIMEOUTS = SUBSCRIPTION_STATUS + ".errors.timeout"; - public static final String ERRORS_OTHER = SUBSCRIPTION_STATUS + ".errors.other"; - public static final String ERRORS_HTTP_BY_FAMILY = SUBSCRIPTION_STATUS + "." + HTTP_CODE_FAMILY; - public static final String ERRORS_HTTP_BY_CODE = ERRORS_HTTP_BY_FAMILY + "." + HTTP_CODE; - public static final String DISCARDED_METER = "discarded-meter"; - public static final String DISCARDED_TOPIC_METER = DISCARDED_METER + "." + GROUP + "." + TOPIC; - public static final String DISCARDED_SUBSCRIPTION_METER = DISCARDED_TOPIC_METER + "." + SUBSCRIPTION; + public static final String THROUGHPUT_BYTES = "throughput"; + public static final String TOPIC_THROUGHPUT_BYTES = THROUGHPUT_BYTES + "." + GROUP + "." + TOPIC; - public static final String DELAYED_PROCESSING = "delayed-processing"; - public static final String TOPIC_DELAYED_PROCESSING = DELAYED_PROCESSING + "." + GROUP + "." + TOPIC; - - public static final String OAUTH_SUBSCRIPTION_TOKEN_REQUEST = "oauth.subscription." + GROUP + "." + TOPIC + "." + SUBSCRIPTION - + ".token-request." + OAUTH_PROVIDER_NAME; - - public static final String PERSISTED_UNDELIVERED_MESSAGES_METER = "undelivered-messages.persisted"; + public static final String PERSISTED_UNDELIVERED_MESSAGES_METER = + "undelivered-messages.persisted"; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricRegistryWithHdrHistogramReservoir.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricRegistryWithHdrHistogramReservoir.java deleted file mode 100644 index a8c931742d..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricRegistryWithHdrHistogramReservoir.java +++ /dev/null @@ -1,19 +0,0 @@ -package pl.allegro.tech.hermes.common.metric; - -import com.codahale.metrics.Histogram; -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; -import org.mpierce.metrics.reservoir.hdrhistogram.HdrHistogramResetOnSnapshotReservoir; - -public class MetricRegistryWithHdrHistogramReservoir extends MetricRegistry { - - @Override - public Histogram histogram(String name) { - return histogram(name, () -> new Histogram(new HdrHistogramResetOnSnapshotReservoir())); - } - - @Override - public Timer timer(String name) { - return timer(name, () -> new Timer(new HdrHistogramResetOnSnapshotReservoir())); - } -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricsFacade.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricsFacade.java index 43ace4994b..2f1bb31897 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricsFacade.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/MetricsFacade.java @@ -1,149 +1,122 @@ package pl.allegro.tech.hermes.common.metric; +import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; + import io.micrometer.core.instrument.Meter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.search.Search; -import pl.allegro.tech.hermes.api.SubscriptionName; - import java.util.Collection; - -import static pl.allegro.tech.hermes.common.metric.Counters.DELIVERED; -import static pl.allegro.tech.hermes.common.metric.Counters.DISCARDED; -import static pl.allegro.tech.hermes.common.metric.Counters.MAXRATE_FETCH_FAILURES; -import static pl.allegro.tech.hermes.common.metric.Counters.MAXRATE_RATE_HISTORY_FAILURES; -import static pl.allegro.tech.hermes.common.metric.Gauges.MAX_RATE_ACTUAL_RATE_VALUE; -import static pl.allegro.tech.hermes.common.metric.Gauges.MAX_RATE_VALUE; -import static pl.allegro.tech.hermes.common.metric.Gauges.OUTPUT_RATE; -import static pl.allegro.tech.hermes.common.metric.Meters.DISCARDED_SUBSCRIPTION_METER; -import static pl.allegro.tech.hermes.common.metric.Meters.FAILED_METER_SUBSCRIPTION; -import static pl.allegro.tech.hermes.common.metric.Meters.FILTERED_METER; -import static pl.allegro.tech.hermes.common.metric.Meters.SUBSCRIPTION_BATCH_METER; -import static pl.allegro.tech.hermes.common.metric.Meters.SUBSCRIPTION_METER; -import static pl.allegro.tech.hermes.common.metric.Meters.SUBSCRIPTION_THROUGHPUT_BYTES; -import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; -import static pl.allegro.tech.hermes.common.metric.Timers.CONSUMER_IDLE_TIME; -import static pl.allegro.tech.hermes.common.metric.Timers.SUBSCRIPTION_LATENCY; +import pl.allegro.tech.hermes.api.SubscriptionName; public class MetricsFacade { - private final MeterRegistry meterRegistry; - private final HermesMetrics hermesMetrics; - private final TopicMetrics topicMetrics; - private final SubscriptionMetrics subscriptionMetrics; - private final ConsumerMetrics consumerMetrics; - private final TrackerElasticSearchMetrics trackerElasticSearchMetrics; - private final PersistentBufferMetrics persistentBufferMetrics; - private final ProducerMetrics producerMetrics; - private final ExecutorMetrics executorMetrics; - private final SchemaClientMetrics schemaClientMetrics; - private final UndeliveredMessagesMetrics undeliveredMessagesMetrics; - private final DeserializationMetrics deserializationMetrics; - private final WorkloadMetrics workloadMetrics; - private final ConsumerSenderMetrics consumerSenderMetrics; - private final OffsetCommitsMetrics offsetCommitsMetrics; - private final MaxRateMetrics maxRateMetrics; - - public MetricsFacade(MeterRegistry meterRegistry, HermesMetrics hermesMetrics) { - this.meterRegistry = meterRegistry; - this.hermesMetrics = hermesMetrics; - this.topicMetrics = new TopicMetrics(hermesMetrics, meterRegistry); - this.subscriptionMetrics = new SubscriptionMetrics(hermesMetrics, meterRegistry); - this.consumerMetrics = new ConsumerMetrics(hermesMetrics, meterRegistry); - this.trackerElasticSearchMetrics = new TrackerElasticSearchMetrics(hermesMetrics, meterRegistry); - this.persistentBufferMetrics = new PersistentBufferMetrics(hermesMetrics, meterRegistry); - this.producerMetrics = new ProducerMetrics(hermesMetrics, meterRegistry); - this.executorMetrics = new ExecutorMetrics(hermesMetrics, meterRegistry); - this.schemaClientMetrics = new SchemaClientMetrics(hermesMetrics, meterRegistry); - this.undeliveredMessagesMetrics = new UndeliveredMessagesMetrics(hermesMetrics, meterRegistry); - this.deserializationMetrics = new DeserializationMetrics(hermesMetrics, meterRegistry); - this.workloadMetrics = new WorkloadMetrics(hermesMetrics, meterRegistry); - this.consumerSenderMetrics = new ConsumerSenderMetrics(hermesMetrics, meterRegistry); - this.offsetCommitsMetrics = new OffsetCommitsMetrics(hermesMetrics, meterRegistry); - this.maxRateMetrics = new MaxRateMetrics(hermesMetrics, meterRegistry); - } - - public TopicMetrics topics() { - return topicMetrics; - } - - public SubscriptionMetrics subscriptions() { - return subscriptionMetrics; - } - - public ConsumerMetrics consumer() { - return consumerMetrics; - } - - public TrackerElasticSearchMetrics trackerElasticSearch() { - return trackerElasticSearchMetrics; - } - - public PersistentBufferMetrics persistentBuffer() { - return persistentBufferMetrics; - } - - public ProducerMetrics producer() { - return producerMetrics; - } - - public ExecutorMetrics executor() { - return executorMetrics; - } - - public SchemaClientMetrics schemaClient() { - return schemaClientMetrics; - } - - public UndeliveredMessagesMetrics undeliveredMessages() { - return undeliveredMessagesMetrics; - } - - public DeserializationMetrics deserialization() { - return deserializationMetrics; - } - - public WorkloadMetrics workload() { - return workloadMetrics; - } - - public ConsumerSenderMetrics consumerSender() { - return consumerSenderMetrics; - } - - public OffsetCommitsMetrics offsetCommits() { - return offsetCommitsMetrics; - } - - public MaxRateMetrics maxRate() { - return maxRateMetrics; - } - - public void unregisterAllMetricsRelatedTo(SubscriptionName subscription) { - Collection meters = Search.in(meterRegistry) - .tags(subscriptionTags(subscription)) - .meters(); - for (Meter meter : meters) { - meterRegistry.remove(meter); - } - hermesMetrics.unregister(DISCARDED_SUBSCRIPTION_METER, subscription); - hermesMetrics.unregister(FAILED_METER_SUBSCRIPTION, subscription); - hermesMetrics.unregister(SUBSCRIPTION_BATCH_METER, subscription); - hermesMetrics.unregister(SUBSCRIPTION_METER, subscription); - hermesMetrics.unregister(DELIVERED, subscription); - hermesMetrics.unregister(DISCARDED, subscription); - hermesMetrics.unregisterInflightGauge(subscription); - hermesMetrics.unregisterInflightTimeHistogram(subscription); - hermesMetrics.unregisterConsumerErrorsTimeoutMeter(subscription); - hermesMetrics.unregisterConsumerErrorsOtherMeter(subscription); - hermesMetrics.unregisterStatusMeters(subscription); - hermesMetrics.unregister(OUTPUT_RATE, subscription); - hermesMetrics.unregister(MAX_RATE_ACTUAL_RATE_VALUE, subscription); - hermesMetrics.unregister(MAX_RATE_VALUE, subscription); - hermesMetrics.unregister(MAXRATE_FETCH_FAILURES, subscription); - hermesMetrics.unregister(MAXRATE_RATE_HISTORY_FAILURES, subscription); - hermesMetrics.unregister(CONSUMER_IDLE_TIME, subscription); - hermesMetrics.unregister(FILTERED_METER, subscription); - hermesMetrics.unregister(SUBSCRIPTION_LATENCY, subscription); - hermesMetrics.unregister(SUBSCRIPTION_THROUGHPUT_BYTES, subscription); - } + private final MeterRegistry meterRegistry; + private final TopicMetrics topicMetrics; + private final SubscriptionMetrics subscriptionMetrics; + private final ConsumerMetrics consumerMetrics; + private final TrackerElasticSearchMetrics trackerElasticSearchMetrics; + private final PersistentBufferMetrics persistentBufferMetrics; + private final ProducerMetrics producerMetrics; + private final ExecutorMetrics executorMetrics; + private final SchemaClientMetrics schemaClientMetrics; + private final UndeliveredMessagesMetrics undeliveredMessagesMetrics; + private final DeserializationMetrics deserializationMetrics; + private final WorkloadMetrics workloadMetrics; + private final ConsumerSenderMetrics consumerSenderMetrics; + private final OffsetCommitsMetrics offsetCommitsMetrics; + private final MaxRateMetrics maxRateMetrics; + private final BrokerMetrics brokerMetrics; + private final ConsistencyMetrics consistencyMetrics; + + public MetricsFacade(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.topicMetrics = new TopicMetrics(meterRegistry); + this.subscriptionMetrics = new SubscriptionMetrics(meterRegistry); + this.consumerMetrics = new ConsumerMetrics(meterRegistry); + this.trackerElasticSearchMetrics = new TrackerElasticSearchMetrics(meterRegistry); + this.persistentBufferMetrics = new PersistentBufferMetrics(meterRegistry); + this.producerMetrics = new ProducerMetrics(meterRegistry); + this.executorMetrics = new ExecutorMetrics(meterRegistry); + this.schemaClientMetrics = new SchemaClientMetrics(meterRegistry); + this.undeliveredMessagesMetrics = new UndeliveredMessagesMetrics(meterRegistry); + this.deserializationMetrics = new DeserializationMetrics(meterRegistry); + this.workloadMetrics = new WorkloadMetrics(meterRegistry); + this.consumerSenderMetrics = new ConsumerSenderMetrics(meterRegistry); + this.offsetCommitsMetrics = new OffsetCommitsMetrics(meterRegistry); + this.maxRateMetrics = new MaxRateMetrics(meterRegistry); + this.brokerMetrics = new BrokerMetrics(meterRegistry); + this.consistencyMetrics = new ConsistencyMetrics(meterRegistry); + } + + public TopicMetrics topics() { + return topicMetrics; + } + + public SubscriptionMetrics subscriptions() { + return subscriptionMetrics; + } + + public ConsumerMetrics consumer() { + return consumerMetrics; + } + + public TrackerElasticSearchMetrics trackerElasticSearch() { + return trackerElasticSearchMetrics; + } + + public PersistentBufferMetrics persistentBuffer() { + return persistentBufferMetrics; + } + + public ProducerMetrics producer() { + return producerMetrics; + } + + public ExecutorMetrics executor() { + return executorMetrics; + } + + public SchemaClientMetrics schemaClient() { + return schemaClientMetrics; + } + + public UndeliveredMessagesMetrics undeliveredMessages() { + return undeliveredMessagesMetrics; + } + + public DeserializationMetrics deserialization() { + return deserializationMetrics; + } + + public WorkloadMetrics workload() { + return workloadMetrics; + } + + public ConsumerSenderMetrics consumerSender() { + return consumerSenderMetrics; + } + + public OffsetCommitsMetrics offsetCommits() { + return offsetCommitsMetrics; + } + + public MaxRateMetrics maxRate() { + return maxRateMetrics; + } + + public BrokerMetrics broker() { + return brokerMetrics; + } + + public ConsistencyMetrics consistency() { + return consistencyMetrics; + } + + public void unregisterAllMetricsRelatedTo(SubscriptionName subscription) { + Collection meters = + Search.in(meterRegistry).tags(subscriptionTags(subscription)).meters(); + for (Meter meter : meters) { + meterRegistry.remove(meter); + } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/OffsetCommitsMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/OffsetCommitsMetrics.java index 84527282a4..bb310aca1f 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/OffsetCommitsMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/OffsetCommitsMetrics.java @@ -7,46 +7,29 @@ public class OffsetCommitsMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - OffsetCommitsMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } - - public HermesCounter skippedCounter() { - return HermesCounters.from( - meterRegistry.counter("offset-commits.skipped"), - hermesMetrics.counter("offset-committer.skipped") - ); - } - - public HermesCounter obsoleteCounter() { - return HermesCounters.from( - meterRegistry.counter("offset-commits.obsolete"), - hermesMetrics.counter("offset-committer.obsolete") - ); - } - - public HermesCounter committedCounter() { - return HermesCounters.from( - meterRegistry.counter("offset-commits.committed"), - hermesMetrics.counter("offset-committer.committed") - ); - } - - public HermesTimer duration() { - return HermesTimer.from( - meterRegistry.timer("offset-commits.duration"), - hermesMetrics.timer("offset-committer.duration") - ); - } - - public HermesCounter failuresCounter() { - return HermesCounters.from( - meterRegistry.counter("offset-commits.failures"), - hermesMetrics.counter("offset-committer.failed") - ); - } + private final MeterRegistry meterRegistry; + + OffsetCommitsMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public HermesCounter skippedCounter() { + return HermesCounters.from(meterRegistry.counter("offset-commits.skipped")); + } + + public HermesCounter obsoleteCounter() { + return HermesCounters.from(meterRegistry.counter("offset-commits.obsolete")); + } + + public HermesCounter committedCounter() { + return HermesCounters.from(meterRegistry.counter("offset-commits.committed")); + } + + public HermesTimer duration() { + return HermesTimer.from(meterRegistry.timer("offset-commits.duration")); + } + + public HermesCounter failuresCounter() { + return HermesCounters.from(meterRegistry.counter("offset-commits.failures")); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/PersistentBufferMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/PersistentBufferMetrics.java index b67c522807..086f44bbd1 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/PersistentBufferMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/PersistentBufferMetrics.java @@ -1,22 +1,18 @@ package pl.allegro.tech.hermes.common.metric; -import io.micrometer.core.instrument.MeterRegistry; +import static pl.allegro.tech.hermes.common.metric.Gauges.BACKUP_STORAGE_SIZE; +import io.micrometer.core.instrument.MeterRegistry; import java.util.function.ToDoubleFunction; -import static pl.allegro.tech.hermes.common.metric.Gauges.BACKUP_STORAGE_SIZE; - public class PersistentBufferMetrics { - private final MeterRegistry meterRegistry; - private final HermesMetrics hermesMetrics; + private final MeterRegistry meterRegistry; - public PersistentBufferMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.meterRegistry = meterRegistry; - this.hermesMetrics = hermesMetrics; - } + public PersistentBufferMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } - public void registerBackupStorageSizeGauge(T obj, ToDoubleFunction f) { - hermesMetrics.registerMessageRepositorySizeGauge(() -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge(BACKUP_STORAGE_SIZE, obj, f); - } + public void registerBackupStorageSizeGauge(T obj, ToDoubleFunction f) { + meterRegistry.gauge(BACKUP_STORAGE_SIZE, obj, f); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ProducerMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ProducerMetrics.java index 14b5edd14f..3817ab294e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ProducerMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/ProducerMetrics.java @@ -1,141 +1,165 @@ package pl.allegro.tech.hermes.common.metric; +import static pl.allegro.tech.hermes.common.metric.Gauges.INFLIGHT_REQUESTS; + import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; - import java.util.concurrent.TimeUnit; import java.util.function.ToDoubleFunction; -import static pl.allegro.tech.hermes.common.metric.Gauges.INFLIGHT_REQUESTS; -import static pl.allegro.tech.hermes.common.metric.HermesMetrics.escapeDots; - public class ProducerMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - private final GaugeRegistrar gaugeRegistrar; - - public ProducerMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - this.gaugeRegistrar = new GaugeRegistrar(meterRegistry, hermesMetrics); - } - - public void registerAckAllTotalBytesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_ALL_BUFFER_TOTAL_BYTES, stateObj, f); - } - - public void registerAckLeaderTotalBytesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_LEADER_BUFFER_TOTAL_BYTES, stateObj, f); - } - - public void registerAckAllAvailableBytesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_ALL_BUFFER_AVAILABLE_BYTES, stateObj, f); - } - - public void registerAckLeaderAvailableBytesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_LEADER_BUFFER_AVAILABLE_BYTES, stateObj, f); - } - - public void registerAckAllCompressionRateGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_ALL_COMPRESSION_RATE, stateObj, f); - } - - public void registerAckLeaderCompressionRateGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_LEADER_COMPRESSION_RATE, stateObj, f); - } - - public void registerAckAllFailedBatchesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_ALL_FAILED_BATCHES_TOTAL, stateObj, f); - } - - public void registerAckLeaderFailedBatchesGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge(ACK_LEADER_FAILED_BATCHES_TOTAL, stateObj, f); - } - - public void registerAckAllMetadataAgeGauge(T stateObj, ToDoubleFunction f) { - registerTimeGauge(stateObj, f, ACK_ALL_METADATA_AGE, ACK_ALL_METADATA_AGE, Tags.empty(), TimeUnit.SECONDS); - } - - public void registerAckLeaderMetadataAgeGauge(T stateObj, ToDoubleFunction f) { - registerTimeGauge(stateObj, f, ACK_LEADER_METADATA_AGE, ACK_LEADER_METADATA_AGE, Tags.empty(), TimeUnit.SECONDS); - } - - public void registerAckAllRecordQueueTimeMaxGauge(T stateObj, ToDoubleFunction f) { - registerTimeGauge(stateObj, f, ACK_ALL_RECORD_QUEUE_TIME_MAX, ACK_ALL_RECORD_QUEUE_TIME_MAX, Tags.empty(), TimeUnit.MILLISECONDS); - } - - public void registerAckLeaderRecordQueueTimeMaxGauge(T stateObj, ToDoubleFunction f) { - registerTimeGauge(stateObj, f, ACK_LEADER_RECORD_QUEUE_TIME_MAX, - ACK_LEADER_RECORD_QUEUE_TIME_MAX, Tags.empty(), TimeUnit.MILLISECONDS); - } - - public double getBufferTotalBytes() { - return meterRegistry.get(ACK_ALL_BUFFER_TOTAL_BYTES).gauge().value() - + meterRegistry.get(ACK_LEADER_BUFFER_TOTAL_BYTES).gauge().value(); - } - - public double getBufferAvailableBytes() { - return meterRegistry.get(ACK_ALL_BUFFER_AVAILABLE_BYTES).gauge().value() - + meterRegistry.get(ACK_LEADER_BUFFER_AVAILABLE_BYTES).gauge().value(); - } - - public void registerProducerInflightRequestGauge(T stateObj, ToDoubleFunction f) { - meterRegistry.gauge(INFLIGHT_REQUESTS, stateObj, f); - hermesMetrics.registerProducerInflightRequest(() -> (int) f.applyAsDouble(stateObj)); - } - - public void registerAckAllMaxLatencyBrokerGauge(T stateObj, ToDoubleFunction f, String brokerNodeId) { - registerLatencyPerBrokerGauge(stateObj, f, "request-latency-max", ACK_ALL, brokerNodeId); - } - - public void registerAckLeaderMaxLatencyPerBrokerGauge(T stateObj, ToDoubleFunction f, String brokerNodeId) { - registerLatencyPerBrokerGauge(stateObj, f, "request-latency-max", ACK_LEADER, brokerNodeId); - } - - public void registerAckAllAvgLatencyPerBrokerGauge(T stateObj, ToDoubleFunction f, String brokerNodeId) { - registerLatencyPerBrokerGauge(stateObj, f, "request-latency-avg", ACK_ALL, brokerNodeId); - } - - public void registerAckLeaderAvgLatencyPerBrokerGauge(T stateObj, ToDoubleFunction f, String brokerNodeId) { - registerLatencyPerBrokerGauge(stateObj, f, "request-latency-avg", ACK_LEADER, brokerNodeId); - } - - private void registerLatencyPerBrokerGauge(T stateObj, - ToDoubleFunction f, - String metricName, - String producerName, - String brokerNodeId) { - String baseMetricName = KAFKA_PRODUCER + producerName + metricName; - String graphiteMetricName = baseMetricName + "." + escapeDots(brokerNodeId); - - registerTimeGauge(stateObj, f, graphiteMetricName, baseMetricName, Tags.of("broker", brokerNodeId), TimeUnit.MILLISECONDS); - } - - private void registerTimeGauge(T stateObj, - ToDoubleFunction f, - String graphiteName, - String prometheusName, - Tags tags, - TimeUnit timeUnit) { - hermesMetrics.registerGauge(graphiteName, () -> f.applyAsDouble(stateObj)); - meterRegistry.more().timeGauge(prometheusName, tags, stateObj, timeUnit, f); - } - - private static final String KAFKA_PRODUCER = "kafka-producer."; - private static final String ACK_LEADER = "ack-leader."; - private static final String ACK_ALL = "ack-all."; - - private static final String ACK_ALL_BUFFER_TOTAL_BYTES = KAFKA_PRODUCER + ACK_ALL + "buffer-total-bytes"; - private static final String ACK_ALL_BUFFER_AVAILABLE_BYTES = KAFKA_PRODUCER + ACK_ALL + "buffer-available-bytes"; - private static final String ACK_ALL_METADATA_AGE = KAFKA_PRODUCER + ACK_ALL + "metadata-age"; - private static final String ACK_ALL_RECORD_QUEUE_TIME_MAX = KAFKA_PRODUCER + ACK_ALL + "record-queue-time-max"; - private static final String ACK_ALL_COMPRESSION_RATE = KAFKA_PRODUCER + ACK_ALL + "compression-rate-avg"; - private static final String ACK_ALL_FAILED_BATCHES_TOTAL = KAFKA_PRODUCER + ACK_ALL + "failed-batches-total"; - - private static final String ACK_LEADER_FAILED_BATCHES_TOTAL = KAFKA_PRODUCER + ACK_LEADER + "failed-batches-total"; - private static final String ACK_LEADER_BUFFER_TOTAL_BYTES = KAFKA_PRODUCER + ACK_LEADER + "buffer-total-bytes"; - private static final String ACK_LEADER_METADATA_AGE = KAFKA_PRODUCER + ACK_LEADER + "metadata-age"; - private static final String ACK_LEADER_RECORD_QUEUE_TIME_MAX = KAFKA_PRODUCER + ACK_LEADER + "record-queue-time-max"; - private static final String ACK_LEADER_BUFFER_AVAILABLE_BYTES = KAFKA_PRODUCER + ACK_LEADER + "buffer-available-bytes"; - private static final String ACK_LEADER_COMPRESSION_RATE = KAFKA_PRODUCER + ACK_LEADER + "compression-rate-avg"; + private final MeterRegistry meterRegistry; + private final GaugeRegistrar gaugeRegistrar; + + public ProducerMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.gaugeRegistrar = new GaugeRegistrar(meterRegistry); + } + + public void registerAckAllTotalBytesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge(ACK_ALL_BUFFER_TOTAL_BYTES, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckLeaderTotalBytesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_LEADER_BUFFER_TOTAL_BYTES, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckAllAvailableBytesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_ALL_BUFFER_AVAILABLE_BYTES, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckLeaderAvailableBytesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_LEADER_BUFFER_AVAILABLE_BYTES, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckAllCompressionRateGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge(ACK_ALL_COMPRESSION_RATE, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckLeaderCompressionRateGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_LEADER_COMPRESSION_RATE, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckAllFailedBatchesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_ALL_FAILED_BATCHES_TOTAL, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckLeaderFailedBatchesGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + gaugeRegistrar.registerGauge( + ACK_LEADER_FAILED_BATCHES_TOTAL, stateObj, f, tags(sender, datacenter)); + } + + public void registerAckAllMetadataAgeGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerTimeGauge( + stateObj, f, ACK_ALL_METADATA_AGE, tags(sender, datacenter), TimeUnit.SECONDS); + } + + public void registerAckLeaderMetadataAgeGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerTimeGauge( + stateObj, f, ACK_LEADER_METADATA_AGE, tags(sender, datacenter), TimeUnit.SECONDS); + } + + public void registerAckAllRecordQueueTimeMaxGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerTimeGauge( + stateObj, + f, + ACK_ALL_RECORD_QUEUE_TIME_MAX, + tags(sender, datacenter), + TimeUnit.MILLISECONDS); + } + + public void registerAckLeaderRecordQueueTimeMaxGauge( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerTimeGauge( + stateObj, + f, + ACK_LEADER_RECORD_QUEUE_TIME_MAX, + tags(sender, datacenter), + TimeUnit.MILLISECONDS); + } + + public double getBufferTotalBytes() { + return meterRegistry.get(ACK_ALL_BUFFER_TOTAL_BYTES).gauge().value() + + meterRegistry.get(ACK_LEADER_BUFFER_TOTAL_BYTES).gauge().value(); + } + + public double getBufferAvailableBytes() { + return meterRegistry.get(ACK_ALL_BUFFER_AVAILABLE_BYTES).gauge().value() + + meterRegistry.get(ACK_LEADER_BUFFER_AVAILABLE_BYTES).gauge().value(); + } + + public void registerAckLeaderRecordSendCounter( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerCounter(ACK_LEADER_RECORD_SEND_TOTAL, tags(sender, datacenter), stateObj, f); + } + + public void registerAckAllRecordSendCounter( + T stateObj, ToDoubleFunction f, String sender, String datacenter) { + registerCounter(ACK_ALL_RECORD_SEND_TOTAL, tags(sender, datacenter), stateObj, f); + } + + public void registerProducerInflightRequestGauge(T stateObj, ToDoubleFunction f) { + meterRegistry.gauge(INFLIGHT_REQUESTS, stateObj, f); + } + + private static Tags tags(String sender, String datacenter) { + return Tags.of("storageDc", datacenter, "sender", sender); + } + + private void registerTimeGauge( + T stateObj, ToDoubleFunction f, String name, Tags tags, TimeUnit timeUnit) { + meterRegistry.more().timeGauge(name, tags, stateObj, timeUnit, f); + } + + private void registerCounter(String name, Tags tags, T stateObj, ToDoubleFunction f) { + meterRegistry.more().counter(name, tags, stateObj, f); + } + + private static final String KAFKA_PRODUCER = "kafka-producer."; + private static final String ACK_LEADER = "ack-leader."; + private static final String ACK_ALL = "ack-all."; + + private static final String ACK_ALL_BUFFER_TOTAL_BYTES = + KAFKA_PRODUCER + ACK_ALL + "buffer-total-bytes"; + private static final String ACK_ALL_BUFFER_AVAILABLE_BYTES = + KAFKA_PRODUCER + ACK_ALL + "buffer-available-bytes"; + private static final String ACK_ALL_METADATA_AGE = KAFKA_PRODUCER + ACK_ALL + "metadata-age"; + private static final String ACK_ALL_RECORD_QUEUE_TIME_MAX = + KAFKA_PRODUCER + ACK_ALL + "record-queue-time-max"; + private static final String ACK_ALL_COMPRESSION_RATE = + KAFKA_PRODUCER + ACK_ALL + "compression-rate-avg"; + private static final String ACK_ALL_FAILED_BATCHES_TOTAL = + KAFKA_PRODUCER + ACK_ALL + "failed-batches-total"; + private static final String ACK_ALL_RECORD_SEND_TOTAL = KAFKA_PRODUCER + ACK_ALL + "record-send"; + + private static final String ACK_LEADER_FAILED_BATCHES_TOTAL = + KAFKA_PRODUCER + ACK_LEADER + "failed-batches-total"; + private static final String ACK_LEADER_BUFFER_TOTAL_BYTES = + KAFKA_PRODUCER + ACK_LEADER + "buffer-total-bytes"; + private static final String ACK_LEADER_METADATA_AGE = + KAFKA_PRODUCER + ACK_LEADER + "metadata-age"; + private static final String ACK_LEADER_RECORD_QUEUE_TIME_MAX = + KAFKA_PRODUCER + ACK_LEADER + "record-queue-time-max"; + private static final String ACK_LEADER_BUFFER_AVAILABLE_BYTES = + KAFKA_PRODUCER + ACK_LEADER + "buffer-available-bytes"; + private static final String ACK_LEADER_COMPRESSION_RATE = + KAFKA_PRODUCER + ACK_LEADER + "compression-rate-avg"; + private static final String ACK_LEADER_RECORD_SEND_TOTAL = + KAFKA_PRODUCER + ACK_LEADER + "record-send"; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SchemaClientMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SchemaClientMetrics.java index a32f1d6910..40faff467c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SchemaClientMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SchemaClientMetrics.java @@ -5,34 +5,22 @@ import io.micrometer.core.instrument.Timer; import pl.allegro.tech.hermes.metrics.HermesTimer; -import static pl.allegro.tech.hermes.common.metric.Timers.GET_SCHEMA_LATENCY; -import static pl.allegro.tech.hermes.common.metric.Timers.GET_SCHEMA_VERSIONS_LATENCY; - public class SchemaClientMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - public SchemaClientMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } + private final MeterRegistry meterRegistry; - public HermesTimer schemaTimer() { - return HermesTimer.from( - timer("schema.get-schema"), - hermesMetrics.schemaTimer(GET_SCHEMA_LATENCY) - ); - } + public SchemaClientMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } - public HermesTimer versionsTimer() { - return HermesTimer.from( - timer("schema.get-versions"), - hermesMetrics.schemaTimer(GET_SCHEMA_VERSIONS_LATENCY) - ); - } + public HermesTimer schemaTimer() { + return HermesTimer.from(timer("schema.get-schema")); + } - private Timer timer(String name) { - return meterRegistry.timer(name, Tags.of("schema_repo_type", "schema-registry")); - } + public HermesTimer versionsTimer() { + return HermesTimer.from(timer("schema.get-versions")); + } + private Timer timer(String name) { + return meterRegistry.timer(name, Tags.of("schema_repo_type", "schema-registry")); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionHermesCounter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionHermesCounter.java index 73da4f18f1..beae420143 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionHermesCounter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionHermesCounter.java @@ -1,37 +1,28 @@ package pl.allegro.tech.hermes.common.metric; -import com.codahale.metrics.Meter; import io.micrometer.core.instrument.Counter; import pl.allegro.tech.hermes.api.SubscriptionName; -import pl.allegro.tech.hermes.metrics.counters.MeterBackedHermesCounter; +import pl.allegro.tech.hermes.metrics.counters.DefaultHermesCounter; -public class SubscriptionHermesCounter extends MeterBackedHermesCounter { +public class SubscriptionHermesCounter extends DefaultHermesCounter { - private final String graphiteName; - private final SubscriptionName subscription; + private final SubscriptionName subscription; - private SubscriptionHermesCounter(Counter micrometerCounter, - Meter graphiteMeter, - String graphiteName, SubscriptionName subscription) { - super(micrometerCounter, graphiteMeter); - this.graphiteName = graphiteName; - this.subscription = subscription; - } + private SubscriptionHermesCounter(Counter micrometerCounter, SubscriptionName subscription) { + super(micrometerCounter); + this.subscription = subscription; + } - public static SubscriptionHermesCounter from(Counter micrometerCounter, Meter graphiteMeter, - String graphiteName, SubscriptionName subscription) { - return new SubscriptionHermesCounter(micrometerCounter, graphiteMeter, graphiteName, subscription); - } + public static SubscriptionHermesCounter from( + Counter micrometerCounter, SubscriptionName subscription) { + return new SubscriptionHermesCounter(micrometerCounter, subscription); + } - String getGraphiteName() { - return graphiteName; - } + SubscriptionName getSubscription() { + return subscription; + } - SubscriptionName getSubscription() { - return subscription; - } - - Counter getMicrometerCounter() { - return micrometerCounter; - } + Counter getMicrometerCounter() { + return micrometerCounter; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionMetrics.java index ea41df5b58..dbb04dda39 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionMetrics.java @@ -1,147 +1,147 @@ package pl.allegro.tech.hermes.common.metric; +import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; + import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; +import java.util.function.ToDoubleFunction; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.metrics.HermesCounter; import pl.allegro.tech.hermes.metrics.HermesHistogram; import pl.allegro.tech.hermes.metrics.HermesTimer; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import java.util.function.ToDoubleFunction; - -import static pl.allegro.tech.hermes.common.metric.SubscriptionTagsFactory.subscriptionTags; - public class SubscriptionMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - public SubscriptionMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } - - public SubscriptionHermesCounter throughputInBytes(SubscriptionName subscription) { - return SubscriptionHermesCounter.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_THROUGHPUT, subscription), - hermesMetrics.meter(Meters.SUBSCRIPTION_THROUGHPUT_BYTES, subscription.getTopicName(), subscription.getName()), - Meters.SUBSCRIPTION_THROUGHPUT_BYTES, subscription); - } - - public HermesCounter successes(SubscriptionName subscription) { - return size -> { - hermesMetrics.meter(Meters.METER).mark(size); - hermesMetrics.meter(Meters.TOPIC_METER, subscription.getTopicName()).mark(size); - hermesMetrics.meter(Meters.SUBSCRIPTION_METER, subscription.getTopicName(), subscription.getName()).mark(size); - hermesMetrics.counter(Counters.DELIVERED, subscription.getTopicName(), subscription.getName()).inc(size); - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_DELIVERED, subscription).increment(size); - }; - } - - public HermesCounter batchSuccesses(SubscriptionName subscription) { - return HermesCounters.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_BATCHES, subscription), - hermesMetrics.meter(Meters.SUBSCRIPTION_BATCH_METER, subscription.getTopicName(), subscription.getName()) - ); - } - - public HermesCounter discarded(SubscriptionName subscription) { - return size -> { - hermesMetrics.meter(Meters.DISCARDED_METER).mark(size); - hermesMetrics.meter(Meters.DISCARDED_TOPIC_METER, subscription.getTopicName()).mark(size); - hermesMetrics.meter(Meters.DISCARDED_SUBSCRIPTION_METER, subscription.getTopicName(), subscription.getName()).mark(size); - hermesMetrics.counter(Counters.DISCARDED, subscription.getTopicName(), subscription.getName()).inc(size); - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_DISCARDED, subscription).increment(size); - }; - } - - public HermesTimer latency(SubscriptionName subscription) { - return HermesTimer.from( - meterRegistry.timer(SubscriptionMetricsNames.SUBSCRIPTION_LATENCY, subscriptionTags(subscription)), - hermesMetrics.timer(Timers.SUBSCRIPTION_LATENCY, subscription.getTopicName(), subscription.getName()) - ); - } - - public void registerInflightGauge(SubscriptionName subscription, T obj, ToDoubleFunction f) { - hermesMetrics.registerInflightGauge(subscription, () -> (int) f.applyAsDouble(obj)); - meterRegistry.gauge(SubscriptionMetricsNames.SUBSCRIPTION_INFLIGHT, subscriptionTags(subscription), obj, f); - } - - public HermesTimer consumerIdleTimer(SubscriptionName subscription) { - return HermesTimer.from( - meterRegistry.timer(SubscriptionMetricsNames.SUBSCRIPTION_IDLE_DURATION, subscriptionTags(subscription)), - hermesMetrics.timer(Timers.CONSUMER_IDLE_TIME, subscription.getTopicName(), subscription.getName()) - ); - } - - public HermesCounter filteredOutCounter(SubscriptionName subscription) { - return HermesCounters.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_FILTERED_OUT, subscription), - hermesMetrics.meter(Meters.FILTERED_METER, subscription.getTopicName(), subscription.getName()) - ); - } - - public HermesCounter httpAnswerCounter(SubscriptionName subscription, int statusCode) { - return size -> { - meterRegistry.counter( - SubscriptionMetricsNames.SUBSCRIPTION_HTTP_STATUS_CODES, - Tags.concat(subscriptionTags(subscription), "status_code", String.valueOf(statusCode)) - ).increment(size); - hermesMetrics.registerConsumerHttpAnswer(subscription, statusCode, size); - }; - } - - public HermesCounter timeoutsCounter(SubscriptionName subscription) { - return HermesCounters.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_TIMEOUTS, subscription), - hermesMetrics.consumerErrorsTimeoutMeter(subscription) - ); - } - - public HermesCounter otherErrorsCounter(SubscriptionName subscription) { - return HermesCounters.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_OTHER_ERRORS, subscription), - hermesMetrics.consumerErrorsOtherMeter(subscription) - ); - } - - public HermesCounter failuresCounter(SubscriptionName subscription) { - return HermesCounters.from( - micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_FAILURES, subscription), - hermesMetrics.meter(Meters.FAILED_METER_SUBSCRIPTION, subscription.getTopicName(), subscription.getName()) - ); - } - - public HermesHistogram inflightTimeInMillisHistogram(SubscriptionName subscriptionName) { - return value -> { - DistributionSummary.builder(SubscriptionMetricsNames.SUBSCRIPTION_INFLIGHT_TIME) - .tags(subscriptionTags(subscriptionName)) - .register(meterRegistry) - .record(value / 1000d); - hermesMetrics.inflightTimeHistogram(subscriptionName).update(value); - }; - } - - private Counter micrometerCounter(String metricName, SubscriptionName subscription) { - return meterRegistry.counter(metricName, subscriptionTags(subscription)); - } - - public static class SubscriptionMetricsNames { - public static final String SUBSCRIPTION_DELIVERED = "subscription.delivered"; - public static final String SUBSCRIPTION_THROUGHPUT = "subscription.throughput-bytes"; - public static final String SUBSCRIPTION_BATCHES = "subscription.batches"; - public static final String SUBSCRIPTION_DISCARDED = "subscription.discarded"; - public static final String SUBSCRIPTION_LATENCY = "subscription.latency"; - public static final String SUBSCRIPTION_INFLIGHT = "subscription.inflight"; - public static final String SUBSCRIPTION_IDLE_DURATION = "subscription.idle-duration"; - public static final String SUBSCRIPTION_FILTERED_OUT = "subscription.filtered-out"; - public static final String SUBSCRIPTION_HTTP_STATUS_CODES = "subscription.http-status-codes"; - public static final String SUBSCRIPTION_TIMEOUTS = "subscription.timeouts"; - public static final String SUBSCRIPTION_OTHER_ERRORS = "subscription.other-errors"; - public static final String SUBSCRIPTION_FAILURES = "subscription.failures"; - public static final String SUBSCRIPTION_INFLIGHT_TIME = "subscription.inflight-time-seconds"; - } - + private final MeterRegistry meterRegistry; + + public SubscriptionMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public SubscriptionHermesCounter throughputInBytes(SubscriptionName subscription) { + return SubscriptionHermesCounter.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_THROUGHPUT, subscription), + subscription); + } + + public HermesCounter successes(SubscriptionName subscription) { + return size -> + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_DELIVERED, subscription) + .increment(size); + } + + public HermesCounter batchSuccesses(SubscriptionName subscription) { + return HermesCounters.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_BATCHES, subscription)); + } + + public HermesCounter discarded(SubscriptionName subscription) { + return size -> + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_DISCARDED, subscription) + .increment(size); + } + + public HermesCounter retries(SubscriptionName subscription) { + return size -> + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_RETRIES, subscription) + .increment(size); + } + + public HermesTimer latency(SubscriptionName subscription) { + return HermesTimer.from( + meterRegistry.timer( + SubscriptionMetricsNames.SUBSCRIPTION_LATENCY, subscriptionTags(subscription))); + } + + public HermesTimer rateLimiterAcquire(SubscriptionName subscription) { + return HermesTimer.from( + meterRegistry.timer( + SubscriptionMetricsNames.SUBSCRIPTION_RATE_LIMITER_ACQUIRE, + subscriptionTags(subscription))); + } + + public void registerInflightGauge( + SubscriptionName subscription, T obj, ToDoubleFunction f) { + meterRegistry.gauge( + SubscriptionMetricsNames.SUBSCRIPTION_INFLIGHT, subscriptionTags(subscription), obj, f); + } + + public void registerPendingOffsetsGauge( + SubscriptionName subscription, T obj, ToDoubleFunction f) { + meterRegistry.gauge( + SubscriptionMetricsNames.SUBSCRIPTION_PENDING_OFFSETS, + subscriptionTags(subscription), + obj, + f); + } + + public HermesTimer consumerIdleTimer(SubscriptionName subscription) { + return HermesTimer.from( + meterRegistry.timer( + SubscriptionMetricsNames.SUBSCRIPTION_IDLE_DURATION, subscriptionTags(subscription))); + } + + public HermesCounter filteredOutCounter(SubscriptionName subscription) { + return HermesCounters.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_FILTERED_OUT, subscription)); + } + + public HermesCounter httpAnswerCounter(SubscriptionName subscription, int statusCode) { + return size -> + meterRegistry + .counter( + SubscriptionMetricsNames.SUBSCRIPTION_HTTP_STATUS_CODES, + Tags.concat( + subscriptionTags(subscription), "status_code", String.valueOf(statusCode))) + .increment(size); + } + + public HermesCounter timeoutsCounter(SubscriptionName subscription) { + return HermesCounters.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_TIMEOUTS, subscription)); + } + + public HermesCounter otherErrorsCounter(SubscriptionName subscription) { + return HermesCounters.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_OTHER_ERRORS, subscription)); + } + + public HermesCounter failuresCounter(SubscriptionName subscription) { + return HermesCounters.from( + micrometerCounter(SubscriptionMetricsNames.SUBSCRIPTION_FAILURES, subscription)); + } + + public HermesHistogram inflightTimeInMillisHistogram(SubscriptionName subscriptionName) { + return value -> + DistributionSummary.builder(SubscriptionMetricsNames.SUBSCRIPTION_INFLIGHT_TIME) + .tags(subscriptionTags(subscriptionName)) + .register(meterRegistry) + .record(value / 1000d); + } + + private Counter micrometerCounter(String metricName, SubscriptionName subscription) { + return meterRegistry.counter(metricName, subscriptionTags(subscription)); + } + + public static class SubscriptionMetricsNames { + public static final String SUBSCRIPTION_DELIVERED = "subscription.delivered"; + public static final String SUBSCRIPTION_THROUGHPUT = "subscription.throughput-bytes"; + public static final String SUBSCRIPTION_BATCHES = "subscription.batches"; + public static final String SUBSCRIPTION_DISCARDED = "subscription.discarded"; + public static final String SUBSCRIPTION_RETRIES = "subscription.retries"; + public static final String SUBSCRIPTION_LATENCY = "subscription.latency"; + public static final String SUBSCRIPTION_RATE_LIMITER_ACQUIRE = + "subscription.rate-limiter-acquire"; + public static final String SUBSCRIPTION_INFLIGHT = "subscription.inflight"; + public static final String SUBSCRIPTION_PENDING_OFFSETS = "subscription.pending-offsets"; + public static final String SUBSCRIPTION_IDLE_DURATION = "subscription.idle-duration"; + public static final String SUBSCRIPTION_FILTERED_OUT = "subscription.filtered-out"; + public static final String SUBSCRIPTION_HTTP_STATUS_CODES = "subscription.http-status-codes"; + public static final String SUBSCRIPTION_TIMEOUTS = "subscription.timeouts"; + public static final String SUBSCRIPTION_OTHER_ERRORS = "subscription.other-errors"; + public static final String SUBSCRIPTION_FAILURES = "subscription.failures"; + public static final String SUBSCRIPTION_INFLIGHT_TIME = "subscription.inflight-time-seconds"; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionTagsFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionTagsFactory.java index 931e21dc5f..47c8766f0e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionTagsFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/SubscriptionTagsFactory.java @@ -1,17 +1,15 @@ package pl.allegro.tech.hermes.common.metric; import io.micrometer.core.instrument.Tag; -import pl.allegro.tech.hermes.api.SubscriptionName; - import java.util.Set; +import pl.allegro.tech.hermes.api.SubscriptionName; class SubscriptionTagsFactory { - static Set subscriptionTags(SubscriptionName subscriptionName) { - return Set.of( - Tag.of("group", subscriptionName.getTopicName().getGroupName()), - Tag.of("topic", subscriptionName.getTopicName().getName()), - Tag.of("subscription", subscriptionName.getName()) - ); - } + static Set subscriptionTags(SubscriptionName subscriptionName) { + return Set.of( + Tag.of("group", subscriptionName.getTopicName().getGroupName()), + Tag.of("topic", subscriptionName.getTopicName().getName()), + Tag.of("subscription", subscriptionName.getName())); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Timers.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Timers.java deleted file mode 100644 index 17d3572570..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/Timers.java +++ /dev/null @@ -1,32 +0,0 @@ -package pl.allegro.tech.hermes.common.metric; - -import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.KAFKA_CLUSTER; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.OAUTH_PROVIDER_NAME; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SCHEMA_REPO_TYPE; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; - -public class Timers { - - public static final String ACK_ALL_BROKER_LATENCY = "ack-all.broker-latency"; - public static final String ACK_LEADER_BROKER_LATENCY = "ack-leader.broker-latency"; - - public static final String ACK_ALL_LATENCY = "ack-all.latency"; - public static final String ACK_ALL_TOPIC_LATENCY = ACK_ALL_LATENCY + "." + GROUP + "." + TOPIC; - - public static final String ACK_LEADER_LATENCY = "ack-leader.latency"; - public static final String ACK_LEADER_TOPIC_LATENCY = ACK_LEADER_LATENCY + "." + GROUP + "." + TOPIC; - - public static final String LATENCY = "latency"; - public static final String SUBSCRIPTION_LATENCY = LATENCY + "." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - - public static final String SCHEMA = "schema." + SCHEMA_REPO_TYPE; - public static final String GET_SCHEMA_LATENCY = SCHEMA + ".get-schema"; - public static final String GET_SCHEMA_VERSIONS_LATENCY = SCHEMA + ".get-schema-versions"; - - public static final String CONSUMER_WORKLOAD_REBALANCE_DURATION = "consumers-workload." + KAFKA_CLUSTER + ".rebalance-duration"; - public static final String CONSUMER_IDLE_TIME = "idle-time." + GROUP + "." + TOPIC + "." + SUBSCRIPTION; - - public static final String OAUTH_PROVIDER_TOKEN_REQUEST_LATENCY = "oauth.provider." + OAUTH_PROVIDER_NAME + ".token-request-latency"; -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TopicMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TopicMetrics.java index 1d840e3b5d..f359ff7799 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TopicMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TopicMetrics.java @@ -12,169 +12,132 @@ import pl.allegro.tech.hermes.metrics.HermesHistogram; import pl.allegro.tech.hermes.metrics.HermesTimer; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import pl.allegro.tech.hermes.metrics.counters.MeterBackedHermesCounter; - -import static pl.allegro.tech.hermes.common.metric.Meters.DELAYED_PROCESSING; public class TopicMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - - public TopicMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } - - public HermesTimer ackAllGlobalLatency() { - return HermesTimer.from( - meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_ALL_GLOBAL_LATENCY), - hermesMetrics.timer(Timers.ACK_ALL_LATENCY) - ); - } - - public HermesTimer ackAllTopicLatency(TopicName topic) { - return HermesTimer.from( - micrometerTimer(TopicMetricsNames.TOPIC_ACK_ALL_LATENCY, topic), - hermesMetrics.timer(Timers.ACK_ALL_TOPIC_LATENCY, topic)); - } - - public HermesTimer ackAllBrokerLatency() { - return HermesTimer.from( - meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_ALL_BROKER_LATENCY), - hermesMetrics.timer(Timers.ACK_ALL_BROKER_LATENCY)); - } - - public HermesTimer ackLeaderGlobalLatency() { - return HermesTimer.from( - meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_LEADER_GLOBAL_LATENCY), - hermesMetrics.timer(Timers.ACK_LEADER_LATENCY)); - } - - public HermesTimer ackLeaderTopicLatency(TopicName topic) { - return HermesTimer.from( - micrometerTimer(TopicMetricsNames.TOPIC_ACK_LEADER_LATENCY, topic), - hermesMetrics.timer(Timers.ACK_LEADER_TOPIC_LATENCY, topic)); - } - - public HermesTimer ackLeaderBrokerLatency() { - return HermesTimer.from( - meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_LEADER_BROKER_LATENCY), - hermesMetrics.timer(Timers.ACK_LEADER_BROKER_LATENCY)); - } - - public MeterBackedHermesCounter topicThroughputBytes(TopicName topicName) { - return HermesCounters.from( - micrometerCounter(TopicMetricsNames.TOPIC_THROUGHPUT, topicName), - hermesMetrics.meter(Meters.TOPIC_THROUGHPUT_BYTES, topicName) - ); - } - - public MeterBackedHermesCounter topicGlobalThroughputBytes() { - return HermesCounters.from( - meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_THROUGHPUT), - hermesMetrics.meter(Meters.THROUGHPUT_BYTES) - ); - } - - public HermesCounter topicPublished(TopicName topicName) { - return HermesCounters.from( - micrometerCounter(TopicMetricsNames.TOPIC_PUBLISHED, topicName), - hermesMetrics.counter(Counters.PUBLISHED, topicName) - ); - } - - public HermesCounter topicGlobalRequestCounter() { - return HermesCounters.from( - meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_REQUESTS), - hermesMetrics.meter(Meters.METER) - ); - } - - public HermesCounter topicRequestCounter(TopicName topicName) { - return HermesCounters.from( - micrometerCounter(TopicMetricsNames.TOPIC_REQUESTS, topicName), - hermesMetrics.meter(Meters.TOPIC_METER, topicName) - ); - } - - public HermesCounter topicGlobalDelayedProcessingCounter() { - return HermesCounters.from( - meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_DELAYED_PROCESSING), - hermesMetrics.meter(DELAYED_PROCESSING) - ); - } - - public HermesCounter topicDelayedProcessingCounter(TopicName topicName) { - return HermesCounters.from( - micrometerCounter(TopicMetricsNames.TOPIC_DELAYED_PROCESSING, topicName), - hermesMetrics.meter(Meters.TOPIC_DELAYED_PROCESSING, topicName) - ); - } - - public HermesCounter topicGlobalHttpStatusCodeCounter(int statusCode) { - return HermesCounters.from( - meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_HTTP_STATUS_CODES, Tags.of("status_code", String.valueOf(statusCode))), - hermesMetrics.httpStatusCodeMeter(statusCode) - ); - } - - public HermesCounter topicHttpStatusCodeCounter(TopicName topicName, int statusCode) { - return HermesCounters.from( - meterRegistry.counter(TopicMetricsNames.TOPIC_HTTP_STATUS_CODES, topicTags(topicName) - .and("status_code", String.valueOf(statusCode))), - hermesMetrics.httpStatusCodeMeter(statusCode, topicName) - ); - } - - public HermesHistogram topicGlobalMessageContentSizeHistogram() { - return DefaultHermesHistogram.of( - DistributionSummary.builder(TopicMetricsNames.TOPIC_GLOBAL_MESSAGE_SIZE_BYTES) - .register(meterRegistry), - hermesMetrics.messageContentSizeHistogram() - ); - } - - public HermesHistogram topicMessageContentSizeHistogram(TopicName topicName) { - return DefaultHermesHistogram.of( - DistributionSummary.builder(TopicMetricsNames.TOPIC_MESSAGE_SIZE_BYTES) - .tags(topicTags(topicName)) - .register(meterRegistry), - hermesMetrics.messageContentSizeHistogram(topicName) - ); - } - - private Timer micrometerTimer(String metricName, TopicName topicName) { - return meterRegistry.timer(metricName, topicTags(topicName)); - } - - private Counter micrometerCounter(String metricName, TopicName topicName) { - return meterRegistry.counter(metricName, topicTags(topicName)); - } - - private Tags topicTags(TopicName topicName) { - return Tags.of( - Tag.of("group", topicName.getGroupName()), - Tag.of("topic", topicName.getName()) - ); - } - - public static class TopicMetricsNames { - public static final String TOPIC_ACK_ALL_GLOBAL_LATENCY = "topic.ack-all.global-latency"; - public static final String TOPIC_ACK_ALL_LATENCY = "topic.ack-all.latency"; - public static final String TOPIC_ACK_ALL_BROKER_LATENCY = "topic.ack-all.broker-latency"; - public static final String TOPIC_ACK_LEADER_GLOBAL_LATENCY = "topic.ack-leader.global-latency"; - public static final String TOPIC_ACK_LEADER_LATENCY = "topic.ack-leader.latency"; - public static final String TOPIC_ACK_LEADER_BROKER_LATENCY = "topic.ack-leader.broker-latency"; - public static final String TOPIC_THROUGHPUT = "topic.throughput-bytes"; - public static final String TOPIC_GLOBAL_THROUGHPUT = "topic.global-throughput-bytes"; - public static final String TOPIC_PUBLISHED = "topic.published"; - public static final String TOPIC_GLOBAL_REQUESTS = "topic.global-requests"; - public static final String TOPIC_REQUESTS = "topic.requests"; - public static final String TOPIC_GLOBAL_DELAYED_PROCESSING = "topic-global-delayed-processing"; - public static final String TOPIC_DELAYED_PROCESSING = "topic-delayed-processing"; - public static final String TOPIC_GLOBAL_HTTP_STATUS_CODES = "topic-global-http-status-codes"; - public static final String TOPIC_HTTP_STATUS_CODES = "topic-http-status-codes"; - public static final String TOPIC_GLOBAL_MESSAGE_SIZE_BYTES = "topic-global-message-size-bytes"; - public static final String TOPIC_MESSAGE_SIZE_BYTES = "topic-message-size-bytes"; - } + private final MeterRegistry meterRegistry; + + public TopicMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public HermesTimer ackAllGlobalLatency() { + return HermesTimer.from(meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_ALL_GLOBAL_LATENCY)); + } + + public HermesTimer ackAllTopicLatency(TopicName topic) { + return HermesTimer.from(micrometerTimer(TopicMetricsNames.TOPIC_ACK_ALL_LATENCY, topic)); + } + + public HermesTimer ackAllBrokerLatency() { + return HermesTimer.from(meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_ALL_BROKER_LATENCY)); + } + + public HermesTimer ackLeaderGlobalLatency() { + return HermesTimer.from(meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_LEADER_GLOBAL_LATENCY)); + } + + public HermesTimer ackLeaderTopicLatency(TopicName topic) { + return HermesTimer.from(micrometerTimer(TopicMetricsNames.TOPIC_ACK_LEADER_LATENCY, topic)); + } + + public HermesTimer ackLeaderBrokerLatency() { + return HermesTimer.from(meterRegistry.timer(TopicMetricsNames.TOPIC_ACK_LEADER_BROKER_LATENCY)); + } + + public HermesCounter topicThroughputBytes(TopicName topicName) { + return HermesCounters.from(micrometerCounter(TopicMetricsNames.TOPIC_THROUGHPUT, topicName)); + } + + public HermesCounter topicGlobalThroughputBytes() { + return HermesCounters.from(meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_THROUGHPUT)); + } + + public HermesCounter topicPublished(TopicName topicName, String datacenter) { + return HermesCounters.from( + micrometerCounter( + TopicMetricsNames.TOPIC_PUBLISHED, topicName, Tag.of("storageDc", datacenter))); + } + + public HermesCounter topicGlobalRequestCounter() { + return HermesCounters.from(meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_REQUESTS)); + } + + public HermesCounter topicRequestCounter(TopicName topicName) { + return HermesCounters.from(micrometerCounter(TopicMetricsNames.TOPIC_REQUESTS, topicName)); + } + + public HermesCounter topicGlobalDelayedProcessingCounter() { + return HermesCounters.from( + meterRegistry.counter(TopicMetricsNames.TOPIC_GLOBAL_DELAYED_PROCESSING)); + } + + public HermesCounter topicDelayedProcessingCounter(TopicName topicName) { + return HermesCounters.from( + micrometerCounter(TopicMetricsNames.TOPIC_DELAYED_PROCESSING, topicName)); + } + + public HermesCounter topicGlobalHttpStatusCodeCounter(int statusCode) { + return HermesCounters.from( + meterRegistry.counter( + TopicMetricsNames.TOPIC_GLOBAL_HTTP_STATUS_CODES, + Tags.of("status_code", String.valueOf(statusCode)))); + } + + public HermesCounter topicHttpStatusCodeCounter(TopicName topicName, int statusCode) { + return HermesCounters.from( + meterRegistry.counter( + TopicMetricsNames.TOPIC_HTTP_STATUS_CODES, + topicTags(topicName).and("status_code", String.valueOf(statusCode)))); + } + + public HermesCounter topicDuplicatedMessageCounter(TopicName topicName) { + return HermesCounters.from( + micrometerCounter(TopicMetricsNames.TOPIC_DUPLICATED_MESSAGE, topicName)); + } + + public HermesHistogram topicGlobalMessageContentSizeHistogram() { + return DefaultHermesHistogram.of( + DistributionSummary.builder(TopicMetricsNames.TOPIC_GLOBAL_MESSAGE_SIZE_BYTES) + .register(meterRegistry)); + } + + public HermesHistogram topicMessageContentSizeHistogram(TopicName topicName) { + return DefaultHermesHistogram.of( + DistributionSummary.builder(TopicMetricsNames.TOPIC_MESSAGE_SIZE_BYTES) + .tags(topicTags(topicName)) + .register(meterRegistry)); + } + + private Timer micrometerTimer(String metricName, TopicName topicName) { + return meterRegistry.timer(metricName, topicTags(topicName)); + } + + private Counter micrometerCounter(String metricName, TopicName topicName, Tag... tags) { + return meterRegistry.counter(metricName, topicTags(topicName).and(tags)); + } + + private Tags topicTags(TopicName topicName) { + return Tags.of(Tag.of("group", topicName.getGroupName()), Tag.of("topic", topicName.getName())); + } + + public static class TopicMetricsNames { + public static final String TOPIC_ACK_ALL_GLOBAL_LATENCY = "topic.ack-all.global-latency"; + public static final String TOPIC_ACK_ALL_LATENCY = "topic.ack-all.latency"; + public static final String TOPIC_ACK_ALL_BROKER_LATENCY = "topic.ack-all.broker-latency"; + public static final String TOPIC_ACK_LEADER_GLOBAL_LATENCY = "topic.ack-leader.global-latency"; + public static final String TOPIC_ACK_LEADER_LATENCY = "topic.ack-leader.latency"; + public static final String TOPIC_ACK_LEADER_BROKER_LATENCY = "topic.ack-leader.broker-latency"; + public static final String TOPIC_THROUGHPUT = "topic.throughput-bytes"; + public static final String TOPIC_GLOBAL_THROUGHPUT = "topic.global-throughput-bytes"; + public static final String TOPIC_PUBLISHED = "topic.published"; + public static final String TOPIC_GLOBAL_REQUESTS = "topic.global-requests"; + public static final String TOPIC_REQUESTS = "topic.requests"; + public static final String TOPIC_GLOBAL_DELAYED_PROCESSING = "topic-global-delayed-processing"; + public static final String TOPIC_DELAYED_PROCESSING = "topic-delayed-processing"; + public static final String TOPIC_GLOBAL_HTTP_STATUS_CODES = "topic-global-http-status-codes"; + public static final String TOPIC_HTTP_STATUS_CODES = "topic-http-status-codes"; + public static final String TOPIC_GLOBAL_MESSAGE_SIZE_BYTES = "topic-global-message-size-bytes"; + public static final String TOPIC_MESSAGE_SIZE_BYTES = "topic-message-size-bytes"; + public static final String TOPIC_DUPLICATED_MESSAGE = "topic-duplicated-message"; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TrackerElasticSearchMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TrackerElasticSearchMetrics.java index b9643f5a30..139700391a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TrackerElasticSearchMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/TrackerElasticSearchMetrics.java @@ -1,82 +1,51 @@ package pl.allegro.tech.hermes.common.metric; import io.micrometer.core.instrument.MeterRegistry; -import pl.allegro.tech.hermes.metrics.HermesTimer; - import java.util.function.ToDoubleFunction; - -import static pl.allegro.tech.hermes.metrics.PathsCompiler.HOSTNAME; +import pl.allegro.tech.hermes.metrics.HermesTimer; public class TrackerElasticSearchMetrics { - private final MeterRegistry meterRegistry; - private final HermesMetrics hermesMetrics; - private final GaugeRegistrar gaugeRegistrar; - - public TrackerElasticSearchMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.meterRegistry = meterRegistry; - this.hermesMetrics = hermesMetrics; - this.gaugeRegistrar = new GaugeRegistrar(meterRegistry, hermesMetrics); - } - - public void registerProducerTrackerElasticSearchQueueSizeGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - Gauges.Graphite.PRODUCER_TRACKER_ELASTICSEARCH_QUEUE_SIZE, - Gauges.Prometheus.TRACKER_ELASTICSEARCH_QUEUE_SIZE, - stateObj, f - ); - } - - public void registerProducerTrackerElasticSearchRemainingCapacity(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - Gauges.Graphite.PRODUCER_TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, - Gauges.Prometheus.TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, - stateObj, f - ); - } - - public void registerConsumerTrackerElasticSearchQueueSizeGauge(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - Gauges.Graphite.CONSUMER_TRACKER_ELASTICSEARCH_QUEUE_SIZE, - Gauges.Prometheus.TRACKER_ELASTICSEARCH_QUEUE_SIZE, - stateObj, f - ); - } - - public void registerConsumerTrackerElasticSearchRemainingCapacity(T stateObj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - Gauges.Graphite.CONSUMER_TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, - Gauges.Prometheus.TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, - stateObj, f - ); - } - - public HermesTimer trackerElasticSearchCommitLatencyTimer() { - return HermesTimer.from( - meterRegistry.timer(Timers.ELASTICSEARCH_COMMIT_LATENCY), - hermesMetrics.timer(Timers.ELASTICSEARCH_COMMIT_LATENCY) - ); - } - - private static class Gauges { - private static class Graphite { - public static final String PRODUCER_TRACKER_ELASTICSEARCH_QUEUE_SIZE = - "producer." + HOSTNAME + ".tracker.elasticsearch.queue-size"; - public static final String PRODUCER_TRACKER_ELASTICSEARCH_REMAINING_CAPACITY = - "producer." + HOSTNAME + ".tracker.elasticsearch.remaining-capacity"; - - public static final String CONSUMER_TRACKER_ELASTICSEARCH_QUEUE_SIZE = - "consumer." + HOSTNAME + ".tracker.elasticsearch.queue-size"; - public static final String CONSUMER_TRACKER_ELASTICSEARCH_REMAINING_CAPACITY = - "consumer." + HOSTNAME + ".tracker.elasticsearch.remaining-capacity"; - } - - private static class Prometheus { - public static final String TRACKER_ELASTICSEARCH_QUEUE_SIZE = "tracker.elasticsearch.queue-size"; - public static final String TRACKER_ELASTICSEARCH_REMAINING_CAPACITY = "tracker.elasticsearch.remaining-capacity"; - } - } - - private static class Timers { - public static final String ELASTICSEARCH_COMMIT_LATENCY = "tracker.elasticsearch.commit-latency"; - } + private final MeterRegistry meterRegistry; + private final GaugeRegistrar gaugeRegistrar; + + public TrackerElasticSearchMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.gaugeRegistrar = new GaugeRegistrar(meterRegistry); + } + + public void registerProducerTrackerElasticSearchQueueSizeGauge( + T stateObj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(Gauges.TRACKER_ELASTICSEARCH_QUEUE_SIZE, stateObj, f); + } + + public void registerProducerTrackerElasticSearchRemainingCapacity( + T stateObj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(Gauges.TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, stateObj, f); + } + + public void registerConsumerTrackerElasticSearchQueueSizeGauge( + T stateObj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(Gauges.TRACKER_ELASTICSEARCH_QUEUE_SIZE, stateObj, f); + } + + public void registerConsumerTrackerElasticSearchRemainingCapacity( + T stateObj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge(Gauges.TRACKER_ELASTICSEARCH_REMAINING_CAPACITY, stateObj, f); + } + + public HermesTimer trackerElasticSearchCommitLatencyTimer() { + return HermesTimer.from(meterRegistry.timer(Timers.ELASTICSEARCH_COMMIT_LATENCY)); + } + + private static class Gauges { + public static final String TRACKER_ELASTICSEARCH_QUEUE_SIZE = + "tracker.elasticsearch.queue-size"; + public static final String TRACKER_ELASTICSEARCH_REMAINING_CAPACITY = + "tracker.elasticsearch.remaining-capacity"; + } + + private static class Timers { + public static final String ELASTICSEARCH_COMMIT_LATENCY = + "tracker.elasticsearch.commit-latency"; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/UndeliveredMessagesMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/UndeliveredMessagesMetrics.java index ca4edaa24e..0148d9ef57 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/UndeliveredMessagesMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/UndeliveredMessagesMetrics.java @@ -7,30 +7,20 @@ import pl.allegro.tech.hermes.metrics.HermesHistogram; import pl.allegro.tech.hermes.metrics.counters.HermesCounters; -import static pl.allegro.tech.hermes.common.metric.Histograms.PERSISTED_UNDELIVERED_MESSAGE_SIZE; -import static pl.allegro.tech.hermes.common.metric.Meters.PERSISTED_UNDELIVERED_MESSAGES_METER; - public class UndeliveredMessagesMetrics { - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; + private final MeterRegistry meterRegistry; - public UndeliveredMessagesMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - } + public UndeliveredMessagesMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } - public HermesCounter undeliveredMessagesCounter() { - return HermesCounters.from( - meterRegistry.counter("undelivered-messages.persisted"), - hermesMetrics.meter(PERSISTED_UNDELIVERED_MESSAGES_METER) - ); - } + public HermesCounter undeliveredMessagesCounter() { + return HermesCounters.from(meterRegistry.counter("undelivered-messages.persisted")); + } - public HermesHistogram undeliveredMessagesSizeHistogram() { - return DefaultHermesHistogram.of( - DistributionSummary.builder("undelivered-messages.persisted.message-size.bytes") - .register(meterRegistry), - hermesMetrics.histogram(PERSISTED_UNDELIVERED_MESSAGE_SIZE) - ); - } + public HermesHistogram undeliveredMessagesSizeHistogram() { + return DefaultHermesHistogram.of( + DistributionSummary.builder("undelivered-messages.persisted.message-size.bytes") + .register(meterRegistry)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/WorkloadMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/WorkloadMetrics.java index 7c2e840d0f..fd14858968 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/WorkloadMetrics.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/WorkloadMetrics.java @@ -4,173 +4,109 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.search.Search; -import pl.allegro.tech.hermes.metrics.HermesTimer; - import java.util.Collection; import java.util.Set; import java.util.function.ToDoubleFunction; +import pl.allegro.tech.hermes.metrics.HermesTimer; public class WorkloadMetrics { - private static final String CONSUMER_ID_TAG = "consumer-id"; - private static final String KAFKA_CLUSTER_TAG = "kafka-cluster"; - private static final String METRICS_PREFIX = "consumer-workload.weighted."; - private static final String CONSUMER_ID_PLACEHOLDER = "$consumerId"; - private static final String CURRENT_SCORE = METRICS_PREFIX + CONSUMER_ID_PLACEHOLDER + ".current-score"; - private static final String PROPOSED_SCORE = METRICS_PREFIX + CONSUMER_ID_PLACEHOLDER + ".proposed-score"; - private static final String SCORING_ERROR = METRICS_PREFIX + CONSUMER_ID_PLACEHOLDER + ".error"; - private static final String CURRENT_WEIGHT_OPS = METRICS_PREFIX + CONSUMER_ID_PLACEHOLDER + ".current-weight.ops"; - private static final String PROPOSED_WEIGHT_OPS = METRICS_PREFIX + CONSUMER_ID_PLACEHOLDER + ".proposed-weight.ops"; - - private final HermesMetrics hermesMetrics; - private final MeterRegistry meterRegistry; - private final GaugeRegistrar gaugeRegistrar; - - WorkloadMetrics(HermesMetrics hermesMetrics, MeterRegistry meterRegistry) { - this.hermesMetrics = hermesMetrics; - this.meterRegistry = meterRegistry; - this.gaugeRegistrar = new GaugeRegistrar(meterRegistry, hermesMetrics); - } - - public void registerAllAssignmentsGauge(T obj, String kafkaCluster, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - "consumers-workload." + kafkaCluster + ".all-assignments", - "workload.all-assignments", - obj, - f, - Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster) - ); - } - - public void registerMissingResourcesGauge(T obj, String kafkaCluster, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - "consumers-workload." + kafkaCluster + ".missing-resources", - "workload.missing-resources", - obj, - f, - Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster) - ); - } - - public void registerDeletedAssignmentsGauge(T obj, String kafkaCluster, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - "consumers-workload." + kafkaCluster + ".deleted-assignments", - "workload.deleted-assignments", - obj, - f, - Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster) - ); - } - - public void registerCreatedAssignmentsGauge(T obj, String kafkaCluster, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - "consumers-workload." + kafkaCluster + ".created-assignments", - "workload.created-assignments", - obj, - f, - Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster) - ); - } - - public HermesTimer rebalanceDurationTimer(String kafkaCluster) { - return HermesTimer.from( - meterRegistry.timer("workload.rebalance-duration", Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster)), - hermesMetrics.consumersWorkloadRebalanceDurationTimer(kafkaCluster) - ); - } - - public void registerRunningSubscriptionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumers-workload.monitor.running", "workload.subscriptions.running", obj, f); - } - - public void registerAssignedSubscriptionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumers-workload.monitor.assigned", "workload.subscriptions.assigned", obj, f); - } - - public void registerMissingSubscriptionsGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumers-workload.monitor.missing", "workload.subscriptions.missing", obj, f); - } - - public void registerOversubscribedGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumers-workload.monitor.oversubscribed", "workload.subscriptions.oversubscribed", obj, f); - } - - public void registerOperationsPerSecondGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumer-workload.weighted.load.ops", "workload.weighted.ops", obj, f); - } - - public void registerCpuUtilizationGauge(T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge("consumer-workload.weighted.load.cpu-utilization", "workload.weighted.cpu-utilization", obj, f); - } - - public void registerCurrentScoreGauge(String consumerId, T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - buildFullGraphiteMetricPath(CURRENT_SCORE, consumerId), - "workload.weighted.current-score", - obj, - f, - Tags.of(CONSUMER_ID_TAG, consumerId) - ); - } - - public void registerProposedErrorGauge(String consumerId, T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - buildFullGraphiteMetricPath(PROPOSED_SCORE, consumerId), - "workload.weighted.proposed-error", - obj, - f, - Tags.of(CONSUMER_ID_TAG, consumerId) - ); - } - - public void registerScoringErrorGauge(String consumerId, T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - buildFullGraphiteMetricPath(SCORING_ERROR, consumerId), - "workload.weighted.scoring-error", - obj, - f, - Tags.of(CONSUMER_ID_TAG, consumerId) - ); - } - - public void registerCurrentWeightGauge(String consumerId, T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - buildFullGraphiteMetricPath(CURRENT_WEIGHT_OPS, consumerId), - "workload.weighted.current-weight.ops", - obj, - f, - Tags.of(CONSUMER_ID_TAG, consumerId) - ); - } - - public void registerProposedWeightGauge(String consumerId, T obj, ToDoubleFunction f) { - gaugeRegistrar.registerGauge( - buildFullGraphiteMetricPath(PROPOSED_WEIGHT_OPS, consumerId), - "workload.weighted.proposed-weight.ops", - obj, - f, - Tags.of(CONSUMER_ID_TAG, consumerId) - ); - } - - public void unregisterAllWorkloadWeightedGaugesForConsumerIds(Set consumerIds) { - Collection gauges = Search.in(meterRegistry) - .tag(CONSUMER_ID_TAG, consumerIds::contains) - .name(s -> s.startsWith("workload.weighted")) - .gauges(); - for (Gauge gauge : gauges) { - meterRegistry.remove(gauge); - } - for (String consumerId : consumerIds) { - hermesMetrics.unregister(buildFullGraphiteMetricPath(CURRENT_SCORE, consumerId)); - hermesMetrics.unregister(buildFullGraphiteMetricPath(PROPOSED_SCORE, consumerId)); - hermesMetrics.unregister(buildFullGraphiteMetricPath(SCORING_ERROR, consumerId)); - hermesMetrics.unregister(buildFullGraphiteMetricPath(CURRENT_WEIGHT_OPS, consumerId)); - hermesMetrics.unregister(buildFullGraphiteMetricPath(PROPOSED_WEIGHT_OPS, consumerId)); - } - } - - private String buildFullGraphiteMetricPath(String metric, String consumerId) { - return metric.replace(CONSUMER_ID_PLACEHOLDER, HermesMetrics.escapeDots(consumerId)); - } + private static final String CONSUMER_ID_TAG = "consumer-id"; + private static final String KAFKA_CLUSTER_TAG = "kafka-cluster"; + + private final MeterRegistry meterRegistry; + private final GaugeRegistrar gaugeRegistrar; + + WorkloadMetrics(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.gaugeRegistrar = new GaugeRegistrar(meterRegistry); + } + + public void registerAllAssignmentsGauge(T obj, String kafkaCluster, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.all-assignments", obj, f, Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster)); + } + + public void registerMissingResourcesGauge(T obj, String kafkaCluster, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.missing-resources", obj, f, Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster)); + } + + public void registerDeletedAssignmentsGauge( + T obj, String kafkaCluster, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.deleted-assignments", obj, f, Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster)); + } + + public void registerCreatedAssignmentsGauge( + T obj, String kafkaCluster, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.created-assignments", obj, f, Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster)); + } + + public HermesTimer rebalanceDurationTimer(String kafkaCluster) { + return HermesTimer.from( + meterRegistry.timer( + "workload.rebalance-duration", Tags.of(KAFKA_CLUSTER_TAG, kafkaCluster))); + } + + public void registerRunningSubscriptionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.subscriptions.running", obj, f); + } + + public void registerAssignedSubscriptionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.subscriptions.assigned", obj, f); + } + + public void registerMissingSubscriptionsGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.subscriptions.missing", obj, f); + } + + public void registerOversubscribedGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.subscriptions.oversubscribed", obj, f); + } + + public void registerOperationsPerSecondGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.weighted.ops", obj, f); + } + + public void registerCpuUtilizationGauge(T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge("workload.weighted.cpu-utilization", obj, f); + } + + public void registerCurrentScoreGauge(String consumerId, T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.weighted.current-score", obj, f, Tags.of(CONSUMER_ID_TAG, consumerId)); + } + + public void registerProposedErrorGauge(String consumerId, T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.weighted.proposed-error", obj, f, Tags.of(CONSUMER_ID_TAG, consumerId)); + } + + public void registerScoringErrorGauge(String consumerId, T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.weighted.scoring-error", obj, f, Tags.of(CONSUMER_ID_TAG, consumerId)); + } + + public void registerCurrentWeightGauge(String consumerId, T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.weighted.current-weight.ops", obj, f, Tags.of(CONSUMER_ID_TAG, consumerId)); + } + + public void registerProposedWeightGauge(String consumerId, T obj, ToDoubleFunction f) { + gaugeRegistrar.registerGauge( + "workload.weighted.proposed-weight.ops", obj, f, Tags.of(CONSUMER_ID_TAG, consumerId)); + } + + public void unregisterAllWorkloadWeightedGaugesForConsumerIds(Set consumerIds) { + Collection gauges = + Search.in(meterRegistry) + .tag(CONSUMER_ID_TAG, consumerIds::contains) + .name(s -> s.startsWith("workload.weighted")) + .gauges(); + for (Gauge gauge : gauges) { + meterRegistry.remove(gauge); + } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/CounterStorage.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/CounterStorage.java index 5ab6c4a078..59da3018e7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/CounterStorage.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/CounterStorage.java @@ -4,17 +4,17 @@ public interface CounterStorage { - void setTopicPublishedCounter(TopicName topicName, long count); + void setTopicPublishedCounter(TopicName topicName, long count); - void setSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName, long count); + void setSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName, long count); - long getTopicPublishedCounter(TopicName topicName); + long getTopicPublishedCounter(TopicName topicName); - long getSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName); + long getSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName); - void setSubscriptionDiscardedCounter(TopicName topicName, String subscription, long value); + void setSubscriptionDiscardedCounter(TopicName topicName, String subscription, long value); - void incrementVolumeCounter(TopicName topicName, String subscriptionName, long value); + void incrementVolumeCounter(TopicName topicName, String subscriptionName, long value); - void incrementVolumeCounter(TopicName topicName, long value); + void incrementVolumeCounter(TopicName topicName, long value); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculator.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculator.java index ddaa73ed36..073ba0e2a9 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculator.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculator.java @@ -4,31 +4,32 @@ import java.util.concurrent.ConcurrentMap; /** - * Small util to calculate delta between previous and current state of metric. It helps in developing incremental metric storage, as Metrics - * always wants to push current state of Metric, not increment. + * Small util to calculate delta between previous and current state of metric. It helps in + * developing incremental metric storage, as Metrics always wants to push current state of Metric, + * not increment. */ public class MetricsDeltaCalculator { - private final ConcurrentMap previousValues = new ConcurrentHashMap<>(); + private final ConcurrentMap previousValues = new ConcurrentHashMap<>(); - public long calculateDelta(String metricName, Long currentValue) { - Long previousValue = previousValues.put(metricName, currentValue); + public long calculateDelta(String metricName, Long currentValue) { + Long previousValue = previousValues.put(metricName, currentValue); - long delta = currentValue; - if (previousValue != null) { - delta = currentValue - previousValue; - } - return delta; + long delta = currentValue; + if (previousValue != null) { + delta = currentValue - previousValue; } + return delta; + } - public void revertDelta(String metricName, Long delta) { - Long previousValue = previousValues.get(metricName); - if (previousValue != null) { - previousValues.put(metricName, previousValue - delta); - } + public void revertDelta(String metricName, Long delta) { + Long previousValue = previousValues.get(metricName); + if (previousValue != null) { + previousValues.put(metricName, previousValue - delta); } + } - public void clear() { - previousValues.clear(); - } + public void clear() { + previousValues.clear(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/CounterMatcher.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/CounterMatcher.java index 7e52b5186b..cc096a488c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/CounterMatcher.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/CounterMatcher.java @@ -1,92 +1,94 @@ package pl.allegro.tech.hermes.common.metric.counter.zookeeper; -import io.micrometer.core.instrument.Counter; -import pl.allegro.tech.hermes.api.TopicName; - -import java.util.Optional; - import static pl.allegro.tech.hermes.common.metric.SubscriptionMetrics.SubscriptionMetricsNames; import static pl.allegro.tech.hermes.common.metric.TopicMetrics.TopicMetricsNames; -class CounterMatcher { - - private static final String GROUP_TAG_NAME = "group"; - private static final String TOPIC_TAG_NAME = "topic"; - private static final String SUBSCRIPTION_TAG_NAME = "subscription"; +import io.micrometer.core.instrument.Counter; +import java.util.Optional; +import pl.allegro.tech.hermes.api.TopicName; - private final Counter counter; - private final String metricSearchPrefix; - private TopicName topicName; - private long value; - private Optional subscription; +class CounterMatcher { - public CounterMatcher(Counter counter, String metricSearchPrefix) { - this.counter = counter; - this.metricSearchPrefix = metricSearchPrefix; - parseCounter(this.counter); + private static final String GROUP_TAG_NAME = "group"; + private static final String TOPIC_TAG_NAME = "topic"; + private static final String SUBSCRIPTION_TAG_NAME = "subscription"; + + private final Counter counter; + private final String metricSearchPrefix; + private TopicName topicName; + private long value; + private Optional subscription; + + public CounterMatcher(Counter counter, String metricSearchPrefix) { + this.counter = counter; + this.metricSearchPrefix = metricSearchPrefix; + parseCounter(this.counter); + } + + private void parseCounter(Counter counter) { + if (isTopicPublished() || isTopicThroughput()) { + topicName = + new TopicName( + counter.getId().getTag(GROUP_TAG_NAME), counter.getId().getTag(TOPIC_TAG_NAME)); + subscription = Optional.empty(); + } else if (isSubscriptionDelivered() + || isSubscriptionThroughput() + || isSubscriptionDiscarded() + || isSubscriptionFiltered()) { + topicName = + new TopicName( + counter.getId().getTag(GROUP_TAG_NAME), counter.getId().getTag(TOPIC_TAG_NAME)); + subscription = Optional.of(counter.getId().getTag(SUBSCRIPTION_TAG_NAME)); } + value = (long) counter.count(); + } - private void parseCounter(Counter counter) { - if (isTopicPublished() || isTopicThroughput()) { - topicName = new TopicName(counter.getId().getTag(GROUP_TAG_NAME), counter.getId().getTag(TOPIC_TAG_NAME)); - subscription = Optional.empty(); - } else if ( - isSubscriptionDelivered() - || isSubscriptionThroughput() - || isSubscriptionDiscarded() - || isSubscriptionFiltered() - ) { - topicName = new TopicName(counter.getId().getTag(GROUP_TAG_NAME), counter.getId().getTag(TOPIC_TAG_NAME)); - subscription = Optional.of(counter.getId().getTag(SUBSCRIPTION_TAG_NAME)); - } - value = (long) counter.count(); - } + public boolean isTopicPublished() { + return isTopicCounter() && nameEquals(TopicMetricsNames.TOPIC_PUBLISHED); + } - public boolean isTopicPublished() { - return isTopicCounter() && nameEquals(TopicMetricsNames.TOPIC_PUBLISHED); - } + public boolean isTopicThroughput() { + return isTopicCounter() && nameEquals(TopicMetricsNames.TOPIC_THROUGHPUT); + } - public boolean isTopicThroughput() { - return isTopicCounter() && nameEquals(TopicMetricsNames.TOPIC_THROUGHPUT); - } - - public boolean isSubscriptionThroughput() { - return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_THROUGHPUT); - } + public boolean isSubscriptionThroughput() { + return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_THROUGHPUT); + } - public boolean isSubscriptionDelivered() { - return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_DELIVERED); - } + public boolean isSubscriptionDelivered() { + return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_DELIVERED); + } - public boolean isSubscriptionDiscarded() { - return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_DISCARDED); - } + public boolean isSubscriptionDiscarded() { + return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_DISCARDED); + } - public boolean isSubscriptionFiltered() { - return isSubscriptionCounter() && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_FILTERED_OUT); - } + public boolean isSubscriptionFiltered() { + return isSubscriptionCounter() + && nameEquals(SubscriptionMetricsNames.SUBSCRIPTION_FILTERED_OUT); + } - public TopicName getTopicName() { - return topicName; - } + public TopicName getTopicName() { + return topicName; + } - public String getSubscriptionName() { - return subscription.orElse(""); - } + public String getSubscriptionName() { + return subscription.orElse(""); + } - public long getValue() { - return value; - } + public long getValue() { + return value; + } - private boolean isTopicCounter() { - return counter.getId().getTag(TOPIC_TAG_NAME) != null; - } + private boolean isTopicCounter() { + return counter.getId().getTag(TOPIC_TAG_NAME) != null; + } - private boolean isSubscriptionCounter() { - return counter.getId().getTag(SUBSCRIPTION_TAG_NAME) != null; - } + private boolean isSubscriptionCounter() { + return counter.getId().getTag(SUBSCRIPTION_TAG_NAME) != null; + } - private boolean nameEquals(String name) { - return counter.getId().getName().equals(metricSearchPrefix + name); - } + private boolean nameEquals(String name) { + return counter.getId().getName().equals(metricSearchPrefix + name); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporter.java index c66efdc640..5d31f60e7c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporter.java @@ -4,103 +4,94 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.search.Search; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import pl.allegro.tech.hermes.api.TopicName; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; -import pl.allegro.tech.hermes.common.metric.counter.CounterStorage; - import java.util.Collection; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pl.allegro.tech.hermes.api.TopicName; +import pl.allegro.tech.hermes.common.metric.counter.CounterStorage; +import pl.allegro.tech.hermes.metrics.PathsCompiler; public class ZookeeperCounterReporter { - private static final Logger logger = LoggerFactory.getLogger(ZookeeperCounterReporter.class); + private static final Logger logger = LoggerFactory.getLogger(ZookeeperCounterReporter.class); - private final CounterStorage counterStorage; - private final String metricsSearchPrefix; - private final MeterRegistry meterRegistry; + private final CounterStorage counterStorage; + private final String metricsSearchPrefix; + private final MeterRegistry meterRegistry; - private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor( - new ThreadFactoryBuilder() - .setNameFormat("zookeeper-reporter-scheduled-executor-%d") - .setDaemon(true) - .build()); + private final ScheduledExecutorService scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor( + new ThreadFactoryBuilder() + .setNameFormat("zookeeper-reporter-scheduled-executor-%d") + .setDaemon(true) + .build()); - public ZookeeperCounterReporter(MeterRegistry registry, - CounterStorage counterStorage, - String metricsSearchPrefix) { - this.meterRegistry = registry; - this.counterStorage = counterStorage; - this.metricsSearchPrefix = metricsSearchPrefix; - } + public ZookeeperCounterReporter( + MeterRegistry registry, CounterStorage counterStorage, String metricsSearchPrefix) { + this.meterRegistry = registry; + this.counterStorage = counterStorage; + this.metricsSearchPrefix = metricsSearchPrefix; + } - public void start(long period, TimeUnit unit) { - scheduledExecutorService.scheduleWithFixedDelay(this::report, 0, period, unit); - } + public void start(long period, TimeUnit unit) { + scheduledExecutorService.scheduleWithFixedDelay(this::report, 0, period, unit); + } - public void report() { - try { - Collection counters = Search.in(meterRegistry).counters(); - counters.forEach(counter -> { - CounterMatcher matcher = new CounterMatcher(counter, metricsSearchPrefix); - reportCounter(matcher); - reportVolumeCounter(matcher); - }); - } catch (RuntimeException ex) { - logger.error("Error during reporting metrics to Zookeeper...", ex); - } + public void report() { + try { + Collection counters = Search.in(meterRegistry).counters(); + counters.forEach( + counter -> { + CounterMatcher matcher = new CounterMatcher(counter, metricsSearchPrefix); + reportCounter(matcher); + reportVolumeCounter(matcher); + }); + } catch (RuntimeException ex) { + logger.error("Error during reporting metrics to Zookeeper...", ex); } + } - private void reportVolumeCounter(CounterMatcher matcher) { - if (matcher.isTopicThroughput()) { - counterStorage.incrementVolumeCounter( - escapedTopicName(matcher.getTopicName()), - matcher.getValue() - ); - } else if (matcher.isSubscriptionThroughput()) { - counterStorage.incrementVolumeCounter( - escapedTopicName(matcher.getTopicName()), - escapeMetricsReplacementChar(matcher.getSubscriptionName()), - matcher.getValue() - ); - } + private void reportVolumeCounter(CounterMatcher matcher) { + if (matcher.isTopicThroughput()) { + counterStorage.incrementVolumeCounter( + escapedTopicName(matcher.getTopicName()), matcher.getValue()); + } else if (matcher.isSubscriptionThroughput()) { + counterStorage.incrementVolumeCounter( + escapedTopicName(matcher.getTopicName()), + escapeMetricsReplacementChar(matcher.getSubscriptionName()), + matcher.getValue()); } + } - private void reportCounter(CounterMatcher matcher) { - if (matcher.getValue() == 0) { - return; - } - - if (matcher.isTopicPublished()) { - counterStorage.setTopicPublishedCounter( - escapedTopicName(matcher.getTopicName()), - matcher.getValue() - ); - } else if (matcher.isSubscriptionDelivered()) { - counterStorage.setSubscriptionDeliveredCounter( - escapedTopicName(matcher.getTopicName()), - escapeMetricsReplacementChar(matcher.getSubscriptionName()), - matcher.getValue() - ); - } else if (matcher.isSubscriptionDiscarded()) { - counterStorage.setSubscriptionDiscardedCounter( - escapedTopicName(matcher.getTopicName()), - escapeMetricsReplacementChar(matcher.getSubscriptionName()), - matcher.getValue() - ); - } + private void reportCounter(CounterMatcher matcher) { + if (matcher.getValue() == 0) { + return; } - private static TopicName escapedTopicName(TopicName topicName) { - return new TopicName( - escapeMetricsReplacementChar(topicName.getGroupName()), - topicName.getName() - ); + if (matcher.isTopicPublished()) { + counterStorage.setTopicPublishedCounter( + escapedTopicName(matcher.getTopicName()), matcher.getValue()); + } else if (matcher.isSubscriptionDelivered()) { + counterStorage.setSubscriptionDeliveredCounter( + escapedTopicName(matcher.getTopicName()), + escapeMetricsReplacementChar(matcher.getSubscriptionName()), + matcher.getValue()); + } else if (matcher.isSubscriptionDiscarded()) { + counterStorage.setSubscriptionDiscardedCounter( + escapedTopicName(matcher.getTopicName()), + escapeMetricsReplacementChar(matcher.getSubscriptionName()), + matcher.getValue()); } + } - private static String escapeMetricsReplacementChar(String value) { - return value.replaceAll(HermesMetrics.REPLACEMENT_CHAR, "\\."); - } + private static TopicName escapedTopicName(TopicName topicName) { + return new TopicName( + escapeMetricsReplacementChar(topicName.getGroupName()), topicName.getName()); + } + + private static String escapeMetricsReplacementChar(String value) { + return value.replaceAll(PathsCompiler.REPLACEMENT_CHAR, "\\."); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorage.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorage.java index aae7619a1a..c5624c7624 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorage.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorage.java @@ -1,5 +1,10 @@ package pl.allegro.tech.hermes.common.metric.counter.zookeeper; +import static pl.allegro.tech.hermes.metrics.PathContext.pathContext; +import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; +import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; +import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.allegro.tech.hermes.api.TopicName; @@ -11,131 +16,163 @@ import pl.allegro.tech.hermes.metrics.PathContext; import pl.allegro.tech.hermes.metrics.PathsCompiler; -import static pl.allegro.tech.hermes.metrics.PathContext.pathContext; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.GROUP; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.SUBSCRIPTION; -import static pl.allegro.tech.hermes.metrics.PathsCompiler.TOPIC; - public class ZookeeperCounterStorage implements CounterStorage { - static final String TOPIC_PUBLISHED = "/groups/" + GROUP + "/topics/" + TOPIC + "/metrics/published"; - static final String TOPIC_VOLUME_COUNTER = "/groups/" + GROUP + "/topics/" + TOPIC + "/metrics/volume"; - static final String SUBSCRIPTION_DELIVERED = - "/groups/" + GROUP + "/topics/" + TOPIC + "/subscriptions/" + SUBSCRIPTION + "/metrics/delivered"; - static final String SUBSCRIPTION_DISCARDED = - "/groups/" + GROUP + "/topics/" + TOPIC + "/subscriptions/" + SUBSCRIPTION + "/metrics/discarded"; - static final String SUBSCRIPTION_VOLUME_COUNTER = - "/groups/" + GROUP + "/topics/" + TOPIC + "/subscriptions/" + SUBSCRIPTION + "/metrics/volume"; - - private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperCounterStorage.class); - - private final MetricsDeltaCalculator deltaCalculator = new MetricsDeltaCalculator(); - private final SharedCounter sharedCounter; - - private final SubscriptionRepository subscriptionRepository; - private final PathsCompiler pathsCompiler; - private final String zookeeperRoot; - - public ZookeeperCounterStorage(SharedCounter sharedCounter, - SubscriptionRepository subscriptionRepository, - PathsCompiler pathsCompiler, - String zookeeperRoot) { - this.sharedCounter = sharedCounter; - this.subscriptionRepository = subscriptionRepository; - this.pathsCompiler = pathsCompiler; - this.zookeeperRoot = zookeeperRoot; - - } - - @Override - public void setTopicPublishedCounter(TopicName topicName, long count) { - String topicPublishedCounter = topicPublishedCounter(topicName); - incrementSharedCounter(topicPublishedCounter, count); - } - - @Override - public long getTopicPublishedCounter(TopicName topicName) { - return sharedCounter.getValue(topicPublishedCounter(topicName)); - } - - @Override - public long getSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName) { - return sharedCounter.getValue(subscriptionDeliveredCounter(topicName, subscriptionName)); - } - - @Override - public void setSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName, long count) { - try { - subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); - incrementSharedCounter(subscriptionDeliveredCounter(topicName, subscriptionName), count); - } catch (SubscriptionNotExistsException e) { - LOGGER.debug("Trying to report metric on not existing subscription {} {}", topicName, subscriptionName); - } - } - - @Override - public void setSubscriptionDiscardedCounter(TopicName topicName, String subscriptionName, long count) { - try { - subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); - incrementSharedCounter(subscriptionDiscardedCounter(topicName, subscriptionName), count); - } catch (SubscriptionNotExistsException e) { - LOGGER.debug("Trying to report metric on not existing subscription {} {}", topicName, subscriptionName); - } - } - - @Override - public void incrementVolumeCounter(TopicName topicName, String subscriptionName, long value) { - try { - subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); - incrementSharedCounter(volumeCounterPath(topicName, subscriptionName), value); - } catch (SubscriptionNotExistsException e) { - LOGGER.debug("Trying to report metric on not existing subscription {} {}", topicName, subscriptionName); - } - } - - @Override - public void incrementVolumeCounter(TopicName topicName, long value) { - incrementSharedCounter(topicVolumeCounter(topicName), value); - } - - private void incrementSharedCounter(String metricPath, long count) { - long delta = deltaCalculator.calculateDelta(metricPath, count); - - if (delta != 0 && !sharedCounter.increment(metricPath, delta)) { - deltaCalculator.revertDelta(metricPath, delta); - } - } - - private String topicPublishedCounter(TopicName topicName) { - PathContext pathContext = pathContext().withGroup(topicName.getGroupName()).withTopic(topicName.getName()).build(); - return pathsCompiler.compile(appendRootPath(TOPIC_PUBLISHED), pathContext); - } - - private String subscriptionDeliveredCounter(TopicName topicName, String subscriptionName) { - PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); - return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_DELIVERED), pathContext); - } - - private String subscriptionDiscardedCounter(TopicName topicName, String subscriptionName) { - PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); - return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_DISCARDED), pathContext); - } - - private String volumeCounterPath(TopicName topicName, String subscriptionName) { - PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); - return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_VOLUME_COUNTER), pathContext); - } - - private String topicVolumeCounter(TopicName topicName) { - PathContext pathContext = pathContext().withGroup(topicName.getGroupName()).withTopic(topicName.getName()).build(); - return pathsCompiler.compile(appendRootPath(TOPIC_VOLUME_COUNTER), pathContext); - } - - private PathContext subscriptionPathContext(TopicName topicName, String subscriptionName) { - return pathContext().withGroup(topicName.getGroupName()).withTopic(topicName.getName()).withSubscription(subscriptionName).build(); - } - - private String appendRootPath(String path) { - return zookeeperRoot + path; - } + static final String TOPIC_PUBLISHED = + "/groups/" + GROUP + "/topics/" + TOPIC + "/metrics/published"; + static final String TOPIC_VOLUME_COUNTER = + "/groups/" + GROUP + "/topics/" + TOPIC + "/metrics/volume"; + static final String SUBSCRIPTION_DELIVERED = + "/groups/" + + GROUP + + "/topics/" + + TOPIC + + "/subscriptions/" + + SUBSCRIPTION + + "/metrics/delivered"; + static final String SUBSCRIPTION_DISCARDED = + "/groups/" + + GROUP + + "/topics/" + + TOPIC + + "/subscriptions/" + + SUBSCRIPTION + + "/metrics/discarded"; + static final String SUBSCRIPTION_VOLUME_COUNTER = + "/groups/" + + GROUP + + "/topics/" + + TOPIC + + "/subscriptions/" + + SUBSCRIPTION + + "/metrics/volume"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperCounterStorage.class); + + private final MetricsDeltaCalculator deltaCalculator = new MetricsDeltaCalculator(); + private final SharedCounter sharedCounter; + + private final SubscriptionRepository subscriptionRepository; + private final PathsCompiler pathsCompiler; + private final String zookeeperRoot; + + public ZookeeperCounterStorage( + SharedCounter sharedCounter, + SubscriptionRepository subscriptionRepository, + PathsCompiler pathsCompiler, + String zookeeperRoot) { + this.sharedCounter = sharedCounter; + this.subscriptionRepository = subscriptionRepository; + this.pathsCompiler = pathsCompiler; + this.zookeeperRoot = zookeeperRoot; + } + + @Override + public void setTopicPublishedCounter(TopicName topicName, long count) { + String topicPublishedCounter = topicPublishedCounter(topicName); + incrementSharedCounter(topicPublishedCounter, count); + } + + @Override + public long getTopicPublishedCounter(TopicName topicName) { + return sharedCounter.getValue(topicPublishedCounter(topicName)); + } + + @Override + public long getSubscriptionDeliveredCounter(TopicName topicName, String subscriptionName) { + return sharedCounter.getValue(subscriptionDeliveredCounter(topicName, subscriptionName)); + } + + @Override + public void setSubscriptionDeliveredCounter( + TopicName topicName, String subscriptionName, long count) { + try { + subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); + incrementSharedCounter(subscriptionDeliveredCounter(topicName, subscriptionName), count); + } catch (SubscriptionNotExistsException e) { + LOGGER.debug( + "Trying to report metric on not existing subscription {} {}", + topicName, + subscriptionName); + } + } + + @Override + public void setSubscriptionDiscardedCounter( + TopicName topicName, String subscriptionName, long count) { + try { + subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); + incrementSharedCounter(subscriptionDiscardedCounter(topicName, subscriptionName), count); + } catch (SubscriptionNotExistsException e) { + LOGGER.debug( + "Trying to report metric on not existing subscription {} {}", + topicName, + subscriptionName); + } + } + + @Override + public void incrementVolumeCounter(TopicName topicName, String subscriptionName, long value) { + try { + subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName); + incrementSharedCounter(volumeCounterPath(topicName, subscriptionName), value); + } catch (SubscriptionNotExistsException e) { + LOGGER.debug( + "Trying to report metric on not existing subscription {} {}", + topicName, + subscriptionName); + } + } + + @Override + public void incrementVolumeCounter(TopicName topicName, long value) { + incrementSharedCounter(topicVolumeCounter(topicName), value); + } + + private void incrementSharedCounter(String metricPath, long count) { + long delta = deltaCalculator.calculateDelta(metricPath, count); + + if (delta != 0 && !sharedCounter.increment(metricPath, delta)) { + deltaCalculator.revertDelta(metricPath, delta); + } + } + + private String topicPublishedCounter(TopicName topicName) { + PathContext pathContext = + pathContext().withGroup(topicName.getGroupName()).withTopic(topicName.getName()).build(); + return pathsCompiler.compile(appendRootPath(TOPIC_PUBLISHED), pathContext); + } + + private String subscriptionDeliveredCounter(TopicName topicName, String subscriptionName) { + PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); + return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_DELIVERED), pathContext); + } + + private String subscriptionDiscardedCounter(TopicName topicName, String subscriptionName) { + PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); + return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_DISCARDED), pathContext); + } + + private String volumeCounterPath(TopicName topicName, String subscriptionName) { + PathContext pathContext = subscriptionPathContext(topicName, subscriptionName); + return pathsCompiler.compile(appendRootPath(SUBSCRIPTION_VOLUME_COUNTER), pathContext); + } + + private String topicVolumeCounter(TopicName topicName) { + PathContext pathContext = + pathContext().withGroup(topicName.getGroupName()).withTopic(topicName.getName()).build(); + return pathsCompiler.compile(appendRootPath(TOPIC_VOLUME_COUNTER), pathContext); + } + + private PathContext subscriptionPathContext(TopicName topicName, String subscriptionName) { + return pathContext() + .withGroup(topicName.getGroupName()) + .withTopic(topicName.getName()) + .withSubscription(subscriptionName) + .build(); + } + + private String appendRootPath(String path) { + return zookeeperRoot + path; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactory.java index a9b03796b3..91a83c82e1 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactory.java @@ -1,7 +1,6 @@ package pl.allegro.tech.hermes.common.metric.executor; import com.google.common.util.concurrent.ThreadFactoryBuilder; - import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; @@ -10,71 +9,90 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import pl.allegro.tech.hermes.common.metric.MetricsFacade; public class InstrumentedExecutorServiceFactory { - private final ThreadPoolMetrics threadPoolMetrics; - private final RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); - - public InstrumentedExecutorServiceFactory(ThreadPoolMetrics threadPoolMetrics) { - this.threadPoolMetrics = threadPoolMetrics; + private final MetricsFacade metricsFacade; + private final RejectedExecutionHandler rejectedExecutionHandler = + new ThreadPoolExecutor.AbortPolicy(); + + public InstrumentedExecutorServiceFactory(MetricsFacade metricsFacade) { + this.metricsFacade = metricsFacade; + } + + public ExecutorService getExecutorService(String name, int size, boolean monitoringEnabled) { + return getExecutorService(name, size, monitoringEnabled, Integer.MAX_VALUE); + } + + public ExecutorService getExecutorService( + String name, int size, boolean monitoringEnabled, int queueCapacity) { + ThreadFactory threadFactory = + new ThreadFactoryBuilder().setNameFormat(name + "-executor-%d").build(); + ThreadPoolExecutor executor = newFixedThreadPool(name, size, threadFactory, queueCapacity); + executor.prestartAllCoreThreads(); + + return monitoringEnabled ? monitor(name, executor) : executor; + } + + public class ScheduledExecutorServiceBuilder { + final String name; + final int size; + boolean monitoringEnabled = false; + boolean removeOnCancel = false; + + public ScheduledExecutorServiceBuilder(String name, int size) { + this.name = name; + this.size = size; } - public ExecutorService getExecutorService(String name, int size, boolean monitoringEnabled) { - ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(name + "-executor-%d").build(); - ThreadPoolExecutor executor = newFixedThreadPool(name, size, threadFactory); - executor.prestartAllCoreThreads(); - - if (monitoringEnabled) { - monitor(name, executor); - } - - return executor; - } - - public ScheduledExecutorService getScheduledExecutorService( - String name, int size, boolean monitoringEnabled - ) { - - ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(name + "-scheduled-executor-%d").build(); - - ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(size, threadFactory); - - if (monitoringEnabled) { - monitor(name, executor); - } - - return executor; + public ScheduledExecutorServiceBuilder withMonitoringEnabled(boolean monitoringEnabled) { + this.monitoringEnabled = monitoringEnabled; + return this; } - private void monitor(String threadPoolName, ThreadPoolExecutor executor) { - threadPoolMetrics.createGauges(threadPoolName, executor); + public ScheduledExecutorServiceBuilder withRemoveOnCancel(boolean removeOnCancel) { + this.removeOnCancel = removeOnCancel; + return this; } - - /** - * Copy of {@link java.util.concurrent.Executors#newFixedThreadPool(int, java.util.concurrent.ThreadFactory)}. - */ - private ThreadPoolExecutor newFixedThreadPool(String executorName, int size, ThreadFactory threadFactory) { - ThreadPoolExecutor executor = new ThreadPoolExecutor( - size, - size, - 0L, - TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - threadFactory, - getMeteredRejectedExecutionHandler(executorName) - ); - - return executor; - + public ScheduledExecutorService create() { + ThreadFactory threadFactory = + new ThreadFactoryBuilder().setNameFormat(name + "-scheduled-executor-%d").build(); + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(size, threadFactory); + executor.setRemoveOnCancelPolicy(removeOnCancel); + return monitoringEnabled ? monitor(name, executor) : executor; } - RejectedExecutionHandler getMeteredRejectedExecutionHandler(String executorName) { - return (r, executor) -> { - threadPoolMetrics.markRequestRejected(executorName); - rejectedExecutionHandler.rejectedExecution(r, executor); - }; + private ScheduledExecutorService monitor( + String threadPoolName, ScheduledExecutorService executor) { + return metricsFacade.executor().monitor(executor, threadPoolName); } - + } + + public ScheduledExecutorServiceBuilder scheduledExecutorBuilder(String name, int size) { + return new ScheduledExecutorServiceBuilder(name, size); + } + + private ExecutorService monitor(String threadPoolName, ExecutorService executor) { + return metricsFacade.executor().monitor(executor, threadPoolName); + } + + /** + * Copy of {@link java.util.concurrent.Executors#newFixedThreadPool(int, + * java.util.concurrent.ThreadFactory)} with configurable queue capacity. + */ + private ThreadPoolExecutor newFixedThreadPool( + String executorName, int size, ThreadFactory threadFactory, int queueCapacity) { + ThreadPoolExecutor executor = + new ThreadPoolExecutor( + size, + size, + 0L, + TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(queueCapacity), + threadFactory, + rejectedExecutionHandler); + return executor; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/ThreadPoolMetrics.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/ThreadPoolMetrics.java deleted file mode 100644 index 200d0d8fcb..0000000000 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/executor/ThreadPoolMetrics.java +++ /dev/null @@ -1,43 +0,0 @@ -package pl.allegro.tech.hermes.common.metric.executor; - -import pl.allegro.tech.hermes.common.metric.MetricsFacade; - -import java.util.concurrent.ThreadPoolExecutor; - -public class ThreadPoolMetrics { - - private final MetricsFacade metricsFacade; - - public ThreadPoolMetrics(MetricsFacade metricsFacade) { - this.metricsFacade = metricsFacade; - } - - public void createGauges( - String executorName, - ThreadPoolExecutor executor) { - - metricsFacade.executor().registerThreadPoolCapacity(executorName, executor, ThreadPoolExecutor::getPoolSize); - metricsFacade.executor().registerThreadPoolActiveThreads(executorName, executor, ThreadPoolExecutor::getActiveCount); - metricsFacade.executor().registerThreadPoolUtilization(executorName, executor, - e -> (double) e.getActiveCount() / (double) e.getPoolSize() - ); - metricsFacade.executor().registerThreadPoolTaskQueueCapacity(executorName, executor, - e -> { - int qCapacity = e.getQueue().size() + e.getQueue().remainingCapacity(); - // overflow in case of unbounded queue, set queueCapacity to Integer.MAX_VALUE - return qCapacity < 0 ? Integer.MAX_VALUE : qCapacity; - }); - metricsFacade.executor().registerThreadPoolTaskQueued(executorName, executor, e -> e.getQueue().size()); - metricsFacade.executor().registerThreadPoolTaskQueueUtilization(executorName, executor, - e -> { - int calculatedCapacity = e.getQueue().size() + e.getQueue().remainingCapacity(); - int queueCapacity = calculatedCapacity < 0 ? Integer.MAX_VALUE : calculatedCapacity; - return (double) e.getQueue().size() / (double) queueCapacity; - }); - } - - public void markRequestRejected(String executorName) { - metricsFacade.executor().incrementRequestRejectedCounter(executorName); - } - -} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/timer/StartedTimersPair.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/timer/StartedTimersPair.java index aa26a97bc1..44f7ac7505 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/timer/StartedTimersPair.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/metric/timer/StartedTimersPair.java @@ -1,22 +1,21 @@ package pl.allegro.tech.hermes.common.metric.timer; -import pl.allegro.tech.hermes.metrics.HermesTimerContext; - import java.io.Closeable; +import pl.allegro.tech.hermes.metrics.HermesTimerContext; public class StartedTimersPair implements Closeable { - private final HermesTimerContext time1; - private final HermesTimerContext time2; + private final HermesTimerContext time1; + private final HermesTimerContext time2; - public StartedTimersPair(HermesTimerContext timer1, HermesTimerContext timer2) { - time1 = timer1; - time2 = timer2; - } + public StartedTimersPair(HermesTimerContext timer1, HermesTimerContext timer2) { + time1 = timer1; + time2 = timer2; + } - @Override - public void close() { - time1.close(); - time2.close(); - } + @Override + public void close() { + time1.close(); + time2.close(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/AvroCompiledSchemaRepositoryFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/AvroCompiledSchemaRepositoryFactory.java index a3e8a49fe7..abccb0da34 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/AvroCompiledSchemaRepositoryFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/AvroCompiledSchemaRepositoryFactory.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.common.schema; +import java.time.Duration; import org.apache.avro.Schema; import pl.allegro.tech.hermes.schema.CachedCompiledSchemaRepository; import pl.allegro.tech.hermes.schema.CompiledSchemaRepository; @@ -7,35 +8,33 @@ import pl.allegro.tech.hermes.schema.RawSchemaClient; import pl.allegro.tech.hermes.schema.SchemaCompilersFactory; -import java.time.Duration; - public class AvroCompiledSchemaRepositoryFactory { - private final RawSchemaClient rawSchemaClient; - private final int maximumSize; - private final Duration expireAfterAccess; - private final boolean cacheEnabled; + private final RawSchemaClient rawSchemaClient; + private final int maximumSize; + private final Duration expireAfterAccess; + private final boolean cacheEnabled; - public AvroCompiledSchemaRepositoryFactory(RawSchemaClient rawSchemaClient, - int maximumSize, - Duration expireAfterAccess, - boolean cacheEnabled) { - this.rawSchemaClient = rawSchemaClient; - this.maximumSize = maximumSize; - this.expireAfterAccess = expireAfterAccess; - this.cacheEnabled = cacheEnabled; - } + public AvroCompiledSchemaRepositoryFactory( + RawSchemaClient rawSchemaClient, + int maximumSize, + Duration expireAfterAccess, + boolean cacheEnabled) { + this.rawSchemaClient = rawSchemaClient; + this.maximumSize = maximumSize; + this.expireAfterAccess = expireAfterAccess; + this.cacheEnabled = cacheEnabled; + } - public CompiledSchemaRepository provide() { - CompiledSchemaRepository repository = new DirectCompiledSchemaRepository<>(rawSchemaClient, - SchemaCompilersFactory.avroSchemaCompiler()); + public CompiledSchemaRepository provide() { + CompiledSchemaRepository repository = + new DirectCompiledSchemaRepository<>( + rawSchemaClient, SchemaCompilersFactory.avroSchemaCompiler()); - if (cacheEnabled) { - return new CachedCompiledSchemaRepository<>(repository, - maximumSize, - expireAfterAccess); - } else { - return repository; - } + if (cacheEnabled) { + return new CachedCompiledSchemaRepository<>(repository, maximumSize, expireAfterAccess); + } else { + return repository; } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/RawSchemaClientFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/RawSchemaClientFactory.java index e4bf8933cc..a6fd3b015d 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/RawSchemaClientFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/RawSchemaClientFactory.java @@ -1,7 +1,6 @@ package pl.allegro.tech.hermes.common.schema; import com.fasterxml.jackson.databind.ObjectMapper; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.schema.RawSchemaClient; import pl.allegro.tech.hermes.schema.SubjectNamingStrategy; @@ -10,46 +9,43 @@ public class RawSchemaClientFactory { - private final String kafkaNamespace; - private final String kafkaNamespaceSeparator; - private final MetricsFacade metricsFacade; - private final ObjectMapper objectMapper; - private final SchemaRepositoryInstanceResolver resolver; - private final boolean subjectSuffixEnabled; - private final boolean subjectNamespaceEnabled; + private final String kafkaNamespace; + private final String kafkaNamespaceSeparator; + private final MetricsFacade metricsFacade; + private final ObjectMapper objectMapper; + private final SchemaRepositoryInstanceResolver resolver; + private final boolean subjectSuffixEnabled; + private final boolean subjectNamespaceEnabled; - public RawSchemaClientFactory(String kafkaNamespace, - String kafkaNamespaceSeparator, - MetricsFacade metricsFacade, - ObjectMapper objectMapper, - SchemaRepositoryInstanceResolver resolver, - boolean subjectSuffixEnabled, - boolean subjectNamespaceEnabled) { - this.kafkaNamespace = kafkaNamespace; - this.kafkaNamespaceSeparator = kafkaNamespaceSeparator; - this.metricsFacade = metricsFacade; - this.objectMapper = objectMapper; - this.resolver = resolver; - this.subjectSuffixEnabled = subjectSuffixEnabled; - this.subjectNamespaceEnabled = subjectNamespaceEnabled; - } + public RawSchemaClientFactory( + String kafkaNamespace, + String kafkaNamespaceSeparator, + MetricsFacade metricsFacade, + ObjectMapper objectMapper, + SchemaRepositoryInstanceResolver resolver, + boolean subjectSuffixEnabled, + boolean subjectNamespaceEnabled) { + this.kafkaNamespace = kafkaNamespace; + this.kafkaNamespaceSeparator = kafkaNamespaceSeparator; + this.metricsFacade = metricsFacade; + this.objectMapper = objectMapper; + this.resolver = resolver; + this.subjectSuffixEnabled = subjectSuffixEnabled; + this.subjectNamespaceEnabled = subjectNamespaceEnabled; + } - public RawSchemaClient provide() { - SubjectNamingStrategy subjectNamingStrategy = SubjectNamingStrategy.qualifiedName - .withValueSuffixIf(subjectSuffixEnabled) - .withNamespacePrefixIf( - subjectNamespaceEnabled, - new SubjectNamingStrategy.Namespace( - kafkaNamespace, - kafkaNamespaceSeparator - ) - ); - return createMetricsTrackingClient( - new SchemaRegistryRawSchemaClient(resolver, objectMapper, subjectNamingStrategy) - ); - } + public RawSchemaClient provide() { + SubjectNamingStrategy subjectNamingStrategy = + SubjectNamingStrategy.qualifiedName + .withValueSuffixIf(subjectSuffixEnabled) + .withNamespacePrefixIf( + subjectNamespaceEnabled, + new SubjectNamingStrategy.Namespace(kafkaNamespace, kafkaNamespaceSeparator)); + return createMetricsTrackingClient( + new SchemaRegistryRawSchemaClient(resolver, objectMapper, subjectNamingStrategy)); + } - private RawSchemaClient createMetricsTrackingClient(RawSchemaClient rawSchemaClient) { - return new ReadMetricsTrackingRawSchemaClient(rawSchemaClient, metricsFacade); - } + private RawSchemaClient createMetricsTrackingClient(RawSchemaClient rawSchemaClient) { + return new ReadMetricsTrackingRawSchemaClient(rawSchemaClient, metricsFacade); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClient.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClient.java index 10b792c9c7..0b699d4654 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClient.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClient.java @@ -1,5 +1,8 @@ package pl.allegro.tech.hermes.common.schema; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; import pl.allegro.tech.hermes.api.RawSchema; import pl.allegro.tech.hermes.api.RawSchemaWithMetadata; import pl.allegro.tech.hermes.api.TopicName; @@ -10,67 +13,64 @@ import pl.allegro.tech.hermes.schema.SchemaId; import pl.allegro.tech.hermes.schema.SchemaVersion; -import java.util.List; -import java.util.Optional; -import java.util.function.Supplier; - public class ReadMetricsTrackingRawSchemaClient implements RawSchemaClient { - private final RawSchemaClient rawSchemaClient; - private final MetricsFacade metricsFacade; + private final RawSchemaClient rawSchemaClient; + private final MetricsFacade metricsFacade; - public ReadMetricsTrackingRawSchemaClient( - RawSchemaClient rawSchemaClient, - MetricsFacade metricsFacade) { - this.rawSchemaClient = rawSchemaClient; - this.metricsFacade = metricsFacade; - } + public ReadMetricsTrackingRawSchemaClient( + RawSchemaClient rawSchemaClient, MetricsFacade metricsFacade) { + this.rawSchemaClient = rawSchemaClient; + this.metricsFacade = metricsFacade; + } - @Override - public Optional getRawSchemaWithMetadata(TopicName topic, SchemaVersion version) { - return timedSchema(() -> rawSchemaClient.getRawSchemaWithMetadata(topic, version)); - } + @Override + public Optional getRawSchemaWithMetadata( + TopicName topic, SchemaVersion version) { + return timedSchema(() -> rawSchemaClient.getRawSchemaWithMetadata(topic, version)); + } - @Override - public Optional getRawSchemaWithMetadata(TopicName topic, SchemaId schemaId) { - return timedSchema(() -> rawSchemaClient.getRawSchemaWithMetadata(topic, schemaId)); - } + @Override + public Optional getRawSchemaWithMetadata( + TopicName topic, SchemaId schemaId) { + return timedSchema(() -> rawSchemaClient.getRawSchemaWithMetadata(topic, schemaId)); + } - @Override - public Optional getLatestRawSchemaWithMetadata(TopicName topic) { - return timedSchema(() -> rawSchemaClient.getLatestRawSchemaWithMetadata(topic)); - } + @Override + public Optional getLatestRawSchemaWithMetadata(TopicName topic) { + return timedSchema(() -> rawSchemaClient.getLatestRawSchemaWithMetadata(topic)); + } - @Override - public List getVersions(TopicName topic) { - return timedVersions(() -> rawSchemaClient.getVersions(topic)); - } + @Override + public List getVersions(TopicName topic) { + return timedVersions(() -> rawSchemaClient.getVersions(topic)); + } - @Override - public void registerSchema(TopicName topic, RawSchema rawSchema) { - rawSchemaClient.registerSchema(topic, rawSchema); - } + @Override + public void registerSchema(TopicName topic, RawSchema rawSchema) { + rawSchemaClient.registerSchema(topic, rawSchema); + } - @Override - public void deleteAllSchemaVersions(TopicName topic) { - rawSchemaClient.deleteAllSchemaVersions(topic); - } + @Override + public void deleteAllSchemaVersions(TopicName topic) { + rawSchemaClient.deleteAllSchemaVersions(topic); + } - @Override - public void validateSchema(TopicName topic, RawSchema rawSchema) { - rawSchemaClient.validateSchema(topic, rawSchema); - } + @Override + public void validateSchema(TopicName topic, RawSchema rawSchema) { + rawSchemaClient.validateSchema(topic, rawSchema); + } - private T timedSchema(Supplier callable) { - return timed(callable, metricsFacade.schemaClient().schemaTimer()); - } + private T timedSchema(Supplier callable) { + return timed(callable, metricsFacade.schemaClient().schemaTimer()); + } - private T timedVersions(Supplier callable) { - return timed(callable, metricsFacade.schemaClient().versionsTimer()); - } + private T timedVersions(Supplier callable) { + return timed(callable, metricsFacade.schemaClient().versionsTimer()); + } - private T timed(Supplier callable, HermesTimer timer) { - try (HermesTimerContext time = timer.time()) { - return callable.get(); - } + private T timed(Supplier callable, HermesTimer timer) { + try (HermesTimerContext time = timer.time()) { + return callable.get(); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaCacheRefresherCallback.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaCacheRefresherCallback.java index b59baf0c0c..f44d109ac7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaCacheRefresherCallback.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaCacheRefresherCallback.java @@ -1,5 +1,6 @@ package pl.allegro.tech.hermes.common.schema; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pl.allegro.tech.hermes.api.ContentType; @@ -11,57 +12,61 @@ import pl.allegro.tech.hermes.schema.SchemaVersion; import pl.allegro.tech.hermes.schema.SchemaVersionsResult; -import java.util.List; - class SchemaCacheRefresherCallback implements TopicCallback { - private static final Logger logger = LoggerFactory.getLogger(SchemaVersionsRepositoryFactory.class); + private static final Logger logger = + LoggerFactory.getLogger(SchemaVersionsRepositoryFactory.class); - public static final boolean REFRESH_ONLINE = true; + public static final boolean REFRESH_ONLINE = true; - private final CachedSchemaVersionsRepository schemaVersionsRepository; - private final CachedCompiledSchemaRepository compiledSchemaRepository; + private final CachedSchemaVersionsRepository schemaVersionsRepository; + private final CachedCompiledSchemaRepository compiledSchemaRepository; - public SchemaCacheRefresherCallback(CachedSchemaVersionsRepository schemaVersionsRepository, - CachedCompiledSchemaRepository compiledSchemaRepository) { - this.schemaVersionsRepository = schemaVersionsRepository; - this.compiledSchemaRepository = compiledSchemaRepository; - } + public SchemaCacheRefresherCallback( + CachedSchemaVersionsRepository schemaVersionsRepository, + CachedCompiledSchemaRepository compiledSchemaRepository) { + this.schemaVersionsRepository = schemaVersionsRepository; + this.compiledSchemaRepository = compiledSchemaRepository; + } - @Override - public void onTopicRemoved(Topic topic) { - schemaVersionsRepository.removeFromCache(topic); - compiledSchemaRepository.removeFromCache(topic); - } + @Override + public void onTopicRemoved(Topic topic) { + schemaVersionsRepository.removeFromCache(topic); + compiledSchemaRepository.removeFromCache(topic); + } - @Override - public void onTopicCreated(Topic topic) { - refreshSchemas(topic); - } + @Override + public void onTopicCreated(Topic topic) { + refreshSchemas(topic); + } - @Override - public void onTopicChanged(Topic topic) { - refreshSchemas(topic); - } + @Override + public void onTopicChanged(Topic topic) { + refreshSchemas(topic); + } - private void refreshSchemas(Topic topic) { - if (topic.getContentType() == ContentType.AVRO) { - logger.info("Refreshing all schemas for {} topic.", topic.getQualifiedName()); - SchemaVersionsResult versions = schemaVersionsRepository.versions(topic, REFRESH_ONLINE); - if (versions.isSuccess()) { - refreshCompiledSchemas(topic, versions.get()); - } - } + private void refreshSchemas(Topic topic) { + if (topic.getContentType() == ContentType.AVRO) { + logger.info("Refreshing all schemas for {} topic.", topic.getQualifiedName()); + SchemaVersionsResult versions = schemaVersionsRepository.versions(topic, REFRESH_ONLINE); + if (versions.isSuccess()) { + refreshCompiledSchemas(topic, versions.get()); + } } + } - private void refreshCompiledSchemas(Topic topic, List schemaVersions) { - schemaVersions.forEach(schemaVersion -> { - try { - compiledSchemaRepository.getSchema(topic, schemaVersion, REFRESH_ONLINE); - } catch (CouldNotLoadSchemaException e) { - logger.warn("Schema for topic {} at version {} could not be loaded", - topic.getQualifiedName(), schemaVersion.value(), e); - } + private void refreshCompiledSchemas(Topic topic, List schemaVersions) { + schemaVersions.forEach( + schemaVersion -> { + try { + compiledSchemaRepository.getSchema(topic, schemaVersion, REFRESH_ONLINE); + } catch (CouldNotLoadSchemaException e) { + logger.warn( + "Schema for topic {} at version {} could not be loaded", + topic.getQualifiedName(), + schemaVersion.value(), + e); + } }); - } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaRepositoryFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaRepositoryFactory.java index 4943073ea9..918273b82d 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaRepositoryFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaRepositoryFactory.java @@ -7,17 +7,18 @@ public class SchemaRepositoryFactory { - private final SchemaVersionsRepository schemaVersionsRepository; + private final SchemaVersionsRepository schemaVersionsRepository; - private final CompiledSchemaRepository compiledAvroSchemaRepository; + private final CompiledSchemaRepository compiledAvroSchemaRepository; - public SchemaRepositoryFactory(SchemaVersionsRepository schemaVersionsRepository, - CompiledSchemaRepository compiledAvroSchemaRepository) { - this.schemaVersionsRepository = schemaVersionsRepository; - this.compiledAvroSchemaRepository = compiledAvroSchemaRepository; - } + public SchemaRepositoryFactory( + SchemaVersionsRepository schemaVersionsRepository, + CompiledSchemaRepository compiledAvroSchemaRepository) { + this.schemaVersionsRepository = schemaVersionsRepository; + this.compiledAvroSchemaRepository = compiledAvroSchemaRepository; + } - public SchemaRepository provide() { - return new SchemaRepository(schemaVersionsRepository, compiledAvroSchemaRepository); - } + public SchemaRepository provide() { + return new SchemaRepository(schemaVersionsRepository, compiledAvroSchemaRepository); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionRepositoryParameters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionRepositoryParameters.java index 4b04ccc34c..555f70907e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionRepositoryParameters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionRepositoryParameters.java @@ -4,11 +4,11 @@ public interface SchemaVersionRepositoryParameters { - boolean isCacheEnabled(); + boolean isCacheEnabled(); - Duration getRefreshAfterWrite(); + Duration getRefreshAfterWrite(); - Duration getExpireAfterWrite(); + Duration getExpireAfterWrite(); - int getReloadThreadPoolSize(); + int getReloadThreadPoolSize(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionsRepositoryFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionsRepositoryFactory.java index 90eaf20685..211b38a2a7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionsRepositoryFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/schema/SchemaVersionsRepositoryFactory.java @@ -1,6 +1,8 @@ package pl.allegro.tech.hermes.common.schema; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import pl.allegro.tech.hermes.domain.notifications.InternalNotificationsBus; import pl.allegro.tech.hermes.schema.CachedCompiledSchemaRepository; import pl.allegro.tech.hermes.schema.CachedSchemaVersionsRepository; @@ -9,47 +11,46 @@ import pl.allegro.tech.hermes.schema.RawSchemaClient; import pl.allegro.tech.hermes.schema.SchemaVersionsRepository; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - public class SchemaVersionsRepositoryFactory { - private final RawSchemaClient rawSchemaClient; - private final SchemaVersionRepositoryParameters schemaVersionsRepositoryParameters; - private final InternalNotificationsBus notificationsBus; - private final CompiledSchemaRepository compiledSchemaRepository; - - public SchemaVersionsRepositoryFactory(RawSchemaClient rawSchemaClient, - SchemaVersionRepositoryParameters schemaVersionsRepositoryParameters, - InternalNotificationsBus notificationsBus, - CompiledSchemaRepository compiledSchemaRepository) { - this.rawSchemaClient = rawSchemaClient; - this.schemaVersionsRepositoryParameters = schemaVersionsRepositoryParameters; - this.notificationsBus = notificationsBus; - this.compiledSchemaRepository = compiledSchemaRepository; - } - - public SchemaVersionsRepository provide() { - if (schemaVersionsRepositoryParameters.isCacheEnabled()) { - CachedSchemaVersionsRepository cachedSchemaVersionsRepository = new CachedSchemaVersionsRepository( - rawSchemaClient, - getVersionsReloader(), - schemaVersionsRepositoryParameters.getRefreshAfterWrite(), - schemaVersionsRepositoryParameters.getExpireAfterWrite()); - - notificationsBus.registerTopicCallback( - new SchemaCacheRefresherCallback<>( - cachedSchemaVersionsRepository, - (CachedCompiledSchemaRepository) compiledSchemaRepository)); - - return cachedSchemaVersionsRepository; - } - return new DirectSchemaVersionsRepository(rawSchemaClient); - } - - private ExecutorService getVersionsReloader() { - return Executors.newFixedThreadPool( - schemaVersionsRepositoryParameters.getReloadThreadPoolSize(), - new ThreadFactoryBuilder().setNameFormat("schema-source-reloader-%d").build()); + private final RawSchemaClient rawSchemaClient; + private final SchemaVersionRepositoryParameters schemaVersionsRepositoryParameters; + private final InternalNotificationsBus notificationsBus; + private final CompiledSchemaRepository compiledSchemaRepository; + + public SchemaVersionsRepositoryFactory( + RawSchemaClient rawSchemaClient, + SchemaVersionRepositoryParameters schemaVersionsRepositoryParameters, + InternalNotificationsBus notificationsBus, + CompiledSchemaRepository compiledSchemaRepository) { + this.rawSchemaClient = rawSchemaClient; + this.schemaVersionsRepositoryParameters = schemaVersionsRepositoryParameters; + this.notificationsBus = notificationsBus; + this.compiledSchemaRepository = compiledSchemaRepository; + } + + public SchemaVersionsRepository provide() { + if (schemaVersionsRepositoryParameters.isCacheEnabled()) { + CachedSchemaVersionsRepository cachedSchemaVersionsRepository = + new CachedSchemaVersionsRepository( + rawSchemaClient, + getVersionsReloader(), + schemaVersionsRepositoryParameters.getRefreshAfterWrite(), + schemaVersionsRepositoryParameters.getExpireAfterWrite()); + + notificationsBus.registerTopicCallback( + new SchemaCacheRefresherCallback<>( + cachedSchemaVersionsRepository, + (CachedCompiledSchemaRepository) compiledSchemaRepository)); + + return cachedSchemaVersionsRepository; } + return new DirectSchemaVersionsRepository(rawSchemaClient); + } + + private ExecutorService getVersionsReloader() { + return Executors.newFixedThreadPool( + schemaVersionsRepositoryParameters.getReloadThreadPoolSize(), + new ThreadFactoryBuilder().setNameFormat("schema-source-reloader-%d").build()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/DefaultSslContextFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/DefaultSslContextFactory.java index f7c1b97914..3425aa0f7e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/DefaultSslContextFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/DefaultSslContextFactory.java @@ -4,24 +4,28 @@ public class DefaultSslContextFactory implements SslContextFactory { - private final String protocol; - private final KeyManagersProvider keyManagersProvider; - private final TrustManagersProvider trustManagersProvider; + private final String protocol; + private final KeyManagersProvider keyManagersProvider; + private final TrustManagersProvider trustManagersProvider; - public DefaultSslContextFactory(String protocol, KeyManagersProvider keyManagersProvider, TrustManagersProvider trustManagersProvider) { - this.protocol = protocol; - this.keyManagersProvider = keyManagersProvider; - this.trustManagersProvider = trustManagersProvider; - } + public DefaultSslContextFactory( + String protocol, + KeyManagersProvider keyManagersProvider, + TrustManagersProvider trustManagersProvider) { + this.protocol = protocol; + this.keyManagersProvider = keyManagersProvider; + this.trustManagersProvider = trustManagersProvider; + } - @Override - public SSLContextHolder create() { - try { - SSLContext sslContext = SSLContext.getInstance(protocol); - sslContext.init(keyManagersProvider.getKeyManagers(), trustManagersProvider.getTrustManagers(), null); - return new SSLContextHolder(sslContext, trustManagersProvider.getTrustManagers()); - } catch (Exception e) { - throw new SslContextCreationException(e); - } + @Override + public SSLContextHolder create() { + try { + SSLContext sslContext = SSLContext.getInstance(protocol); + sslContext.init( + keyManagersProvider.getKeyManagers(), trustManagersProvider.getTrustManagers(), null); + return new SSLContextHolder(sslContext, trustManagersProvider.getTrustManagers()); + } catch (Exception e) { + throw new SslContextCreationException(e); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeyManagersProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeyManagersProvider.java index 305e3e3194..6136684e6a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeyManagersProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeyManagersProvider.java @@ -3,5 +3,5 @@ import javax.net.ssl.KeyManager; public interface KeyManagersProvider { - KeyManager[] getKeyManagers() throws Exception; + KeyManager[] getKeyManagers() throws Exception; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreConfigurationException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreConfigurationException.java index de4d3b8fca..65e9abba69 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreConfigurationException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreConfigurationException.java @@ -1,7 +1,7 @@ package pl.allegro.tech.hermes.common.ssl; public class KeystoreConfigurationException extends RuntimeException { - public KeystoreConfigurationException(String keystoreSource) { - super(String.format("Unknown key store source: %s", keystoreSource)); - } + public KeystoreConfigurationException(String keystoreSource) { + super(String.format("Unknown key store source: %s", keystoreSource)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreProperties.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreProperties.java index 796e570acc..4cee1c9e78 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreProperties.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreProperties.java @@ -1,32 +1,31 @@ package pl.allegro.tech.hermes.common.ssl; - import java.net.URI; public class KeystoreProperties { - private final String location; - private final String format; - private final String password; - - public KeystoreProperties(String location, String format, String password) { - this.location = location; - this.format = format; - this.password = password; - } - - public String getLocation() { - return location; - } - - public String getFormat() { - return format; - } - - public String getPassword() { - return password; - } - - public URI getLocationAsURI() { - return URI.create(getLocation()); - } + private final String location; + private final String format; + private final String password; + + public KeystoreProperties(String location, String format, String password) { + this.location = location; + this.format = format; + this.password = password; + } + + public String getLocation() { + return location; + } + + public String getFormat() { + return format; + } + + public String getPassword() { + return password; + } + + public URI getLocationAsURI() { + return URI.create(getLocation()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreSource.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreSource.java index e6b5963a58..ecab7f9927 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreSource.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/KeystoreSource.java @@ -1,16 +1,16 @@ package pl.allegro.tech.hermes.common.ssl; public enum KeystoreSource { - JRE("jre"), - PROVIDED("provided"); + JRE("jre"), + PROVIDED("provided"); - KeystoreSource(String value) { - this.value = value; - } + KeystoreSource(String value) { + this.value = value; + } - private final String value; + private final String value; - public final String getValue() { - return value; - } + public final String getValue() { + return value; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SSLContextHolder.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SSLContextHolder.java index 010fe816fb..c5a9499004 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SSLContextHolder.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SSLContextHolder.java @@ -5,19 +5,19 @@ public final class SSLContextHolder { - private final SSLContext sslContext; - private final TrustManager[] trustManagers; + private final SSLContext sslContext; + private final TrustManager[] trustManagers; - public SSLContextHolder(SSLContext sslContext, TrustManager[] trustManagers) { - this.sslContext = sslContext; - this.trustManagers = trustManagers; - } + public SSLContextHolder(SSLContext sslContext, TrustManager[] trustManagers) { + this.sslContext = sslContext; + this.trustManagers = trustManagers; + } - public SSLContext getSslContext() { - return sslContext; - } + public SSLContext getSslContext() { + return sslContext; + } - public TrustManager[] getTrustManagers() { - return trustManagers; - } + public TrustManager[] getTrustManagers() { + return trustManagers; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextCreationException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextCreationException.java index fd4c44709d..11d173bc99 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextCreationException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextCreationException.java @@ -1,7 +1,7 @@ package pl.allegro.tech.hermes.common.ssl; class SslContextCreationException extends RuntimeException { - SslContextCreationException(Exception e) { - super(e); - } + SslContextCreationException(Exception e) { + super(e); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextFactory.java index c380098827..aaa64129fc 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/SslContextFactory.java @@ -2,5 +2,5 @@ public interface SslContextFactory { - SSLContextHolder create(); + SSLContextHolder create(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TrustManagersProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TrustManagersProvider.java index 0be68ca256..edb8a15a95 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TrustManagersProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TrustManagersProvider.java @@ -3,5 +3,5 @@ import javax.net.ssl.TrustManager; public interface TrustManagersProvider { - TrustManager[] getTrustManagers() throws Exception; + TrustManager[] getTrustManagers() throws Exception; } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TruststoreConfigurationException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TruststoreConfigurationException.java index 89461be73b..8a77790465 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TruststoreConfigurationException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/TruststoreConfigurationException.java @@ -1,7 +1,7 @@ package pl.allegro.tech.hermes.common.ssl; public class TruststoreConfigurationException extends RuntimeException { - public TruststoreConfigurationException(String truststoreSource) { - super(String.format("Unknown trust store source: %s", truststoreSource)); - } + public TruststoreConfigurationException(String truststoreSource) { + super(String.format("Unknown trust store source: %s", truststoreSource)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmKeyManagersProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmKeyManagersProvider.java index c4432bff3f..ae8a3aa88e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmKeyManagersProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmKeyManagersProvider.java @@ -1,16 +1,16 @@ package pl.allegro.tech.hermes.common.ssl.jvm; -import pl.allegro.tech.hermes.common.ssl.KeyManagersProvider; - import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; +import pl.allegro.tech.hermes.common.ssl.KeyManagersProvider; public class JvmKeyManagersProvider implements KeyManagersProvider { - @Override - public KeyManager[] getKeyManagers() throws Exception { - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(null, null); - return keyManagerFactory.getKeyManagers(); - } + @Override + public KeyManager[] getKeyManagers() throws Exception { + KeyManagerFactory keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(null, null); + return keyManagerFactory.getKeyManagers(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmTrustManagerProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmTrustManagerProvider.java index 18c31d14d1..aa99072471 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmTrustManagerProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/jvm/JvmTrustManagerProvider.java @@ -1,6 +1,6 @@ package pl.allegro.tech.hermes.common.ssl.jvm; -import pl.allegro.tech.hermes.common.ssl.TrustManagersProvider; +import static java.lang.String.format; import java.io.File; import java.io.FileInputStream; @@ -9,50 +9,52 @@ import java.security.KeyStore; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; - -import static java.lang.String.format; +import pl.allegro.tech.hermes.common.ssl.TrustManagersProvider; public class JvmTrustManagerProvider implements TrustManagersProvider { - @Override - public TrustManager[] getTrustManagers() throws Exception { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(loadJvmKeyStore()); - return trustManagerFactory.getTrustManagers(); + @Override + public TrustManager[] getTrustManagers() throws Exception { + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(loadJvmKeyStore()); + return trustManagerFactory.getTrustManagers(); + } + + private KeyStore loadJvmKeyStore() throws Exception { + String trustStore = System.getProperty("javax.net.ssl.trustStore", ""); + String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); + String trustStoreType = + System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); + String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider", ""); + + KeyStore keyStore = + trustStoreProvider.isEmpty() + ? KeyStore.getInstance(trustStoreType) + : KeyStore.getInstance(trustStoreType, trustStoreProvider); + char[] password = trustStorePassword == null ? null : trustStorePassword.toCharArray(); + keyStore.load(trustStoreInputStream(trustStore), password); + + return keyStore; + } + + private InputStream trustStoreInputStream(String trustStore) throws FileNotFoundException { + return new FileInputStream(trustStore.isEmpty() ? defaultTrustStore() : new File(trustStore)); + } + + private File defaultTrustStore() throws FileNotFoundException { + String javaHome = System.getProperty("java.home"); + + File jssecacerts = new File(format("%s/lib/security/jssecacerts", javaHome)); + if (jssecacerts.exists()) { + return jssecacerts; } - private KeyStore loadJvmKeyStore() throws Exception { - String trustStore = System.getProperty("javax.net.ssl.trustStore", ""); - String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); - String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); - String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider", ""); - - KeyStore keyStore = trustStoreProvider.isEmpty() - ? KeyStore.getInstance(trustStoreType) - : KeyStore.getInstance(trustStoreType, trustStoreProvider); - char[] password = trustStorePassword == null ? null : trustStorePassword.toCharArray(); - keyStore.load(trustStoreInputStream(trustStore), password); - - return keyStore; + File cacerts = new File(format("%s/lib/security/cacerts", javaHome)); + if (cacerts.exists()) { + return cacerts; } - private InputStream trustStoreInputStream(String trustStore) throws FileNotFoundException { - return new FileInputStream(trustStore.isEmpty() ? defaultTrustStore() : new File(trustStore)); - } - - private File defaultTrustStore() throws FileNotFoundException { - String javaHome = System.getProperty("java.home"); - - File jssecacerts = new File(format("%s/lib/security/jssecacerts", javaHome)); - if (jssecacerts.exists()) { - return jssecacerts; - } - - File cacerts = new File(format("%s/lib/security/cacerts", javaHome)); - if (cacerts.exists()) { - return cacerts; - } - - throw new FileNotFoundException("Default trust store not found."); - } + throw new FileNotFoundException("Default trust store not found."); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedKeyManagersProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedKeyManagersProvider.java index 99d4d0627d..b2933d11af 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedKeyManagersProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedKeyManagersProvider.java @@ -1,29 +1,29 @@ package pl.allegro.tech.hermes.common.ssl.provided; -import pl.allegro.tech.hermes.common.ssl.KeyManagersProvider; -import pl.allegro.tech.hermes.common.ssl.KeystoreProperties; - import java.io.InputStream; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; +import pl.allegro.tech.hermes.common.ssl.KeyManagersProvider; +import pl.allegro.tech.hermes.common.ssl.KeystoreProperties; public class ProvidedKeyManagersProvider implements KeyManagersProvider, ResourceLoader { - private final KeystoreProperties keystoreProperties; + private final KeystoreProperties keystoreProperties; - public ProvidedKeyManagersProvider(KeystoreProperties keystoreProperties) { - this.keystoreProperties = keystoreProperties; - } + public ProvidedKeyManagersProvider(KeystoreProperties keystoreProperties) { + this.keystoreProperties = keystoreProperties; + } - @Override - public KeyManager[] getKeyManagers() throws Exception { - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - KeyStore keyStore = KeyStore.getInstance(keystoreProperties.getFormat()); - try (InputStream stream = getResourceAsInputStream(keystoreProperties.getLocationAsURI())) { - keyStore.load(stream, keystoreProperties.getPassword().toCharArray()); - keyManagerFactory.init(keyStore, keystoreProperties.getPassword().toCharArray()); - } - return keyManagerFactory.getKeyManagers(); + @Override + public KeyManager[] getKeyManagers() throws Exception { + KeyManagerFactory keyManagerFactory = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + KeyStore keyStore = KeyStore.getInstance(keystoreProperties.getFormat()); + try (InputStream stream = getResourceAsInputStream(keystoreProperties.getLocationAsURI())) { + keyStore.load(stream, keystoreProperties.getPassword().toCharArray()); + keyManagerFactory.init(keyStore, keystoreProperties.getPassword().toCharArray()); } + return keyManagerFactory.getKeyManagers(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedTrustManagersProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedTrustManagersProvider.java index e8d2532d56..b93037a69d 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedTrustManagersProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ProvidedTrustManagersProvider.java @@ -1,29 +1,29 @@ package pl.allegro.tech.hermes.common.ssl.provided; -import pl.allegro.tech.hermes.common.ssl.KeystoreProperties; -import pl.allegro.tech.hermes.common.ssl.TrustManagersProvider; - import java.io.InputStream; import java.security.KeyStore; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import pl.allegro.tech.hermes.common.ssl.KeystoreProperties; +import pl.allegro.tech.hermes.common.ssl.TrustManagersProvider; public class ProvidedTrustManagersProvider implements TrustManagersProvider, ResourceLoader { - private final KeystoreProperties keystoreProperties; + private final KeystoreProperties keystoreProperties; - public ProvidedTrustManagersProvider(KeystoreProperties keystoreProperties) { - this.keystoreProperties = keystoreProperties; - } + public ProvidedTrustManagersProvider(KeystoreProperties keystoreProperties) { + this.keystoreProperties = keystoreProperties; + } - @Override - public TrustManager[] getTrustManagers() throws Exception { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - KeyStore keyStore = KeyStore.getInstance(keystoreProperties.getFormat()); - try (InputStream stream = getResourceAsInputStream(keystoreProperties.getLocationAsURI())) { - keyStore.load(stream, keystoreProperties.getPassword().toCharArray()); - trustManagerFactory.init(keyStore); - } - return trustManagerFactory.getTrustManagers(); + @Override + public TrustManager[] getTrustManagers() throws Exception { + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + KeyStore keyStore = KeyStore.getInstance(keystoreProperties.getFormat()); + try (InputStream stream = getResourceAsInputStream(keystoreProperties.getLocationAsURI())) { + keyStore.load(stream, keystoreProperties.getPassword().toCharArray()); + trustManagerFactory.init(keyStore); } + return trustManagerFactory.getTrustManagers(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ResourceLoader.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ResourceLoader.java index 42047351eb..b7177fcaac 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ResourceLoader.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/ssl/provided/ResourceLoader.java @@ -1,17 +1,18 @@ package pl.allegro.tech.hermes.common.ssl.provided; +import static com.google.common.base.Strings.isNullOrEmpty; + import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.net.URI; -import static com.google.common.base.Strings.isNullOrEmpty; - interface ResourceLoader { - default InputStream getResourceAsInputStream(URI location) throws FileNotFoundException { - if ("classpath".equalsIgnoreCase(location.getScheme())) { - return getClass().getClassLoader().getResourceAsStream(location.getSchemeSpecificPart()); - } - return new FileInputStream(isNullOrEmpty(location.getPath()) ? location.getSchemeSpecificPart() : location.getPath()); + default InputStream getResourceAsInputStream(URI location) throws FileNotFoundException { + if ("classpath".equalsIgnoreCase(location.getScheme())) { + return getClass().getClassLoader().getResourceAsStream(location.getSchemeSpecificPart()); } + return new FileInputStream( + isNullOrEmpty(location.getPath()) ? location.getSchemeSpecificPart() : location.getPath()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InetAddressInstanceIdResolver.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InetAddressInstanceIdResolver.java index ef98c2c8bc..29c3c84023 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InetAddressInstanceIdResolver.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InetAddressInstanceIdResolver.java @@ -1,25 +1,23 @@ package pl.allegro.tech.hermes.common.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.net.InetAddress; import java.net.UnknownHostException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class InetAddressInstanceIdResolver implements InstanceIdResolver { - private static final Logger LOGGER = LoggerFactory.getLogger(InetAddressInstanceIdResolver.class); + private static final Logger LOGGER = LoggerFactory.getLogger(InetAddressInstanceIdResolver.class); - public InetAddressInstanceIdResolver() { } + public InetAddressInstanceIdResolver() {} - public String resolve() { - String hostname = "hostname-could-not-be-detected"; - try { - hostname = InetAddress.getLocalHost().getCanonicalHostName(); - } catch (UnknownHostException e) { - LOGGER.warn("Could not determine hostname"); - } - return hostname; + public String resolve() { + String hostname = "hostname-could-not-be-detected"; + try { + hostname = InetAddress.getLocalHost().getCanonicalHostName(); + } catch (UnknownHostException e) { + LOGGER.warn("Could not determine hostname"); } - + return hostname; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InstanceIdResolver.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InstanceIdResolver.java index 0373cec8fa..2171a491f2 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InstanceIdResolver.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/common/util/InstanceIdResolver.java @@ -1,5 +1,5 @@ package pl.allegro.tech.hermes.common.util; public interface InstanceIdResolver { - String resolve(); + String resolve(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/CredentialsRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/CredentialsRepository.java index 7b21bf1475..7e9eb78100 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/CredentialsRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/CredentialsRepository.java @@ -1,7 +1,7 @@ package pl.allegro.tech.hermes.domain; public interface CredentialsRepository { - NodePassword readAdminPassword(); + NodePassword readAdminPassword(); - void overwriteAdminPassword(String password); + void overwriteAdminPassword(String password); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/NodePassword.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/NodePassword.java index 1211ef8f59..b9a7a39504 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/NodePassword.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/NodePassword.java @@ -2,61 +2,60 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.apache.commons.codec.digest.DigestUtils; - import java.util.Arrays; +import org.apache.commons.codec.digest.DigestUtils; public class NodePassword { - private static final int DEFAULT_STRING_LENGTH = 12; + private static final int DEFAULT_STRING_LENGTH = 12; - private final byte[] hashedPassword; + private final byte[] hashedPassword; - @JsonCreator - public NodePassword(@JsonProperty("hashedPassword") byte[] hashedPassword) { - this.hashedPassword = Arrays.copyOf(hashedPassword, hashedPassword.length); - } + @JsonCreator + public NodePassword(@JsonProperty("hashedPassword") byte[] hashedPassword) { + this.hashedPassword = Arrays.copyOf(hashedPassword, hashedPassword.length); + } - public NodePassword(String password) { - this.hashedPassword = NodePassword.hashString(password); - } + public NodePassword(String password) { + this.hashedPassword = NodePassword.hashString(password); + } - public byte[] getHashedPassword() { - return Arrays.copyOf(hashedPassword, hashedPassword.length); - } + public byte[] getHashedPassword() { + return Arrays.copyOf(hashedPassword, hashedPassword.length); + } - private static byte[] hashString(String string) { - return DigestUtils.sha256(string); - } + private static byte[] hashString(String string) { + return DigestUtils.sha256(string); + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } - if (o == null) { - return false; - } + if (o == null) { + return false; + } - if (o instanceof String) { - return this.equals(NodePassword.fromString((String) o)); - } + if (o instanceof String) { + return this.equals(NodePassword.fromString((String) o)); + } - if (getClass() != o.getClass()) { - return false; - } + if (getClass() != o.getClass()) { + return false; + } - NodePassword that = (NodePassword) o; + NodePassword that = (NodePassword) o; - return Arrays.equals(hashedPassword, that.hashedPassword); - } + return Arrays.equals(hashedPassword, that.hashedPassword); + } - @Override - public int hashCode() { - return Arrays.hashCode(hashedPassword); - } + @Override + public int hashCode() { + return Arrays.hashCode(hashedPassword); + } - public static NodePassword fromString(String string) { - return new NodePassword(string); - } + public static NodePassword fromString(String string) { + return new NodePassword(string); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilterableMessage.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilterableMessage.java index d4e8f901f2..9c769e10a8 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilterableMessage.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilterableMessage.java @@ -1,19 +1,18 @@ package pl.allegro.tech.hermes.domain.filtering; +import java.util.Map; +import java.util.Optional; import org.apache.avro.Schema; import pl.allegro.tech.hermes.api.ContentType; import pl.allegro.tech.hermes.schema.CompiledSchema; -import java.util.Map; -import java.util.Optional; - public interface FilterableMessage { - ContentType getContentType(); + ContentType getContentType(); - Map getExternalMetadata(); + Map getExternalMetadata(); - byte[] getData(); + byte[] getData(); - Optional> getSchema(); + Optional> getSchema(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilteringException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilteringException.java index 8b6340da05..62f7da8c82 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilteringException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/FilteringException.java @@ -3,21 +3,21 @@ import pl.allegro.tech.hermes.common.exception.InternalProcessingException; public class FilteringException extends InternalProcessingException { - public FilteringException(Throwable throwable) { - super(throwable); - } + public FilteringException(Throwable throwable) { + super(throwable); + } - public FilteringException(String message) { - super(message); - } + public FilteringException(String message) { + super(message); + } - public FilteringException(String message, Throwable throwable) { - super(message, throwable); - } + public FilteringException(String message, Throwable throwable) { + super(message, throwable); + } - public static void check(boolean condition, String message) { - if (!condition) { - throw new FilteringException(message); - } + public static void check(boolean condition, String message) { + if (!condition) { + throw new FilteringException(message); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MatchingStrategy.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MatchingStrategy.java index b3b55ad50c..177b73a320 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MatchingStrategy.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MatchingStrategy.java @@ -3,17 +3,17 @@ import java.util.Optional; public enum MatchingStrategy { - ALL, - ANY; + ALL, + ANY; - public static MatchingStrategy fromString(String value, MatchingStrategy defaultValue) { - try { - return Optional.ofNullable(value) - .map(String::toUpperCase) - .map(MatchingStrategy::valueOf) - .orElse(defaultValue); - } catch (IllegalArgumentException ex) { - return defaultValue; - } + public static MatchingStrategy fromString(String value, MatchingStrategy defaultValue) { + try { + return Optional.ofNullable(value) + .map(String::toUpperCase) + .map(MatchingStrategy::valueOf) + .orElse(defaultValue); + } catch (IllegalArgumentException ex) { + return defaultValue; } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilter.java index 8a03f435b0..5ce32efdcc 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilter.java @@ -3,20 +3,20 @@ import java.util.function.Predicate; public class MessageFilter implements Predicate { - private final String type; - private final Predicate predicate; + private final String type; + private final Predicate predicate; - public MessageFilter(String type, Predicate predicate) { - this.type = type; - this.predicate = predicate; - } + public MessageFilter(String type, Predicate predicate) { + this.type = type; + this.predicate = predicate; + } - @Override - public boolean test(FilterableMessage message) { - return predicate.test(message); - } + @Override + public boolean test(FilterableMessage message) { + return predicate.test(message); + } - public String getType() { - return type; - } + public String getType() { + return type; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilterSource.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilterSource.java index a946227e17..6c67564592 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilterSource.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilterSource.java @@ -1,11 +1,10 @@ package pl.allegro.tech.hermes.domain.filtering; -import pl.allegro.tech.hermes.api.MessageFilterSpecification; - import java.util.List; +import pl.allegro.tech.hermes.api.MessageFilterSpecification; public interface MessageFilterSource { - MessageFilter compile(MessageFilterSpecification specification); + MessageFilter compile(MessageFilterSpecification specification); - List getGlobalFilters(); + List getGlobalFilters(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilters.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilters.java index c8b3f4cfae..00ed213719 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilters.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/MessageFilters.java @@ -1,33 +1,35 @@ package pl.allegro.tech.hermes.domain.filtering; -import pl.allegro.tech.hermes.api.MessageFilterSpecification; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import java.util.List; import java.util.Map; - -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toMap; +import pl.allegro.tech.hermes.api.MessageFilterSpecification; public class MessageFilters implements MessageFilterSource { - private final Map filters; - private final List globalFilters; + private final Map filters; + private final List globalFilters; - public MessageFilters(List globalFilters, - List subscriptionFilterCompilers) { - this.globalFilters = globalFilters; - this.filters = subscriptionFilterCompilers.stream().collect(toMap(SubscriptionMessageFilterCompiler::getType, identity())); - } + public MessageFilters( + List globalFilters, + List subscriptionFilterCompilers) { + this.globalFilters = globalFilters; + this.filters = + subscriptionFilterCompilers.stream() + .collect(toMap(SubscriptionMessageFilterCompiler::getType, identity())); + } - @Override - public MessageFilter compile(MessageFilterSpecification specification) { - if (!filters.containsKey(specification.getType())) { - throw new NoSuchFilterException(specification.getType()); - } - return filters.get(specification.getType()).getMessageFilter(specification); + @Override + public MessageFilter compile(MessageFilterSpecification specification) { + if (!filters.containsKey(specification.getType())) { + throw new NoSuchFilterException(specification.getType()); } + return filters.get(specification.getType()).getMessageFilter(specification); + } - @Override - public List getGlobalFilters() { - return globalFilters; - } + @Override + public List getGlobalFilters() { + return globalFilters; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/NoSuchFilterException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/NoSuchFilterException.java index bcc9f1cdf3..d5557d9680 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/NoSuchFilterException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/NoSuchFilterException.java @@ -1,15 +1,15 @@ package pl.allegro.tech.hermes.domain.filtering; public class NoSuchFilterException extends FilteringException { - public NoSuchFilterException(Throwable throwable) { - super(throwable); - } + public NoSuchFilterException(Throwable throwable) { + super(throwable); + } - public NoSuchFilterException(String filterType) { - super("Filter of type '" + filterType + "' not found."); - } + public NoSuchFilterException(String filterType) { + super("Filter of type '" + filterType + "' not found."); + } - public NoSuchFilterException(String message, Throwable throwable) { - super(message, throwable); - } + public NoSuchFilterException(String message, Throwable throwable) { + super(message, throwable); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/SubscriptionMessageFilterCompiler.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/SubscriptionMessageFilterCompiler.java index c4a83f2d2c..bf337662f8 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/SubscriptionMessageFilterCompiler.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/SubscriptionMessageFilterCompiler.java @@ -1,15 +1,14 @@ package pl.allegro.tech.hermes.domain.filtering; -import pl.allegro.tech.hermes.api.MessageFilterSpecification; - import java.util.function.Predicate; +import pl.allegro.tech.hermes.api.MessageFilterSpecification; public interface SubscriptionMessageFilterCompiler { - String getType(); + String getType(); - Predicate compile(MessageFilterSpecification specification); + Predicate compile(MessageFilterSpecification specification); - default MessageFilter getMessageFilter(MessageFilterSpecification specification) { - return new MessageFilter(getType(), compile(specification)); - } + default MessageFilter getMessageFilter(MessageFilterSpecification specification) { + return new MessageFilter(getType(), compile(specification)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/UnsupportedMatchingStrategyException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/UnsupportedMatchingStrategyException.java index 9e62421ca4..5526c57920 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/UnsupportedMatchingStrategyException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/UnsupportedMatchingStrategyException.java @@ -1,7 +1,12 @@ package pl.allegro.tech.hermes.domain.filtering; public class UnsupportedMatchingStrategyException extends FilteringException { - public UnsupportedMatchingStrategyException(String filterType, MatchingStrategy strategy) { - super("Matching strategy '" + strategy + "' is not supported in filters of type '" + filterType + "'."); - } + public UnsupportedMatchingStrategyException(String filterType, MatchingStrategy strategy) { + super( + "Matching strategy '" + + strategy + + "' is not supported in filters of type '" + + filterType + + "'."); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathPredicate.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathPredicate.java index f49a22d7ba..cfee70ac7c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathPredicate.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathPredicate.java @@ -1,16 +1,13 @@ package pl.allegro.tech.hermes.domain.filtering.avro; -import org.apache.avro.Schema; -import org.apache.avro.generic.GenericArray; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.util.Utf8; -import pl.allegro.tech.hermes.api.ContentType; -import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; -import pl.allegro.tech.hermes.domain.filtering.FilteringException; -import pl.allegro.tech.hermes.domain.filtering.MatchingStrategy; -import pl.allegro.tech.hermes.domain.filtering.UnsupportedMatchingStrategyException; -import pl.allegro.tech.hermes.schema.CompiledSchema; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyListIterator; +import static java.util.Collections.singletonList; +import static org.apache.commons.lang3.StringUtils.strip; +import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; +import static pl.allegro.tech.hermes.domain.filtering.FilteringException.check; +import jakarta.annotation.Nullable; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -22,124 +19,145 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyListIterator; -import static java.util.Collections.singletonList; -import static org.apache.commons.lang.StringUtils.strip; -import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; -import static pl.allegro.tech.hermes.domain.filtering.FilteringException.check; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericArray; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.util.Utf8; +import pl.allegro.tech.hermes.api.ContentType; +import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; +import pl.allegro.tech.hermes.domain.filtering.FilteringException; +import pl.allegro.tech.hermes.domain.filtering.MatchingStrategy; +import pl.allegro.tech.hermes.domain.filtering.UnsupportedMatchingStrategyException; +import pl.allegro.tech.hermes.schema.CompiledSchema; class AvroPathPredicate implements Predicate { - private static final String WILDCARD_IDX = "*"; - private static final String GROUP_SELECTOR = "selector"; - private static final String GROUP_IDX = "index"; - private static final String ARRAY_PATTERN_SELECTOR_PART = "(?<" + GROUP_SELECTOR + ">..*)"; - private static final String ARRAY_PATTERN_IDX_PART = "\\[(?<" + GROUP_IDX + ">\\" + WILDCARD_IDX + "|\\d+)]"; - private static final Pattern ARRAY_PATTERN = Pattern.compile(ARRAY_PATTERN_SELECTOR_PART + ARRAY_PATTERN_IDX_PART); - private static final String NULL_AS_STRING = "null"; - private final List path; - private final Pattern pattern; - private final MatchingStrategy matchingStrategy; - - AvroPathPredicate(String path, Pattern pattern, MatchingStrategy matchingStrategy) { - this.path = Arrays.asList(strip(path, ".").split("\\.")); - this.pattern = pattern; - this.matchingStrategy = matchingStrategy; + private static final String WILDCARD_IDX = "*"; + private static final String GROUP_SELECTOR = "selector"; + private static final String GROUP_IDX = "index"; + private static final String ARRAY_PATTERN_SELECTOR_PART = "(?<" + GROUP_SELECTOR + ">..*)"; + private static final String ARRAY_PATTERN_IDX_PART = + "\\[(?<" + GROUP_IDX + ">\\" + WILDCARD_IDX + "|\\d+)]"; + private static final Pattern ARRAY_PATTERN = + Pattern.compile(ARRAY_PATTERN_SELECTOR_PART + ARRAY_PATTERN_IDX_PART); + private static final String NULL_AS_STRING = "null"; + private final List path; + private final Pattern pattern; + private final MatchingStrategy matchingStrategy; + + AvroPathPredicate(String path, Pattern pattern, MatchingStrategy matchingStrategy) { + this.path = Arrays.asList(strip(path, ".").split("\\.")); + this.pattern = pattern; + this.matchingStrategy = matchingStrategy; + } + + @Override + public boolean test(final FilterableMessage message) { + check( + message.getContentType() == ContentType.AVRO, + "This filter supports only AVRO contentType."); + try { + List result = select(message); + Stream resultStream = result.stream().map(Object::toString); + + return !result.isEmpty() && matchResultsStream(resultStream); + } catch (Exception exception) { + throw new FilteringException(exception); } - - @Override - public boolean test(final FilterableMessage message) { - check(message.getContentType() == ContentType.AVRO, "This filter supports only AVRO contentType."); - try { - List result = select(message); - Stream resultStream = result.stream().map(Object::toString); - - return !result.isEmpty() && matchResultsStream(resultStream); - } catch (Exception exception) { - throw new FilteringException(exception); + } + + private List select(final FilterableMessage message) throws IOException { + CompiledSchema compiledSchema = message.getSchema().get(); + return select(bytesToRecord(message.getData(), compiledSchema.getSchema())); + } + + private List select(GenericRecord record) { + ListIterator iter = path.listIterator(); + return select(record, iter); + } + + private List select(Object record, ListIterator iter) { + Object current = record; + while (iter.hasNext() && isSupportedType(current)) { + if (current instanceof GenericRecord) { + GenericRecord currentRecord = (GenericRecord) current; + String selector = iter.next(); + Matcher arrayMatcher = ARRAY_PATTERN.matcher(selector); + + if (arrayMatcher.matches()) { + selector = arrayMatcher.group(GROUP_SELECTOR); + + current = recordFieldValueOrNull(selector, currentRecord); + if (!(current instanceof GenericArray)) { + return emptyList(); + } + + GenericArray currentArray = (GenericArray) current; + String idx = arrayMatcher.group(GROUP_IDX); + + if (idx.equals(WILDCARD_IDX)) { + return selectMultipleArrayItems(iter, currentArray); + } else { + current = selectSingleArrayItem(Integer.valueOf(idx), currentArray); + } + + } else { + current = recordFieldValueOrNull(selector, currentRecord); } + } else if (current instanceof HashMap) { + Map currentRecord = (HashMap) current; + Utf8 selector = new Utf8(iter.next()); + current = currentRecord.get(selector); + } } - private List select(final FilterableMessage message) throws IOException { - CompiledSchema compiledSchema = message.getSchema().get(); - return select(bytesToRecord(message.getData(), compiledSchema.getSchema())); + return iter.hasNext() ? emptyList() : singletonList(current == null ? NULL_AS_STRING : current); + } + + private boolean isSupportedType(Object record) { + return record instanceof GenericRecord || record instanceof HashMap; + } + + private List selectMultipleArrayItems( + ListIterator iter, GenericArray currentArray) { + return currentArray.stream() + .map( + item -> + select( + item, + iter.hasNext() ? path.listIterator(iter.nextIndex()) : emptyListIterator())) + .flatMap(List::stream) + .collect(Collectors.toList()); + } + + private Object selectSingleArrayItem(int idx, GenericArray currentArray) { + if (currentArray.size() <= idx) { + return null; } - private List select(GenericRecord record) { - ListIterator iter = path.listIterator(); - return select(record, iter); + return currentArray.get(idx); + } + + private boolean matchResultsStream(Stream results) { + switch (matchingStrategy) { + case ALL: + return results.allMatch(this::matches); + case ANY: + return results.anyMatch(this::matches); + default: + throw new UnsupportedMatchingStrategyException("avropath", matchingStrategy); } + } - private List select(Object record, ListIterator iter) { - Object current = record; - while (iter.hasNext() && isSupportedType(current)) { - if (current instanceof GenericRecord) { - GenericRecord currentRecord = (GenericRecord) current; - String selector = iter.next(); - Matcher arrayMatcher = ARRAY_PATTERN.matcher(selector); - - if (arrayMatcher.matches()) { - selector = arrayMatcher.group(GROUP_SELECTOR); - - current = currentRecord.get(selector); - if (!(current instanceof GenericArray)) { - return emptyList(); - } - - GenericArray currentArray = (GenericArray) current; - String idx = arrayMatcher.group(GROUP_IDX); - - if (idx.equals(WILDCARD_IDX)) { - return selectMultipleArrayItems(iter, currentArray); - } else { - current = selectSingleArrayItem(Integer.valueOf(idx), currentArray); - } - - } else { - current = currentRecord.get(selector); - } - } else if (current instanceof HashMap) { - Map currentRecord = (HashMap) current; - Utf8 selector = new Utf8(iter.next()); - current = currentRecord.get(selector); - } - } - - return iter.hasNext() ? emptyList() : singletonList(current == null ? NULL_AS_STRING : current); + @Nullable + private Object recordFieldValueOrNull(String selector, GenericRecord record) { + Schema.Field field = record.getSchema().getField(selector); + if (field == null) { + return null; } + return record.get(field.pos()); + } - private boolean isSupportedType(Object record) { - return record instanceof GenericRecord || record instanceof HashMap; - } - - private List selectMultipleArrayItems(ListIterator iter, GenericArray currentArray) { - return currentArray.stream() - .map(item -> select(item, iter.hasNext() ? path.listIterator(iter.nextIndex()) : emptyListIterator())) - .flatMap(List::stream) - .collect(Collectors.toList()); - } - - private Object selectSingleArrayItem(int idx, GenericArray currentArray) { - if (currentArray.size() <= idx) { - return null; - } - - return currentArray.get(idx); - } - - private boolean matchResultsStream(Stream results) { - switch (matchingStrategy) { - case ALL: - return results.allMatch(this::matches); - case ANY: - return results.anyMatch(this::matches); - default: - throw new UnsupportedMatchingStrategyException("avropath", matchingStrategy); - } - } - - private boolean matches(String value) { - return pattern.matcher(value).matches(); - } + private boolean matches(String value) { + return pattern.matcher(value).matches(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathSubscriptionMessageFilterCompiler.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathSubscriptionMessageFilterCompiler.java index a3072bc66d..1579be965c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathSubscriptionMessageFilterCompiler.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/avro/AvroPathSubscriptionMessageFilterCompiler.java @@ -1,26 +1,25 @@ package pl.allegro.tech.hermes.domain.filtering.avro; +import java.util.function.Predicate; +import java.util.regex.Pattern; import pl.allegro.tech.hermes.api.MessageFilterSpecification; import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; import pl.allegro.tech.hermes.domain.filtering.MatchingStrategy; import pl.allegro.tech.hermes.domain.filtering.SubscriptionMessageFilterCompiler; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -public class AvroPathSubscriptionMessageFilterCompiler implements SubscriptionMessageFilterCompiler { +public class AvroPathSubscriptionMessageFilterCompiler + implements SubscriptionMessageFilterCompiler { - @Override - public String getType() { - return "avropath"; - } + @Override + public String getType() { + return "avropath"; + } - @Override - public Predicate compile(MessageFilterSpecification specification) { - return new AvroPathPredicate( - specification.getPath(), - Pattern.compile(specification.getMatcher()), - MatchingStrategy.fromString(specification.getMatchingStrategy(), MatchingStrategy.ALL) - ); - } + @Override + public Predicate compile(MessageFilterSpecification specification) { + return new AvroPathPredicate( + specification.getPath(), + Pattern.compile(specification.getMatcher()), + MatchingStrategy.fromString(specification.getMatchingStrategy(), MatchingStrategy.ALL)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChain.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChain.java index 815a12b9b4..15ff95c5e6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChain.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChain.java @@ -1,28 +1,27 @@ package pl.allegro.tech.hermes.domain.filtering.chain; -import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; -import pl.allegro.tech.hermes.domain.filtering.MessageFilter; - import java.util.ArrayList; import java.util.List; +import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; +import pl.allegro.tech.hermes.domain.filtering.MessageFilter; public final class FilterChain { - private final List messageFilters; + private final List messageFilters; - FilterChain(final List messageFilters) { - this.messageFilters = new ArrayList<>(messageFilters); - } + FilterChain(final List messageFilters) { + this.messageFilters = new ArrayList<>(messageFilters); + } - public FilterResult apply(final FilterableMessage message) { - for (MessageFilter filter : messageFilters) { - try { - if (!filter.test(message)) { - return FilterResult.failed(filter.getType(), "logical"); - } - } catch (Exception ex) { - return FilterResult.failed(filter.getType(), ex); - } + public FilterResult apply(final FilterableMessage message) { + for (MessageFilter filter : messageFilters) { + try { + if (!filter.test(message)) { + return FilterResult.failed(filter.getType(), "logical"); } - return FilterResult.PASS; + } catch (Exception ex) { + return FilterResult.failed(filter.getType(), ex); + } } + return FilterResult.PASS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChainFactory.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChainFactory.java index b418abd259..99f9f3fd43 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChainFactory.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterChainFactory.java @@ -1,26 +1,24 @@ package pl.allegro.tech.hermes.domain.filtering.chain; -import pl.allegro.tech.hermes.api.MessageFilterSpecification; -import pl.allegro.tech.hermes.domain.filtering.MessageFilter; -import pl.allegro.tech.hermes.domain.filtering.MessageFilterSource; +import static java.util.stream.Stream.concat; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static java.util.stream.Stream.concat; +import pl.allegro.tech.hermes.api.MessageFilterSpecification; +import pl.allegro.tech.hermes.domain.filtering.MessageFilter; +import pl.allegro.tech.hermes.domain.filtering.MessageFilterSource; public class FilterChainFactory { - private final MessageFilterSource availableFilters; + private final MessageFilterSource availableFilters; - public FilterChainFactory(MessageFilterSource filters) { - this.availableFilters = filters; - } + public FilterChainFactory(MessageFilterSource filters) { + this.availableFilters = filters; + } - public FilterChain create(final List filters) { - Stream globalFilters = availableFilters.getGlobalFilters().stream(); - Stream subscriptionFilters = filters.stream() - .map(availableFilters::compile); - return new FilterChain(concat(globalFilters, subscriptionFilters).collect(Collectors.toList())); - } + public FilterChain create(final List filters) { + Stream globalFilters = availableFilters.getGlobalFilters().stream(); + Stream subscriptionFilters = filters.stream().map(availableFilters::compile); + return new FilterChain(concat(globalFilters, subscriptionFilters).collect(Collectors.toList())); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterResult.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterResult.java index 5a7fd4ceb2..6342456dd7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterResult.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/chain/FilterResult.java @@ -1,64 +1,68 @@ package pl.allegro.tech.hermes.domain.filtering.chain; -import com.google.common.base.Joiner; - -import java.util.Optional; - import static java.lang.String.format; import static java.util.Optional.empty; +import com.google.common.base.Joiner; +import java.util.Optional; + public final class FilterResult { - private final boolean filtered; - private final Optional filterType; - private final Optional message; - private final Optional cause; + private final boolean filtered; + private final Optional filterType; + private final Optional message; + private final Optional cause; - public static final FilterResult PASS = new FilterResult(false, empty(), empty(), empty()); + public static final FilterResult PASS = new FilterResult(false, empty(), empty(), empty()); - public static FilterResult failed(String filterType, String message) { - return new FilterResult(true, Optional.of(filterType), Optional.ofNullable(message), empty()); - } + public static FilterResult failed(String filterType, String message) { + return new FilterResult(true, Optional.of(filterType), Optional.ofNullable(message), empty()); + } - public static FilterResult failed(String filterType, Exception exception) { - return new FilterResult(true, Optional.of(filterType), empty(), Optional.ofNullable(exception)); - } + public static FilterResult failed(String filterType, Exception exception) { + return new FilterResult(true, Optional.of(filterType), empty(), Optional.ofNullable(exception)); + } - private FilterResult(boolean filtered, - Optional filterType, - Optional message, - Optional cause) { - this.filtered = filtered; - this.filterType = filterType; - this.message = message; - this.cause = cause; - } + private FilterResult( + boolean filtered, + Optional filterType, + Optional message, + Optional cause) { + this.filtered = filtered; + this.filterType = filterType; + this.message = message; + this.cause = cause; + } - public boolean isFiltered() { - return filtered; - } + public boolean isFiltered() { + return filtered; + } - public Optional getFilterType() { - return filterType; - } + public Optional getFilterType() { + return filterType; + } - public Optional getMessage() { - return message; - } + public Optional getMessage() { + return message; + } - public Optional getCause() { - return cause; - } + public Optional getCause() { + return cause; + } - @Override - public String toString() { - return "[" + Joiner.on(",").skipNulls() - .join(format("%s={%s}", "filtered", filtered), - toString("filterType", filterType), - toString("message", message), - toString("cause", cause)) + "]"; - } + @Override + public String toString() { + return "[" + + Joiner.on(",") + .skipNulls() + .join( + format("%s={%s}", "filtered", filtered), + toString("filterType", filterType), + toString("message", message), + toString("cause", cause)) + + "]"; + } - private String toString(String fieldName, Optional value) { - return value.map(v -> format("%s={%s}", fieldName, v)).orElse(null); - } + private String toString(String fieldName, Optional value) { + return value.map(v -> format("%s={%s}", fieldName, v)).orElse(null); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderPredicate.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderPredicate.java index 33bd8819e2..e8e9102adc 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderPredicate.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderPredicate.java @@ -1,27 +1,25 @@ package pl.allegro.tech.hermes.domain.filtering.header; -import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; - import java.util.function.Predicate; import java.util.regex.Pattern; +import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; class HeaderPredicate implements Predicate { - private final String name; - private final Pattern valuePattern; + private final String name; + private final Pattern valuePattern; - HeaderPredicate(String name, Pattern valuePattern) { - this.name = name; - this.valuePattern = valuePattern; - } + HeaderPredicate(String name, Pattern valuePattern) { + this.name = name; + this.valuePattern = valuePattern; + } - @Override - public boolean test(FilterableMessage message) { - return message.getExternalMetadata() - .entrySet().stream() - .filter(h -> h.getKey().equals(name)) - .findFirst() - .filter(h -> valuePattern.matcher(h.getValue()).matches()) - .isPresent(); - } + @Override + public boolean test(FilterableMessage message) { + return message.getExternalMetadata().entrySet().stream() + .filter(h -> h.getKey().equals(name)) + .findFirst() + .filter(h -> valuePattern.matcher(h.getValue()).matches()) + .isPresent(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderSubscriptionMessageFilterCompiler.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderSubscriptionMessageFilterCompiler.java index 4a1249a097..5b8d82ae1c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderSubscriptionMessageFilterCompiler.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/header/HeaderSubscriptionMessageFilterCompiler.java @@ -1,22 +1,21 @@ package pl.allegro.tech.hermes.domain.filtering.header; +import java.util.function.Predicate; +import java.util.regex.Pattern; import pl.allegro.tech.hermes.api.MessageFilterSpecification; import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; import pl.allegro.tech.hermes.domain.filtering.SubscriptionMessageFilterCompiler; -import java.util.function.Predicate; -import java.util.regex.Pattern; - public class HeaderSubscriptionMessageFilterCompiler implements SubscriptionMessageFilterCompiler { - @Override - public String getType() { - return "header"; - } - - @Override - public Predicate compile(MessageFilterSpecification specification) { - return new HeaderPredicate(specification.getHeader(), Pattern.compile(specification.getMatcher())); + @Override + public String getType() { + return "header"; + } - } + @Override + public Predicate compile(MessageFilterSpecification specification) { + return new HeaderPredicate( + specification.getHeader(), Pattern.compile(specification.getMatcher())); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathPredicate.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathPredicate.java index 2e90cc13c0..11793392d3 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathPredicate.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathPredicate.java @@ -1,59 +1,65 @@ package pl.allegro.tech.hermes.domain.filtering.json; +import static pl.allegro.tech.hermes.domain.filtering.FilteringException.check; + import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; import pl.allegro.tech.hermes.api.ContentType; import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; import pl.allegro.tech.hermes.domain.filtering.FilteringException; import pl.allegro.tech.hermes.domain.filtering.MatchingStrategy; import pl.allegro.tech.hermes.domain.filtering.UnsupportedMatchingStrategyException; -import java.io.ByteArrayInputStream; -import java.util.List; -import java.util.function.Predicate; -import java.util.regex.Pattern; -import java.util.stream.Stream; +class JsonPathPredicate implements Predicate { + private final Configuration configuration; + private final String path; + private final Pattern matcher; + private final MatchingStrategy matchingStrategy; -import static pl.allegro.tech.hermes.domain.filtering.FilteringException.check; + JsonPathPredicate( + String path, + Pattern matcher, + Configuration configuration, + MatchingStrategy matchingStrategy) { + this.path = path; + this.matcher = matcher; + this.configuration = configuration; + this.matchingStrategy = matchingStrategy; + } -class JsonPathPredicate implements Predicate { - private final Configuration configuration; - private final String path; - private final Pattern matcher; - private final MatchingStrategy matchingStrategy; - - JsonPathPredicate(String path, Pattern matcher, Configuration configuration, MatchingStrategy matchingStrategy) { - this.path = path; - this.matcher = matcher; - this.configuration = configuration; - this.matchingStrategy = matchingStrategy; - } + @Override + public boolean test(FilterableMessage message) { + check( + message.getContentType() == ContentType.JSON, + "This filter supports only JSON contentType."); + try { + List result = + JsonPath.parse(new ByteArrayInputStream(message.getData()), configuration).read(path); + Stream resultStream = result.stream().map(Object::toString); - @Override - public boolean test(FilterableMessage message) { - check(message.getContentType() == ContentType.JSON, "This filter supports only JSON contentType."); - try { - List result = JsonPath.parse(new ByteArrayInputStream(message.getData()), configuration).read(path); - Stream resultStream = result.stream().map(Object::toString); - - return !result.isEmpty() && matchResultsStream(resultStream); - } catch (Exception ex) { - throw new FilteringException(ex); - } + return !result.isEmpty() && matchResultsStream(resultStream); + } catch (Exception ex) { + throw new FilteringException(ex); } + } - private boolean matchResultsStream(Stream results) { - switch (matchingStrategy) { - case ALL: - return results.allMatch(this::matches); - case ANY: - return results.anyMatch(this::matches); - default: - throw new UnsupportedMatchingStrategyException("avropath", matchingStrategy); - } + private boolean matchResultsStream(Stream results) { + switch (matchingStrategy) { + case ALL: + return results.allMatch(this::matches); + case ANY: + return results.anyMatch(this::matches); + default: + throw new UnsupportedMatchingStrategyException("avropath", matchingStrategy); } + } - private boolean matches(String value) { - return matcher.matcher(value).matches(); - } + private boolean matches(String value) { + return matcher.matcher(value).matches(); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathSubscriptionMessageFilterCompiler.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathSubscriptionMessageFilterCompiler.java index c5fec7ca53..347b91621b 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathSubscriptionMessageFilterCompiler.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/filtering/json/JsonPathSubscriptionMessageFilterCompiler.java @@ -1,32 +1,32 @@ package pl.allegro.tech.hermes.domain.filtering.json; +import static com.jayway.jsonpath.Configuration.defaultConfiguration; + import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.Option; +import java.util.function.Predicate; +import java.util.regex.Pattern; import pl.allegro.tech.hermes.api.MessageFilterSpecification; import pl.allegro.tech.hermes.domain.filtering.FilterableMessage; import pl.allegro.tech.hermes.domain.filtering.MatchingStrategy; import pl.allegro.tech.hermes.domain.filtering.SubscriptionMessageFilterCompiler; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -import static com.jayway.jsonpath.Configuration.defaultConfiguration; - -public class JsonPathSubscriptionMessageFilterCompiler implements SubscriptionMessageFilterCompiler { - private final Configuration configuration = defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST, Option.SUPPRESS_EXCEPTIONS); +public class JsonPathSubscriptionMessageFilterCompiler + implements SubscriptionMessageFilterCompiler { + private final Configuration configuration = + defaultConfiguration().addOptions(Option.ALWAYS_RETURN_LIST, Option.SUPPRESS_EXCEPTIONS); - @Override - public String getType() { - return "jsonpath"; - } + @Override + public String getType() { + return "jsonpath"; + } - @Override - public Predicate compile(MessageFilterSpecification specification) { - return new JsonPathPredicate( - specification.getPath(), - Pattern.compile(specification.getMatcher()), - configuration, - MatchingStrategy.fromString(specification.getMatchingStrategy(), MatchingStrategy.ALL) - ); - } + @Override + public Predicate compile(MessageFilterSpecification specification) { + return new JsonPathPredicate( + specification.getPath(), + Pattern.compile(specification.getMatcher()), + configuration, + MatchingStrategy.fromString(specification.getMatchingStrategy(), MatchingStrategy.ALL)); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupAlreadyExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupAlreadyExistsException.java index 4945b8be1a..a24359d4a7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupAlreadyExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupAlreadyExistsException.java @@ -5,17 +5,16 @@ public class GroupAlreadyExistsException extends HermesException { - public GroupAlreadyExistsException(String groupName) { - super(String.format("Group %s already exists", groupName)); - } + public GroupAlreadyExistsException(String groupName) { + super(String.format("Group %s already exists", groupName)); + } - public GroupAlreadyExistsException(String groupName, Throwable cause) { - super(String.format("Group %s already exists", groupName), cause); - } - - @Override - public ErrorCode getCode() { - return ErrorCode.GROUP_ALREADY_EXISTS; - } + public GroupAlreadyExistsException(String groupName, Throwable cause) { + super(String.format("Group %s already exists", groupName), cause); + } + @Override + public ErrorCode getCode() { + return ErrorCode.GROUP_ALREADY_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotEmptyException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotEmptyException.java index 929f847f6d..74ad8f8530 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotEmptyException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotEmptyException.java @@ -5,12 +5,12 @@ public class GroupNotEmptyException extends HermesException { - public GroupNotEmptyException(String groupName) { - super(String.format("Group %s is not empty", groupName)); - } + public GroupNotEmptyException(String groupName) { + super(String.format("Group %s is not empty", groupName)); + } - @Override - public ErrorCode getCode() { - return ErrorCode.GROUP_NOT_EMPTY; - } + @Override + public ErrorCode getCode() { + return ErrorCode.GROUP_NOT_EMPTY; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotExistsException.java index 35a5e03da0..0c6d589586 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupNotExistsException.java @@ -5,16 +5,16 @@ public class GroupNotExistsException extends HermesException { - public GroupNotExistsException(String groupName, Exception exception) { - super(String.format("Group %s does not exist", groupName), exception); - } + public GroupNotExistsException(String groupName, Exception exception) { + super(String.format("Group %s does not exist", groupName), exception); + } - public GroupNotExistsException(String groupName) { - this(groupName, null); - } + public GroupNotExistsException(String groupName) { + this(groupName, null); + } - @Override - public ErrorCode getCode() { - return ErrorCode.GROUP_NOT_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.GROUP_NOT_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupRepository.java index 9dc3c65fb6..8def903fac 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/group/GroupRepository.java @@ -1,25 +1,23 @@ package pl.allegro.tech.hermes.domain.group; -import pl.allegro.tech.hermes.api.Group; - import java.util.List; +import pl.allegro.tech.hermes.api.Group; public interface GroupRepository { - boolean groupExists(String groupName); - - void ensureGroupExists(String groupName); + boolean groupExists(String groupName); - void createGroup(Group group); + void ensureGroupExists(String groupName); - void updateGroup(Group group); + void createGroup(Group group); - void removeGroup(String groupName); + void updateGroup(Group group); - List listGroupNames(); + void removeGroup(String groupName); - List listGroups(); + List listGroupNames(); - Group getGroupDetails(String groupName); + List listGroups(); + Group getGroupDetails(String groupName); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/AdminCallback.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/AdminCallback.java index 3fff93f658..ae9057f5d5 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/AdminCallback.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/AdminCallback.java @@ -2,6 +2,5 @@ public interface AdminCallback { - void onAdminOperationCreated(String type, String content); - + void onAdminOperationCreated(String type, String content); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/InternalNotificationsBus.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/InternalNotificationsBus.java index 1aae720cd8..a6bf890919 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/InternalNotificationsBus.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/InternalNotificationsBus.java @@ -1,13 +1,11 @@ package pl.allegro.tech.hermes.domain.notifications; -/** - * All callbacks must be nonblocking. - */ +/** All callbacks must be nonblocking. */ public interface InternalNotificationsBus { - void registerSubscriptionCallback(SubscriptionCallback callback); + void registerSubscriptionCallback(SubscriptionCallback callback); - void registerTopicCallback(TopicCallback callback); + void registerTopicCallback(TopicCallback callback); - void registerAdminCallback(AdminCallback callback); + void registerAdminCallback(AdminCallback callback); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/SubscriptionCallback.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/SubscriptionCallback.java index f6098e4851..5e5eeeec6f 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/SubscriptionCallback.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/SubscriptionCallback.java @@ -4,13 +4,9 @@ public interface SubscriptionCallback { - default void onSubscriptionCreated(Subscription subscription) { - } + default void onSubscriptionCreated(Subscription subscription) {} - default void onSubscriptionRemoved(Subscription subscription) { - } - - default void onSubscriptionChanged(Subscription subscription) { - } + default void onSubscriptionRemoved(Subscription subscription) {} + default void onSubscriptionChanged(Subscription subscription) {} } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/TopicCallback.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/TopicCallback.java index ce8f7e140d..a8a74df733 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/TopicCallback.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/notifications/TopicCallback.java @@ -4,12 +4,9 @@ public interface TopicCallback { - default void onTopicCreated(Topic topic) { - } + default void onTopicCreated(Topic topic) {} - default void onTopicRemoved(Topic topic) { - } + default void onTopicRemoved(Topic topic) {} - default void onTopicChanged(Topic topic) { - } + default void onTopicChanged(Topic topic) {} } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderAlreadyExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderAlreadyExistsException.java index 3657510235..ef11b4e71e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderAlreadyExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderAlreadyExistsException.java @@ -6,12 +6,12 @@ public class OAuthProviderAlreadyExistsException extends HermesException { - public OAuthProviderAlreadyExistsException(OAuthProvider oAuthProvider, Throwable cause) { - super(String.format("OAuth Provider %s already exists", oAuthProvider.getName()), cause); - } + public OAuthProviderAlreadyExistsException(OAuthProvider oAuthProvider, Throwable cause) { + super(String.format("OAuth Provider %s already exists", oAuthProvider.getName()), cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.OAUTH_PROVIDER_ALREADY_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.OAUTH_PROVIDER_ALREADY_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderNotExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderNotExistsException.java index 2b1359cb90..b209534f54 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderNotExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderNotExistsException.java @@ -5,12 +5,12 @@ public class OAuthProviderNotExistsException extends HermesException { - public OAuthProviderNotExistsException(String oAuthProviderName) { - super(String.format("OAuth provider %s does not exist", oAuthProviderName)); - } + public OAuthProviderNotExistsException(String oAuthProviderName) { + super(String.format("OAuth provider %s does not exist", oAuthProviderName)); + } - @Override - public ErrorCode getCode() { - return ErrorCode.OAUTH_PROVIDER_NOT_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.OAUTH_PROVIDER_NOT_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderRepository.java index 3d443dac9d..2fea65ef59 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/oauth/OAuthProviderRepository.java @@ -1,24 +1,23 @@ package pl.allegro.tech.hermes.domain.oauth; -import pl.allegro.tech.hermes.api.OAuthProvider; - import java.util.List; +import pl.allegro.tech.hermes.api.OAuthProvider; public interface OAuthProviderRepository { - boolean oAuthProviderExists(String oAuthProviderName); + boolean oAuthProviderExists(String oAuthProviderName); - void ensureOAuthProviderExists(String oAuthProviderName); + void ensureOAuthProviderExists(String oAuthProviderName); - List listOAuthProviderNames(); + List listOAuthProviderNames(); - List listOAuthProviders(); + List listOAuthProviders(); - OAuthProvider getOAuthProviderDetails(String oAuthProviderName); + OAuthProvider getOAuthProviderDetails(String oAuthProviderName); - void createOAuthProvider(OAuthProvider oAuthProvider); + void createOAuthProvider(OAuthProvider oAuthProvider); - void updateOAuthProvider(OAuthProvider oAuthprovider); + void updateOAuthProvider(OAuthProvider oAuthprovider); - void removeOAuthProvider(String oAuthProviderName); + void removeOAuthProvider(String oAuthProviderName); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/readiness/DatacenterReadinessList.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/readiness/DatacenterReadinessList.java new file mode 100644 index 0000000000..72736874b7 --- /dev/null +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/readiness/DatacenterReadinessList.java @@ -0,0 +1,14 @@ +package pl.allegro.tech.hermes.domain.readiness; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import pl.allegro.tech.hermes.api.DatacenterReadiness; + +public record DatacenterReadinessList(List datacenters) { + @JsonCreator + public DatacenterReadinessList( + @JsonProperty("datacenters") List datacenters) { + this.datacenters = datacenters; + } +} diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionAlreadyExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionAlreadyExistsException.java index 17a23bfd20..7a1da173a8 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionAlreadyExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionAlreadyExistsException.java @@ -6,21 +6,22 @@ public class SubscriptionAlreadyExistsException extends HermesException { - public SubscriptionAlreadyExistsException(Subscription subscription, Throwable cause) { - super(message(subscription), cause); - } + public SubscriptionAlreadyExistsException(Subscription subscription, Throwable cause) { + super(message(subscription), cause); + } - public SubscriptionAlreadyExistsException(Subscription subscription) { - super(message(subscription)); - } + public SubscriptionAlreadyExistsException(Subscription subscription) { + super(message(subscription)); + } - @Override - public ErrorCode getCode() { - return ErrorCode.SUBSCRIPTION_ALREADY_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.SUBSCRIPTION_ALREADY_EXISTS; + } - private static String message(Subscription subscription) { - return String.format("Subscription %s for topic %s already exists.", - subscription.getName(), subscription.getQualifiedTopicName()); - } + private static String message(Subscription subscription) { + return String.format( + "Subscription %s for topic %s already exists.", + subscription.getName(), subscription.getQualifiedTopicName()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionNotExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionNotExistsException.java index 6d1b8d5827..034c8ac5b7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionNotExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionNotExistsException.java @@ -6,12 +6,14 @@ public class SubscriptionNotExistsException extends HermesException { - public SubscriptionNotExistsException(TopicName topic, String subscription) { - super(String.format("Subscription %s for topic %s does not exist.", subscription, topic.qualifiedName())); - } + public SubscriptionNotExistsException(TopicName topic, String subscription) { + super( + String.format( + "Subscription %s for topic %s does not exist.", subscription, topic.qualifiedName())); + } - @Override - public ErrorCode getCode() { - return ErrorCode.SUBSCRIPTION_NOT_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.SUBSCRIPTION_NOT_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionRepository.java index 7aabc431a0..3c6c690d6e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/subscription/SubscriptionRepository.java @@ -1,35 +1,35 @@ package pl.allegro.tech.hermes.domain.subscription; +import java.util.Collection; +import java.util.List; import pl.allegro.tech.hermes.api.Subscription; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.api.TopicName; -import java.util.Collection; -import java.util.List; - public interface SubscriptionRepository { - boolean subscriptionExists(TopicName topicName, String subscriptionName); + boolean subscriptionExists(TopicName topicName, String subscriptionName); - void ensureSubscriptionExists(TopicName topicName, String subscriptionName); + void ensureSubscriptionExists(TopicName topicName, String subscriptionName); - void createSubscription(Subscription subscription); + void createSubscription(Subscription subscription); - void removeSubscription(TopicName topicName, String subscriptionName); + void removeSubscription(TopicName topicName, String subscriptionName); - void updateSubscription(Subscription modifiedSubscription); + void updateSubscription(Subscription modifiedSubscription); - void updateSubscriptionState(TopicName topicName, String subscriptionName, Subscription.State state); + void updateSubscriptionState( + TopicName topicName, String subscriptionName, Subscription.State state); - Subscription getSubscriptionDetails(TopicName topicName, String subscriptionName); + Subscription getSubscriptionDetails(TopicName topicName, String subscriptionName); - Subscription getSubscriptionDetails(SubscriptionName subscriptionNames); + Subscription getSubscriptionDetails(SubscriptionName subscriptionNames); - List getSubscriptionDetails(Collection subscriptionNames); + List getSubscriptionDetails(Collection subscriptionNames); - List listSubscriptionNames(TopicName topicName); + List listSubscriptionNames(TopicName topicName); - List listSubscriptions(TopicName topicName); + List listSubscriptions(TopicName topicName); - List listAllSubscriptions(); + List listAllSubscriptions(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicAlreadyExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicAlreadyExistsException.java index 0bb0530150..6aca9b0222 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicAlreadyExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicAlreadyExistsException.java @@ -6,16 +6,16 @@ public class TopicAlreadyExistsException extends HermesException { - public TopicAlreadyExistsException(TopicName topicName, Throwable cause) { - super(String.format("Topic %s already exists", topicName.qualifiedName()), cause); - } + public TopicAlreadyExistsException(TopicName topicName, Throwable cause) { + super(String.format("Topic %s already exists", topicName.qualifiedName()), cause); + } - public TopicAlreadyExistsException(TopicName topicName) { - super(String.format("Topic %s already exists", topicName.qualifiedName())); - } + public TopicAlreadyExistsException(TopicName topicName) { + super(String.format("Topic %s already exists", topicName.qualifiedName())); + } - @Override - public ErrorCode getCode() { - return ErrorCode.TOPIC_ALREADY_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.TOPIC_ALREADY_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotEmptyException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotEmptyException.java index 4c00fd8888..23e26e7334 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotEmptyException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotEmptyException.java @@ -6,12 +6,12 @@ public class TopicNotEmptyException extends HermesException { - public TopicNotEmptyException(TopicName topicName) { - super(String.format("Topic %s is not empty", topicName.qualifiedName())); - } + public TopicNotEmptyException(TopicName topicName) { + super(String.format("Topic %s is not empty", topicName.qualifiedName())); + } - @Override - public ErrorCode getCode() { - return ErrorCode.TOPIC_NOT_EMPTY; - } + @Override + public ErrorCode getCode() { + return ErrorCode.TOPIC_NOT_EMPTY; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotExistsException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotExistsException.java index dc36d258aa..da889f6baf 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotExistsException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicNotExistsException.java @@ -6,16 +6,16 @@ public class TopicNotExistsException extends HermesException { - public TopicNotExistsException(TopicName topicName, Exception exception) { - super(String.format("Topic %s does not exist", topicName.qualifiedName()), exception); - } + public TopicNotExistsException(TopicName topicName, Exception exception) { + super(String.format("Topic %s does not exist", topicName.qualifiedName()), exception); + } - public TopicNotExistsException(TopicName topicName) { - this(topicName, null); - } + public TopicNotExistsException(TopicName topicName) { + this(topicName, null); + } - @Override - public ErrorCode getCode() { - return ErrorCode.TOPIC_NOT_EXISTS; - } + @Override + public ErrorCode getCode() { + return ErrorCode.TOPIC_NOT_EXISTS; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicRepository.java index 9c43d87fe9..889e3e6754 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/TopicRepository.java @@ -1,33 +1,31 @@ package pl.allegro.tech.hermes.domain.topic; -import pl.allegro.tech.hermes.api.Topic; -import pl.allegro.tech.hermes.api.TopicName; - import java.util.Collection; import java.util.List; +import pl.allegro.tech.hermes.api.Topic; +import pl.allegro.tech.hermes.api.TopicName; public interface TopicRepository { - boolean topicExists(TopicName topicName); - - void ensureTopicExists(TopicName topicName); + boolean topicExists(TopicName topicName); - List listTopicNames(String groupName); + void ensureTopicExists(TopicName topicName); - List listTopics(String groupName); + List listTopicNames(String groupName); - void createTopic(Topic topic); + List listTopics(String groupName); - void removeTopic(TopicName topicName); + void createTopic(Topic topic); - void updateTopic(Topic topic); + void removeTopic(TopicName topicName); - void touchTopic(TopicName topicName); + void updateTopic(Topic topic); - Topic getTopicDetails(TopicName topicName); + void touchTopic(TopicName topicName); - List getTopicsDetails(Collection topicNames); + Topic getTopicDetails(TopicName topicName); - List listAllTopics(); + List getTopicsDetails(Collection topicNames); + List listAllTopics(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreview.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreview.java index 9f9d3ce5d9..a0a7dd7649 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreview.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreview.java @@ -5,25 +5,26 @@ public class MessagePreview { - private final byte[] content; + private final byte[] content; - private final boolean truncated; + private final boolean truncated; - @JsonCreator - public MessagePreview(@JsonProperty("content") byte[] content, @JsonProperty("truncated") boolean truncated) { - this.content = content; - this.truncated = truncated; - } + @JsonCreator + public MessagePreview( + @JsonProperty("content") byte[] content, @JsonProperty("truncated") boolean truncated) { + this.content = content; + this.truncated = truncated; + } - public MessagePreview(byte[] content) { - this(content, false); - } + public MessagePreview(byte[] content) { + this(content, false); + } - public byte[] getContent() { - return content; - } + public byte[] getContent() { + return content; + } - public boolean isTruncated() { - return truncated; - } + public boolean isTruncated() { + return truncated; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreviewRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreviewRepository.java index 8f06e044aa..fe8024d798 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreviewRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/MessagePreviewRepository.java @@ -1,13 +1,11 @@ package pl.allegro.tech.hermes.domain.topic.preview; -import pl.allegro.tech.hermes.api.TopicName; - import java.util.List; +import pl.allegro.tech.hermes.api.TopicName; public interface MessagePreviewRepository { - List loadPreview(TopicName topicName); - - void persist(TopicsMessagesPreview topicsMessagesPreview); + List loadPreview(TopicName topicName); + void persist(TopicsMessagesPreview topicsMessagesPreview); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/TopicsMessagesPreview.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/TopicsMessagesPreview.java index e15431b058..6ab775bf8c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/TopicsMessagesPreview.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/topic/preview/TopicsMessagesPreview.java @@ -1,27 +1,26 @@ package pl.allegro.tech.hermes.domain.topic.preview; -import pl.allegro.tech.hermes.api.TopicName; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import pl.allegro.tech.hermes.api.TopicName; public class TopicsMessagesPreview { - private final Map> messages = new HashMap<>(); + private final Map> messages = new HashMap<>(); - public void add(TopicName topicName, MessagePreview message) { - List messageList = messages.computeIfAbsent(topicName, k -> new ArrayList<>()); - messageList.add(message); - } + public void add(TopicName topicName, MessagePreview message) { + List messageList = messages.computeIfAbsent(topicName, k -> new ArrayList<>()); + messageList.add(message); + } - public Collection topics() { - return messages.keySet(); - } + public Collection topics() { + return messages.keySet(); + } - public List previewOf(TopicName topic) { - return messages.getOrDefault(topic, new ArrayList<>()); - } + public List previewOf(TopicName topic) { + return messages.getOrDefault(topic, new ArrayList<>()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/ConsumersWorkloadConstraints.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/ConsumersWorkloadConstraints.java index 436a2c0722..4efc1ac8e9 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/ConsumersWorkloadConstraints.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/ConsumersWorkloadConstraints.java @@ -1,47 +1,47 @@ package pl.allegro.tech.hermes.domain.workload.constraints; import com.google.common.collect.ImmutableMap; +import java.util.Map; +import java.util.Objects; import pl.allegro.tech.hermes.api.Constraints; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.api.TopicName; -import java.util.Map; -import java.util.Objects; - public class ConsumersWorkloadConstraints { - private final Map topicConstraints; - private final Map subscriptionConstraints; + private final Map topicConstraints; + private final Map subscriptionConstraints; - public ConsumersWorkloadConstraints(Map topicConstraints, - Map subscriptionConstraints) { - this.topicConstraints = ImmutableMap.copyOf(topicConstraints); - this.subscriptionConstraints = ImmutableMap.copyOf(subscriptionConstraints); - } + public ConsumersWorkloadConstraints( + Map topicConstraints, + Map subscriptionConstraints) { + this.topicConstraints = ImmutableMap.copyOf(topicConstraints); + this.subscriptionConstraints = ImmutableMap.copyOf(subscriptionConstraints); + } - public Map getTopicConstraints() { - return topicConstraints; - } + public Map getTopicConstraints() { + return topicConstraints; + } - public Map getSubscriptionConstraints() { - return subscriptionConstraints; - } + public Map getSubscriptionConstraints() { + return subscriptionConstraints; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConsumersWorkloadConstraints that = (ConsumersWorkloadConstraints) o; - return Objects.equals(topicConstraints, that.topicConstraints) - && Objects.equals(subscriptionConstraints, that.subscriptionConstraints); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; } - - @Override - public int hashCode() { - return Objects.hash(topicConstraints, subscriptionConstraints); + if (o == null || getClass() != o.getClass()) { + return false; } + ConsumersWorkloadConstraints that = (ConsumersWorkloadConstraints) o; + return Objects.equals(topicConstraints, that.topicConstraints) + && Objects.equals(subscriptionConstraints, that.subscriptionConstraints); + } + + @Override + public int hashCode() { + return Objects.hash(topicConstraints, subscriptionConstraints); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsAlreadyExistException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsAlreadyExistException.java index 9deab17e7e..501a81681a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsAlreadyExistException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsAlreadyExistException.java @@ -1,19 +1,23 @@ package pl.allegro.tech.hermes.domain.workload.constraints; +import static pl.allegro.tech.hermes.api.ErrorCode.SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST; + import pl.allegro.tech.hermes.api.ErrorCode; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.common.exception.HermesException; -import static pl.allegro.tech.hermes.api.ErrorCode.SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST; - public class SubscriptionConstraintsAlreadyExistException extends HermesException { - public SubscriptionConstraintsAlreadyExistException(SubscriptionName subscriptionName, Throwable cause) { - super(String.format("Constraints for subscription %s already exist.", subscriptionName.getQualifiedName()), cause); - } + public SubscriptionConstraintsAlreadyExistException( + SubscriptionName subscriptionName, Throwable cause) { + super( + String.format( + "Constraints for subscription %s already exist.", subscriptionName.getQualifiedName()), + cause); + } - @Override - public ErrorCode getCode() { - return SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST; - } + @Override + public ErrorCode getCode() { + return SUBSCRIPTION_CONSTRAINTS_ALREADY_EXIST; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsDoNotExistException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsDoNotExistException.java index b1f7ca258d..133e60ff57 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsDoNotExistException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/SubscriptionConstraintsDoNotExistException.java @@ -1,19 +1,23 @@ package pl.allegro.tech.hermes.domain.workload.constraints; +import static pl.allegro.tech.hermes.api.ErrorCode.SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST; + import pl.allegro.tech.hermes.api.ErrorCode; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.common.exception.HermesException; -import static pl.allegro.tech.hermes.api.ErrorCode.SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST; - public class SubscriptionConstraintsDoNotExistException extends HermesException { - public SubscriptionConstraintsDoNotExistException(SubscriptionName subscriptionName, Throwable cause) { - super(String.format("Constraints for subscription %s do not exist.", subscriptionName.getQualifiedName()), cause); - } + public SubscriptionConstraintsDoNotExistException( + SubscriptionName subscriptionName, Throwable cause) { + super( + String.format( + "Constraints for subscription %s do not exist.", subscriptionName.getQualifiedName()), + cause); + } - @Override - public ErrorCode getCode() { - return SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST; - } + @Override + public ErrorCode getCode() { + return SUBSCRIPTION_CONSTRAINTS_DO_NOT_EXIST; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsAlreadyExistException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsAlreadyExistException.java index 485eda8cfa..12196494b2 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsAlreadyExistException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsAlreadyExistException.java @@ -5,12 +5,13 @@ import pl.allegro.tech.hermes.common.exception.HermesException; public class TopicConstraintsAlreadyExistException extends HermesException { - public TopicConstraintsAlreadyExistException(TopicName topicName, Throwable cause) { - super(String.format("Constraints for topic %s already exist.", topicName.qualifiedName()), cause); - } + public TopicConstraintsAlreadyExistException(TopicName topicName, Throwable cause) { + super( + String.format("Constraints for topic %s already exist.", topicName.qualifiedName()), cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.TOPIC_CONSTRAINTS_ALREADY_EXIST; - } + @Override + public ErrorCode getCode() { + return ErrorCode.TOPIC_CONSTRAINTS_ALREADY_EXIST; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsDoNotExistException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsDoNotExistException.java index e34385ed0a..bbd7899e8a 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsDoNotExistException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/TopicConstraintsDoNotExistException.java @@ -1,19 +1,20 @@ package pl.allegro.tech.hermes.domain.workload.constraints; +import static pl.allegro.tech.hermes.api.ErrorCode.TOPIC_CONSTRAINTS_DO_NOT_EXIST; + import pl.allegro.tech.hermes.api.ErrorCode; import pl.allegro.tech.hermes.api.TopicName; import pl.allegro.tech.hermes.common.exception.HermesException; -import static pl.allegro.tech.hermes.api.ErrorCode.TOPIC_CONSTRAINTS_DO_NOT_EXIST; - public class TopicConstraintsDoNotExistException extends HermesException { - public TopicConstraintsDoNotExistException(TopicName topicName, Throwable cause) { - super(String.format("Constraints for topic %s do not exist.", topicName.qualifiedName()), cause); - } + public TopicConstraintsDoNotExistException(TopicName topicName, Throwable cause) { + super( + String.format("Constraints for topic %s do not exist.", topicName.qualifiedName()), cause); + } - @Override - public ErrorCode getCode() { - return TOPIC_CONSTRAINTS_DO_NOT_EXIST; - } + @Override + public ErrorCode getCode() { + return TOPIC_CONSTRAINTS_DO_NOT_EXIST; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/WorkloadConstraintsRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/WorkloadConstraintsRepository.java index e5b153ee8a..ce1bf4aa5c 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/WorkloadConstraintsRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/domain/workload/constraints/WorkloadConstraintsRepository.java @@ -6,21 +6,21 @@ public interface WorkloadConstraintsRepository { - ConsumersWorkloadConstraints getConsumersWorkloadConstraints(); + ConsumersWorkloadConstraints getConsumersWorkloadConstraints(); - void createConstraints(TopicName topicName, Constraints constraints); + void createConstraints(TopicName topicName, Constraints constraints); - void createConstraints(SubscriptionName subscriptionName, Constraints constraints); + void createConstraints(SubscriptionName subscriptionName, Constraints constraints); - void updateConstraints(TopicName topicName, Constraints constraints); + void updateConstraints(TopicName topicName, Constraints constraints); - void updateConstraints(SubscriptionName subscriptionName, Constraints constraints); + void updateConstraints(SubscriptionName subscriptionName, Constraints constraints); - void deleteConstraints(TopicName topicName); + void deleteConstraints(TopicName topicName); - void deleteConstraints(SubscriptionName subscriptionName); + void deleteConstraints(SubscriptionName subscriptionName); - boolean constraintsExist(TopicName topicName); + boolean constraintsExist(TopicName topicName); - boolean constraintsExist(SubscriptionName subscriptionName); + boolean constraintsExist(SubscriptionName subscriptionName); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/MalformedDataException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/MalformedDataException.java index 62495f9acf..137bdfe42e 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/MalformedDataException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/MalformedDataException.java @@ -2,8 +2,7 @@ public class MalformedDataException extends RuntimeException { - public MalformedDataException(String path, Throwable throwable) { - super(String.format("Unable to read data from path %s", path), throwable); - } - + public MalformedDataException(String path, Throwable throwable) { + super(String.format("Unable to read data from path %s", path), throwable); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DatacenterNameProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DatacenterNameProvider.java index e0ad86da8b..ede547f611 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DatacenterNameProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DatacenterNameProvider.java @@ -1,5 +1,5 @@ package pl.allegro.tech.hermes.infrastructure.dc; public interface DatacenterNameProvider { - String getDatacenterName(); + String getDatacenterName(); } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameProvisionException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameProvisionException.java index ed7f41ccca..0a4967b028 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameProvisionException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameProvisionException.java @@ -2,7 +2,7 @@ public class DcNameProvisionException extends RuntimeException { - public DcNameProvisionException(String message) { - super(message); - } + public DcNameProvisionException(String message) { + super(message); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameSource.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameSource.java index adc1a28b66..864b838a47 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameSource.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DcNameSource.java @@ -1,5 +1,5 @@ package pl.allegro.tech.hermes.infrastructure.dc; public enum DcNameSource { - ENV + ENV } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DefaultDatacenterNameProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DefaultDatacenterNameProvider.java index 9da8bd9872..aa8f65af26 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DefaultDatacenterNameProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/DefaultDatacenterNameProvider.java @@ -5,13 +5,13 @@ public class DefaultDatacenterNameProvider implements DatacenterNameProvider { - private static final Logger logger = LoggerFactory.getLogger(DefaultDatacenterNameProvider.class); + private static final Logger logger = LoggerFactory.getLogger(DefaultDatacenterNameProvider.class); - public static final String DEFAULT_DC_NAME = "dc"; + public static final String DEFAULT_DC_NAME = "dc"; - @Override - public String getDatacenterName() { - logger.info("Providing default datacenter name: {}", DEFAULT_DC_NAME); - return DEFAULT_DC_NAME; - } + @Override + public String getDatacenterName() { + logger.info("Providing default datacenter name: {}", DEFAULT_DC_NAME); + return DEFAULT_DC_NAME; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/EnvironmentVariableDatacenterNameProvider.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/EnvironmentVariableDatacenterNameProvider.java index 5588b4a3f9..7f31348041 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/EnvironmentVariableDatacenterNameProvider.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/dc/EnvironmentVariableDatacenterNameProvider.java @@ -4,22 +4,23 @@ import org.slf4j.LoggerFactory; public class EnvironmentVariableDatacenterNameProvider implements DatacenterNameProvider { - private static final Logger logger = LoggerFactory.getLogger(EnvironmentVariableDatacenterNameProvider.class); + private static final Logger logger = + LoggerFactory.getLogger(EnvironmentVariableDatacenterNameProvider.class); - private final String variableName; + private final String variableName; - public EnvironmentVariableDatacenterNameProvider(String variableName) { - this.variableName = variableName; - } + public EnvironmentVariableDatacenterNameProvider(String variableName) { + this.variableName = variableName; + } - @Override - public String getDatacenterName() { - String dcName = System.getenv(variableName); - if (dcName == null) { - logger.info("Undefined environment variable: " + variableName); - throw new DcNameProvisionException("Undefined environment variable: " + variableName); - } - logger.info("Providing DC name from environment variable: {}={}", variableName, dcName); - return dcName; + @Override + public String getDatacenterName() { + String dcName = System.getenv(variableName); + if (dcName == null) { + logger.info("Undefined environment variable: " + variableName); + throw new DcNameProvisionException("Undefined environment variable: " + variableName); } + logger.info("Providing DC name from environment variable: {}={}", variableName, dcName); + return dcName; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/logback/AggregatingTurboFilter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/logback/AggregatingTurboFilter.java index 45507bb45e..6ac82a8920 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/logback/AggregatingTurboFilter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/logback/AggregatingTurboFilter.java @@ -5,9 +5,6 @@ import ch.qos.logback.classic.turbo.TurboFilter; import ch.qos.logback.core.spi.FilterReply; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -20,22 +17,25 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; /** - *

This class is an implementation of {@link TurboFilter} interface that allows messages of configured loggers - * to be aggregated and logged only once with specified time interval.

+ * This class is an implementation of {@link TurboFilter} interface that allows messages of + * configured loggers to be aggregated and logged only once with specified time interval. * - *

Logged message contains a suffix "[occurrences=N]" with a number of logged events. - * The logging part is a little bit tricky, as the TurboFilter interface does not support a way of logging out of the box, - * it just tells whether a logged message should be passed further or not. - * In order to logging be possible from the TurboFilter a small trick has to be applied, the logged message is enriched - * with a custom {@link Marker} which is checked by the filter itself, so it won't filter it's own messages - * and we manage to avoid recursion.

+ *

Logged message contains a suffix "[occurrences=N]" with a number of logged events. The logging + * part is a little bit tricky, as the TurboFilter interface does not support a way of + * logging out of the box, it just tells whether a logged message should be passed further or not. + * In order to logging be possible from the TurboFilter a small trick has to be + * applied, the logged message is enriched with a custom {@link Marker} which is checked by the + * filter itself, so it won't filter it's own messages and we manage to avoid recursion. * - *

An instance of AggregatingTurboFilter starts it's own scheduled executor service with a single thread - * that logs the messages asynchronously.

+ *

An instance of AggregatingTurboFilter starts it's own scheduled executor service + * with a single thread that logs the messages asynchronously. * *

Example logback configuration: + * *

{@code
  *  
  *      ...
@@ -51,167 +51,189 @@
  */
 public class AggregatingTurboFilter extends TurboFilter {
 
-    static final Marker MARKER = MarkerFactory.getMarker("AggregatingTurboFilterMarker");
-
-    private ScheduledExecutorService executorService;
-    private final List aggregatedLogger = new ArrayList<>();
-    private long reportingIntervalMillis = 10_000;
+  static final Marker MARKER = MarkerFactory.getMarker("AggregatingTurboFilterMarker");
 
-    private final Map logAggregates = new ConcurrentHashMap<>();
+  private ScheduledExecutorService executorService;
+  private final List aggregatedLogger = new ArrayList<>();
+  private long reportingIntervalMillis = 10_000;
 
-    private static final LongAdder filterClassCounter = new LongAdder();
+  private final Map logAggregates = new ConcurrentHashMap<>();
 
-    @Override
-    public void start() {
-        super.start();
-        if (!aggregatedLogger.isEmpty()) {
-            ThreadFactory threadFactory = new ThreadFactoryBuilder()
-                    .setNameFormat("aggregating-filter-" + filterClassCounter.longValue() + "-thread-%d")
-                    .build();
-            filterClassCounter.increment();
-            executorService = Executors.newSingleThreadScheduledExecutor(threadFactory);
-            executorService.scheduleAtFixedRate(this::report, 0L, getReportingIntervalMillis(), TimeUnit.MILLISECONDS);
-        }
-    }
+  private static final LongAdder filterClassCounter = new LongAdder();
 
-    @Override
-    public void stop() {
-        executorService.shutdownNow();
-        super.stop();
+  @Override
+  public void start() {
+    super.start();
+    if (!aggregatedLogger.isEmpty()) {
+      ThreadFactory threadFactory =
+          new ThreadFactoryBuilder()
+              .setNameFormat("aggregating-filter-" + filterClassCounter.longValue() + "-thread-%d")
+              .build();
+      filterClassCounter.increment();
+      executorService = Executors.newSingleThreadScheduledExecutor(threadFactory);
+      executorService.scheduleAtFixedRate(
+          this::report, 0L, getReportingIntervalMillis(), TimeUnit.MILLISECONDS);
     }
-
-    void report() {
-        logAggregates.forEach((logger, loggerAggregates) ->
-                loggerAggregates.aggregates.keySet().forEach(entry -> {
-                    loggerAggregates.aggregates.computeIfPresent(entry, (key, summary) -> {
-                        logger.log(key.marker, Logger.FQCN, key.level,
+  }
+
+  @Override
+  public void stop() {
+    executorService.shutdownNow();
+    super.stop();
+  }
+
+  void report() {
+    logAggregates.forEach(
+        (logger, loggerAggregates) ->
+            loggerAggregates
+                .aggregates
+                .keySet()
+                .forEach(
+                    entry -> {
+                      loggerAggregates.aggregates.computeIfPresent(
+                          entry,
+                          (key, summary) -> {
+                            logger.log(
+                                key.marker,
+                                Logger.FQCN,
+                                key.level,
                                 key.message + " [occurrences=" + summary.logsCount + "]",
-                                key.params, summary.lastException);
-                        return null;
-                    });
-                }));
+                                key.params,
+                                summary.lastException);
+                            return null;
+                          });
+                    }));
+  }
+
+  @Override
+  public FilterReply decide(
+      Marker marker, Logger logger, Level level, String message, Object[] params, Throwable ex) {
+    if (isAggregatedLog(marker)) { // prevent recursion for aggregated logger events
+      return FilterReply.NEUTRAL;
     }
-
-    @Override
-    public FilterReply decide(Marker marker, Logger logger, Level level, String message, Object[] params, Throwable ex) {
-        if (isAggregatedLog(marker)) { // prevent recursion for aggregated logger events
-            return FilterReply.NEUTRAL;
-        }
-        if (!aggregatedLogger.contains(logger.getName())) {
-            return FilterReply.NEUTRAL;
-        }
-
-        if (ex == null) {
-            Optional throwable = extractLastParamThrowable(params);
-            if (throwable.isPresent()) {
-                ex = throwable.get();
-                params = Arrays.copyOfRange(params, 0, params.length - 1);
-            }
-        }
-        Throwable exception = ex;
-
-        LoggingEventKey loggingEventKey = new LoggingEventKey(message, params, level, getEnrichedMarker(marker));
-        logAggregates.computeIfAbsent(logger, l -> new LoggerAggregates())
-                .aggregates.merge(loggingEventKey, new AggregateSummary(ex),
-                (currentAggregate, emptyAggregate) -> AggregateSummary.incrementCount(currentAggregate, exception));
-
-        return FilterReply.DENY;
+    if (!aggregatedLogger.contains(logger.getName())) {
+      return FilterReply.NEUTRAL;
     }
 
-    private boolean isAggregatedLog(Marker marker) {
-        return marker != null && (marker.equals(MARKER) || marker.contains(MARKER));
+    if (ex == null) {
+      Optional throwable = extractLastParamThrowable(params);
+      if (throwable.isPresent()) {
+        ex = throwable.get();
+        params = Arrays.copyOfRange(params, 0, params.length - 1);
+      }
     }
-
-    private Optional extractLastParamThrowable(Object[] params) {
-        return Optional.ofNullable(params)
-                .map(Arrays::stream)
-                .flatMap(a -> a.skip(params.length - 1)
-                        .findFirst()
-                        .filter(o -> o instanceof Throwable)
-                        .map(Throwable.class::cast));
+    Throwable exception = ex;
+
+    LoggingEventKey loggingEventKey =
+        new LoggingEventKey(message, params, level, getEnrichedMarker(marker));
+    logAggregates
+        .computeIfAbsent(logger, l -> new LoggerAggregates())
+        .aggregates
+        .merge(
+            loggingEventKey,
+            new AggregateSummary(ex),
+            (currentAggregate, emptyAggregate) ->
+                AggregateSummary.incrementCount(currentAggregate, exception));
+
+    return FilterReply.DENY;
+  }
+
+  private boolean isAggregatedLog(Marker marker) {
+    return marker != null && (marker.equals(MARKER) || marker.contains(MARKER));
+  }
+
+  private Optional extractLastParamThrowable(Object[] params) {
+    return Optional.ofNullable(params)
+        .map(Arrays::stream)
+        .flatMap(
+            a ->
+                a.skip(params.length - 1)
+                    .findFirst()
+                    .filter(o -> o instanceof Throwable)
+                    .map(Throwable.class::cast));
+  }
+
+  private Marker getEnrichedMarker(Marker marker) {
+    if (marker == null) {
+      return MARKER;
     }
+    marker.add(MARKER);
+    return marker;
+  }
 
-    private Marker getEnrichedMarker(Marker marker) {
-        if (marker == null) {
-            return MARKER;
-        }
-        marker.add(MARKER);
-        return marker;
-    }
+  public void addAggregatedLogger(String logger) {
+    this.aggregatedLogger.add(logger);
+  }
 
-    public void addAggregatedLogger(String logger) {
-        this.aggregatedLogger.add(logger);
-    }
+  public long getReportingIntervalMillis() {
+    return reportingIntervalMillis;
+  }
 
-    public long getReportingIntervalMillis() {
-        return reportingIntervalMillis;
-    }
+  public void setReportingIntervalMillis(long reportingIntervalMillis) {
+    this.reportingIntervalMillis = reportingIntervalMillis;
+  }
 
-    public void setReportingIntervalMillis(long reportingIntervalMillis) {
-        this.reportingIntervalMillis = reportingIntervalMillis;
-    }
+  private static class LoggerAggregates {
+
+    private final Map aggregates = new ConcurrentHashMap<>();
+  }
+
+  private static class AggregateSummary {
+
+    private final int logsCount;
+    private final Throwable lastException;
 
-    private static class LoggerAggregates {
+    private AggregateSummary(Throwable lastException) {
+      this(1, lastException);
+    }
 
-        private final Map aggregates = new ConcurrentHashMap<>();
+    private AggregateSummary(int logsCount, Throwable lastException) {
+      this.logsCount = logsCount;
+      this.lastException = lastException;
     }
 
-    private static class AggregateSummary {
+    private static AggregateSummary incrementCount(
+        AggregateSummary currentAggregate, Throwable lastException) {
+      return new AggregateSummary(
+          currentAggregate.logsCount + 1,
+          Optional.ofNullable(lastException).orElse(currentAggregate.lastException));
+    }
+  }
 
-        private final int logsCount;
-        private final Throwable lastException;
+  private static class LoggingEventKey {
 
-        private AggregateSummary(Throwable lastException) {
-            this(1, lastException);
-        }
+    private final String message;
+    private final int level;
+    private final Marker marker;
+    private final Object[] params;
 
-        private AggregateSummary(int logsCount, Throwable lastException) {
-            this.logsCount = logsCount;
-            this.lastException = lastException;
-        }
+    LoggingEventKey(String message, Object[] params, Level level, Marker marker) {
+      this.message = message;
+      this.params = params;
+      this.level = Level.toLocationAwareLoggerInteger(level);
+      this.marker = marker;
+    }
 
-        private static AggregateSummary incrementCount(AggregateSummary currentAggregate, Throwable lastException) {
-            return new AggregateSummary(currentAggregate.logsCount + 1,
-                    Optional.ofNullable(lastException).orElse(currentAggregate.lastException));
-        }
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      LoggingEventKey that = (LoggingEventKey) o;
+      return level == that.level
+          && Objects.equals(message, that.message)
+          && Objects.equals(marker, that.marker)
+          && Arrays.equals(params, that.params);
     }
 
-    private static class LoggingEventKey {
-
-        private final String message;
-        private final int level;
-        private final Marker marker;
-        private final Object[] params;
-
-        LoggingEventKey(String message, Object[] params, Level level, Marker marker) {
-            this.message = message;
-            this.params = params;
-            this.level = Level.toLocationAwareLoggerInteger(level);
-            this.marker = marker;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            LoggingEventKey that = (LoggingEventKey) o;
-            return level == that.level
-                    && Objects.equals(message, that.message)
-                    && Objects.equals(marker, that.marker)
-                    && Arrays.equals(params, that.params);
-        }
-
-        @Override
-        public int hashCode() {
-            int result = Objects.hash(message, level, marker);
-            result = 31 * result + Arrays.hashCode(params);
-            return result;
-        }
+    @Override
+    public int hashCode() {
+      int result = Objects.hash(message, level, marker);
+      result = 31 * result + Arrays.hashCode(params);
+      return result;
     }
+  }
 }
-
-
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperBasedRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperBasedRepository.java
index b688aa314d..85827afb21 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperBasedRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperBasedRepository.java
@@ -3,8 +3,17 @@
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang.ArrayUtils;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.api.transaction.CuratorTransactionFinal;
+import org.apache.curator.utils.ZKPaths;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -12,172 +21,194 @@
 import pl.allegro.tech.hermes.common.exception.RepositoryNotAvailableException;
 import pl.allegro.tech.hermes.infrastructure.MalformedDataException;
 
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.function.BiConsumer;
-
 public abstract class ZookeeperBasedRepository {
 
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperBasedRepository.class);
-
-    private final CuratorFramework zookeeper;
-
-    private final ObjectMapper mapper;
-
-    protected final ZookeeperPaths paths;
-
-    protected ZookeeperBasedRepository(CuratorFramework zookeeper,
-                                       ObjectMapper mapper,
-                                       ZookeeperPaths paths) {
-        this.zookeeper = zookeeper;
-        this.mapper = mapper;
-        this.paths = paths;
-    }
-
-    private void ensureConnected() {
-        if (!zookeeper.getZookeeperClient().isConnected()) {
-            throw new RepositoryNotAvailableException("Could not establish connection to a Zookeeper instance");
-        }
-    }
-
-    protected void ensurePathExists(String path) {
-        ensureConnected();
-        if (!pathExists(path)) {
-            try {
-                zookeeper.create().creatingParentsIfNeeded().forPath(path);
-            } catch (Exception e) {
-                throw new InternalProcessingException(e);
-            }
-        }
-    }
-
-    protected boolean pathExists(String path) {
-        ensureConnected();
-        try {
-            Optional optionalStat = Optional.ofNullable(zookeeper.checkExists().forPath(path));
-            return optionalStat.isPresent();
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
-    }
-
-    protected List childrenOf(String path) {
-        ensureConnected();
-        try {
-            List retrievedNodes = new ArrayList<>(zookeeper.getChildren().forPath(path));
-            Collections.sort(retrievedNodes);
-            return retrievedNodes;
-        } catch (Exception ex) {
-            throw new InternalProcessingException(ex);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected byte[] readFrom(String path) {
-        return readWithStatFrom(path, bytes -> bytes, (t, stat) -> {}, false).get();
-    }
-
-    @SuppressWarnings("unchecked")
-    protected  T readFrom(String path, Class clazz) {
-        return readFrom(path, clazz, false).get();
-    }
-
-    @SuppressWarnings("unchecked")
-    protected  Optional readFrom(String path, Class clazz, boolean quiet) {
-        return readWithStatFrom(path, b -> (T) mapper.readValue(b, clazz), (t, stat) -> {}, quiet);
-    }
-
-    @SuppressWarnings("unchecked")
-    protected  Optional readFrom(String path, TypeReference type, boolean quiet) {
-        return readWithStatFrom(path, b -> (T) mapper.readValue(b, type), (t, stat) -> {}, quiet);
-    }
-
-    protected  Optional readWithStatFrom(String path, Class clazz, BiConsumer statDecorator, boolean quiet) {
-        return readWithStatFrom(path, b -> mapper.readValue(b, clazz), statDecorator, quiet);
-    }
-
-    private  Optional readWithStatFrom(String path, ThrowingReader supplier, BiConsumer statDecorator, boolean quiet) {
-        ensureConnected();
-        try {
-            Stat stat = new Stat();
-            byte[] data = zookeeper.getData().storingStatIn(stat).forPath(path);
-            if (ArrayUtils.isNotEmpty(data)) {
-                T t = supplier.read(data);
-                statDecorator.accept(t, stat);
-                return Optional.of(t);
-            }
-        } catch (JsonMappingException malformedException) {
-            logWarnOrThrowException("Unable to read data from path " + path,
-                    new MalformedDataException(path, malformedException), quiet);
-        } catch (InternalProcessingException e) {
-            throw e;
-        } catch (Exception exception) {
-            logWarnOrThrowException("Unable to read data from path " + path, new InternalProcessingException(exception),
-                    quiet);
-        }
-        return Optional.empty();
-    }
-
-    private void logWarnOrThrowException(String message, RuntimeException e, Boolean quiet) {
-        if (quiet) {
-            logger.warn(message, e);
-        } else {
-            throw e;
-        }
-    }
-
-    protected void overwrite(String path, Object value) throws Exception {
-        ensureConnected();
-        zookeeper.setData().forPath(path, mapper.writeValueAsBytes(value));
-    }
-
-    protected void overwrite(String path, byte[] value) throws Exception {
-        ensureConnected();
-        zookeeper.setData().forPath(path, value);
-    }
-
-    protected void createRecursively(String path, Object value) throws Exception {
-        ensureConnected();
-        zookeeper.create()
-                .creatingParentsIfNeeded()
-                .forPath(path, mapper.writeValueAsBytes(value));
-    }
-
-    protected void createInTransaction(String path, Object value, String childPath) throws Exception {
-        ensureConnected();
-        zookeeper.inTransaction()
-                .create().forPath(path, mapper.writeValueAsBytes(value))
-                .and()
-                .create().forPath(childPath)
-                .and()
-                .commit();
-    }
-
-    protected void create(String path, Object value) throws Exception {
-        ensureConnected();
-        zookeeper.create().forPath(path, mapper.writeValueAsBytes(value));
-    }
-
-    protected void create(String path, byte[] value) throws Exception {
-        ensureConnected();
-        zookeeper.create().forPath(path, value);
-    }
-
-    protected void touch(String path) throws Exception {
-        ensureConnected();
-        byte[] oldData = zookeeper.getData().forPath(path);
-        zookeeper.setData().forPath(path, oldData);
-    }
-
-    protected void remove(String path) throws Exception {
-        ensureConnected();
-        zookeeper.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
-    }
-
-    private interface ThrowingReader {
-        T read(byte[] data) throws IOException;
-    }
+  private static final Logger logger = LoggerFactory.getLogger(ZookeeperBasedRepository.class);
+
+  private final CuratorFramework zookeeper;
+
+  private final ObjectMapper mapper;
+
+  protected final ZookeeperPaths paths;
+
+  protected ZookeeperBasedRepository(
+      CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
+    this.zookeeper = zookeeper;
+    this.mapper = mapper;
+    this.paths = paths;
+  }
+
+  private void ensureConnected() {
+    if (!zookeeper.getZookeeperClient().isConnected()) {
+      throw new RepositoryNotAvailableException(
+          "Could not establish connection to a Zookeeper instance");
+    }
+  }
+
+  protected void ensurePathExists(String path) {
+    ensureConnected();
+    if (!pathExists(path)) {
+      try {
+        zookeeper.create().creatingParentsIfNeeded().forPath(path);
+      } catch (Exception e) {
+        throw new InternalProcessingException(e);
+      }
+    }
+  }
+
+  protected boolean pathExists(String path) {
+    ensureConnected();
+    try {
+      Optional optionalStat = Optional.ofNullable(zookeeper.checkExists().forPath(path));
+      return optionalStat.isPresent();
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
+    }
+  }
+
+  protected List childrenOf(String path) {
+    ensureConnected();
+    try {
+      List retrievedNodes = new ArrayList<>(zookeeper.getChildren().forPath(path));
+      Collections.sort(retrievedNodes);
+      return retrievedNodes;
+    } catch (Exception ex) {
+      throw new InternalProcessingException(ex);
+    }
+  }
+
+  protected List childrenPathsOf(String path) {
+    List childNodes = childrenOf(path);
+    return childNodes.stream()
+        .map(child -> ZKPaths.makePath(path, child))
+        .collect(Collectors.toList());
+  }
+
+  @SuppressWarnings("unchecked")
+  protected byte[] readFrom(String path) {
+    return readWithStatFrom(path, bytes -> bytes, (t, stat) -> {}, false).get();
+  }
+
+  @SuppressWarnings("unchecked")
+  protected  T readFrom(String path, Class clazz) {
+    return readFrom(path, clazz, false).get();
+  }
+
+  @SuppressWarnings("unchecked")
+  protected  Optional readFrom(String path, Class clazz, boolean quiet) {
+    return readWithStatFrom(path, b -> (T) mapper.readValue(b, clazz), (t, stat) -> {}, quiet);
+  }
+
+  @SuppressWarnings("unchecked")
+  protected  Optional readFrom(String path, TypeReference type, boolean quiet) {
+    return readWithStatFrom(path, b -> (T) mapper.readValue(b, type), (t, stat) -> {}, quiet);
+  }
+
+  protected  Optional readWithStatFrom(
+      String path, Class clazz, BiConsumer statDecorator, boolean quiet) {
+    return readWithStatFrom(path, b -> mapper.readValue(b, clazz), statDecorator, quiet);
+  }
+
+  private  Optional readWithStatFrom(
+      String path, ThrowingReader supplier, BiConsumer statDecorator, boolean quiet) {
+    ensureConnected();
+    try {
+      Stat stat = new Stat();
+      byte[] data = zookeeper.getData().storingStatIn(stat).forPath(path);
+      if (ArrayUtils.isNotEmpty(data)) {
+        T t = supplier.read(data);
+        statDecorator.accept(t, stat);
+        return Optional.of(t);
+      }
+    } catch (JsonMappingException malformedException) {
+      logWarnOrThrowException(
+          "Unable to read data from path " + path,
+          new MalformedDataException(path, malformedException),
+          quiet);
+    } catch (InternalProcessingException e) {
+      throw e;
+    } catch (Exception exception) {
+      logWarnOrThrowException(
+          "Unable to read data from path " + path,
+          new InternalProcessingException(exception),
+          quiet);
+    }
+    return Optional.empty();
+  }
+
+  private void logWarnOrThrowException(String message, RuntimeException e, Boolean quiet) {
+    if (quiet) {
+      logger.warn(message, e);
+    } else {
+      throw e;
+    }
+  }
+
+  protected void overwrite(String path, Object value) throws Exception {
+    ensureConnected();
+    zookeeper.setData().forPath(path, mapper.writeValueAsBytes(value));
+  }
+
+  protected void overwrite(String path, byte[] value) throws Exception {
+    ensureConnected();
+    zookeeper.setData().forPath(path, value);
+  }
+
+  protected void createRecursively(String path, Object value) throws Exception {
+    ensureConnected();
+    zookeeper.create().creatingParentsIfNeeded().forPath(path, mapper.writeValueAsBytes(value));
+  }
+
+  protected void createInTransaction(String path, Object value, String childPath) throws Exception {
+    ensureConnected();
+    zookeeper
+        .inTransaction()
+        .create()
+        .forPath(path, mapper.writeValueAsBytes(value))
+        .and()
+        .create()
+        .forPath(childPath)
+        .and()
+        .commit();
+  }
+
+  protected void deleteInTransaction(List paths) throws Exception {
+    if (paths.isEmpty()) {
+      throw new InternalProcessingException("Attempting to remove empty set of paths from ZK");
+    }
+    ensureConnected();
+    CuratorTransactionFinal transaction =
+        zookeeper.inTransaction().delete().forPath(paths.get(0)).and();
+
+    for (int i = 1; i < paths.size(); i++) {
+      transaction = transaction.delete().forPath(paths.get(i)).and();
+    }
+
+    transaction.commit();
+  }
+
+  protected void create(String path, Object value) throws Exception {
+    ensureConnected();
+    zookeeper.create().forPath(path, mapper.writeValueAsBytes(value));
+  }
+
+  protected void create(String path, byte[] value) throws Exception {
+    ensureConnected();
+    zookeeper.create().forPath(path, value);
+  }
+
+  protected void touch(String path) throws Exception {
+    ensureConnected();
+    byte[] oldData = zookeeper.getData().forPath(path);
+    zookeeper.setData().forPath(path, oldData);
+  }
+
+  protected void remove(String path) throws Exception {
+    ensureConnected();
+    zookeeper.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
+  }
+
+  private interface ThrowingReader {
+    T read(byte[] data) throws IOException;
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperCredentialsRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperCredentialsRepository.java
index bf29c41a08..a0390f5267 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperCredentialsRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperCredentialsRepository.java
@@ -6,23 +6,25 @@
 import pl.allegro.tech.hermes.domain.CredentialsRepository;
 import pl.allegro.tech.hermes.domain.NodePassword;
 
-public class ZookeeperCredentialsRepository extends ZookeeperBasedRepository implements CredentialsRepository {
+public class ZookeeperCredentialsRepository extends ZookeeperBasedRepository
+    implements CredentialsRepository {
 
-    public ZookeeperCredentialsRepository(CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
-        super(zookeeper, mapper, paths);
-    }
+  public ZookeeperCredentialsRepository(
+      CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
+    super(zookeeper, mapper, paths);
+  }
 
-    @Override
-    public NodePassword readAdminPassword() {
-        return readFrom(paths.groupsPath(), NodePassword.class);
-    }
+  @Override
+  public NodePassword readAdminPassword() {
+    return readFrom(paths.groupsPath(), NodePassword.class);
+  }
 
-    @Override
-    public void overwriteAdminPassword(String password) {
-        try {
-            overwrite(paths.groupsPath(), new NodePassword(password));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  @Override
+  public void overwriteAdminPassword(String password) {
+    try {
+      overwrite(paths.groupsPath(), new NodePassword(password));
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperGroupRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperGroupRepository.java
index d546ba6044..67cece9fa9 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperGroupRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperGroupRepository.java
@@ -2,119 +2,123 @@
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import jakarta.annotation.PostConstruct;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import pl.allegro.tech.hermes.api.Group;
+import pl.allegro.tech.hermes.api.TopicName;
 import pl.allegro.tech.hermes.common.exception.InternalProcessingException;
 import pl.allegro.tech.hermes.domain.group.GroupAlreadyExistsException;
 import pl.allegro.tech.hermes.domain.group.GroupNotEmptyException;
 import pl.allegro.tech.hermes.domain.group.GroupNotExistsException;
 import pl.allegro.tech.hermes.domain.group.GroupRepository;
 
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
 public class ZookeeperGroupRepository extends ZookeeperBasedRepository implements GroupRepository {
 
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperGroupRepository.class);
-
-    public ZookeeperGroupRepository(CuratorFramework zookeeper,
-                                    ObjectMapper mapper,
-                                    ZookeeperPaths paths) {
-        super(zookeeper, mapper, paths);
-    }
-
-    @Override
-    public boolean groupExists(String groupName) {
-        return pathExists(paths.groupPath(groupName));
-    }
-
-    @Override
-    public void ensureGroupExists(String groupName) {
-        if (!groupExists(groupName)) {
-            throw new GroupNotExistsException(groupName);
-        }
-    }
+  private static final Logger logger = LoggerFactory.getLogger(ZookeeperGroupRepository.class);
 
-    @Override
-    public void createGroup(Group group) {
-        String groupPath = paths.groupPath(group.getGroupName());
-        logger.info("Creating group {} for path {}", group.getGroupName(), groupPath);
-
-        try {
-            createInTransaction(groupPath, group, paths.topicsPath(group.getGroupName()));
-        } catch (KeeperException.NodeExistsException ex) {
-            throw new GroupAlreadyExistsException(group.getGroupName(), ex);
-        } catch (Exception ex) {
-            throw new InternalProcessingException(ex);
-        }
-    }
+  public ZookeeperGroupRepository(
+      CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
+    super(zookeeper, mapper, paths);
+  }
 
-    @Override
-    public void updateGroup(Group group) {
-        ensureGroupExists(group.getGroupName());
+  @Override
+  public boolean groupExists(String groupName) {
+    return pathExists(paths.groupPath(groupName));
+  }
 
-        logger.info("Updating group {}", group.getGroupName());
-        try {
-            overwrite(paths.groupPath(group.getGroupName()), group);
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  @Override
+  public void ensureGroupExists(String groupName) {
+    if (!groupExists(groupName)) {
+      throw new GroupNotExistsException(groupName);
     }
-
-    @Override
-    public void removeGroup(String groupName) {
-        ensureGroupExists(groupName);
-        ensureGroupIsEmpty(groupName);
-
-        logger.info("Removing group: {}", groupName);
-        try {
-            remove(paths.groupPath(groupName));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  }
+
+  @Override
+  public void createGroup(Group group) {
+    String groupPath = paths.groupPath(group.getGroupName());
+    logger.info("Creating group {} for path {}", group.getGroupName(), groupPath);
+
+    try {
+      createInTransaction(groupPath, group, paths.topicsPath(group.getGroupName()));
+    } catch (KeeperException.NodeExistsException ex) {
+      throw new GroupAlreadyExistsException(group.getGroupName(), ex);
+    } catch (Exception ex) {
+      throw new InternalProcessingException(ex);
     }
+  }
 
-    private void ensureGroupIsEmpty(String groupName) {
-        if (!childrenOf(paths.topicsPath(groupName)).isEmpty()) {
-            throw new GroupNotEmptyException(groupName);
-        }
-    }
+  @Override
+  public void updateGroup(Group group) {
+    ensureGroupExists(group.getGroupName());
 
-    @Override
-    public List listGroupNames() {
-        return childrenOf(paths.groupsPath());
+    logger.info("Updating group {}", group.getGroupName());
+    try {
+      overwrite(paths.groupPath(group.getGroupName()), group);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    @Override
-    public List listGroups() {
-        return listGroupNames()
-                .stream()
-                .map(n -> getGroupDetails(n, true))
-                .filter(Optional::isPresent)
-                .map(Optional::get)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public Group getGroupDetails(String groupName) {
-        return getGroupDetails(groupName, false).get();
-    }
-
-    private Optional getGroupDetails(String groupName, boolean quiet) {
-        ensureGroupExists(groupName);
-
-        String path = paths.groupPath(groupName);
-        return readFrom(path, Group.class, quiet);
+  }
+
+  /**
+   * Atomic removal of group and group/topics nodes is required to prevent
+   * lengthy loop during removal, see: {@link
+   * pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperTopicRepository#removeTopic(TopicName)}.
+   */
+  @Override
+  public void removeGroup(String groupName) {
+    ensureGroupExists(groupName);
+    ensureGroupIsEmpty(groupName);
+
+    logger.info("Removing group: {}", groupName);
+    List pathsToDelete = List.of(paths.topicsPath(groupName), paths.groupPath(groupName));
+    try {
+      deleteInTransaction(pathsToDelete);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
+  }
 
-    @PostConstruct
-    public void init() {
-        logger.info("Before ensuring init path exists");
-        ensurePathExists(paths.groupsPath());
-        logger.info("After ensuring init path exists");
+  private void ensureGroupIsEmpty(String groupName) {
+    if (!childrenOf(paths.topicsPath(groupName)).isEmpty()) {
+      throw new GroupNotEmptyException(groupName);
     }
+  }
+
+  @Override
+  public List listGroupNames() {
+    return childrenOf(paths.groupsPath());
+  }
+
+  @Override
+  public List listGroups() {
+    return listGroupNames().stream()
+        .map(n -> getGroupDetails(n, true))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public Group getGroupDetails(String groupName) {
+    return getGroupDetails(groupName, false).get();
+  }
+
+  private Optional getGroupDetails(String groupName, boolean quiet) {
+    ensureGroupExists(groupName);
+
+    String path = paths.groupPath(groupName);
+    return readFrom(path, Group.class, quiet);
+  }
+
+  @PostConstruct
+  public void init() {
+    logger.info("Before ensuring init path exists");
+    ensurePathExists(paths.groupsPath());
+    logger.info("After ensuring init path exists");
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperMessagePreviewRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperMessagePreviewRepository.java
index 0fd938b668..d343c0b531 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperMessagePreviewRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperMessagePreviewRepository.java
@@ -1,7 +1,12 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
+import static java.lang.String.format;
+
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
 import org.apache.curator.framework.CuratorFramework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -11,54 +16,50 @@
 import pl.allegro.tech.hermes.domain.topic.preview.MessagePreviewRepository;
 import pl.allegro.tech.hermes.domain.topic.preview.TopicsMessagesPreview;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static java.lang.String.format;
+public class ZookeeperMessagePreviewRepository extends ZookeeperBasedRepository
+    implements MessagePreviewRepository {
 
-public class ZookeeperMessagePreviewRepository extends ZookeeperBasedRepository implements MessagePreviewRepository {
+  private static final Logger logger =
+      LoggerFactory.getLogger(ZookeeperMessagePreviewRepository.class);
 
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperMessagePreviewRepository.class);
+  public ZookeeperMessagePreviewRepository(
+      CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
+    super(zookeeper, mapper, paths);
+  }
 
-    public ZookeeperMessagePreviewRepository(CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
-        super(zookeeper, mapper, paths);
+  @Override
+  public List loadPreview(TopicName topicName) {
+    try {
+      return Optional.of(paths.topicPreviewPath(topicName))
+          .filter(this::pathExists)
+          .flatMap(p -> readFrom(p, new TypeReference>() {}, true))
+          .orElseGet(ArrayList::new);
+    } catch (Exception e) {
+      throw new InternalProcessingException(
+          format("Could not read latest preview message for topic: %s.", topicName.qualifiedName()),
+          e);
     }
+  }
 
-    @Override
-    public List loadPreview(TopicName topicName) {
-        try {
-            return Optional.of(paths.topicPath(topicName, ZookeeperPaths.PREVIEW_PATH))
-                    .filter(this::pathExists)
-                    .flatMap(p -> readFrom(p, new TypeReference>() {}, true))
-                    .orElseGet(ArrayList::new);
-        } catch (Exception e) {
-            throw new InternalProcessingException(
-                    format("Could not read latest preview message for topic: %s.", topicName.qualifiedName()), e);
-        }
-    }
-
-
-    @Override
-    public void persist(TopicsMessagesPreview topicsMessagesPreview) {
-        for (TopicName topic : topicsMessagesPreview.topics()) {
-            persistMessage(topic, topicsMessagesPreview.previewOf(topic));
-        }
+  @Override
+  public void persist(TopicsMessagesPreview topicsMessagesPreview) {
+    for (TopicName topic : topicsMessagesPreview.topics()) {
+      persistMessage(topic, topicsMessagesPreview.previewOf(topic));
     }
+  }
 
-    private void persistMessage(TopicName topic, List messages) {
-        logger.debug("Persisting {} messages for preview of topic: {}", messages.size(), topic.qualifiedName());
-        try {
-            if (pathExists(paths.topicPath(topic))) {
-                String previewPath = paths.topicPath(topic, ZookeeperPaths.PREVIEW_PATH);
-                ensurePathExists(previewPath);
-                overwrite(previewPath, messages);
-            }
-        } catch (Exception exception) {
-            logger.warn(
-                    format("Could not log preview messages for topic: %s", topic.qualifiedName()),
-                    exception
-            );
-        }
+  private void persistMessage(TopicName topic, List messages) {
+    logger.debug(
+        "Persisting {} messages for preview of topic: {}", messages.size(), topic.qualifiedName());
+    try {
+      if (pathExists(paths.topicPath(topic))) {
+        String previewPath = paths.topicPreviewPath(topic);
+        ensurePathExists(previewPath);
+        overwrite(previewPath, messages);
+      }
+    } catch (Exception exception) {
+      logger.warn(
+          format("Could not log preview messages for topic: %s", topic.qualifiedName()), exception);
     }
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperOAuthProviderRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperOAuthProviderRepository.java
index 4673888b4e..c84a3f6c51 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperOAuthProviderRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperOAuthProviderRepository.java
@@ -1,6 +1,9 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
+import static java.util.stream.Collectors.toList;
+
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.List;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
@@ -11,85 +14,82 @@
 import pl.allegro.tech.hermes.domain.oauth.OAuthProviderNotExistsException;
 import pl.allegro.tech.hermes.domain.oauth.OAuthProviderRepository;
 
-import java.util.List;
-
-import static java.util.stream.Collectors.toList;
-
-public class ZookeeperOAuthProviderRepository extends ZookeeperBasedRepository implements OAuthProviderRepository {
-
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperOAuthProviderRepository.class);
-
-    public ZookeeperOAuthProviderRepository(CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
-        super(zookeeper, mapper, paths);
+public class ZookeeperOAuthProviderRepository extends ZookeeperBasedRepository
+    implements OAuthProviderRepository {
+
+  private static final Logger logger =
+      LoggerFactory.getLogger(ZookeeperOAuthProviderRepository.class);
+
+  public ZookeeperOAuthProviderRepository(
+      CuratorFramework zookeeper, ObjectMapper mapper, ZookeeperPaths paths) {
+    super(zookeeper, mapper, paths);
+  }
+
+  @Override
+  public List listOAuthProviderNames() {
+    ensurePathExists(paths.oAuthProvidersPath());
+    return childrenOf(paths.oAuthProvidersPath());
+  }
+
+  @Override
+  public List listOAuthProviders() {
+    ensurePathExists(paths.oAuthProvidersPath());
+    return listOAuthProviderNames().stream().map(this::getOAuthProviderDetails).collect(toList());
+  }
+
+  @Override
+  public OAuthProvider getOAuthProviderDetails(String oAuthProviderName) {
+    ensureOAuthProviderExists(oAuthProviderName);
+    return readFrom(paths.oAuthProviderPath(oAuthProviderName), OAuthProvider.class);
+  }
+
+  @Override
+  public void createOAuthProvider(OAuthProvider oAuthProvider) {
+    String oAuthProviderPath = paths.oAuthProviderPath(oAuthProvider.getName());
+    logger.info("Creating OAuthProvider for path {}", oAuthProviderPath);
+
+    try {
+      createRecursively(oAuthProviderPath, oAuthProvider);
+    } catch (KeeperException.NodeExistsException ex) {
+      throw new OAuthProviderAlreadyExistsException(oAuthProvider, ex);
+    } catch (Exception ex) {
+      throw new InternalProcessingException(ex);
     }
+  }
 
-    @Override
-    public List listOAuthProviderNames() {
-        ensurePathExists(paths.oAuthProvidersPath());
-        return childrenOf(paths.oAuthProvidersPath());
-    }
+  @Override
+  public void updateOAuthProvider(OAuthProvider oAuthprovider) {
+    ensureOAuthProviderExists(oAuthprovider.getName());
 
-    @Override
-    public List listOAuthProviders() {
-        ensurePathExists(paths.oAuthProvidersPath());
-        return listOAuthProviderNames().stream()
-                .map(this::getOAuthProviderDetails)
-                .collect(toList());
+    logger.info("Updating OAuthProvider {}", oAuthprovider.getName());
+    try {
+      overwrite(paths.oAuthProviderPath(oAuthprovider.getName()), oAuthprovider);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
+  }
 
-    @Override
-    public OAuthProvider getOAuthProviderDetails(String oAuthProviderName) {
-        ensureOAuthProviderExists(oAuthProviderName);
-        return readFrom(paths.oAuthProviderPath(oAuthProviderName), OAuthProvider.class);
-    }
+  @Override
+  public void removeOAuthProvider(String oAuthProviderName) {
+    ensureOAuthProviderExists(oAuthProviderName);
 
-    @Override
-    public void createOAuthProvider(OAuthProvider oAuthProvider) {
-        String oAuthProviderPath = paths.oAuthProviderPath(oAuthProvider.getName());
-        logger.info("Creating OAuthProvider for path {}", oAuthProviderPath);
-
-        try {
-            createRecursively(oAuthProviderPath, oAuthProvider);
-        } catch (KeeperException.NodeExistsException ex) {
-            throw new OAuthProviderAlreadyExistsException(oAuthProvider, ex);
-        } catch (Exception ex) {
-            throw new InternalProcessingException(ex);
-        }
+    logger.info("Removing OAuthProvider {}", oAuthProviderName);
+    try {
+      remove(paths.oAuthProviderPath(oAuthProviderName));
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
+  }
 
-    @Override
-    public void updateOAuthProvider(OAuthProvider oAuthprovider) {
-        ensureOAuthProviderExists(oAuthprovider.getName());
-
-        logger.info("Updating OAuthProvider {}", oAuthprovider.getName());
-        try {
-            overwrite(paths.oAuthProviderPath(oAuthprovider.getName()), oAuthprovider);
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  @Override
+  public void ensureOAuthProviderExists(String oAuthProviderName) {
+    if (!oAuthProviderExists(oAuthProviderName)) {
+      throw new OAuthProviderNotExistsException(oAuthProviderName);
     }
+  }
 
-    @Override
-    public void removeOAuthProvider(String oAuthProviderName) {
-        ensureOAuthProviderExists(oAuthProviderName);
-
-        logger.info("Removing OAuthProvider {}", oAuthProviderName);
-        try {
-            remove(paths.oAuthProviderPath(oAuthProviderName));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
-    }
-
-    @Override
-    public void ensureOAuthProviderExists(String oAuthProviderName) {
-        if (!oAuthProviderExists(oAuthProviderName)) {
-            throw new OAuthProviderNotExistsException(oAuthProviderName);
-        }
-    }
-
-    @Override
-    public boolean oAuthProviderExists(String oAuthProviderName) {
-        return pathExists(paths.oAuthProviderPath(oAuthProviderName));
-    }
+  @Override
+  public boolean oAuthProviderExists(String oAuthProviderName) {
+    return pathExists(paths.oAuthProviderPath(oAuthProviderName));
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperPaths.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperPaths.java
index a5090a3061..e6d44c5830 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperPaths.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperPaths.java
@@ -1,7 +1,7 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
 import com.google.common.base.Joiner;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import pl.allegro.tech.hermes.api.Subscription;
 import pl.allegro.tech.hermes.api.SubscriptionName;
 import pl.allegro.tech.hermes.api.TopicName;
@@ -9,162 +9,180 @@
 
 public class ZookeeperPaths {
 
-    public static final String TOPICS_PATH = "topics";
-    public static final String GROUPS_PATH = "groups";
-    public static final String SUBSCRIPTIONS_PATH = "subscriptions";
-    public static final String KAFKA_TOPICS_PATH = "kafka_topics";
-    public static final String URL_SEPARATOR = "/";
-    public static final String CONSUMERS_WORKLOAD_PATH = "consumers-workload";
-    public static final String CONSUMER_LOAD_PATH = "consumer-load";
-    public static final String SUBSCRIPTION_PROFILES_PATH = "subscription-profiles";
-    public static final String CONSUMERS_WORKLOAD_CONSTRAINTS_PATH = "consumers-workload-constraints";
-    public static final String CONSUMERS_RATE_PATH = "consumers-rate";
-    public static final String METRICS_PATH = "metrics";
-    public static final String ADMIN_PATH = "admin";
-    public static final String PREVIEW_PATH = "preview";
-    public static final String OAUTH_PROVIDERS_PATH = "oauth-providers";
-    public static final String BLACKLIST_PATH = "blacklist";
-    public static final String MAX_RATE_PATH = "max-rate";
-    public static final String MAX_RATE_HISTORY_PATH = "history";
-    public static final String STORAGE_HEALTH_PATH = "storage-health";
-    public static final String FRONTEND_PATH = "frontend";
-    public static final String READINESS_PATH = "readiness";
-    public static final String OFFLINE_RETRANSMISSION_PATH = "offline-retransmission";
-    public static final String OFFLINE_RETRANSMISSION_TASKS_PATH = "tasks";
-
-    private final String basePath;
-
-    public ZookeeperPaths(String basePath) {
-        this.basePath = basePath;
-    }
-
-    public String basePath() {
-        return basePath;
-    }
-
-    public String extractChildNode(String fullChildPath, String prefixPath) {
-        return StringUtils.removeStart(fullChildPath, prefixPath + URL_SEPARATOR);
-    }
-
-    public String adminPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, ADMIN_PATH);
-    }
-
-    public String adminOperationPath(String operation) {
-        return Joiner.on(URL_SEPARATOR).join(adminPath(), operation);
-    }
-
-    public String groupsPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, GROUPS_PATH);
-    }
-
-    public String groupPath(String groupName) {
-        return Joiner.on(URL_SEPARATOR).join(groupsPath(), groupName);
-    }
-
-    public String topicsPath(String groupName) {
-        return Joiner.on(URL_SEPARATOR).join(groupPath(groupName), TOPICS_PATH);
-    }
-
-    public String topicMetricPath(TopicName topicName, String metricName) {
-        return topicPath(topicName, "metrics", metricName);
-    }
-
-    public String subscriptionsPath(TopicName topicName) {
-        return Joiner.on(URL_SEPARATOR).join(topicPath(topicName), SUBSCRIPTIONS_PATH);
-    }
-
-    public String topicPath(TopicName topicName, String... tail) {
-        return Joiner.on(URL_SEPARATOR).join(topicsPath(topicName.getGroupName()), topicName.getName(), (Object[]) tail);
-    }
-
-    public String subscriptionPath(TopicName topicName, String subscriptionName, String... tail) {
-        return Joiner.on(URL_SEPARATOR).join(subscriptionsPath(topicName), subscriptionName, (Object[]) tail);
-    }
-
-    public String subscriptionPath(Subscription subscription) {
-        return subscriptionPath(subscription.getTopicName(), subscription.getName());
-    }
-
-    public String subscriptionMetricsPath(TopicName topicName, String subscriptionName) {
-        return subscriptionPath(topicName, subscriptionName, METRICS_PATH);
-    }
-
-    public String subscriptionMetricPath(TopicName topicName, String subscriptionName, String metricName) {
-        return subscriptionPath(topicName, subscriptionName, METRICS_PATH, metricName);
-    }
-
-    public String subscriptionMetricPath(SubscriptionName subscriptionName, String metricName) {
-        return subscriptionPath(subscriptionName.getTopicName(), subscriptionName.getName(), METRICS_PATH, metricName);
-    }
-
-    public String offsetPath(TopicName topicName,
-                             String subscriptionName,
-                             KafkaTopicName kafkaTopicName,
-                             String brokersClusterName,
-                             int partitionId) {
-        return Joiner.on(URL_SEPARATOR).join(
-                offsetsPath(topicName, subscriptionName, kafkaTopicName, brokersClusterName),
-                partitionId);
-    }
-
-    public String offsetsPath(TopicName topicName,
-                              String subscriptionName,
-                              KafkaTopicName kafkaTopicName,
-                              String brokersClusterName) {
-        return Joiner.on(URL_SEPARATOR).join(
-                subscribedKafkaTopicsPath(topicName, subscriptionName),
-                kafkaTopicName.asString(),
-                "offset",
-                brokersClusterName);
-    }
-
-    public String subscribedKafkaTopicsPath(TopicName topicName, String subscriptionName) {
-        return Joiner.on(URL_SEPARATOR).join(subscriptionPath(topicName, subscriptionName), KAFKA_TOPICS_PATH);
-    }
-
-    public String consumersWorkloadConstraintsPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, CONSUMERS_WORKLOAD_CONSTRAINTS_PATH);
-    }
-
-    public String consumersWorkloadConstraintsPath(String constraintsPath) {
-        return Joiner.on(URL_SEPARATOR).join(consumersWorkloadConstraintsPath(), constraintsPath);
-    }
-
-    public String topicsBlacklistPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, BLACKLIST_PATH, TOPICS_PATH);
-    }
-
-    public String blacklistedTopicPath(String qualifiedTopicName) {
-        return Joiner.on(URL_SEPARATOR).join(topicsBlacklistPath(), qualifiedTopicName);
-    }
-
-    public String oAuthProvidersPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, OAUTH_PROVIDERS_PATH);
-    }
-
-    public String oAuthProviderPath(String oAuthProviderName) {
-        return Joiner.on(URL_SEPARATOR).join(oAuthProvidersPath(), oAuthProviderName);
-    }
-
-    public String nodeHealthPathForManagementHost(String host, String port) {
-        return Joiner.on(URL_SEPARATOR).join(basePath, STORAGE_HEALTH_PATH, String.format("%s_%s", host, port));
-    }
-
-    public String frontendReadinessPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, FRONTEND_PATH, READINESS_PATH);
-    }
-
-    public String offlineRetransmissionPath() {
-        return Joiner.on(URL_SEPARATOR).join(basePath, OFFLINE_RETRANSMISSION_PATH, OFFLINE_RETRANSMISSION_TASKS_PATH);
-    }
-
-    public String offlineRetransmissionPath(String taskId) {
-        return Joiner.on(URL_SEPARATOR)
-                .join(basePath, OFFLINE_RETRANSMISSION_PATH, OFFLINE_RETRANSMISSION_TASKS_PATH, taskId);
-    }
-
-    public String join(String... parts) {
-        return Joiner.on(URL_SEPARATOR).join(parts);
-    }
+  public static final String TOPICS_PATH = "topics";
+  public static final String GROUPS_PATH = "groups";
+  public static final String SUBSCRIPTIONS_PATH = "subscriptions";
+  public static final String KAFKA_TOPICS_PATH = "kafka_topics";
+  public static final String URL_SEPARATOR = "/";
+  public static final String CONSUMERS_WORKLOAD_PATH = "consumers-workload";
+  public static final String CONSUMER_LOAD_PATH = "consumer-load";
+  public static final String SUBSCRIPTION_PROFILES_PATH = "subscription-profiles";
+  public static final String CONSUMERS_WORKLOAD_CONSTRAINTS_PATH = "consumers-workload-constraints";
+  public static final String CONSUMERS_RATE_PATH = "consumers-rate";
+  public static final String METRICS_PATH = "metrics";
+  public static final String ADMIN_PATH = "admin";
+  public static final String PREVIEW_PATH = "preview";
+  public static final String OAUTH_PROVIDERS_PATH = "oauth-providers";
+  public static final String BLACKLIST_PATH = "blacklist";
+  public static final String MAX_RATE_PATH = "max-rate";
+  public static final String MAX_RATE_HISTORY_PATH = "history";
+  public static final String STORAGE_HEALTH_PATH = "storage-health";
+  public static final String DATACENTER_READINESS_PATH = "datacenter-readiness";
+  public static final String OFFLINE_RETRANSMISSION_PATH = "offline-retransmission";
+  public static final String OFFLINE_RETRANSMISSION_TASKS_PATH = "tasks";
+
+  private final String basePath;
+
+  public ZookeeperPaths(String basePath) {
+    this.basePath = basePath;
+  }
+
+  public String basePath() {
+    return basePath;
+  }
+
+  public String extractChildNode(String fullChildPath, String prefixPath) {
+    return StringUtils.removeStart(fullChildPath, prefixPath + URL_SEPARATOR);
+  }
+
+  public String adminPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, ADMIN_PATH);
+  }
+
+  public String adminOperationPath(String operation) {
+    return Joiner.on(URL_SEPARATOR).join(adminPath(), operation);
+  }
+
+  public String groupsPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, GROUPS_PATH);
+  }
+
+  public String groupPath(String groupName) {
+    return Joiner.on(URL_SEPARATOR).join(groupsPath(), groupName);
+  }
+
+  public String topicsPath(String groupName) {
+    return Joiner.on(URL_SEPARATOR).join(groupPath(groupName), TOPICS_PATH);
+  }
+
+  public String topicMetricPath(TopicName topicName, String metricName) {
+    return topicPath(topicName, "metrics", metricName);
+  }
+
+  public String subscriptionsPath(TopicName topicName) {
+    return Joiner.on(URL_SEPARATOR).join(topicPath(topicName), SUBSCRIPTIONS_PATH);
+  }
+
+  public String topicPath(TopicName topicName, String... tail) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(topicsPath(topicName.getGroupName()), topicName.getName(), (Object[]) tail);
+  }
+
+  public String topicPreviewPath(TopicName topicName) {
+    return topicPath(topicName, ZookeeperPaths.PREVIEW_PATH);
+  }
+
+  public String topicMetricsPath(TopicName topicName) {
+    return topicPath(topicName, METRICS_PATH);
+  }
+
+  public String subscriptionPath(TopicName topicName, String subscriptionName, String... tail) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(subscriptionsPath(topicName), subscriptionName, (Object[]) tail);
+  }
+
+  public String subscriptionPath(Subscription subscription) {
+    return subscriptionPath(subscription.getTopicName(), subscription.getName());
+  }
+
+  public String subscriptionMetricsPath(TopicName topicName, String subscriptionName) {
+    return subscriptionPath(topicName, subscriptionName, METRICS_PATH);
+  }
+
+  public String subscriptionMetricPath(
+      TopicName topicName, String subscriptionName, String metricName) {
+    return subscriptionPath(topicName, subscriptionName, METRICS_PATH, metricName);
+  }
+
+  public String subscriptionMetricPath(SubscriptionName subscriptionName, String metricName) {
+    return subscriptionPath(
+        subscriptionName.getTopicName(), subscriptionName.getName(), METRICS_PATH, metricName);
+  }
+
+  public String offsetPath(
+      TopicName topicName,
+      String subscriptionName,
+      KafkaTopicName kafkaTopicName,
+      String brokersClusterName,
+      int partitionId) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(
+            offsetsPath(topicName, subscriptionName, kafkaTopicName, brokersClusterName),
+            partitionId);
+  }
+
+  public String offsetsPath(
+      TopicName topicName,
+      String subscriptionName,
+      KafkaTopicName kafkaTopicName,
+      String brokersClusterName) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(
+            subscribedKafkaTopicsPath(topicName, subscriptionName),
+            kafkaTopicName.asString(),
+            "offset",
+            brokersClusterName);
+  }
+
+  public String subscribedKafkaTopicsPath(TopicName topicName, String subscriptionName) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(subscriptionPath(topicName, subscriptionName), KAFKA_TOPICS_PATH);
+  }
+
+  public String consumersWorkloadConstraintsPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, CONSUMERS_WORKLOAD_CONSTRAINTS_PATH);
+  }
+
+  public String consumersWorkloadConstraintsPath(String constraintsPath) {
+    return Joiner.on(URL_SEPARATOR).join(consumersWorkloadConstraintsPath(), constraintsPath);
+  }
+
+  public String topicsBlacklistPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, BLACKLIST_PATH, TOPICS_PATH);
+  }
+
+  public String blacklistedTopicPath(String qualifiedTopicName) {
+    return Joiner.on(URL_SEPARATOR).join(topicsBlacklistPath(), qualifiedTopicName);
+  }
+
+  public String oAuthProvidersPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, OAUTH_PROVIDERS_PATH);
+  }
+
+  public String oAuthProviderPath(String oAuthProviderName) {
+    return Joiner.on(URL_SEPARATOR).join(oAuthProvidersPath(), oAuthProviderName);
+  }
+
+  public String nodeHealthPathForManagementHost(String host, String port) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(basePath, STORAGE_HEALTH_PATH, String.format("%s_%s", host, port));
+  }
+
+  public String datacenterReadinessPath() {
+    return Joiner.on(URL_SEPARATOR).join(basePath, DATACENTER_READINESS_PATH);
+  }
+
+  public String offlineRetransmissionPath() {
+    return Joiner.on(URL_SEPARATOR)
+        .join(basePath, OFFLINE_RETRANSMISSION_PATH, OFFLINE_RETRANSMISSION_TASKS_PATH);
+  }
+
+  public String offlineRetransmissionPath(String taskId) {
+    return Joiner.on(URL_SEPARATOR)
+        .join(basePath, OFFLINE_RETRANSMISSION_PATH, OFFLINE_RETRANSMISSION_TASKS_PATH, taskId);
+  }
+
+  public String join(String... parts) {
+    return Joiner.on(URL_SEPARATOR).join(parts);
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionOffsetChangeIndicator.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionOffsetChangeIndicator.java
index 633992a8c1..0ea7cf0c18 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionOffsetChangeIndicator.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionOffsetChangeIndicator.java
@@ -1,5 +1,7 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
+import java.nio.charset.StandardCharsets;
+import java.util.List;
 import org.apache.curator.framework.CuratorFramework;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -12,149 +14,165 @@
 import pl.allegro.tech.hermes.common.kafka.offset.SubscriptionOffsetChangeIndicator;
 import pl.allegro.tech.hermes.domain.subscription.SubscriptionRepository;
 
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-public class ZookeeperSubscriptionOffsetChangeIndicator implements SubscriptionOffsetChangeIndicator {
-
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperSubscriptionOffsetChangeIndicator.class);
-
-    private final CuratorFramework zookeeper;
-
-    private final ZookeeperPaths paths;
-
-    private final SubscriptionRepository subscriptionRepository;
-
-    public ZookeeperSubscriptionOffsetChangeIndicator(
-            CuratorFramework zookeeper, ZookeeperPaths paths, SubscriptionRepository repository) {
-
-        this.zookeeper = zookeeper;
-        this.paths = paths;
-        this.subscriptionRepository = repository;
-    }
-
-    @Override
-    public void setSubscriptionOffset(TopicName topicName,
-                                      String subscriptionName,
-                                      String brokersClusterName,
-                                      PartitionOffset partitionOffset) {
-        subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName);
-
-        String offsetPath = paths.offsetPath(
-                topicName,
-                subscriptionName,
-                partitionOffset.getTopic(),
-                brokersClusterName,
-                partitionOffset.getPartition());
-        try {
-            byte[] offset = String.valueOf(partitionOffset.getOffset()).getBytes(StandardCharsets.UTF_8);
-            if (zookeeper.checkExists().forPath(offsetPath) == null) {
-                zookeeper.create()
-                        .creatingParentsIfNeeded()
-                        .forPath(offsetPath, offset);
-            } else {
-                zookeeper.setData().forPath(offsetPath, offset);
-            }
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
-    }
-
-    @Override
-    public PartitionOffsets getSubscriptionOffsets(TopicName topic, String subscriptionName, String brokersClusterName) {
-        subscriptionRepository.ensureSubscriptionExists(topic, subscriptionName);
-        String kafkaTopicsPath = paths.subscribedKafkaTopicsPath(topic, subscriptionName);
-
-        PartitionOffsets allOffsets = new PartitionOffsets();
-        getZookeeperChildrenForPath(kafkaTopicsPath).stream().map(KafkaTopicName::valueOf).forEach(kafkaTopic ->
-                allOffsets.addAll(getOffsetsForKafkaTopic(topic, kafkaTopic, subscriptionName, brokersClusterName))
-        );
-        return allOffsets;
-    }
-
-    @Override
-    public boolean areOffsetsMoved(TopicName topicName,
-                                   String subscriptionName,
-                                   String brokersClusterName,
-                                   KafkaTopic kafkaTopic,
-                                   List partitionIds) {
-        return partitionIds.stream()
-                .allMatch(partitionId -> offsetDoesNotExist(topicName, subscriptionName, brokersClusterName, partitionId, kafkaTopic));
+public class ZookeeperSubscriptionOffsetChangeIndicator
+    implements SubscriptionOffsetChangeIndicator {
+
+  private static final Logger logger =
+      LoggerFactory.getLogger(ZookeeperSubscriptionOffsetChangeIndicator.class);
+
+  private final CuratorFramework zookeeper;
+
+  private final ZookeeperPaths paths;
+
+  private final SubscriptionRepository subscriptionRepository;
+
+  public ZookeeperSubscriptionOffsetChangeIndicator(
+      CuratorFramework zookeeper, ZookeeperPaths paths, SubscriptionRepository repository) {
+
+    this.zookeeper = zookeeper;
+    this.paths = paths;
+    this.subscriptionRepository = repository;
+  }
+
+  @Override
+  public void setSubscriptionOffset(
+      TopicName topicName,
+      String subscriptionName,
+      String brokersClusterName,
+      PartitionOffset partitionOffset) {
+    subscriptionRepository.ensureSubscriptionExists(topicName, subscriptionName);
+
+    String offsetPath =
+        paths.offsetPath(
+            topicName,
+            subscriptionName,
+            partitionOffset.getTopic(),
+            brokersClusterName,
+            partitionOffset.getPartition());
+    try {
+      byte[] offset = String.valueOf(partitionOffset.getOffset()).getBytes(StandardCharsets.UTF_8);
+      if (zookeeper.checkExists().forPath(offsetPath) == null) {
+        zookeeper.create().creatingParentsIfNeeded().forPath(offsetPath, offset);
+      } else {
+        zookeeper.setData().forPath(offsetPath, offset);
+      }
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    @Override
-    public void removeOffset(TopicName topicName,
-                             String subscriptionName,
-                             String brokersClusterName,
-                             KafkaTopicName kafkaTopicName,
-                             int partitionId) {
-        String offsetPath = paths.offsetPath(topicName, subscriptionName, kafkaTopicName, brokersClusterName, partitionId);
-
-        try {
-            zookeeper.delete().guaranteed().deletingChildrenIfNeeded().forPath(offsetPath);
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  }
+
+  @Override
+  public PartitionOffsets getSubscriptionOffsets(
+      TopicName topic, String subscriptionName, String brokersClusterName) {
+    subscriptionRepository.ensureSubscriptionExists(topic, subscriptionName);
+    String kafkaTopicsPath = paths.subscribedKafkaTopicsPath(topic, subscriptionName);
+
+    PartitionOffsets allOffsets = new PartitionOffsets();
+    getZookeeperChildrenForPath(kafkaTopicsPath).stream()
+        .map(KafkaTopicName::valueOf)
+        .forEach(
+            kafkaTopic ->
+                allOffsets.addAll(
+                    getOffsetsForKafkaTopic(
+                        topic, kafkaTopic, subscriptionName, brokersClusterName)));
+    return allOffsets;
+  }
+
+  @Override
+  public boolean areOffsetsMoved(
+      TopicName topicName,
+      String subscriptionName,
+      String brokersClusterName,
+      KafkaTopic kafkaTopic,
+      List partitionIds) {
+    return partitionIds.stream()
+        .allMatch(
+            partitionId ->
+                offsetDoesNotExist(
+                    topicName, subscriptionName, brokersClusterName, partitionId, kafkaTopic));
+  }
+
+  @Override
+  public void removeOffset(
+      TopicName topicName,
+      String subscriptionName,
+      String brokersClusterName,
+      KafkaTopicName kafkaTopicName,
+      int partitionId) {
+    String offsetPath =
+        paths.offsetPath(
+            topicName, subscriptionName, kafkaTopicName, brokersClusterName, partitionId);
+
+    try {
+      zookeeper.delete().guaranteed().deletingChildrenIfNeeded().forPath(offsetPath);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    private boolean offsetDoesNotExist(TopicName topicName,
-                                       String subscriptionName,
-                                       String brokersClusterName,
-                                       Integer partitionId,
-                                       KafkaTopic kafkaTopic) {
-        String offsetPath = paths.offsetPath(topicName, subscriptionName, kafkaTopic.name(), brokersClusterName, partitionId);
-        try {
-            boolean result = zookeeper.checkExists().forPath(offsetPath) == null;
-            if (!result) {
-                logger.info("Leftover on path {}", offsetPath);
-            }
-            return result;
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  }
+
+  private boolean offsetDoesNotExist(
+      TopicName topicName,
+      String subscriptionName,
+      String brokersClusterName,
+      Integer partitionId,
+      KafkaTopic kafkaTopic) {
+    String offsetPath =
+        paths.offsetPath(
+            topicName, subscriptionName, kafkaTopic.name(), brokersClusterName, partitionId);
+    try {
+      boolean result = zookeeper.checkExists().forPath(offsetPath) == null;
+      if (!result) {
+        logger.info("Leftover on path {}", offsetPath);
+      }
+      return result;
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    private PartitionOffsets getOffsetsForKafkaTopic(TopicName topic,
-                                                     KafkaTopicName kafkaTopicName,
-                                                     String subscriptionName,
-                                                     String brokersClusterName) {
-        String offsetsPath = paths.offsetsPath(topic, subscriptionName, kafkaTopicName, brokersClusterName);
-
-        PartitionOffsets offsets = new PartitionOffsets();
-        for (String partitionAsString : getZookeeperChildrenForPath(offsetsPath)) {
-            Integer partition = Integer.valueOf(partitionAsString);
-            offsets.add(new PartitionOffset(
-                    kafkaTopicName,
-                    getOffsetForPartition(topic, kafkaTopicName, subscriptionName, brokersClusterName, partition),
-                    partition
-            ));
-        }
-        return offsets;
+  }
+
+  private PartitionOffsets getOffsetsForKafkaTopic(
+      TopicName topic,
+      KafkaTopicName kafkaTopicName,
+      String subscriptionName,
+      String brokersClusterName) {
+    String offsetsPath =
+        paths.offsetsPath(topic, subscriptionName, kafkaTopicName, brokersClusterName);
+
+    PartitionOffsets offsets = new PartitionOffsets();
+    for (String partitionAsString : getZookeeperChildrenForPath(offsetsPath)) {
+      Integer partition = Integer.valueOf(partitionAsString);
+      offsets.add(
+          new PartitionOffset(
+              kafkaTopicName,
+              getOffsetForPartition(
+                  topic, kafkaTopicName, subscriptionName, brokersClusterName, partition),
+              partition));
     }
-
-    private List getZookeeperChildrenForPath(String path) {
-        try {
-            return zookeeper.getChildren().forPath(path);
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+    return offsets;
+  }
+
+  private List getZookeeperChildrenForPath(String path) {
+    try {
+      return zookeeper.getChildren().forPath(path);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    private Long getOffsetForPartition(TopicName topic,
-                                       KafkaTopicName kafkaTopicName,
-                                       String subscriptionName,
-                                       String brokersClusterName,
-                                       int partitionId) {
-        try {
-            String offsetPath = paths.offsetPath(topic,
-                    subscriptionName,
-                    kafkaTopicName,
-                    brokersClusterName,
-                    partitionId);
-            return Long.valueOf(new String(zookeeper.getData().forPath(offsetPath), StandardCharsets.UTF_8));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  }
+
+  private Long getOffsetForPartition(
+      TopicName topic,
+      KafkaTopicName kafkaTopicName,
+      String subscriptionName,
+      String brokersClusterName,
+      int partitionId) {
+    try {
+      String offsetPath =
+          paths.offsetPath(
+              topic, subscriptionName, kafkaTopicName, brokersClusterName, partitionId);
+      return Long.valueOf(
+          new String(zookeeper.getData().forPath(offsetPath), StandardCharsets.UTF_8));
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionRepository.java
index 104fabb23d..59221b04e6 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperSubscriptionRepository.java
@@ -1,6 +1,10 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
@@ -14,142 +18,146 @@
 import pl.allegro.tech.hermes.domain.subscription.SubscriptionRepository;
 import pl.allegro.tech.hermes.domain.topic.TopicRepository;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-public class ZookeeperSubscriptionRepository extends ZookeeperBasedRepository implements SubscriptionRepository {
-
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperSubscriptionRepository.class);
-
-    private final TopicRepository topicRepository;
-
-    public ZookeeperSubscriptionRepository(CuratorFramework zookeeper,
-                                           ObjectMapper mapper,
-                                           ZookeeperPaths paths,
-                                           TopicRepository topicRepository) {
-        super(zookeeper, mapper, paths);
-        this.topicRepository = topicRepository;
-    }
-
-    @Override
-    public boolean subscriptionExists(TopicName topicName, String subscriptionName) {
-        return pathExists(paths.subscriptionPath(topicName, subscriptionName));
-    }
-
-    @Override
-    public void ensureSubscriptionExists(TopicName topicName, String subscriptionName) {
-        if (!subscriptionExists(topicName, subscriptionName)) {
-            throw new SubscriptionNotExistsException(topicName, subscriptionName);
-        }
-    }
-
-    @Override
-    public void createSubscription(Subscription subscription) {
-        topicRepository.ensureTopicExists(subscription.getTopicName());
-
-        String subscriptionPath = paths.subscriptionPath(subscription);
-        logger.info("Creating subscription {}", subscription.getQualifiedName());
-
-        try {
-            create(subscriptionPath, subscription);
-        } catch (KeeperException.NodeExistsException ex) {
-            throw new SubscriptionAlreadyExistsException(subscription, ex);
-        } catch (Exception ex) {
-            throw new InternalProcessingException(ex);
-        }
-    }
-
-    @Override
-    public void removeSubscription(TopicName topicName, String subscriptionName) {
-        ensureSubscriptionExists(topicName, subscriptionName);
-        logger.info("Removing subscription {}", new SubscriptionName(subscriptionName, topicName).getQualifiedName());
-
-        try {
-            remove(paths.subscriptionPath(topicName, subscriptionName));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
-    }
+public class ZookeeperSubscriptionRepository extends ZookeeperBasedRepository
+    implements SubscriptionRepository {
 
-    @Override
-    public void updateSubscription(Subscription modifiedSubscription) {
-        ensureSubscriptionExists(modifiedSubscription.getTopicName(), modifiedSubscription.getName());
-        logger.info("Updating subscription {}", modifiedSubscription.getQualifiedName());
-        try {
-            overwrite(paths.subscriptionPath(modifiedSubscription), modifiedSubscription);
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
-    }
+  private static final Logger logger =
+      LoggerFactory.getLogger(ZookeeperSubscriptionRepository.class);
 
-    @Override
-    public void updateSubscriptionState(TopicName topicName, String subscriptionName, Subscription.State state) {
-        ensureSubscriptionExists(topicName, subscriptionName);
+  private final TopicRepository topicRepository;
 
-        logger.info("Changing subscription {} state to {}",
-                new SubscriptionName(subscriptionName, topicName).getQualifiedName(), state.toString());
+  public ZookeeperSubscriptionRepository(
+      CuratorFramework zookeeper,
+      ObjectMapper mapper,
+      ZookeeperPaths paths,
+      TopicRepository topicRepository) {
+    super(zookeeper, mapper, paths);
+    this.topicRepository = topicRepository;
+  }
 
-        Subscription modifiedSubscription = getSubscriptionDetails(topicName, subscriptionName);
-        if (!modifiedSubscription.getState().equals(state)) {
-            modifiedSubscription.setState(state);
-            updateSubscription(modifiedSubscription);
-        }
-    }
+  @Override
+  public boolean subscriptionExists(TopicName topicName, String subscriptionName) {
+    return pathExists(paths.subscriptionPath(topicName, subscriptionName));
+  }
 
-    @Override
-    public Subscription getSubscriptionDetails(TopicName topicName, String subscriptionName) {
-        ensureSubscriptionExists(topicName, subscriptionName);
-        return readWithStatFrom(
-                paths.subscriptionPath(topicName, subscriptionName),
-                Subscription.class,
-                (sub, stat) -> {
-                    sub.setCreatedAt(stat.getCtime());
-                    sub.setModifiedAt(stat.getMtime());
-                },
-                false
-        ).get();
+  @Override
+  public void ensureSubscriptionExists(TopicName topicName, String subscriptionName) {
+    if (!subscriptionExists(topicName, subscriptionName)) {
+      throw new SubscriptionNotExistsException(topicName, subscriptionName);
     }
+  }
 
-    private Optional getSubscriptionDetails(TopicName topicName, String subscriptionName, boolean quiet) {
-        ensureSubscriptionExists(topicName, subscriptionName);
-        return readFrom(paths.subscriptionPath(topicName, subscriptionName), Subscription.class, quiet);
-    }
+  @Override
+  public void createSubscription(Subscription subscription) {
+    topicRepository.ensureTopicExists(subscription.getTopicName());
 
-    @Override
-    public Subscription getSubscriptionDetails(SubscriptionName name) {
-        return getSubscriptionDetails(name.getTopicName(), name.getName());
-    }
+    String subscriptionPath = paths.subscriptionPath(subscription);
+    logger.info("Creating subscription {}", subscription.getQualifiedName());
 
-    @Override
-    public List getSubscriptionDetails(Collection subscriptionNames) {
-        return subscriptionNames.stream()
-                .map(n -> getSubscriptionDetails(n.getTopicName(), n.getName()))
-                .collect(Collectors.toList());
+    try {
+      create(subscriptionPath, subscription);
+    } catch (KeeperException.NodeExistsException ex) {
+      throw new SubscriptionAlreadyExistsException(subscription, ex);
+    } catch (Exception ex) {
+      throw new InternalProcessingException(ex);
     }
-
-    @Override
-    public List listSubscriptionNames(TopicName topicName) {
-        topicRepository.ensureTopicExists(topicName);
-
-        return childrenOf(paths.subscriptionsPath(topicName));
+  }
+
+  @Override
+  public void removeSubscription(TopicName topicName, String subscriptionName) {
+    ensureSubscriptionExists(topicName, subscriptionName);
+    logger.info(
+        "Removing subscription {}",
+        new SubscriptionName(subscriptionName, topicName).getQualifiedName());
+
+    try {
+      remove(paths.subscriptionPath(topicName, subscriptionName));
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    @Override
-    public List listSubscriptions(TopicName topicName) {
-        return listSubscriptionNames(topicName).stream()
-                .map(subscription -> getSubscriptionDetails(topicName, subscription, true))
-                .filter(Optional::isPresent)
-                .map(Optional::get)
-                .collect(Collectors.toList());
+  }
+
+  @Override
+  public void updateSubscription(Subscription modifiedSubscription) {
+    ensureSubscriptionExists(modifiedSubscription.getTopicName(), modifiedSubscription.getName());
+    logger.info("Updating subscription {}", modifiedSubscription.getQualifiedName());
+    try {
+      overwrite(paths.subscriptionPath(modifiedSubscription), modifiedSubscription);
+    } catch (Exception e) {
+      throw new InternalProcessingException(e);
     }
-
-    @Override
-    public List listAllSubscriptions() {
-        return topicRepository.listAllTopics().stream()
-                .map(topic -> listSubscriptions(topic.getName()))
-                .flatMap(Collection::stream)
-                .collect(Collectors.toList());
+  }
+
+  @Override
+  public void updateSubscriptionState(
+      TopicName topicName, String subscriptionName, Subscription.State state) {
+    ensureSubscriptionExists(topicName, subscriptionName);
+
+    logger.info(
+        "Changing subscription {} state to {}",
+        new SubscriptionName(subscriptionName, topicName).getQualifiedName(),
+        state.toString());
+
+    Subscription modifiedSubscription = getSubscriptionDetails(topicName, subscriptionName);
+    if (!modifiedSubscription.getState().equals(state)) {
+      modifiedSubscription.setState(state);
+      updateSubscription(modifiedSubscription);
     }
+  }
+
+  @Override
+  public Subscription getSubscriptionDetails(TopicName topicName, String subscriptionName) {
+    ensureSubscriptionExists(topicName, subscriptionName);
+    return readWithStatFrom(
+            paths.subscriptionPath(topicName, subscriptionName),
+            Subscription.class,
+            (sub, stat) -> {
+              sub.setCreatedAt(stat.getCtime());
+              sub.setModifiedAt(stat.getMtime());
+            },
+            false)
+        .get();
+  }
+
+  private Optional getSubscriptionDetails(
+      TopicName topicName, String subscriptionName, boolean quiet) {
+    ensureSubscriptionExists(topicName, subscriptionName);
+    return readFrom(paths.subscriptionPath(topicName, subscriptionName), Subscription.class, quiet);
+  }
+
+  @Override
+  public Subscription getSubscriptionDetails(SubscriptionName name) {
+    return getSubscriptionDetails(name.getTopicName(), name.getName());
+  }
+
+  @Override
+  public List getSubscriptionDetails(Collection subscriptionNames) {
+    return subscriptionNames.stream()
+        .map(n -> getSubscriptionDetails(n.getTopicName(), n.getName()))
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List listSubscriptionNames(TopicName topicName) {
+    topicRepository.ensureTopicExists(topicName);
+
+    return childrenOf(paths.subscriptionsPath(topicName));
+  }
+
+  @Override
+  public List listSubscriptions(TopicName topicName) {
+    return listSubscriptionNames(topicName).stream()
+        .map(subscription -> getSubscriptionDetails(topicName, subscription, true))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public List listAllSubscriptions() {
+    return topicRepository.listAllTopics().stream()
+        .map(topic -> listSubscriptions(topic.getName()))
+        .flatMap(Collection::stream)
+        .collect(Collectors.toList());
+  }
 }
diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepository.java
index 60dcb49bc5..7e840b4aed 100644
--- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepository.java
+++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepository.java
@@ -1,6 +1,11 @@
 package pl.allegro.tech.hermes.infrastructure.zookeeper;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
@@ -13,138 +18,194 @@
 import pl.allegro.tech.hermes.domain.topic.TopicNotExistsException;
 import pl.allegro.tech.hermes.domain.topic.TopicRepository;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
 public class ZookeeperTopicRepository extends ZookeeperBasedRepository implements TopicRepository {
 
-    private static final Logger logger = LoggerFactory.getLogger(ZookeeperTopicRepository.class);
-
-    private final GroupRepository groupRepository;
-
-    public ZookeeperTopicRepository(CuratorFramework zookeeper,
-                                    ObjectMapper mapper,
-                                    ZookeeperPaths paths,
-                                    GroupRepository groupRepository) {
-        super(zookeeper, mapper, paths);
-        this.groupRepository = groupRepository;
-    }
-
-
-    @Override
-    public boolean topicExists(TopicName topicName) {
-        return pathExists(paths.topicPath(topicName));
-    }
+  private static final Logger logger = LoggerFactory.getLogger(ZookeeperTopicRepository.class);
 
-    @Override
-    public void ensureTopicExists(TopicName topicName) {
-        if (!topicExists(topicName)) {
-            throw new TopicNotExistsException(topicName);
-        }
-    }
+  private final GroupRepository groupRepository;
 
-    @Override
-    public List listTopicNames(String groupName) {
-        groupRepository.ensureGroupExists(groupName);
+  public ZookeeperTopicRepository(
+      CuratorFramework zookeeper,
+      ObjectMapper mapper,
+      ZookeeperPaths paths,
+      GroupRepository groupRepository) {
+    super(zookeeper, mapper, paths);
+    this.groupRepository = groupRepository;
+  }
 
-        return childrenOf(paths.topicsPath(groupName));
-    }
+  @Override
+  public boolean topicExists(TopicName topicName) {
+    return pathExists(paths.topicPath(topicName));
+  }
 
-    @Override
-    public List listTopics(String groupName) {
-        return listTopicNames(groupName).stream()
-                .map(name -> getTopicDetails(new TopicName(groupName, name), true))
-                .filter(Optional::isPresent)
-                .map(Optional::get)
-                .collect(Collectors.toList());
+  @Override
+  public void ensureTopicExists(TopicName topicName) {
+    if (!topicExists(topicName)) {
+      throw new TopicNotExistsException(topicName);
     }
-
-    @Override
-    public void createTopic(Topic topic) {
-        groupRepository.ensureGroupExists(topic.getName().getGroupName());
-
-        String topicPath = paths.topicPath(topic.getName());
-        logger.info("Creating topic for path {}", topicPath);
-
-        try {
-            createInTransaction(topicPath, topic, paths.subscriptionsPath(topic.getName()));
-        } catch (KeeperException.NodeExistsException ex) {
-            throw new TopicAlreadyExistsException(topic.getName(), ex);
-        } catch (Exception ex) {
-            throw new InternalProcessingException(ex);
-        }
+  }
+
+  @Override
+  public List listTopicNames(String groupName) {
+    groupRepository.ensureGroupExists(groupName);
+
+    return childrenOf(paths.topicsPath(groupName));
+  }
+
+  @Override
+  public List listTopics(String groupName) {
+    return listTopicNames(groupName).stream()
+        .map(name -> getTopicDetails(new TopicName(groupName, name), true))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .collect(Collectors.toList());
+  }
+
+  @Override
+  public void createTopic(Topic topic) {
+    groupRepository.ensureGroupExists(topic.getName().getGroupName());
+
+    String topicPath = paths.topicPath(topic.getName());
+    logger.info("Creating topic for path {}", topicPath);
+
+    try {
+      createInTransaction(topicPath, topic, paths.subscriptionsPath(topic.getName()));
+    } catch (KeeperException.NodeExistsException ex) {
+      throw new TopicAlreadyExistsException(topic.getName(), ex);
+    } catch (Exception ex) {
+      throw new InternalProcessingException(ex);
     }
-
-    @Override
-    public void removeTopic(TopicName topicName) {
-        ensureTopicExists(topicName);
-        logger.info("Removing topic: " + topicName);
-        try {
-            remove(paths.topicPath(topicName));
-        } catch (Exception e) {
-            throw new InternalProcessingException(e);
-        }
+  }
+
+  /**
+   * To remove topic node, we must remove topic node and its children. The tree looks like this:
+   *
+   * 
    + *
  • - topic + *
  • ----- /subscriptions (required) + *
  • ----- /preview (optional) + *
  • ----- /metrics (optional) + *
  • --------------- /volume + *
  • --------------- /published + *
+ * + *

One way to remove the whole tree for topic that would be to use + * deletingChildrenIfNeeded(): e.g. + * zookeeper.delete().deletingChildrenIfNeeded().forPath(topicPath). However, + * deletingChildrenIfNeeded is not atomic. It first tries to remove the node topic + * and upon receiving KeeperException.NotEmptyException it tries to remove + * children recursively and then retries the node removal. This means that there is a potentially + * large time gap between removal of topic/subscriptions node and topic + * node, especially when topic removal is being done in remote DC. + * + *

It turns out that PathChildrenCache used by HierarchicalCacheLevel + * in Consumers and Frontend listens for topics/subscriptions changes and recreates + * that node when deleted. If the recreation happens between the topic/subscriptions + * and topic node removal than the whole removal process must be repeated resulting + * in a lengthy loop that may even result in StackOverflowException. Example of that + * scenario would be + * + *

    + *
  1. DELETE topic - issued by management, fails with + * KeeperException.NotEmptyException + *
  2. DELETE topic/subscriptions - issued by management, succeeds + *
  3. CREATE topic/subscriptions - issued by frontend, succeeds + *
  4. DELETE topic - issued by management, fails with + * KeeperException.NotEmptyException + *
  5. [...] + *
+ * + *

To solve this we must remove topic and topic/subscriptions + * atomically. However, we must also remove other topic children. Transaction API + * does not allow for optional deletes so we: + * + *

    + *
  1. find all children paths + *
  2. delete all children in one transaction + *
+ */ + @Override + public void removeTopic(TopicName topicName) { + ensureTopicExists(topicName); + logger.info("Removing topic: " + topicName); + + List pathsForRemoval = new ArrayList<>(); + String topicMetricsPath = paths.topicMetricsPath(topicName); + if (pathExists(topicMetricsPath)) { + pathsForRemoval.addAll(childrenPathsOf(topicMetricsPath)); + pathsForRemoval.add(topicMetricsPath); } - @Override - public void updateTopic(Topic topic) { - ensureTopicExists(topic.getName()); - - logger.info("Updating topic: " + topic.getName()); - try { - overwrite(paths.topicPath(topic.getName()), topic); - } catch (Exception e) { - throw new InternalProcessingException(e); - } + String topicPreviewPath = paths.topicPreviewPath(topicName); + if (pathExists(topicPreviewPath)) { + pathsForRemoval.add(topicPreviewPath); } - @Override - public void touchTopic(TopicName topicName) { - ensureTopicExists(topicName); + pathsForRemoval.add(paths.subscriptionsPath(topicName)); + pathsForRemoval.add(paths.topicPath(topicName)); - logger.info("Touching topic: " + topicName.qualifiedName()); - try { - touch(paths.topicPath(topicName)); - } catch (Exception ex) { - throw new InternalProcessingException(ex); - } + try { + deleteInTransaction(pathsForRemoval); + } catch (Exception e) { + throw new InternalProcessingException(e); } + } - @Override - public Topic getTopicDetails(TopicName topicName) { - return getTopicDetails(topicName, false).get(); - } + @Override + public void updateTopic(Topic topic) { + ensureTopicExists(topic.getName()); - private Optional getTopicDetails(TopicName topicName, boolean quiet) { - ensureTopicExists(topicName); - return readWithStatFrom( - paths.topicPath(topicName), - Topic.class, - (topic, stat) -> { - topic.setCreatedAt(stat.getCtime()); - topic.setModifiedAt(stat.getMtime()); - }, - quiet - ); + logger.info("Updating topic: " + topic.getName()); + try { + overwrite(paths.topicPath(topic.getName()), topic); + } catch (Exception e) { + throw new InternalProcessingException(e); } + } - @Override - public List getTopicsDetails(Collection topicNames) { - return topicNames.stream() - .map(topicName -> getTopicDetails(topicName, true)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); - } + @Override + public void touchTopic(TopicName topicName) { + ensureTopicExists(topicName); - @Override - public List listAllTopics() { - return groupRepository.listGroupNames() - .stream() - .map(this::listTopics) - .flatMap(List::stream) - .collect(Collectors.toList()); + logger.info("Touching topic: " + topicName.qualifiedName()); + try { + touch(paths.topicPath(topicName)); + } catch (Exception ex) { + throw new InternalProcessingException(ex); } + } + + @Override + public Topic getTopicDetails(TopicName topicName) { + return getTopicDetails(topicName, false).get(); + } + + private Optional getTopicDetails(TopicName topicName, boolean quiet) { + ensureTopicExists(topicName); + return readWithStatFrom( + paths.topicPath(topicName), + Topic.class, + (topic, stat) -> { + topic.setCreatedAt(stat.getCtime()); + topic.setModifiedAt(stat.getMtime()); + }, + quiet); + } + + @Override + public List getTopicsDetails(Collection topicNames) { + return topicNames.stream() + .map(topicName -> getTopicDetails(topicName, true)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } + + @Override + public List listAllTopics() { + return groupRepository.listGroupNames().stream() + .map(this::listTopics) + .flatMap(List::stream) + .collect(Collectors.toList()); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCache.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCache.java index b3d7c6ce33..6f6a821981 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCache.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCache.java @@ -1,6 +1,9 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; @@ -12,83 +15,87 @@ import pl.allegro.tech.hermes.api.TopicName; import pl.allegro.tech.hermes.domain.workload.constraints.ConsumersWorkloadConstraints; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; +class ZookeeperWorkloadConstraintsCache extends PathChildrenCache + implements PathChildrenCacheListener { -class ZookeeperWorkloadConstraintsCache extends PathChildrenCache implements PathChildrenCacheListener { + private static final Logger logger = + LoggerFactory.getLogger(ZookeeperWorkloadConstraintsCache.class); - private static final Logger logger = LoggerFactory.getLogger(ZookeeperWorkloadConstraintsCache.class); + private final Map topicConstraintsCache = new ConcurrentHashMap<>(); + private final Map subscriptionConstraintsCache = + new ConcurrentHashMap<>(); + private final ObjectMapper objectMapper; + private final ZookeeperPaths paths; - private final Map topicConstraintsCache = new ConcurrentHashMap<>(); - private final Map subscriptionConstraintsCache = new ConcurrentHashMap<>(); - private final ObjectMapper objectMapper; - private final ZookeeperPaths paths; + ZookeeperWorkloadConstraintsCache( + CuratorFramework curatorFramework, ObjectMapper objectMapper, ZookeeperPaths paths) { + super(curatorFramework, paths.consumersWorkloadConstraintsPath(), true); + this.objectMapper = objectMapper; + this.paths = paths; + getListenable().addListener(this); + } - ZookeeperWorkloadConstraintsCache(CuratorFramework curatorFramework, ObjectMapper objectMapper, ZookeeperPaths paths) { - super(curatorFramework, paths.consumersWorkloadConstraintsPath(), true); - this.objectMapper = objectMapper; - this.paths = paths; - getListenable().addListener(this); - } + ConsumersWorkloadConstraints getConsumersWorkloadConstraints() { + return new ConsumersWorkloadConstraints(topicConstraintsCache, subscriptionConstraintsCache); + } - ConsumersWorkloadConstraints getConsumersWorkloadConstraints() { - return new ConsumersWorkloadConstraints(topicConstraintsCache, subscriptionConstraintsCache); + @Override + public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) { + switch (event.getType()) { + case CHILD_ADDED: + updateCache(event.getData().getPath(), event.getData().getData()); + break; + case CHILD_REMOVED: + removeFromCache(event.getData().getPath()); + break; + case CHILD_UPDATED: + updateCache(event.getData().getPath(), event.getData().getData()); + break; + default: + break; } + } - @Override - public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) { - switch (event.getType()) { - case CHILD_ADDED: - updateCache(event.getData().getPath(), event.getData().getData()); - break; - case CHILD_REMOVED: - removeFromCache(event.getData().getPath()); - break; - case CHILD_UPDATED: - updateCache(event.getData().getPath(), event.getData().getData()); - break; - default: - break; - } + private void updateCache(String path, byte[] bytes) { + Optional constraints = bytesToConstraints(bytes, path); + if (!constraints.isPresent()) { + return; } - - private void updateCache(String path, byte[] bytes) { - Optional constraints = bytesToConstraints(bytes, path); - if (!constraints.isPresent()) { - return; - } - if (isSubscription(path)) { - subscriptionConstraintsCache.put( - SubscriptionName.fromString(paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath())), - constraints.get()); - } else { - topicConstraintsCache.put( - TopicName.fromQualifiedName(paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath())), - constraints.get()); - } + if (isSubscription(path)) { + subscriptionConstraintsCache.put( + SubscriptionName.fromString( + paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath())), + constraints.get()); + } else { + topicConstraintsCache.put( + TopicName.fromQualifiedName( + paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath())), + constraints.get()); } + } - private Optional bytesToConstraints(byte[] bytes, String path) { - try { - return Optional.ofNullable(objectMapper.readValue(bytes, Constraints.class)); - } catch (Exception e) { - logger.error("Cannot read data from node: {}", path, e); - return Optional.empty(); - } + private Optional bytesToConstraints(byte[] bytes, String path) { + try { + return Optional.ofNullable(objectMapper.readValue(bytes, Constraints.class)); + } catch (Exception e) { + logger.error("Cannot read data from node: {}", path, e); + return Optional.empty(); } + } - private void removeFromCache(String path) { - if (isSubscription(path)) { - subscriptionConstraintsCache.remove( - SubscriptionName.fromString(paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath()))); - } else { - topicConstraintsCache.remove( - TopicName.fromQualifiedName(paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath()))); - } + private void removeFromCache(String path) { + if (isSubscription(path)) { + subscriptionConstraintsCache.remove( + SubscriptionName.fromString( + paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath()))); + } else { + topicConstraintsCache.remove( + TopicName.fromQualifiedName( + paths.extractChildNode(path, paths.consumersWorkloadConstraintsPath()))); } + } - private boolean isSubscription(String path) { - return path.contains("$"); - } + private boolean isSubscription(String path) { + return path.contains("$"); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepository.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepository.java index c1a0ad6f5f..6f1ba031e7 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepository.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepository.java @@ -16,123 +16,130 @@ import pl.allegro.tech.hermes.domain.workload.constraints.TopicConstraintsDoNotExistException; import pl.allegro.tech.hermes.domain.workload.constraints.WorkloadConstraintsRepository; -public class ZookeeperWorkloadConstraintsRepository extends ZookeeperBasedRepository implements WorkloadConstraintsRepository { - - private static final Logger logger = LoggerFactory.getLogger(ZookeeperWorkloadConstraintsRepository.class); - - private final ZookeeperWorkloadConstraintsCache pathChildrenCache; - - public ZookeeperWorkloadConstraintsRepository(CuratorFramework curator, ObjectMapper mapper, ZookeeperPaths paths) { - this(curator, mapper, paths, new ZookeeperWorkloadConstraintsCache(curator, mapper, paths)); +public class ZookeeperWorkloadConstraintsRepository extends ZookeeperBasedRepository + implements WorkloadConstraintsRepository { + + private static final Logger logger = + LoggerFactory.getLogger(ZookeeperWorkloadConstraintsRepository.class); + + private final ZookeeperWorkloadConstraintsCache pathChildrenCache; + + public ZookeeperWorkloadConstraintsRepository( + CuratorFramework curator, ObjectMapper mapper, ZookeeperPaths paths) { + this(curator, mapper, paths, new ZookeeperWorkloadConstraintsCache(curator, mapper, paths)); + } + + ZookeeperWorkloadConstraintsRepository( + CuratorFramework curator, + ObjectMapper mapper, + ZookeeperPaths paths, + ZookeeperWorkloadConstraintsCache pathChildrenCache) { + super(curator, mapper, paths); + this.pathChildrenCache = pathChildrenCache; + try { + this.pathChildrenCache.start(); + } catch (Exception e) { + throw new InternalProcessingException("ZookeeperWorkloadConstraintsCache cannot start.", e); } - - ZookeeperWorkloadConstraintsRepository(CuratorFramework curator, ObjectMapper mapper, ZookeeperPaths paths, - ZookeeperWorkloadConstraintsCache pathChildrenCache) { - super(curator, mapper, paths); - this.pathChildrenCache = pathChildrenCache; - try { - this.pathChildrenCache.start(); - } catch (Exception e) { - throw new InternalProcessingException("ZookeeperWorkloadConstraintsCache cannot start.", e); - } + } + + @Override + public ConsumersWorkloadConstraints getConsumersWorkloadConstraints() { + return pathChildrenCache.getConsumersWorkloadConstraints(); + } + + @Override + public void createConstraints(TopicName topicName, Constraints constraints) { + logger.info("Creating constraints for topic {}", topicName.qualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); + try { + createConstraints(path, constraints); + } catch (KeeperException.NodeExistsException e) { + throw new TopicConstraintsAlreadyExistException(topicName, e); } - - @Override - public ConsumersWorkloadConstraints getConsumersWorkloadConstraints() { - return pathChildrenCache.getConsumersWorkloadConstraints(); + } + + @Override + public void createConstraints(SubscriptionName subscriptionName, Constraints constraints) { + logger.info("Creating constraints for subscription {}", subscriptionName.getQualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); + try { + createConstraints(path, constraints); + } catch (KeeperException.NodeExistsException e) { + throw new SubscriptionConstraintsAlreadyExistException(subscriptionName, e); } - - @Override - public void createConstraints(TopicName topicName, Constraints constraints) { - logger.info("Creating constraints for topic {}", topicName.qualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); - try { - createConstraints(path, constraints); - } catch (KeeperException.NodeExistsException e) { - throw new TopicConstraintsAlreadyExistException(topicName, e); - } + } + + private void createConstraints(String path, Constraints constraints) + throws KeeperException.NodeExistsException { + try { + createRecursively(path, constraints); + } catch (KeeperException.NodeExistsException e) { + throw e; + } catch (Exception e) { + throw new InternalProcessingException(e); } - - @Override - public void createConstraints(SubscriptionName subscriptionName, Constraints constraints) { - logger.info("Creating constraints for subscription {}", subscriptionName.getQualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); - try { - createConstraints(path, constraints); - } catch (KeeperException.NodeExistsException e) { - throw new SubscriptionConstraintsAlreadyExistException(subscriptionName, e); - } + } + + @Override + public void updateConstraints(TopicName topicName, Constraints constraints) { + logger.info("Updating constraints for topic {}", topicName.qualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); + try { + overwrite(path, constraints); + } catch (KeeperException.NoNodeException e) { + throw new TopicConstraintsDoNotExistException(topicName, e); + } catch (Exception e) { + throw new InternalProcessingException(e); } - - private void createConstraints(String path, Constraints constraints) throws KeeperException.NodeExistsException { - try { - createRecursively(path, constraints); - } catch (KeeperException.NodeExistsException e) { - throw e; - } catch (Exception e) { - throw new InternalProcessingException(e); - } + } + + @Override + public void updateConstraints(SubscriptionName subscriptionName, Constraints constraints) { + logger.info("Updating constraints for subscription {}", subscriptionName.getQualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); + try { + overwrite(path, constraints); + } catch (KeeperException.NoNodeException e) { + throw new SubscriptionConstraintsDoNotExistException(subscriptionName, e); + } catch (Exception e) { + throw new InternalProcessingException(e); } - - @Override - public void updateConstraints(TopicName topicName, Constraints constraints) { - logger.info("Updating constraints for topic {}", topicName.qualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); - try { - overwrite(path, constraints); - } catch (KeeperException.NoNodeException e) { - throw new TopicConstraintsDoNotExistException(topicName, e); - } catch (Exception e) { - throw new InternalProcessingException(e); - } - } - - @Override - public void updateConstraints(SubscriptionName subscriptionName, Constraints constraints) { - logger.info("Updating constraints for subscription {}", subscriptionName.getQualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); - try { - overwrite(path, constraints); - } catch (KeeperException.NoNodeException e) { - throw new SubscriptionConstraintsDoNotExistException(subscriptionName, e); - } catch (Exception e) { - throw new InternalProcessingException(e); - } - } - - @Override - public void deleteConstraints(TopicName topicName) { - logger.info("Deleting constraints for topic {}", topicName.qualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); - deleteConstraints(path); - } - - @Override - public void deleteConstraints(SubscriptionName subscriptionName) { - logger.info("Deleting constraints for subscription {}", subscriptionName.getQualifiedName()); - String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); - deleteConstraints(path); - } - - private void deleteConstraints(String path) { - try { - remove(path); - } catch (KeeperException.NoNodeException e) { - // ignore - it's ok - } catch (Exception e) { - throw new InternalProcessingException(e); - } - } - - @Override - public boolean constraintsExist(TopicName topicName) { - String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); - return pathExists(path); - } - - @Override - public boolean constraintsExist(SubscriptionName subscriptionName) { - String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); - return pathExists(path); + } + + @Override + public void deleteConstraints(TopicName topicName) { + logger.info("Deleting constraints for topic {}", topicName.qualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); + deleteConstraints(path); + } + + @Override + public void deleteConstraints(SubscriptionName subscriptionName) { + logger.info("Deleting constraints for subscription {}", subscriptionName.getQualifiedName()); + String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); + deleteConstraints(path); + } + + private void deleteConstraints(String path) { + try { + remove(path); + } catch (KeeperException.NoNodeException e) { + // ignore - it's ok + } catch (Exception e) { + throw new InternalProcessingException(e); } + } + + @Override + public boolean constraintsExist(TopicName topicName) { + String path = paths.consumersWorkloadConstraintsPath(topicName.qualifiedName()); + return pathExists(path); + } + + @Override + public boolean constraintsExist(SubscriptionName subscriptionName) { + String path = paths.consumersWorkloadConstraintsPath(subscriptionName.getQualifiedName()); + return pathExists(path); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/CacheListeners.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/CacheListeners.java index 69d0292b64..fc3c15bf33 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/CacheListeners.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/CacheListeners.java @@ -1,31 +1,33 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.cache; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class CacheListeners { - private static final Logger logger = LoggerFactory.getLogger(CacheListeners.class); + private static final Logger logger = LoggerFactory.getLogger(CacheListeners.class); - private final Queue> callbacks = new ConcurrentLinkedQueue<>(); + private final Queue> callbacks = new ConcurrentLinkedQueue<>(); - void addListener(Consumer callback) { - callbacks.add(callback); - } + void addListener(Consumer callback) { + callbacks.add(callback); + } - void call(PathChildrenCacheEvent event) { - for (Consumer callback : callbacks) { - try { - callback.accept(event); - } catch (Exception exception) { - logger.error("Failed to run callback action {} for event with data: {}", - callback.getClass().getSimpleName(), event.getData(), exception); - } - } + void call(PathChildrenCacheEvent event) { + for (Consumer callback : callbacks) { + try { + callback.accept(event); + } catch (Exception exception) { + logger.error( + "Failed to run callback action {} for event with data: {}", + callback.getClass().getSimpleName(), + event.getData(), + exception); + } } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCache.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCache.java index 23a7952f79..4f79d10549 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCache.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCache.java @@ -1,102 +1,109 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.cache; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.apache.zookeeper.KeeperException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import pl.allegro.tech.hermes.common.exception.InternalProcessingException; - import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.BiFunction; import java.util.function.Consumer; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.zookeeper.KeeperException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import pl.allegro.tech.hermes.common.exception.InternalProcessingException; public class HierarchicalCache { - private static final Logger logger = LoggerFactory.getLogger(HierarchicalCache.class); + private static final Logger logger = LoggerFactory.getLogger(HierarchicalCache.class); - private final CuratorFramework curatorFramework; + private final CuratorFramework curatorFramework; - private final ExecutorService executorService; + private final ExecutorService executorService; - private final String basePath; - private final boolean removeNodesWithNoData; + private final String basePath; + private final boolean removeNodesWithNoData; - private final List levelPrefixes = new ArrayList<>(); + private final List levelPrefixes = new ArrayList<>(); - private final int maxDepth; + private final int maxDepth; - private final List levelCallbacks = new ArrayList<>(); + private final List levelCallbacks = new ArrayList<>(); - private HierarchicalCacheLevel rootCache; + private HierarchicalCacheLevel rootCache; - public HierarchicalCache(CuratorFramework curatorFramework, - ExecutorService executorService, - String basePath, - int maxDepth, - List levelPrefixes, - boolean removeNodesWithNoData) { - this.curatorFramework = curatorFramework; - this.executorService = executorService; - this.basePath = basePath; - this.removeNodesWithNoData = removeNodesWithNoData; - this.levelPrefixes.addAll(levelPrefixes); - this.maxDepth = maxDepth; + public HierarchicalCache( + CuratorFramework curatorFramework, + ExecutorService executorService, + String basePath, + int maxDepth, + List levelPrefixes, + boolean removeNodesWithNoData) { + this.curatorFramework = curatorFramework; + this.executorService = executorService; + this.basePath = basePath; + this.removeNodesWithNoData = removeNodesWithNoData; + this.levelPrefixes.addAll(levelPrefixes); + this.maxDepth = maxDepth; - for (int i = 0; i < maxDepth; ++i) { - levelCallbacks.add(new CacheListeners()); - } - } - - public void start() throws Exception { - ensureBasePath(); - rootCache = createLevelCache(0, basePath); - } - - public void stop() throws Exception { - rootCache.stop(); - } - - public void registerCallback(int depth, Consumer callback) { - levelCallbacks.get(depth).addListener(callback); - } - - private HierarchicalCacheLevel createLevelCache(int depth, String path) { - BiFunction function = depth + 1 < maxDepth ? this::createLevelCache : null; - HierarchicalCacheLevel levelCache = new HierarchicalCacheLevel(curatorFramework, - executorService, - path(depth, path), - depth, - levelCallbacks.get(depth), - Optional.ofNullable(function), - removeNodesWithNoData); - try { - logger.debug("Starting hierarchical cache level for path {} and depth {}", path, depth); - levelCache.start(); - } catch (Exception e) { - logger.error("Failed to start hierarchical cache level for path {} and depth {}", path(depth, path), depth, e); - } - return levelCache; + for (int i = 0; i < maxDepth; ++i) { + levelCallbacks.add(new CacheListeners()); } - - private String path(int depth, String basePath) { - return basePath + (levelPrefixes.size() > depth ? "/" + levelPrefixes.get(depth) : ""); + } + + public void start() throws Exception { + ensureBasePath(); + rootCache = createLevelCache(0, basePath); + } + + public void stop() throws Exception { + rootCache.stop(); + } + + public void registerCallback(int depth, Consumer callback) { + levelCallbacks.get(depth).addListener(callback); + } + + private HierarchicalCacheLevel createLevelCache(int depth, String path) { + BiFunction function = + depth + 1 < maxDepth ? this::createLevelCache : null; + HierarchicalCacheLevel levelCache = + new HierarchicalCacheLevel( + curatorFramework, + executorService, + path(depth, path), + depth, + levelCallbacks.get(depth), + Optional.ofNullable(function), + removeNodesWithNoData); + try { + logger.debug("Starting hierarchical cache level for path {} and depth {}", path, depth); + levelCache.start(); + } catch (Exception e) { + logger.error( + "Failed to start hierarchical cache level for path {} and depth {}", + path(depth, path), + depth, + e); } - - private void ensureBasePath() { - try { - try { - if (curatorFramework.checkExists().forPath(basePath) == null) { - curatorFramework.create().creatingParentsIfNeeded().forPath(basePath); - } - } catch (KeeperException.NodeExistsException e) { - // ignore - } - } catch (Exception e) { - throw new InternalProcessingException(e); + return levelCache; + } + + private String path(int depth, String basePath) { + return basePath + (levelPrefixes.size() > depth ? "/" + levelPrefixes.get(depth) : ""); + } + + private void ensureBasePath() { + try { + try { + if (curatorFramework.checkExists().forPath(basePath) == null) { + curatorFramework.create().creatingParentsIfNeeded().forPath(basePath); } + } catch (KeeperException.NodeExistsException e) { + // ignore + } + } catch (Exception e) { + throw new InternalProcessingException(e); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheLevel.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheLevel.java index 320a60d0a1..51377f9e18 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheLevel.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheLevel.java @@ -1,14 +1,5 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.cache; -import org.apache.commons.lang.ArrayUtils; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.recipes.cache.PathChildrenCache; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; -import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; -import org.apache.zookeeper.KeeperException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -20,156 +11,183 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.BiFunction; import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.cache.PathChildrenCache; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; +import org.apache.zookeeper.KeeperException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class HierarchicalCacheLevel extends PathChildrenCache implements PathChildrenCacheListener { - private static Logger logger = LoggerFactory.getLogger(HierarchicalCacheLevel.class); - - private final ReadWriteLock subcacheLock = new ReentrantReadWriteLock(true); - - private final CacheListeners consumer; - - private final CuratorFramework curatorClient; - private final int currentDepth; - - private final Optional> nextLevelFactory; - private final boolean removeNodesWithNoData; - - private final Map subcacheMap = new HashMap<>(); - - HierarchicalCacheLevel(CuratorFramework curatorClient, - ExecutorService executorService, - String path, - int depth, - CacheListeners eventConsumer, - Optional> nextLevelFactory, - boolean removeNodesWithNoData) { - super(curatorClient, path, true, false, executorService); - this.curatorClient = curatorClient; - this.currentDepth = depth; - this.consumer = eventConsumer; - this.nextLevelFactory = nextLevelFactory; - this.removeNodesWithNoData = removeNodesWithNoData; - getListenable().addListener(this); + private static Logger logger = LoggerFactory.getLogger(HierarchicalCacheLevel.class); + + private final ReadWriteLock subcacheLock = new ReentrantReadWriteLock(true); + + private final CacheListeners consumer; + + private final CuratorFramework curatorClient; + private final int currentDepth; + + private final Optional> nextLevelFactory; + private final boolean removeNodesWithNoData; + + private final Map subcacheMap = new HashMap<>(); + + HierarchicalCacheLevel( + CuratorFramework curatorClient, + ExecutorService executorService, + String path, + int depth, + CacheListeners eventConsumer, + Optional> nextLevelFactory, + boolean removeNodesWithNoData) { + super(curatorClient, path, true, false, executorService); + this.curatorClient = curatorClient; + this.currentDepth = depth; + this.consumer = eventConsumer; + this.nextLevelFactory = nextLevelFactory; + this.removeNodesWithNoData = removeNodesWithNoData; + getListenable().addListener(this); + } + + @Override + public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { + if (event.getData() == null) { + return; } - @Override - public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { - if (event.getData() == null) { - return; - } - - String path = event.getData().getPath(); - String cacheName = cacheNameFromPath(path); - logger.debug("Got {} event for path {}", event.getType(), path); - - switch (event.getType()) { - case CHILD_ADDED: - addSubcache(path, cacheName, event.getData().getData()); - break; - case CHILD_REMOVED: - removeSubcache(path, cacheName); - break; - default: - break; - } - - consumer.call(event); + String path = event.getData().getPath(); + String cacheName = cacheNameFromPath(path); + logger.debug("Got {} event for path {}", event.getType(), path); + + switch (event.getType()) { + case CHILD_ADDED: + addSubcache(path, cacheName, event.getData().getData()); + break; + case CHILD_REMOVED: + removeSubcache(path, cacheName); + break; + default: + break; } - void stop() throws IOException { - Lock writeLock = subcacheLock.writeLock(); - writeLock.lock(); - try { - for (HierarchicalCacheLevel subcache : subcacheMap.values()) { - subcache.stop(); - } - subcacheMap.clear(); - this.close(); - } finally { - writeLock.unlock(); - } + consumer.call(event); + } + + void stop() throws IOException { + Lock writeLock = subcacheLock.writeLock(); + writeLock.lock(); + try { + for (HierarchicalCacheLevel subcache : subcacheMap.values()) { + subcache.stop(); + } + subcacheMap.clear(); + this.close(); + } finally { + writeLock.unlock(); } - - private void addSubcache(String path, String cacheName, byte[] data) { - Lock writeLock = subcacheLock.writeLock(); - writeLock.lock(); - try { - logger.debug("Adding cache for path {}; Cache name: {}; Depth: {}; InstanceId: {}", - path, cacheName, currentDepth, Integer.toHexString(hashCode())); - - if (ArrayUtils.isEmpty(data) && removeNodesWithNoData) { - logger.warn("Removing path {} due to no data in the znode", path); - printOrphanedChildren(path); - removeNodeRecursively(path); - return; - } - if (subcacheMap.containsKey(cacheName)) { - logger.debug("Possible duplicate of new entry for {}, ignoring", cacheName); - return; - } - nextLevelFactory.ifPresent(f -> subcacheMap.put(cacheName, f.apply(currentDepth + 1, path))); - } finally { - writeLock.unlock(); - } + } + + private void addSubcache(String path, String cacheName, byte[] data) { + Lock writeLock = subcacheLock.writeLock(); + writeLock.lock(); + try { + logger.debug( + "Adding cache for path {}; Cache name: {}; Depth: {}; InstanceId: {}", + path, + cacheName, + currentDepth, + Integer.toHexString(hashCode())); + + if (ArrayUtils.isEmpty(data) && removeNodesWithNoData) { + logger.warn("Removing path {} due to no data in the znode", path); + printOrphanedChildren(path); + removeNodeRecursively(path); + return; + } + if (subcacheMap.containsKey(cacheName)) { + logger.debug("Possible duplicate of new entry for {}, ignoring", cacheName); + return; + } + nextLevelFactory.ifPresent(f -> subcacheMap.put(cacheName, f.apply(currentDepth + 1, path))); + } finally { + writeLock.unlock(); } - - private void printOrphanedChildren(String path) { - try { - List children = curatorClient.getChildren().forPath(path); - logger.warn("Nodes with empty parent {}: {}", - path, children.stream().map(Object::toString).collect(Collectors.joining(","))); - printChildrenWithEmptyParentRecursively(path, children); - } catch (KeeperException.NoNodeException e) { - logger.info("Could not receive list of children for path {} as the path does not exist", path); - } catch (Exception e) { - logger.warn("Could not receive list of children for path {} due to error", path, e); - } + } + + private void printOrphanedChildren(String path) { + try { + List children = curatorClient.getChildren().forPath(path); + logger.warn( + "Nodes with empty parent {}: {}", + path, + children.stream().map(Object::toString).collect(Collectors.joining(","))); + printChildrenWithEmptyParentRecursively(path, children); + } catch (KeeperException.NoNodeException e) { + logger.info( + "Could not receive list of children for path {} as the path does not exist", path); + } catch (Exception e) { + logger.warn("Could not receive list of children for path {} due to error", path, e); } - - private void printChildrenWithEmptyParentRecursively(String pathWithEmptyParent, List children) { - children.forEach(c -> { - try { - String nextPath = pathWithEmptyParent + "/" + c; - logger.warn("Node with empty parent: {}", nextPath); - List nextChildren = curatorClient.getChildren().forPath(nextPath); - - printChildrenWithEmptyParentRecursively(nextPath, nextChildren); - } catch (KeeperException.NoNodeException e) { - logger.info("Could not receive list of children for path {} as the path does not exist", pathWithEmptyParent); - } catch (Exception e) { - logger.warn("Could not receive list of children for path {} due to error", pathWithEmptyParent, e); - } + } + + private void printChildrenWithEmptyParentRecursively( + String pathWithEmptyParent, List children) { + children.forEach( + c -> { + try { + String nextPath = pathWithEmptyParent + "/" + c; + logger.warn("Node with empty parent: {}", nextPath); + List nextChildren = curatorClient.getChildren().forPath(nextPath); + + printChildrenWithEmptyParentRecursively(nextPath, nextChildren); + } catch (KeeperException.NoNodeException e) { + logger.info( + "Could not receive list of children for path {} as the path does not exist", + pathWithEmptyParent); + } catch (Exception e) { + logger.warn( + "Could not receive list of children for path {} due to error", + pathWithEmptyParent, + e); + } }); + } + + private void removeNodeRecursively(String path) { + try { + curatorClient.delete().deletingChildrenIfNeeded().forPath(path); + logger.warn("Removed recursively path {}", path); + } catch (Exception e) { + logger.warn("Error while deleting recursively path {}", path, e); } - - private void removeNodeRecursively(String path) { - try { - curatorClient.delete().deletingChildrenIfNeeded().forPath(path); - logger.warn("Removed recursively path {}", path); - } catch (Exception e) { - logger.warn("Error while deleting recursively path {}", path, e); - } + } + + private void removeSubcache(String path, String cacheName) throws Exception { + Lock writeLock = subcacheLock.writeLock(); + writeLock.lock(); + logger.debug( + "Removing cache for path {}; Cache name: {}; Depth {}; InstanceId: {}", + path, + cacheName, + currentDepth, + Integer.toHexString(hashCode())); + try { + HierarchicalCacheLevel subcache = subcacheMap.remove(cacheName); + if (subcache == null) { + logger.debug("Possible duplicate of removed entry for {}, ignoring", cacheName); + return; + } + subcache.close(); + } finally { + writeLock.unlock(); } + } - private void removeSubcache(String path, String cacheName) throws Exception { - Lock writeLock = subcacheLock.writeLock(); - writeLock.lock(); - logger.debug("Removing cache for path {}; Cache name: {}; Depth {}; InstanceId: {}", - path, cacheName, currentDepth, Integer.toHexString(hashCode())); - try { - HierarchicalCacheLevel subcache = subcacheMap.remove(cacheName); - if (subcache == null) { - logger.debug("Possible duplicate of removed entry for {}, ignoring", cacheName); - return; - } - subcache.close(); - } finally { - writeLock.unlock(); - } - } - - private String cacheNameFromPath(String path) { - return path.substring(path.lastIndexOf('/') + 1); - } + private String cacheNameFromPath(String path) { + return path.substring(path.lastIndexOf('/') + 1); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/ModelAwareZookeeperNotifyingCache.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/ModelAwareZookeeperNotifyingCache.java index 6a544acd01..ea16b86317 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/ModelAwareZookeeperNotifyingCache.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/ModelAwareZookeeperNotifyingCache.java @@ -1,73 +1,62 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.cache; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.function.Consumer; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pl.allegro.tech.hermes.common.cache.queue.LinkedHashSetBlockingQueue; import pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperPaths; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - public class ModelAwareZookeeperNotifyingCache { - private static final Logger logger = LoggerFactory.getLogger(ModelAwareZookeeperNotifyingCache.class); + private static final Logger logger = + LoggerFactory.getLogger(ModelAwareZookeeperNotifyingCache.class); - private static final int GROUP_LEVEL = 0; + private static final int GROUP_LEVEL = 0; - private static final int TOPIC_LEVEL = 1; + private static final int TOPIC_LEVEL = 1; - private static final int SUBSCRIPTION_LEVEL = 2; + private static final int SUBSCRIPTION_LEVEL = 2; - private final HierarchicalCache cache; - private final ThreadPoolExecutor executor; + private final HierarchicalCache cache; + private final ExecutorService executor; - public ModelAwareZookeeperNotifyingCache(CuratorFramework curator, String rootPath, int processingThreadPoolSize) { - List levelPrefixes = Arrays.asList( - ZookeeperPaths.GROUPS_PATH, ZookeeperPaths.TOPICS_PATH, ZookeeperPaths.SUBSCRIPTIONS_PATH - ); - ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(rootPath + "-zk-cache-%d").build(); - executor = new ThreadPoolExecutor(1, processingThreadPoolSize, - Integer.MAX_VALUE, TimeUnit.SECONDS, new LinkedHashSetBlockingQueue<>(), threadFactory); - this.cache = new HierarchicalCache( - curator, - executor, - rootPath, - 3, - levelPrefixes, - true - ); - } - - public void start() throws Exception { - cache.start(); - } + public ModelAwareZookeeperNotifyingCache( + CuratorFramework curator, ExecutorService executor, String rootPath) { + List levelPrefixes = + Arrays.asList( + ZookeeperPaths.GROUPS_PATH, + ZookeeperPaths.TOPICS_PATH, + ZookeeperPaths.SUBSCRIPTIONS_PATH); + this.executor = executor; + this.cache = new HierarchicalCache(curator, executor, rootPath, 3, levelPrefixes, true); + } - public void stop() { - try { - cache.stop(); - executor.shutdownNow(); - } catch (Exception e) { - logger.warn("Failed to stop Zookeeper cache", e); - } - } + public void start() throws Exception { + cache.start(); + } - public void registerGroupCallback(Consumer callback) { - cache.registerCallback(GROUP_LEVEL, callback); + public void stop() { + try { + cache.stop(); + executor.shutdownNow(); + } catch (Exception e) { + logger.warn("Failed to stop Zookeeper cache", e); } + } + public void registerGroupCallback(Consumer callback) { + cache.registerCallback(GROUP_LEVEL, callback); + } - public void registerTopicCallback(Consumer callback) { - cache.registerCallback(TOPIC_LEVEL, callback); - } + public void registerTopicCallback(Consumer callback) { + cache.registerCallback(TOPIC_LEVEL, callback); + } - public void registerSubscriptionCallback(Consumer callback) { - cache.registerCallback(SUBSCRIPTION_LEVEL, callback); - } + public void registerSubscriptionCallback(Consumer callback) { + cache.registerCallback(SUBSCRIPTION_LEVEL, callback); + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounter.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounter.java index 4a051053ac..498389cde6 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounter.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounter.java @@ -3,58 +3,63 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import java.time.Duration; +import java.util.concurrent.TimeUnit; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong; import org.apache.curator.retry.ExponentialBackoffRetry; -import java.time.Duration; -import java.util.concurrent.TimeUnit; - public class SharedCounter { - private final LoadingCache distributedAtomicLongs; + private final LoadingCache distributedAtomicLongs; - public SharedCounter(CuratorFramework curatorClient, Duration expireAfter, - Duration distributedLoaderBackoff, int distributedLoaderRetries) { - distributedAtomicLongs = CacheBuilder.newBuilder() - .expireAfterAccess(expireAfter.toHours(), TimeUnit.HOURS) - .build(new DistributedAtomicLongLoader( - curatorClient, - new ExponentialBackoffRetry((int) distributedLoaderBackoff.toMillis(), distributedLoaderRetries)) - ); - } + public SharedCounter( + CuratorFramework curatorClient, + Duration expireAfter, + Duration distributedLoaderBackoff, + int distributedLoaderRetries) { + distributedAtomicLongs = + CacheBuilder.newBuilder() + .expireAfterAccess(expireAfter.toHours(), TimeUnit.HOURS) + .build( + new DistributedAtomicLongLoader( + curatorClient, + new ExponentialBackoffRetry( + (int) distributedLoaderBackoff.toMillis(), distributedLoaderRetries))); + } - public boolean increment(String path, long count) { - try { - return distributedAtomicLongs.get(path).add(count).succeeded(); - } catch (Exception e) { - throw new ZookeeperCounterException(path, e); - } + public boolean increment(String path, long count) { + try { + return distributedAtomicLongs.get(path).add(count).succeeded(); + } catch (Exception e) { + throw new ZookeeperCounterException(path, e); } + } - public long getValue(String path) { - try { - return distributedAtomicLongs.get(path).get().preValue(); - } catch (Exception e) { - throw new ZookeeperCounterException(path, e); - } + public long getValue(String path) { + try { + return distributedAtomicLongs.get(path).get().preValue(); + } catch (Exception e) { + throw new ZookeeperCounterException(path, e); } + } - private static final class DistributedAtomicLongLoader extends CacheLoader { + private static final class DistributedAtomicLongLoader + extends CacheLoader { - private final CuratorFramework client; + private final CuratorFramework client; - private final RetryPolicy retryPolicy; + private final RetryPolicy retryPolicy; - DistributedAtomicLongLoader(CuratorFramework client, RetryPolicy retryPolicy) { - this.client = client; - this.retryPolicy = retryPolicy; - } + DistributedAtomicLongLoader(CuratorFramework client, RetryPolicy retryPolicy) { + this.client = client; + this.retryPolicy = retryPolicy; + } - @Override - public DistributedAtomicLong load(String key) throws Exception { - return new DistributedAtomicLong(client, key, retryPolicy); - } + @Override + public DistributedAtomicLong load(String key) throws Exception { + return new DistributedAtomicLong(client, key, retryPolicy); } + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/ZookeeperCounterException.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/ZookeeperCounterException.java index 8d0fdfa0b8..06b792be86 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/ZookeeperCounterException.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/ZookeeperCounterException.java @@ -6,16 +6,16 @@ @SuppressWarnings("serial") public class ZookeeperCounterException extends HermesException { - public ZookeeperCounterException(String key, String message) { - super("Exception while trying to access counter " + key + " via Zookeeper. " + message); - } + public ZookeeperCounterException(String key, String message) { + super("Exception while trying to access counter " + key + " via Zookeeper. " + message); + } - public ZookeeperCounterException(String key, Throwable cause) { - super("Exception while trying to access counter " + key + " via Zookeeper.", cause); - } + public ZookeeperCounterException(String key, Throwable cause) { + super("Exception while trying to access counter " + key + " via Zookeeper.", cause); + } - @Override - public ErrorCode getCode() { - return ErrorCode.INTERNAL_ERROR; - } + @Override + public ErrorCode getCode() { + return ErrorCode.INTERNAL_ERROR; + } } diff --git a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/notifications/ZookeeperInternalNotificationBus.java b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/notifications/ZookeeperInternalNotificationBus.java index d7989ba7d0..0ba00581db 100644 --- a/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/notifications/ZookeeperInternalNotificationBus.java +++ b/hermes-common/src/main/java/pl/allegro/tech/hermes/infrastructure/zookeeper/notifications/ZookeeperInternalNotificationBus.java @@ -1,7 +1,12 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.notifications; +import static java.util.Optional.empty; +import static java.util.Optional.of; + import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.commons.lang.ArrayUtils; +import java.io.IOException; +import java.util.Optional; +import org.apache.commons.lang3.ArrayUtils; import org.apache.curator.framework.recipes.cache.ChildData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,78 +18,79 @@ import pl.allegro.tech.hermes.domain.notifications.TopicCallback; import pl.allegro.tech.hermes.infrastructure.zookeeper.cache.ModelAwareZookeeperNotifyingCache; -import java.io.IOException; -import java.util.Optional; - -import static java.util.Optional.empty; -import static java.util.Optional.of; - public class ZookeeperInternalNotificationBus implements InternalNotificationsBus { - private static final Logger logger = LoggerFactory.getLogger(ZookeeperInternalNotificationBus.class); + private static final Logger logger = + LoggerFactory.getLogger(ZookeeperInternalNotificationBus.class); - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper; - private final ModelAwareZookeeperNotifyingCache modelNotifyingCache; + private final ModelAwareZookeeperNotifyingCache modelNotifyingCache; - public ZookeeperInternalNotificationBus(ObjectMapper objectMapper, ModelAwareZookeeperNotifyingCache modelNotifyingCache) { - this.objectMapper = objectMapper; - this.modelNotifyingCache = modelNotifyingCache; - } + public ZookeeperInternalNotificationBus( + ObjectMapper objectMapper, ModelAwareZookeeperNotifyingCache modelNotifyingCache) { + this.objectMapper = objectMapper; + this.modelNotifyingCache = modelNotifyingCache; + } - @Override - public void registerSubscriptionCallback(SubscriptionCallback callback) { - modelNotifyingCache.registerSubscriptionCallback((e) -> { - switch (e.getType()) { - case CHILD_ADDED: - readSilently(e.getData(), Subscription.class).ifPresent(callback::onSubscriptionCreated); - break; - case CHILD_UPDATED: - readSilently(e.getData(), Subscription.class).ifPresent(callback::onSubscriptionChanged); - break; - case CHILD_REMOVED: - readSilently(e.getData(), Subscription.class).ifPresent(callback::onSubscriptionRemoved); - break; - default: - break; - } + @Override + public void registerSubscriptionCallback(SubscriptionCallback callback) { + modelNotifyingCache.registerSubscriptionCallback( + (e) -> { + switch (e.getType()) { + case CHILD_ADDED: + readSilently(e.getData(), Subscription.class) + .ifPresent(callback::onSubscriptionCreated); + break; + case CHILD_UPDATED: + readSilently(e.getData(), Subscription.class) + .ifPresent(callback::onSubscriptionChanged); + break; + case CHILD_REMOVED: + readSilently(e.getData(), Subscription.class) + .ifPresent(callback::onSubscriptionRemoved); + break; + default: + break; + } }); - } + } - @Override - public void registerTopicCallback(TopicCallback callback) { - modelNotifyingCache.registerTopicCallback((e) -> { - switch (e.getType()) { - case CHILD_ADDED: - readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicCreated); - break; - case CHILD_UPDATED: - readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicChanged); - break; - case CHILD_REMOVED: - readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicRemoved); - break; - default: - break; - } + @Override + public void registerTopicCallback(TopicCallback callback) { + modelNotifyingCache.registerTopicCallback( + (e) -> { + switch (e.getType()) { + case CHILD_ADDED: + readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicCreated); + break; + case CHILD_UPDATED: + readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicChanged); + break; + case CHILD_REMOVED: + readSilently(e.getData(), Topic.class).ifPresent(callback::onTopicRemoved); + break; + default: + break; + } }); - } + } - @Override - public void registerAdminCallback(AdminCallback callback) { - // TODO we should move admin callbacks here in favor of AdminTool - } + @Override + public void registerAdminCallback(AdminCallback callback) { + // TODO we should move admin callbacks here in favor of AdminTool + } - private Optional readSilently(ChildData data, Class clazz) { - if (ArrayUtils.isEmpty(data.getData())) { - logger.warn("No data at path {}", data.getPath()); - return empty(); - } - try { - return of(objectMapper.readValue(data.getData(), clazz)); - } catch (IOException exception) { - logger.warn("Failed to parse object at path {}", data.getPath(), exception); - return empty(); - } + private Optional readSilently(ChildData data, Class clazz) { + if (ArrayUtils.isEmpty(data.getData())) { + logger.warn("No data at path {}", data.getPath()); + return empty(); + } + try { + return of(objectMapper.readValue(data.getData(), clazz)); + } catch (IOException exception) { + logger.warn("Failed to parse object at path {}", data.getPath(), exception); + return empty(); } + } } diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactoryMetricsTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactoryMetricsTest.groovy new file mode 100644 index 0000000000..7ad8cee715 --- /dev/null +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/metric/executor/InstrumentedExecutorServiceFactoryMetricsTest.groovy @@ -0,0 +1,67 @@ +package pl.allegro.tech.hermes.common.metric.executor + +import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.search.Search +import io.micrometer.core.instrument.simple.SimpleMeterRegistry +import pl.allegro.tech.hermes.common.metric.MetricsFacade +import spock.lang.Specification +import spock.lang.Subject + +import java.util.concurrent.ExecutorService +import java.util.concurrent.Future +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture + +import static java.util.concurrent.TimeUnit.SECONDS + +class InstrumentedExecutorServiceFactoryMetricsTest extends Specification { + + private final MeterRegistry meterRegistry = new SimpleMeterRegistry() + + @Subject + private final InstrumentedExecutorServiceFactory factory = + new InstrumentedExecutorServiceFactory( + new MetricsFacade( + meterRegistry + ) + ) + + def "should record metrics for executor service (monitoring enabled: #monitoringEnabled)"() { + given: + ExecutorService executor = factory.getExecutorService("test-executor", 10, monitoringEnabled) + + when: + Future task = executor.submit { println("task executed") } + task.get() + + then: + metric("executor.completed", "test-executor").functionCounter()?.count() == expectedCompletedCount + + where: + monitoringEnabled || expectedCompletedCount + true || 1.0d + false || null + } + + def "should record metrics for scheduled executor service (monitoring enabled: #monitoringEnabled)"() { + given: + ScheduledExecutorService executor = factory.scheduledExecutorBuilder("test-scheduled-executor", 10) + .withMonitoringEnabled(monitoringEnabled).create() + + when: + ScheduledFuture task = executor.schedule({ println("scheduled task executed") }, 1, SECONDS) + task.get() + + then: + metric("executor.completed", "test-scheduled-executor").functionCounter()?.count() == expectedCompletedCount + + where: + monitoringEnabled || expectedCompletedCount + true || 1.0d + false || null + } + + private Search metric(String name, String executorName) { + return meterRegistry.find(name).tag("name", executorName) + } +} diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClientTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClientTest.groovy index 8e8d3d4e8a..f6e128fc8a 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClientTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/common/schema/ReadMetricsTrackingRawSchemaClientTest.groovy @@ -1,16 +1,12 @@ package pl.allegro.tech.hermes.common.schema -import com.codahale.metrics.MetricRegistry import io.micrometer.core.instrument.MeterRegistry import io.micrometer.core.instrument.Timer import io.micrometer.core.instrument.search.Search import io.micrometer.core.instrument.simple.SimpleMeterRegistry import pl.allegro.tech.hermes.api.RawSchema import pl.allegro.tech.hermes.api.TopicName -import pl.allegro.tech.hermes.common.metric.HermesMetrics import pl.allegro.tech.hermes.common.metric.MetricsFacade -import pl.allegro.tech.hermes.common.metric.Timers -import pl.allegro.tech.hermes.metrics.PathsCompiler import pl.allegro.tech.hermes.schema.RawSchemaClient import pl.allegro.tech.hermes.schema.SchemaVersion import pl.allegro.tech.hermes.test.helper.metrics.MicrometerUtils @@ -29,9 +25,8 @@ class ReadMetricsTrackingRawSchemaClientTest extends Specification { RawSchema schema = RawSchema.valueOf("some_schema") MeterRegistry meterRegistry = new SimpleMeterRegistry() - HermesMetrics hermesMetrics = new HermesMetrics(new MetricRegistry(), new PathsCompiler("")) - MetricsFacade metricsFacade = new MetricsFacade(meterRegistry, hermesMetrics) + MetricsFacade metricsFacade = new MetricsFacade(meterRegistry) RawSchemaClient rawSchemaClient = Mock() @@ -91,17 +86,15 @@ class ReadMetricsTrackingRawSchemaClientTest extends Specification { } private long getSchemaCounterValue() { - return getCounterValue("schema.get-schema", Timers.GET_SCHEMA_LATENCY) + return getCounterValue("schema.get-schema") } private long getVersionsCounterValue() { - return getCounterValue("schema.get-versions", Timers.GET_SCHEMA_VERSIONS_LATENCY) + return getCounterValue("schema.get-versions") } - private long getCounterValue(String meterRegistryName, String hermesMetricsName) { + private long getCounterValue(String meterRegistryName) { def meterRegistryCount = MicrometerUtils.metricValue(meterRegistry, meterRegistryName, Search.&timer, Timer.&count).orElse(0L); - def hermesMetricsCount = hermesMetrics.schemaTimer(hermesMetricsName).count - assert meterRegistryCount == hermesMetricsCount return meterRegistryCount } } diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepositoryTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepositoryTest.groovy index 71bcd15bb4..7ac1377790 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepositoryTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperTopicRepositoryTest.groovy @@ -185,17 +185,16 @@ class ZookeeperTopicRepositoryTest extends IntegrationTest { !repository.topicExists(new TopicName(GROUP, 'remove')) } - def "should remove topic with metrics but without subscriptions"() { + def "should remove topic with metrics and without preview"() { given: def topicName = "topicWithMetrics" repository.createTopic(topic(GROUP, topicName).build()) wait.untilTopicCreated(GROUP, topicName) - def path = pathsCompiler.compile(BASE_ZOOKEEPER_PATH + ZookeeperCounterStorage.SUBSCRIPTION_DELIVERED, pathContext() + def path = pathsCompiler.compile(BASE_ZOOKEEPER_PATH + ZookeeperCounterStorage.TOPIC_VOLUME_COUNTER, pathContext() .withGroup(GROUP) .withTopic(topicName) - .withSubscription("sample") .build()) zookeeper().create().creatingParentsIfNeeded().forPath(path, '1'.bytes) wait.untilZookeeperPathIsCreated(path) @@ -207,6 +206,29 @@ class ZookeeperTopicRepositoryTest extends IntegrationTest { !repository.topicExists(new TopicName(GROUP, topicName)) } + def "should remove topic with metrics and preview"() { + given: "a topic" + Topic topic = topic(GROUP, "topicWithMetricsAndPreview").build() + repository.createTopic(topic) + wait.untilTopicCreated(GROUP, topic.getName().getName()) + + and: "volume metric in zk for that topic" + String metricPath = paths.topicMetricPath(topic.getName(), "volume") + zookeeper().create().creatingParentsIfNeeded().forPath(metricPath, '1'.bytes) + wait.untilZookeeperPathIsCreated(metricPath) + + and: "preview in zk for that topic" + String previewPath = paths.topicPreviewPath(topic.getName()) + zookeeper().create().creatingParentsIfNeeded().forPath(previewPath , '1'.bytes) + wait.untilZookeeperPathIsCreated(previewPath) + + when: + repository.removeTopic(topic.getName()) + + then: + !repository.topicExists(topic.getName()) + } + def "should not throw exception on malformed topic when reading list of all topics"() { given: zookeeper().create().forPath(paths.topicPath(new TopicName(GROUP, 'malformed')), ''.bytes) diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCacheTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCacheTest.groovy index 07cb8e1a60..e42121af43 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCacheTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsCacheTest.groovy @@ -5,6 +5,7 @@ import ch.qos.logback.classic.Logger import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.read.ListAppender import com.fasterxml.jackson.databind.ObjectMapper +import org.awaitility.Awaitility import org.slf4j.LoggerFactory import pl.allegro.tech.hermes.api.Constraints import pl.allegro.tech.hermes.api.SubscriptionName @@ -14,8 +15,8 @@ import pl.allegro.tech.hermes.test.IntegrationTest import java.util.concurrent.TimeUnit -import static com.jayway.awaitility.Awaitility.await import static java.util.Collections.emptyMap +import static org.awaitility.Awaitility.await class ZookeeperWorkloadConstraintsCacheTest extends IntegrationTest { diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepositoryTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepositoryTest.groovy index 485def9354..98f069b7a3 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepositoryTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/ZookeeperWorkloadConstraintsRepositoryTest.groovy @@ -2,6 +2,7 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper import com.fasterxml.jackson.databind.ObjectMapper import org.apache.zookeeper.KeeperException +import org.awaitility.Awaitility import pl.allegro.tech.hermes.api.SubscriptionName import pl.allegro.tech.hermes.api.TopicName import pl.allegro.tech.hermes.api.Constraints @@ -13,7 +14,8 @@ import pl.allegro.tech.hermes.test.IntegrationTest import java.util.concurrent.TimeUnit -import static com.jayway.awaitility.Awaitility.await +import static org.awaitility.Awaitility.await + class ZookeeperWorkloadConstraintsRepositoryTest extends IntegrationTest { diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheTest.groovy index 64411665ed..48b9c9490d 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/infrastructure/zookeeper/cache/HierarchicalCacheTest.groovy @@ -1,16 +1,16 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.cache -import com.jayway.awaitility.Duration -import com.jayway.awaitility.groovy.AwaitilityTrait +import org.awaitility.Awaitility import pl.allegro.tech.hermes.test.IntegrationTest +import java.time.Duration import java.util.concurrent.Executors -import static com.jayway.awaitility.Awaitility.await import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_ADDED import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED +import static org.awaitility.Awaitility.await -class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { +class HierarchicalCacheTest extends IntegrationTest { private HierarchicalCache cache = new HierarchicalCache( zookeeper(), @@ -48,7 +48,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { .and().commit() then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupA', 'groupA')) }) @@ -59,7 +59,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { .and().commit() then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupA/topics/topicA', 'topicA')) }) @@ -67,7 +67,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { zookeeper().create().forPath('/hierarchicalCacheTest/groups/groupA/topics/topicA/subscriptions/subA', 'subA'.bytes) then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupA/topics/topicA/subscriptions/subA', 'subA')) }) @@ -89,7 +89,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { cache.start() then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupB', 'groupB')) && calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupB/topics/topicB', 'topicB')) && calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/groups/groupB/topics/topicB/subscriptions/subB', 'subB')) @@ -110,7 +110,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { '/hierarchicalCacheTest/groups/groupC/topics/topicC/metrics/published', '123'.bytes) then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains( new Tuple(CHILD_REMOVED, '/hierarchicalCacheTest/groups/groupC/topics/topicC', '')) }) @@ -143,7 +143,7 @@ class HierarchicalCacheTest extends IntegrationTest implements AwaitilityTrait { '/hierarchicalCacheTest/workload/runtime/topic$subscription/hs-consumer1', 'AUTO_ASSIGNED'.bytes) then: - await().atMost(Duration.FIVE_SECONDS).until({ + await().atMost(Duration.ofSeconds(5)).until({ calledCallbacks.contains(new Tuple(CHILD_ADDED, '/hierarchicalCacheTest/workload/runtime/topic$subscription', '')) && !calledCallbacks.contains(new Tuple(CHILD_REMOVED, '/hierarchicalCacheTest/workload/runtime/topic$subscription', '')) }) diff --git a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/test/IntegrationTest.groovy b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/test/IntegrationTest.groovy index 2f4a09a291..95bf3b028f 100644 --- a/hermes-common/src/test/groovy/pl/allegro/tech/hermes/test/IntegrationTest.groovy +++ b/hermes-common/src/test/groovy/pl/allegro/tech/hermes/test/IntegrationTest.groovy @@ -31,7 +31,7 @@ abstract class IntegrationTest extends Specification { protected RepositoryWaiter wait = new RepositoryWaiter(zookeeperResource.curator(), paths) - protected ObjectMapper mapper = new ObjectMapperFactory(true).provide() + protected ObjectMapper mapper = new ObjectMapperFactory(true, false).provide() protected ZookeeperGroupRepository groupRepository = new ZookeeperGroupRepository(zookeeper(), mapper, paths) diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/di/ObjectMapperFactoryTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/di/ObjectMapperFactoryTest.java index ae1846c956..bfa508d545 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/di/ObjectMapperFactoryTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/di/ObjectMapperFactoryTest.java @@ -1,63 +1,62 @@ package pl.allegro.tech.hermes.common.di; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; + import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Before; import org.junit.Test; import pl.allegro.tech.hermes.common.di.factories.ObjectMapperFactory; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; - public class ObjectMapperFactoryTest { - ObjectMapper mapper; + ObjectMapper mapper; - @Before - public void init() { - ObjectMapperFactory factory = new ObjectMapperFactory(false); - mapper = factory.provide(); - } + @Before + public void init() { + ObjectMapperFactory factory = new ObjectMapperFactory(false, false); + mapper = factory.provide(); + } - @Test - public void shouldDeserializeClassWithUnknownFields() throws Exception { - //given - String json = "{\"unknownProperty\": \"value\", \"name\":\"Charles\"}"; + @Test + public void shouldDeserializeClassWithUnknownFields() throws Exception { + // given + String json = "{\"unknownProperty\": \"value\", \"name\":\"Charles\"}"; - //when - DummyUser subscription = mapper.readValue(json, DummyUser.class); + // when + DummyUser subscription = mapper.readValue(json, DummyUser.class); - //then - assertEquals("Charles", subscription.name); - } + // then + assertEquals("Charles", subscription.name); + } - @Test - public void shouldSerializeObjectWithoutNullPropertiesInclusion() throws Exception { - //given - DummyUser object = new DummyUser(null); + @Test + public void shouldSerializeObjectWithoutNullPropertiesInclusion() throws Exception { + // given + DummyUser object = new DummyUser(null); - //when - final String jsonValue = mapper.writeValueAsString(object); + // when + final String jsonValue = mapper.writeValueAsString(object); - //then - assertThat(jsonValue).doesNotContain("name").doesNotContain("null"); - } + // then + assertThat(jsonValue).doesNotContain("name").doesNotContain("null"); + } - private static final class DummyUser { - private String name; + private static final class DummyUser { + private String name; - private DummyUser() { - } + private DummyUser() {} - private DummyUser(String name) { - this.name = name; - } + private DummyUser(String name) { + this.name = name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getName() { - return name; - } + public String getName() { + return name; } + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLogTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLogTest.java index 0de45d0638..f04e72631a 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLogTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/undelivered/ZookeeperUndeliveredMessageLogTest.java @@ -1,139 +1,138 @@ package pl.allegro.tech.hermes.common.message.undelivered; -import com.codahale.metrics.MetricRegistry; +import static org.assertj.core.api.Assertions.assertThat; +import static pl.allegro.tech.hermes.api.SentMessageTrace.Builder.undeliveredMessage; +import static pl.allegro.tech.hermes.common.metric.Histograms.PERSISTED_UNDELIVERED_MESSAGE_SIZE; +import static pl.allegro.tech.hermes.common.metric.Meters.PERSISTED_UNDELIVERED_MESSAGES_METER; +import static pl.allegro.tech.hermes.test.helper.metrics.MicrometerUtils.metricValue; + import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.search.Search; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.Optional; import org.junit.After; import org.junit.Before; import org.junit.Test; import pl.allegro.tech.hermes.api.SentMessageTrace; import pl.allegro.tech.hermes.api.TopicName; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.infrastructure.zookeeper.ZookeeperPaths; -import pl.allegro.tech.hermes.metrics.PathsCompiler; import pl.allegro.tech.hermes.test.helper.zookeeper.ZookeeperBaseTest; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; -import static pl.allegro.tech.hermes.api.SentMessageTrace.Builder.undeliveredMessage; -import static pl.allegro.tech.hermes.common.metric.Histograms.PERSISTED_UNDELIVERED_MESSAGE_SIZE; -import static pl.allegro.tech.hermes.common.metric.Meters.PERSISTED_UNDELIVERED_MESSAGES_METER; -import static pl.allegro.tech.hermes.test.helper.metrics.MicrometerUtils.metricValue; - public class ZookeeperUndeliveredMessageLogTest extends ZookeeperBaseTest { - private static final TopicName TOPIC = new TopicName("undeliveredMessageLogGroup", "topic"); - - private static final String SUBSCRIPTION = "subscription"; - - private final ZookeeperPaths paths = new ZookeeperPaths("/hermes"); - - private final HermesMetrics hermesMetrics = new HermesMetrics( - new MetricRegistry(), new PathsCompiler("host")); - private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); - private final MetricsFacade metricsFacade = new MetricsFacade(meterRegistry, hermesMetrics); - - private final ZookeeperUndeliveredMessageLog log = new ZookeeperUndeliveredMessageLog( - zookeeperClient, - paths, - new ObjectMapper(), - metricsFacade - ); - - private final ZookeeperLastUndeliveredMessageReader reader = new ZookeeperLastUndeliveredMessageReader( - zookeeperClient, - paths, - new ObjectMapper() - ); - - @Before - public void setUp() throws Exception { - zookeeperClient.create().creatingParentsIfNeeded().forPath(paths.subscriptionPath(TOPIC, SUBSCRIPTION)); - } - - @After - public void cleanUp() throws Exception { - deleteData(paths.basePath()); - hermesMetrics.unregister(PERSISTED_UNDELIVERED_MESSAGES_METER); - hermesMetrics.unregister(PERSISTED_UNDELIVERED_MESSAGE_SIZE); - } - - @Test - public void shouldAddUndeliveredMessageToLog() throws Exception { - // given when - log.add(createUndeliveredMessage(SUBSCRIPTION, "message")); - log.persist(); - - // then - SentMessageTrace lastMessage = reader.last(TOPIC, "subscription").get(); - assertThat(lastMessage.getMessage()).isEqualTo("message"); - assertThatMetricsHaveBeenReported(1); - } - - @Test - public void shouldReturnLatestUndeliveredMessage() throws Exception { - // given - log.add(createUndeliveredMessage(SUBSCRIPTION, "old message")); - log.add(createUndeliveredMessage(SUBSCRIPTION, "new message")); - log.persist(); - - // when - SentMessageTrace lastMessage = reader.last(TOPIC, "subscription").get(); - - // then - assertThat(lastMessage.getMessage()).isEqualTo("new message"); - assertThatMetricsHaveBeenReported(1); - } - - @Test - public void shouldReturnAbsentIfThereAreNoUndeliveredMessagesForGivenSubscription() { - // when - Optional result = reader.last(new TopicName("unknown", "topic"), "subscription"); - - // then - assertThat(result.isPresent()).isFalse(); - assertThatMetricsHaveBeenReported(0); - } - - @Test - public void shouldNotAddUndeliveredMessageLogToNonExistingSubscriptionPath() { - // given - log.add(createUndeliveredMessage("unknownSubscription", "message")); - log.persist(); - - // when - Optional result = reader.last(TOPIC, "unknownSubscription"); - - // then - assertThat(result.isPresent()).isFalse(); - assertThatMetricsHaveBeenReported(0); - } - - private SentMessageTrace createUndeliveredMessage(String subscription, String message) { - return undeliveredMessage() - .withTopicName(TOPIC.qualifiedName()) - .withSubscription(subscription) - .withMessage(message) - .withReason(new IllegalArgumentException().getMessage()) - .withTimestamp(1L) - .withPartition(1) - .withOffset(1L) - .withCluster("cluster") - .build(); - } - - private void assertThatMetricsHaveBeenReported(int persistedMessageCount) { - assertThat(hermesMetrics.meter(PERSISTED_UNDELIVERED_MESSAGES_METER).getCount()).isEqualTo(persistedMessageCount); - assertThat(hermesMetrics.histogram(PERSISTED_UNDELIVERED_MESSAGE_SIZE).getCount()).isEqualTo(persistedMessageCount); - assertThat(metricValue(meterRegistry, PERSISTED_UNDELIVERED_MESSAGES_METER, Search::counter, Counter::count).orElse(0.0d)) - .isEqualTo(persistedMessageCount); - assertThat(metricValue(meterRegistry, PERSISTED_UNDELIVERED_MESSAGE_SIZE + ".bytes", Search::summary, DistributionSummary::count) - .orElse(0L)).isEqualTo(persistedMessageCount); - } + private static final TopicName TOPIC = new TopicName("undeliveredMessageLogGroup", "topic"); + + private static final String SUBSCRIPTION = "subscription"; + + private final ZookeeperPaths paths = new ZookeeperPaths("/hermes"); + + private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); + private final MetricsFacade metricsFacade = new MetricsFacade(meterRegistry); + + private final ZookeeperUndeliveredMessageLog log = + new ZookeeperUndeliveredMessageLog(zookeeperClient, paths, new ObjectMapper(), metricsFacade); + + private final ZookeeperLastUndeliveredMessageReader reader = + new ZookeeperLastUndeliveredMessageReader(zookeeperClient, paths, new ObjectMapper()); + + @Before + public void setUp() throws Exception { + zookeeperClient + .create() + .creatingParentsIfNeeded() + .forPath(paths.subscriptionPath(TOPIC, SUBSCRIPTION)); + } + + @After + public void cleanUp() throws Exception { + deleteData(paths.basePath()); + } + + @Test + public void shouldAddUndeliveredMessageToLog() throws Exception { + // given when + log.add(createUndeliveredMessage(SUBSCRIPTION, "message")); + log.persist(); + + // then + SentMessageTrace lastMessage = reader.last(TOPIC, "subscription").get(); + assertThat(lastMessage.getMessage()).isEqualTo("message"); + assertThatMetricsHaveBeenReported(1); + } + + @Test + public void shouldReturnLatestUndeliveredMessage() throws Exception { + // given + log.add(createUndeliveredMessage(SUBSCRIPTION, "old message")); + log.add(createUndeliveredMessage(SUBSCRIPTION, "new message")); + log.persist(); + + // when + SentMessageTrace lastMessage = reader.last(TOPIC, "subscription").get(); + + // then + assertThat(lastMessage.getMessage()).isEqualTo("new message"); + assertThatMetricsHaveBeenReported(1); + } + + @Test + public void shouldReturnAbsentIfThereAreNoUndeliveredMessagesForGivenSubscription() { + // when + Optional result = + reader.last(new TopicName("unknown", "topic"), "subscription"); + + // then + assertThat(result.isPresent()).isFalse(); + assertThatMetricsHaveBeenReported(0); + } + + @Test + public void shouldNotAddUndeliveredMessageLogToNonExistingSubscriptionPath() { + // given + log.add(createUndeliveredMessage("unknownSubscription", "message")); + log.persist(); + + // when + Optional result = reader.last(TOPIC, "unknownSubscription"); + + // then + assertThat(result.isPresent()).isFalse(); + assertThatMetricsHaveBeenReported(0); + } + + private SentMessageTrace createUndeliveredMessage(String subscription, String message) { + return undeliveredMessage() + .withTopicName(TOPIC.qualifiedName()) + .withSubscription(subscription) + .withMessage(message) + .withReason(new IllegalArgumentException().getMessage()) + .withTimestamp(1L) + .withPartition(1) + .withOffset(1L) + .withCluster("cluster") + .build(); + } + + private void assertThatMetricsHaveBeenReported(int persistedMessageCount) { + assertThat( + metricValue( + meterRegistry, + PERSISTED_UNDELIVERED_MESSAGES_METER, + Search::counter, + Counter::count) + .orElse(0.0d)) + .isEqualTo(persistedMessageCount); + assertThat( + metricValue( + meterRegistry, + PERSISTED_UNDELIVERED_MESSAGE_SIZE + ".bytes", + Search::summary, + DistributionSummary::count) + .orElse(0L)) + .isEqualTo(persistedMessageCount); + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapperTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapperTest.java index 4d7599ee11..bf018d6511 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapperTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/AvroMessageContentWrapperTest.java @@ -1,12 +1,12 @@ package pl.allegro.tech.hermes.common.message.wrapper; -import org.apache.avro.Schema; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.util.Utf8; -import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Test; -import pl.allegro.tech.hermes.test.helper.avro.AvroUser; +import static java.lang.Long.valueOf; +import static org.assertj.core.api.Assertions.assertThat; +import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; +import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.recordToBytes; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MARKER; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MESSAGE_ID_KEY; +import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_TIMESTAMP_KEY; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -15,121 +15,127 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; - -import static java.lang.Long.valueOf; -import static org.assertj.core.api.Assertions.assertThat; -import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.bytesToRecord; -import static pl.allegro.tech.hermes.common.message.converter.AvroRecordToBytesConverter.recordToBytes; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MARKER; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_MESSAGE_ID_KEY; -import static pl.allegro.tech.hermes.common.message.wrapper.AvroMetadataMarker.METADATA_TIMESTAMP_KEY; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.util.Utf8; +import org.apache.commons.io.IOUtils; +import org.junit.Before; +import org.junit.Test; +import pl.allegro.tech.hermes.test.helper.avro.AvroUser; public class AvroMessageContentWrapperTest { - private AvroMessageContentWrapper avroMessageContentWrapper; - private AvroUser avroUser; - private byte[] content; - - private final String id = UUID.randomUUID().toString(); - private final Long timestamp = System.currentTimeMillis(); - - @Before - public void setup() throws IOException { - avroUser = new AvroUser("Bob", 10, "red"); - content = avroUser.asBytes(); - avroMessageContentWrapper = new AvroMessageContentWrapper(Clock.systemDefaultZone()); - } - - @Test - public void shouldWrapAndUnwrapAvroMessageWithMetadata() { - // when - byte[] wrappedMessage = - avroMessageContentWrapper.wrapContent(content, id, timestamp, avroUser.getSchema(), Collections.emptyMap()); - UnwrappedMessageContent unwrappedMessageContent = - avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); - - // then - assertThat(unwrappedMessageContent.getMessageMetadata().getId()).isEqualTo(id); - assertThat(unwrappedMessageContent.getMessageMetadata().getTimestamp()).isEqualTo(timestamp); - assertThat(unwrappedMessageContent.getContent()).contains(content); - } - - @Test - @SuppressWarnings("unchecked") - public void shouldWrappedMessageContainsMetadata() { - // when - byte[] wrappedMessage = avroMessageContentWrapper.wrapContent(content, id, timestamp, avroUser.getSchema(), Collections.emptyMap()); - - // then - GenericRecord messageWithMetadata = bytesToRecord(wrappedMessage, avroUser.getSchema()); - Map metadata = (Map) messageWithMetadata.get(METADATA_MARKER); - assertThat(metadata.get(METADATA_MESSAGE_ID_KEY).toString()).isEqualTo(id); - assertThat(valueOf(metadata.get(METADATA_TIMESTAMP_KEY).toString())).isEqualTo(timestamp); - assertThat(wrappedMessage).contains(content); - } - - @Test - public void shouldWrappedMessageBeUnchangedWhenSchemaNotContainsMetadata() throws IOException { - // when - Schema schemaWithoutMetadata = new Schema.Parser().parse( - IOUtils.toString( - getClass().getResourceAsStream("/schema/user_no_metadata.avsc"), - StandardCharsets.UTF_8 - ) - ); - byte[] wrappedMessage = - avroMessageContentWrapper.wrapContent(content, id, timestamp, schemaWithoutMetadata, Collections.emptyMap()); - - // then - GenericRecord wrappedMessageAsRecord = bytesToRecord(wrappedMessage, avroUser.getSchema()); - assertThat(wrappedMessageAsRecord.get(METADATA_MARKER)).isNull(); - } - - @Test - public void shouldUnwrapAvroMessageAndGenerateMetadataWhenNotExists() throws Throwable { - //given - byte [] wrappedMessage = wrapContentWithoutMetadata(content, avroUser.getSchema()); - - //when - UnwrappedMessageContent unwrappedMessage = - avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); - - //then - assertThat(unwrappedMessage.getMessageMetadata().getId()).isEmpty(); - assertThat(unwrappedMessage.getMessageMetadata().getTimestamp()).isNotNull(); - assertThat(unwrappedMessage.getContent()).startsWith(content); - } - - @Test - public void shouldUnwrapAvroMessageAndSetEmptyMessageIdWhenNotGivenInMetadata() throws Throwable { - // given - byte [] wrappedMessage = wrapContentWithoutMessageIdInMetadata(content, avroUser.getSchema()); - - //when - UnwrappedMessageContent unwrappedMessage = avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); - - // then - assertThat(unwrappedMessage.getMessageMetadata().getId()).isEmpty(); - assertThat(unwrappedMessage.getMessageMetadata().getTimestamp()).isNotNull(); - assertThat(unwrappedMessage.getContent()).contains(content); - } - - private byte[] wrapContentWithoutMetadata(byte[] message, Schema schema) throws Exception { - return wrapContent(message, schema, null); - } - - private byte[] wrapContentWithoutMessageIdInMetadata(byte[] message, Schema schema) throws Exception { - return wrapContent(message, schema, metadataMapWithoutMessageId(timestamp)); - } - - private byte[] wrapContent(byte[] message, Schema schema, Map metadata) throws Exception { - GenericRecord genericRecord = bytesToRecord(message, schema); - genericRecord.put(METADATA_MARKER, metadata); - return recordToBytes(genericRecord, schema); - } - - private Map metadataMapWithoutMessageId(long timestamp) { - Map metadata = new HashMap<>(); - metadata.put(METADATA_TIMESTAMP_KEY, new Utf8(Long.toString(timestamp))); - return metadata; - } + private AvroMessageContentWrapper avroMessageContentWrapper; + private AvroUser avroUser; + private byte[] content; + + private final String id = UUID.randomUUID().toString(); + private final Long timestamp = System.currentTimeMillis(); + + @Before + public void setup() throws IOException { + avroUser = new AvroUser("Bob", 10, "red"); + content = avroUser.asBytes(); + avroMessageContentWrapper = new AvroMessageContentWrapper(Clock.systemDefaultZone()); + } + + @Test + public void shouldWrapAndUnwrapAvroMessageWithMetadata() { + // when + byte[] wrappedMessage = + avroMessageContentWrapper.wrapContent( + content, id, timestamp, avroUser.getSchema(), Collections.emptyMap()); + UnwrappedMessageContent unwrappedMessageContent = + avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); + + // then + assertThat(unwrappedMessageContent.getMessageMetadata().getId()).isEqualTo(id); + assertThat(unwrappedMessageContent.getMessageMetadata().getTimestamp()).isEqualTo(timestamp); + assertThat(unwrappedMessageContent.getContent()).contains(content); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldWrappedMessageContainsMetadata() { + // when + byte[] wrappedMessage = + avroMessageContentWrapper.wrapContent( + content, id, timestamp, avroUser.getSchema(), Collections.emptyMap()); + + // then + GenericRecord messageWithMetadata = bytesToRecord(wrappedMessage, avroUser.getSchema()); + Map metadata = (Map) messageWithMetadata.get(METADATA_MARKER); + assertThat(metadata.get(METADATA_MESSAGE_ID_KEY).toString()).isEqualTo(id); + assertThat(valueOf(metadata.get(METADATA_TIMESTAMP_KEY).toString())).isEqualTo(timestamp); + assertThat(wrappedMessage).contains(content); + } + + @Test + public void shouldWrappedMessageBeUnchangedWhenSchemaNotContainsMetadata() throws IOException { + // when + Schema schemaWithoutMetadata = + new Schema.Parser() + .parse( + IOUtils.toString( + getClass().getResourceAsStream("/schema/user_no_metadata.avsc"), + StandardCharsets.UTF_8)); + byte[] wrappedMessage = + avroMessageContentWrapper.wrapContent( + content, id, timestamp, schemaWithoutMetadata, Collections.emptyMap()); + + // then + GenericRecord wrappedMessageAsRecord = bytesToRecord(wrappedMessage, avroUser.getSchema()); + assertThat(wrappedMessageAsRecord.get(METADATA_MARKER)).isNull(); + } + + @Test + public void shouldUnwrapAvroMessageAndGenerateMetadataWhenNotExists() throws Throwable { + // given + byte[] wrappedMessage = wrapContentWithoutMetadata(content, avroUser.getSchema()); + + // when + UnwrappedMessageContent unwrappedMessage = + avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); + + // then + assertThat(unwrappedMessage.getMessageMetadata().getId()).isEmpty(); + assertThat(unwrappedMessage.getMessageMetadata().getTimestamp()).isNotNull(); + assertThat(unwrappedMessage.getContent()).startsWith(content); + } + + @Test + public void shouldUnwrapAvroMessageAndSetEmptyMessageIdWhenNotGivenInMetadata() throws Throwable { + // given + byte[] wrappedMessage = wrapContentWithoutMessageIdInMetadata(content, avroUser.getSchema()); + + // when + UnwrappedMessageContent unwrappedMessage = + avroMessageContentWrapper.unwrapContent(wrappedMessage, avroUser.getCompiledSchema()); + + // then + assertThat(unwrappedMessage.getMessageMetadata().getId()).isEmpty(); + assertThat(unwrappedMessage.getMessageMetadata().getTimestamp()).isNotNull(); + assertThat(unwrappedMessage.getContent()).contains(content); + } + + private byte[] wrapContentWithoutMetadata(byte[] message, Schema schema) throws Exception { + return wrapContent(message, schema, null); + } + + private byte[] wrapContentWithoutMessageIdInMetadata(byte[] message, Schema schema) + throws Exception { + return wrapContent(message, schema, metadataMapWithoutMessageId(timestamp)); + } + + private byte[] wrapContent(byte[] message, Schema schema, Map metadata) + throws Exception { + GenericRecord genericRecord = bytesToRecord(message, schema); + genericRecord.put(METADATA_MARKER, metadata); + return recordToBytes(genericRecord, schema); + } + + private Map metadataMapWithoutMessageId(long timestamp) { + Map metadata = new HashMap<>(); + metadata.put(METADATA_TIMESTAMP_KEY, new Utf8(Long.toString(timestamp))); + return metadata; + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapperTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapperTest.java index c699023402..e6d332bffe 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapperTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/JsonMessageContentWrapperTest.java @@ -1,77 +1,87 @@ package pl.allegro.tech.hermes.common.message.wrapper; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; + import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; -import org.assertj.core.data.MapEntry; -import org.junit.Ignore; -import org.junit.Test; - import java.io.IOException; import java.util.Collections; import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; +import org.assertj.core.data.MapEntry; +import org.junit.Ignore; +import org.junit.Test; public class JsonMessageContentWrapperTest { - private final byte[] testContent = "{\"key\":\"value\"}".getBytes(); - private final ObjectMapper mapper = new ObjectMapper(); - private final MessageMetadata metadata = - new MessageMetadata(System.currentTimeMillis(), "14cf17ea-f1ea-a464-6bd6478615bb", ImmutableMap.of()); - private final Map metadataAsMap = - ImmutableMap.of("timestamp", metadata.getTimestamp(), "id", metadata.getId(), "externalMetadata", Collections.emptyMap()); - private final MapEntry unwrappingMarker = entry("_w", true); - private final MapEntry content = entry("message", readMap(testContent)); + private final byte[] testContent = "{\"key\":\"value\"}".getBytes(); + private final ObjectMapper mapper = new ObjectMapper(); + private final MessageMetadata metadata = + new MessageMetadata( + System.currentTimeMillis(), "14cf17ea-f1ea-a464-6bd6478615bb", ImmutableMap.of()); + private final Map metadataAsMap = + ImmutableMap.of( + "timestamp", + metadata.getTimestamp(), + "id", + metadata.getId(), + "externalMetadata", + Collections.emptyMap()); + private final MapEntry unwrappingMarker = entry("_w", true); + private final MapEntry content = entry("message", readMap(testContent)); - private final JsonMessageContentWrapper contentWrapper = new JsonMessageContentWrapper(content.key.toString(), "metadata", mapper); - private Map externalMetadata = ImmutableMap.of(); + private final JsonMessageContentWrapper contentWrapper = + new JsonMessageContentWrapper(content.key.toString(), "metadata", mapper); + private Map externalMetadata = ImmutableMap.of(); - @Test - @SuppressWarnings("unchecked") - public void shouldWrapJsonWithMetadata() { - //when - byte[] result = contentWrapper.wrapContent(testContent, metadata.getId(), metadata.getTimestamp(), externalMetadata); + @Test + @SuppressWarnings("unchecked") + public void shouldWrapJsonWithMetadata() { + // when + byte[] result = + contentWrapper.wrapContent( + testContent, metadata.getId(), metadata.getTimestamp(), externalMetadata); - //then - assertThat(readMap(result)).containsExactly(unwrappingMarker, entry("metadata", metadataAsMap), content); - } + // then + assertThat(readMap(result)) + .containsExactly(unwrappingMarker, entry("metadata", metadataAsMap), content); + } - @Test - public void shouldUnwrapMessageWithMetadata() { - //when - UnwrappedMessageContent result = contentWrapper.unwrapContent( - contentWrapper.wrapContent(testContent, metadata.getId(), metadata.getTimestamp(), externalMetadata) - ); + @Test + public void shouldUnwrapMessageWithMetadata() { + // when + UnwrappedMessageContent result = + contentWrapper.unwrapContent( + contentWrapper.wrapContent( + testContent, metadata.getId(), metadata.getTimestamp(), externalMetadata)); - //then - assertThat(result.getContent()).isEqualTo(testContent); - assertThat(result.getMessageMetadata()).isEqualTo(metadata); - } + // then + assertThat(result.getContent()).isEqualTo(testContent); + assertThat(result.getMessageMetadata()).isEqualTo(metadata); + } - @Test - public void shouldTolerateUnwrappingUnwrappedMessage() { - //when - UnwrappedMessageContent result = contentWrapper.unwrapContent(testContent); + @Test + public void shouldTolerateUnwrappingUnwrappedMessage() { + // when + UnwrappedMessageContent result = contentWrapper.unwrapContent(testContent); - //then - assertThat(result.getMessageMetadata().getId()).isNotEmpty(); - assertThat(result.getMessageMetadata().getTimestamp()).isEqualTo(1L); - } + // then + assertThat(result.getMessageMetadata().getId()).isNotEmpty(); + assertThat(result.getMessageMetadata().getTimestamp()).isEqualTo(1L); + } - @Ignore - @Test(expected = UnwrappingException.class) - public void shouldThrowExceptionWhenMetadataNotFound() { - contentWrapper.unwrapContent(testContent); - } + @Ignore + @Test(expected = UnwrappingException.class) + public void shouldThrowExceptionWhenMetadataNotFound() { + contentWrapper.unwrapContent(testContent); + } - private Map readMap(byte[] result) { - try { - return mapper.readValue(new String(result), new TypeReference>(){}); - } catch (IOException e) { - throw new IllegalStateException("Error while reading map", e); - } + private Map readMap(byte[] result) { + try { + return mapper.readValue(new String(result), new TypeReference>() {}); + } catch (IOException e) { + throw new IllegalStateException("Error while reading map", e); } - + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapperTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapperTest.java index 3a38d65ca8..2f8507021f 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapperTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/MessageContentWrapperTest.java @@ -1,20 +1,24 @@ package pl.allegro.tech.hermes.common.message.wrapper; -import com.codahale.metrics.MetricRegistry; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static pl.allegro.tech.hermes.test.helper.avro.AvroUserSchemaLoader.load; +import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; + import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.search.Search; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.nio.ByteBuffer; +import java.time.Clock; +import java.util.List; import org.apache.avro.Schema; import org.apache.commons.collections4.map.HashedMap; -import org.junit.Before; import org.junit.Test; import pl.allegro.tech.hermes.api.Topic; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; -import pl.allegro.tech.hermes.metrics.PathsCompiler; import pl.allegro.tech.hermes.schema.CompiledSchema; import pl.allegro.tech.hermes.schema.CompiledSchemaRepository; import pl.allegro.tech.hermes.schema.SchemaId; @@ -25,386 +29,418 @@ import pl.allegro.tech.hermes.test.helper.avro.AvroUser; import pl.allegro.tech.hermes.test.helper.metrics.MicrometerUtils; -import java.nio.ByteBuffer; -import java.time.Clock; -import java.util.List; - -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static pl.allegro.tech.hermes.test.helper.avro.AvroUserSchemaLoader.load; -import static pl.allegro.tech.hermes.test.helper.builder.TopicBuilder.topic; - public class MessageContentWrapperTest { - private static final Integer NO_VERSION_IN_HEADER = null; - private static final Integer NO_ID_IN_HEADER = null; - private static final int MESSAGE_TIMESTAMP = 1582029457; - private static final HashedMap NO_EXTERNAL_METADATA = new HashedMap<>(); - private static final String MESSAGE_ID = "messageId-1"; - private static final int VERSION_ONE = 1; - private static final int VERSION_TWO = 2; - private static final int VERSION_THREE = 3; - private static final int ID_ONE = 1; - private static final int ID_THREE = 3; - private static final int ID_FIVE = 5; - - private final MetricRegistry metricRegistry = new MetricRegistry(); - private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); - private final HermesMetrics hermesMetrics = new HermesMetrics(metricRegistry, new PathsCompiler("")); - private final MetricsFacade metricsFacade = new MetricsFacade(meterRegistry, hermesMetrics); - private final JsonMessageContentWrapper jsonWrapper = new JsonMessageContentWrapper("message", "metadata", new ObjectMapper()); - private final AvroMessageContentWrapper avroWrapper = new AvroMessageContentWrapper(Clock.systemDefaultZone()); - - - private final AvroMessageHeaderSchemaVersionContentWrapper headerSchemaVersionWrapper = - new AvroMessageHeaderSchemaVersionContentWrapper(schemaRepository, avroWrapper, metricsFacade); - private final AvroMessageSchemaIdAwareContentWrapper schemaAwareWrapper = - new AvroMessageSchemaIdAwareContentWrapper(schemaRepository, avroWrapper, metricsFacade); - - private static final CompiledSchema schema1 = CompiledSchema.of(load("/schema/user.avsc"), ID_ONE, VERSION_ONE); - private static final CompiledSchema schema2 = CompiledSchema.of(load("/schema/user_v2.avsc"), ID_THREE, VERSION_TWO); - private static final CompiledSchema schema3 = CompiledSchema.of(load("/schema/user_v3.avsc"), ID_FIVE, VERSION_THREE); - - private CompositeMessageContentWrapper createMessageContentWrapper( - boolean schemaHeaderIdEnabled, - boolean schemaVersionTruncationEnabled - ) { - - final AvroMessageHeaderSchemaIdContentWrapper headerSchemaIdWrapper = - new AvroMessageHeaderSchemaIdContentWrapper(schemaRepository, avroWrapper, metricsFacade, schemaHeaderIdEnabled); - - final AvroMessageSchemaVersionTruncationContentWrapper schemaIdAndHeaderContentWrapper = - new AvroMessageSchemaVersionTruncationContentWrapper( - schemaRepository, - avroWrapper, - metricsFacade, - schemaVersionTruncationEnabled - ); - - return new CompositeMessageContentWrapper(jsonWrapper, avroWrapper, schemaAwareWrapper, - headerSchemaVersionWrapper, headerSchemaIdWrapper, schemaIdAndHeaderContentWrapper); - } - - private final CompositeMessageContentWrapper compositeMessageContentWrapper = createMessageContentWrapper(false, true); - - static SchemaVersionsRepository schemaVersionsRepository = new SchemaVersionsRepository() { + private static final Integer NO_VERSION_IN_HEADER = null; + private static final Integer NO_ID_IN_HEADER = null; + private static final int MESSAGE_TIMESTAMP = 1582029457; + private static final HashedMap NO_EXTERNAL_METADATA = new HashedMap<>(); + private static final String MESSAGE_ID = "messageId-1"; + private static final int VERSION_ONE = 1; + private static final int VERSION_TWO = 2; + private static final int VERSION_THREE = 3; + private static final int ID_ONE = 1; + private static final int ID_THREE = 3; + private static final int ID_FIVE = 5; + + private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); + private final MetricsFacade metricsFacade = new MetricsFacade(meterRegistry); + private final JsonMessageContentWrapper jsonWrapper = + new JsonMessageContentWrapper("message", "metadata", new ObjectMapper()); + private final AvroMessageContentWrapper avroWrapper = + new AvroMessageContentWrapper(Clock.systemDefaultZone()); + + private final AvroMessageHeaderSchemaVersionContentWrapper headerSchemaVersionWrapper = + new AvroMessageHeaderSchemaVersionContentWrapper( + schemaRepository, avroWrapper, metricsFacade); + private final AvroMessageSchemaIdAwareContentWrapper schemaAwareWrapper = + new AvroMessageSchemaIdAwareContentWrapper(schemaRepository, avroWrapper, metricsFacade); + + private static final CompiledSchema schema1 = + CompiledSchema.of(load("/schema/user.avsc"), ID_ONE, VERSION_ONE); + private static final CompiledSchema schema2 = + CompiledSchema.of(load("/schema/user_v2.avsc"), ID_THREE, VERSION_TWO); + private static final CompiledSchema schema3 = + CompiledSchema.of(load("/schema/user_v3.avsc"), ID_FIVE, VERSION_THREE); + + private CompositeMessageContentWrapper createMessageContentWrapper( + boolean schemaHeaderIdEnabled, boolean schemaVersionTruncationEnabled) { + + final AvroMessageHeaderSchemaIdContentWrapper headerSchemaIdWrapper = + new AvroMessageHeaderSchemaIdContentWrapper( + schemaRepository, avroWrapper, metricsFacade, schemaHeaderIdEnabled); + + final AvroMessageSchemaVersionTruncationContentWrapper schemaIdAndHeaderContentWrapper = + new AvroMessageSchemaVersionTruncationContentWrapper( + schemaRepository, avroWrapper, metricsFacade, schemaVersionTruncationEnabled); + + return new CompositeMessageContentWrapper( + jsonWrapper, + avroWrapper, + schemaAwareWrapper, + headerSchemaVersionWrapper, + headerSchemaIdWrapper, + schemaIdAndHeaderContentWrapper); + } + + private final CompositeMessageContentWrapper compositeMessageContentWrapper = + createMessageContentWrapper(false, true); + + static SchemaVersionsRepository schemaVersionsRepository = + new SchemaVersionsRepository() { @Override public SchemaVersionsResult versions(Topic topic, boolean online) { - List onlineVersions = asList(schema3.getVersion(), schema2.getVersion(), schema1.getVersion()); - List cachedVersions = asList(schema2.getVersion(), schema1.getVersion()); - return online - ? SchemaVersionsResult.succeeded(onlineVersions) - : SchemaVersionsResult.succeeded(cachedVersions); + List onlineVersions = + asList(schema3.getVersion(), schema2.getVersion(), schema1.getVersion()); + List cachedVersions = asList(schema2.getVersion(), schema1.getVersion()); + return online + ? SchemaVersionsResult.succeeded(onlineVersions) + : SchemaVersionsResult.succeeded(cachedVersions); } @Override - public void close() { - } - }; + public void close() {} + }; - static CompiledSchemaRepository compiledSchemaRepository = new CompiledSchemaRepository() { + static CompiledSchemaRepository compiledSchemaRepository = + new CompiledSchemaRepository() { @Override - public CompiledSchema getSchema(Topic topic, SchemaVersion version, boolean online) { - switch (version.value()) { - case VERSION_ONE: - return schema1; - case VERSION_TWO: - return schema2; - case VERSION_THREE: - return schema3; - default: - throw new RuntimeException("sry"); - } + public CompiledSchema getSchema( + Topic topic, SchemaVersion version, boolean online) { + switch (version.value()) { + case VERSION_ONE: + return schema1; + case VERSION_TWO: + return schema2; + case VERSION_THREE: + return schema3; + default: + throw new RuntimeException("sry"); + } } @Override public CompiledSchema getSchema(Topic topic, SchemaId id) { - switch (id.value()) { - case ID_ONE: - return schema1; - case ID_THREE: - return schema2; - case ID_FIVE: - return schema3; - default: - throw new RuntimeException("sry"); - } + switch (id.value()) { + case ID_ONE: + return schema1; + case ID_THREE: + return schema2; + case ID_FIVE: + return schema3; + default: + throw new RuntimeException("sry"); + } } - }; - - static SchemaRepository schemaRepository = new SchemaRepository(schemaVersionsRepository, compiledSchemaRepository); - - - @Before - public void clean() { - metricRegistry.getCounters().forEach((s, counter) -> counter.dec(counter.getCount())); - } - - @Test - public void shouldUnwrapMessageUsingSchemaIdFromPayload() { - // given - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaId schemaId = createSchemaId(ID_FIVE); - Topic topic = createTopicWithSchemaIdAwarePayload(); - AvroUser user = createAvroUser(schemaId, topic); - - byte[] wrapped = compositeMessageContentWrapper.wrapAvro( - user.asBytes(), - messageId, - messageTimestamp, - topic, - user.getCompiledSchema(), - NO_EXTERNAL_METADATA - ); - - // when - UnwrappedMessageContent unwrappedMessageContent = - compositeMessageContentWrapper.unwrapAvro(wrapped, topic, NO_ID_IN_HEADER, NO_VERSION_IN_HEADER); - - // then - assertResult(unwrappedMessageContent, schemaId, user.asBytes(), messageId, messageTimestamp); - assertMetrics(0, 0, 0, 0, 0, 1, 0, 0, 0); - } - - @Test - public void shouldUnwrapUsingHeaderSchemaVersionIfHeaderPresent() { - // given - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaVersion schemaVersion = createSchemaVersion(VERSION_TWO); - Topic topic = createTopic(); - AvroUser user = createAvroUser(schemaVersion, topic); - - byte[] wrapped = compositeMessageContentWrapper.wrapAvro( - user.asBytes(), - messageId, - messageTimestamp, - topic, - user.getCompiledSchema(), - NO_EXTERNAL_METADATA - ); - - // when - UnwrappedMessageContent unwrappedMessageContent = - compositeMessageContentWrapper.unwrapAvro(wrapped, topic, NO_ID_IN_HEADER, schemaVersion.value()); - - // then - assertResult(unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); - assertMetrics(0, 0, 0, 0, 0, 0, 1, 0, 0); - } - - @Test - public void shouldUnwrapUsingHeaderSchemaIdIfHeaderPresent() { - // given - CompositeMessageContentWrapper compositeMessageContentWrapperWithHeaderEnabled = createMessageContentWrapper(true, false); - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaId schemaId = createSchemaId(ID_THREE); - Topic topic = createTopic(); - AvroUser user = createAvroUser(schemaId, topic); - - byte[] wrapped = compositeMessageContentWrapperWithHeaderEnabled - .wrapAvro(user.asBytes(), messageId, messageTimestamp, topic, user.getCompiledSchema(), NO_EXTERNAL_METADATA); - - // when - UnwrappedMessageContent unwrappedMessageContent = compositeMessageContentWrapperWithHeaderEnabled - .unwrapAvro(wrapped, topic, schemaId.value(), NO_VERSION_IN_HEADER); - - // then - assertResult(unwrappedMessageContent, schemaId, user.asBytes(), messageId, messageTimestamp); - assertMetrics(0, 0, 0, 0, 0, 0, 0, 1, 0); - } - - @Test - public void shouldUnwrapUsingSchemaIdAwareIfVersionAndIdInSchemaPresentDespiteServiceHeaderPresent() { - // given - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaId schemaId = createSchemaId(ID_THREE); - Topic topic = createTopicWithSchemaIdAwarePayload(); - AvroUser user = createAvroUser(schemaId, topic); - CompiledSchema schema = user.getCompiledSchema(); - - byte[] wrapped = - compositeMessageContentWrapper.wrapAvro(user.asBytes(), messageId, messageTimestamp, topic, schema, NO_EXTERNAL_METADATA); - - // when - UnwrappedMessageContent unwrappedMessageContent = - compositeMessageContentWrapper.unwrapAvro(wrapped, topic, schema.getId().value(), schema.getVersion().value()); - - // then - assertResult(unwrappedMessageContent, schema.getVersion(), user.asBytes(), messageId, messageTimestamp); - assertMetrics(0, 0, 0, 0, 0, 1, 0, 0, 0); - } - - @Test - public void shouldUnwrapUsingHeaderSchemaVersionIfHeaderPresentAndNoMagicByte() { - // given - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaVersion schemaVersion = createSchemaVersion(VERSION_TWO); // no magic byte - Topic topicToWrap = createTopic(); - AvroUser user = createAvroUser(schemaVersion, topicToWrap); - - byte[] wrapped = - compositeMessageContentWrapper - .wrapAvro(user.asBytes(), messageId, messageTimestamp, topicToWrap, user.getCompiledSchema(), NO_EXTERNAL_METADATA); - - Topic topicToUnwrap = createTopicWithSchemaIdAwarePayload(); - - // when - UnwrappedMessageContent unwrappedMessageContent = - compositeMessageContentWrapper.unwrapAvro(wrapped, topicToUnwrap, NO_ID_IN_HEADER, schemaVersion.value()); - - // then - assertResult(unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); - - // missedSchemaVersionInPayload == no magic byte - assertMetrics(1, 0, 0, 0, 0, 0, 1, 0, 0); - } - - @Test - public void shouldTrimSchemaVersionFromMessageWhenSchemaVersionPresentInHeader() { - // given - String messageId = MESSAGE_ID; - int messageTimestamp = MESSAGE_TIMESTAMP; - - SchemaVersion schemaVersion = createSchemaVersion(VERSION_ONE); - Topic topic = createTopic(); - AvroUser user = createAvroUser(schemaVersion, topic); - - byte[] message = - compositeMessageContentWrapper - .wrapAvro(user.asBytes(), messageId, messageTimestamp, topic, user.getCompiledSchema(), NO_EXTERNAL_METADATA); - byte[] wrapped = serializeWithSchemaVersionInPayload(schemaVersion, message); - - // when - UnwrappedMessageContent unwrappedMessageContent = - compositeMessageContentWrapper.unwrapAvro(wrapped, topic, NO_ID_IN_HEADER, VERSION_ONE); - - // then - assertResult(unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); - assertMetrics(0, 0, 0, 0, 0, 0, 0, 0, 1); - } - - private void assertResult(UnwrappedMessageContent result, - SchemaVersion schemaVersion, - byte[] recordBytes, - String messageId, - int timestamp) { - assertThat(result.getSchema().get().getVersion()).isEqualTo(schemaVersion); - assertResult(result, recordBytes, messageId, timestamp); - } - - private void assertResult(UnwrappedMessageContent result, - SchemaId schemaId, - byte[] recordBytes, - String messageId, - int timestamp) { - assertThat(result.getSchema().get().getId()).isEqualTo(schemaId); - assertResult(result, recordBytes, messageId, timestamp); - } - - private void assertResult(UnwrappedMessageContent result, byte[] recordBytes, String messageId, int timestamp) { - assertThat(result.getContent()).contains(recordBytes); - assertThat(result.getMessageMetadata().getId()).isEqualTo(messageId); - assertThat(result.getMessageMetadata().getTimestamp()).isEqualTo(timestamp); - } - - private void assertMetrics(int missedSchemaIdInPayload, - int errorsForPayloadWithSchemaId, - int errorsForHeaderSchemaVersion, - int errorsForHeaderSchemaId, - int errorsWithSchemaVersionTruncation, - int usingSchemaIdAware, - int usingHeaderSchemaVersion, - int usingHeaderSchemaId, - int usingSchemaVersionTruncation) { - final String basePath = "content.avro.deserialization"; - assertThat(metricRegistryCounterValue(basePath + ".missed.schemaIdInPayload")).isEqualTo(missedSchemaIdInPayload); - assertThat(meterRegistryCounterValue(basePath + ".missing_schemaIdInPayload", Tags.empty())) - .isEqualTo(missedSchemaIdInPayload); - - assertThat(metricRegistryCounterValue(basePath + ".errors.payloadWithSchemaId")).isEqualTo(errorsForPayloadWithSchemaId); - assertThat(meterRegistryCounterValue(basePath + ".errors", Tags.of("deserialization_type", "payloadWithSchemaId"))) - .isEqualTo(errorsForPayloadWithSchemaId); - - assertThat(metricRegistryCounterValue(basePath + ".errors.headerSchemaVersion")).isEqualTo(errorsForHeaderSchemaVersion); - assertThat(meterRegistryCounterValue(basePath + ".errors", Tags.of("deserialization_type", "headerSchemaVersion"))) - .isEqualTo(errorsForHeaderSchemaVersion); - - assertThat(metricRegistryCounterValue(basePath + ".errors.headerSchemaId")).isEqualTo(errorsForHeaderSchemaId); - assertThat(meterRegistryCounterValue(basePath + ".errors", Tags.of("deserialization_type", "headerSchemaId"))) - .isEqualTo(errorsForHeaderSchemaId); - - assertThat(metricRegistryCounterValue(basePath + ".errors.schemaVersionTruncation")).isEqualTo(errorsWithSchemaVersionTruncation); - assertThat(meterRegistryCounterValue(basePath + ".errors", Tags.of("deserialization_type", "schemaVersionTruncation"))) - .isEqualTo(errorsWithSchemaVersionTruncation); - - assertThat(metricRegistryCounterValue(basePath + ".using.schemaIdAware")).isEqualTo(usingSchemaIdAware); - assertThat(meterRegistryCounterValue(basePath, Tags.of("deserialization_type", "payloadWithSchemaId"))) - .isEqualTo(usingSchemaIdAware); - - assertThat(metricRegistryCounterValue(basePath + ".using.headerSchemaVersion")).isEqualTo(usingHeaderSchemaVersion); - assertThat(meterRegistryCounterValue(basePath, Tags.of("deserialization_type", "headerSchemaVersion"))) - .isEqualTo(usingHeaderSchemaVersion); - - assertThat(metricRegistryCounterValue(basePath + ".using.headerSchemaId")).isEqualTo(usingHeaderSchemaId); - assertThat(meterRegistryCounterValue(basePath, Tags.of("deserialization_type", "headerSchemaId"))).isEqualTo(usingHeaderSchemaId); - - assertThat(metricRegistryCounterValue(basePath + ".using.schemaVersionTruncation")).isEqualTo(usingSchemaVersionTruncation); - assertThat(meterRegistryCounterValue(basePath, Tags.of("deserialization_type", "schemaVersionTruncation"))) - .isEqualTo(usingSchemaVersionTruncation); - } - - private long metricRegistryCounterValue(String metricName) { - return metricRegistry.counter(metricName).getCount(); - } - - private int meterRegistryCounterValue(String metricName, Tags tags) { - return MicrometerUtils.metricValue(meterRegistry, metricName, tags, Search::counter, Counter::count).orElse(0.0d).intValue(); - } - - - private Topic createTopic() { - return topic("group", "topic").build(); - } - - private Topic createTopicWithSchemaIdAwarePayload() { - return topic("group", "topic-idAware").withSchemaIdAwareSerialization().build(); - } - - private AvroUser createAvroUser(SchemaVersion schemaVersion, Topic topic) { - CompiledSchema schema = schemaRepository.getKnownAvroSchemaVersion(topic, schemaVersion); - return new AvroUser(schema, "user-1", 15, "colour-1"); - } - - private AvroUser createAvroUser(SchemaId id, Topic topic) { - CompiledSchema schema = schemaRepository.getAvroSchema(topic, id); - return new AvroUser(schema, "user-1", 15, "colour-1"); - } - - private static SchemaVersion createSchemaVersion(int schemaVersionValue) { - return SchemaVersion.valueOf(schemaVersionValue); - } - - private static SchemaId createSchemaId(int schemaIdValue) { - return SchemaId.valueOf(schemaIdValue); - } - - private static byte[] serializeWithSchemaVersionInPayload(SchemaVersion schemaVersion, byte[] data) { - final int HEADER_SIZE = 5; - final byte MAGIC_BYTE_VALUE = 0; - ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + data.length); - buffer.put(MAGIC_BYTE_VALUE); - buffer.putInt(schemaVersion.value()); - buffer.put(data); - return buffer.array(); - } + }; + + static SchemaRepository schemaRepository = + new SchemaRepository(schemaVersionsRepository, compiledSchemaRepository); + + @Test + public void shouldUnwrapMessageUsingSchemaIdFromPayload() { + // given + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaId schemaId = createSchemaId(ID_FIVE); + Topic topic = createTopicWithSchemaIdAwarePayload(); + AvroUser user = createAvroUser(schemaId, topic); + + byte[] wrapped = + compositeMessageContentWrapper.wrapAvro( + user.asBytes(), + messageId, + messageTimestamp, + topic, + user.getCompiledSchema(), + NO_EXTERNAL_METADATA); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapper.unwrapAvro( + wrapped, topic, NO_ID_IN_HEADER, NO_VERSION_IN_HEADER); + + // then + assertResult(unwrappedMessageContent, schemaId, user.asBytes(), messageId, messageTimestamp); + assertMetrics(0, 0, 0, 0, 0, 1, 0, 0, 0); + } + + @Test + public void shouldUnwrapUsingHeaderSchemaVersionIfHeaderPresent() { + // given + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaVersion schemaVersion = createSchemaVersion(VERSION_TWO); + Topic topic = createTopic(); + AvroUser user = createAvroUser(schemaVersion, topic); + + byte[] wrapped = + compositeMessageContentWrapper.wrapAvro( + user.asBytes(), + messageId, + messageTimestamp, + topic, + user.getCompiledSchema(), + NO_EXTERNAL_METADATA); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapper.unwrapAvro( + wrapped, topic, NO_ID_IN_HEADER, schemaVersion.value()); + + // then + assertResult( + unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); + assertMetrics(0, 0, 0, 0, 0, 0, 1, 0, 0); + } + + @Test + public void shouldUnwrapUsingHeaderSchemaIdIfHeaderPresent() { + // given + CompositeMessageContentWrapper compositeMessageContentWrapperWithHeaderEnabled = + createMessageContentWrapper(true, false); + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaId schemaId = createSchemaId(ID_THREE); + Topic topic = createTopic(); + AvroUser user = createAvroUser(schemaId, topic); + + byte[] wrapped = + compositeMessageContentWrapperWithHeaderEnabled.wrapAvro( + user.asBytes(), + messageId, + messageTimestamp, + topic, + user.getCompiledSchema(), + NO_EXTERNAL_METADATA); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapperWithHeaderEnabled.unwrapAvro( + wrapped, topic, schemaId.value(), NO_VERSION_IN_HEADER); + + // then + assertResult(unwrappedMessageContent, schemaId, user.asBytes(), messageId, messageTimestamp); + assertMetrics(0, 0, 0, 0, 0, 0, 0, 1, 0); + } + + @Test + public void + shouldUnwrapUsingSchemaIdAwareIfVersionAndIdInSchemaPresentDespiteServiceHeaderPresent() { + // given + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaId schemaId = createSchemaId(ID_THREE); + Topic topic = createTopicWithSchemaIdAwarePayload(); + AvroUser user = createAvroUser(schemaId, topic); + CompiledSchema schema = user.getCompiledSchema(); + + byte[] wrapped = + compositeMessageContentWrapper.wrapAvro( + user.asBytes(), messageId, messageTimestamp, topic, schema, NO_EXTERNAL_METADATA); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapper.unwrapAvro( + wrapped, topic, schema.getId().value(), schema.getVersion().value()); + + // then + assertResult( + unwrappedMessageContent, schema.getVersion(), user.asBytes(), messageId, messageTimestamp); + assertMetrics(0, 0, 0, 0, 0, 1, 0, 0, 0); + } + + @Test + public void shouldUnwrapUsingHeaderSchemaVersionIfHeaderPresentAndNoMagicByte() { + // given + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaVersion schemaVersion = createSchemaVersion(VERSION_TWO); // no magic byte + Topic topicToWrap = createTopic(); + AvroUser user = createAvroUser(schemaVersion, topicToWrap); + + byte[] wrapped = + compositeMessageContentWrapper.wrapAvro( + user.asBytes(), + messageId, + messageTimestamp, + topicToWrap, + user.getCompiledSchema(), + NO_EXTERNAL_METADATA); + + Topic topicToUnwrap = createTopicWithSchemaIdAwarePayload(); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapper.unwrapAvro( + wrapped, topicToUnwrap, NO_ID_IN_HEADER, schemaVersion.value()); + + // then + assertResult( + unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); + + // missedSchemaVersionInPayload == no magic byte + assertMetrics(1, 0, 0, 0, 0, 0, 1, 0, 0); + } + + @Test + public void shouldTrimSchemaVersionFromMessageWhenSchemaVersionPresentInHeader() { + // given + String messageId = MESSAGE_ID; + int messageTimestamp = MESSAGE_TIMESTAMP; + + SchemaVersion schemaVersion = createSchemaVersion(VERSION_ONE); + Topic topic = createTopic(); + AvroUser user = createAvroUser(schemaVersion, topic); + + byte[] message = + compositeMessageContentWrapper.wrapAvro( + user.asBytes(), + messageId, + messageTimestamp, + topic, + user.getCompiledSchema(), + NO_EXTERNAL_METADATA); + byte[] wrapped = serializeWithSchemaVersionInPayload(schemaVersion, message); + + // when + UnwrappedMessageContent unwrappedMessageContent = + compositeMessageContentWrapper.unwrapAvro(wrapped, topic, NO_ID_IN_HEADER, VERSION_ONE); + + // then + assertResult( + unwrappedMessageContent, schemaVersion, user.asBytes(), messageId, messageTimestamp); + assertMetrics(0, 0, 0, 0, 0, 0, 0, 0, 1); + } + + private void assertResult( + UnwrappedMessageContent result, + SchemaVersion schemaVersion, + byte[] recordBytes, + String messageId, + int timestamp) { + assertThat(result.getSchema().get().getVersion()).isEqualTo(schemaVersion); + assertResult(result, recordBytes, messageId, timestamp); + } + + private void assertResult( + UnwrappedMessageContent result, + SchemaId schemaId, + byte[] recordBytes, + String messageId, + int timestamp) { + assertThat(result.getSchema().get().getId()).isEqualTo(schemaId); + assertResult(result, recordBytes, messageId, timestamp); + } + + private void assertResult( + UnwrappedMessageContent result, byte[] recordBytes, String messageId, int timestamp) { + assertThat(result.getContent()).contains(recordBytes); + assertThat(result.getMessageMetadata().getId()).isEqualTo(messageId); + assertThat(result.getMessageMetadata().getTimestamp()).isEqualTo(timestamp); + } + + private void assertMetrics( + int missedSchemaIdInPayload, + int errorsForPayloadWithSchemaId, + int errorsForHeaderSchemaVersion, + int errorsForHeaderSchemaId, + int errorsWithSchemaVersionTruncation, + int usingSchemaIdAware, + int usingHeaderSchemaVersion, + int usingHeaderSchemaId, + int usingSchemaVersionTruncation) { + final String basePath = "content.avro.deserialization"; + assertThat(meterRegistryCounterValue(basePath + ".missing_schemaIdInPayload", Tags.empty())) + .isEqualTo(missedSchemaIdInPayload); + + assertThat( + meterRegistryCounterValue( + basePath + ".errors", Tags.of("deserialization_type", "payloadWithSchemaId"))) + .isEqualTo(errorsForPayloadWithSchemaId); + + assertThat( + meterRegistryCounterValue( + basePath + ".errors", Tags.of("deserialization_type", "headerSchemaVersion"))) + .isEqualTo(errorsForHeaderSchemaVersion); + + assertThat( + meterRegistryCounterValue( + basePath + ".errors", Tags.of("deserialization_type", "headerSchemaId"))) + .isEqualTo(errorsForHeaderSchemaId); + + assertThat( + meterRegistryCounterValue( + basePath + ".errors", Tags.of("deserialization_type", "schemaVersionTruncation"))) + .isEqualTo(errorsWithSchemaVersionTruncation); + + assertThat( + meterRegistryCounterValue( + basePath, Tags.of("deserialization_type", "payloadWithSchemaId"))) + .isEqualTo(usingSchemaIdAware); + + assertThat( + meterRegistryCounterValue( + basePath, Tags.of("deserialization_type", "headerSchemaVersion"))) + .isEqualTo(usingHeaderSchemaVersion); + + assertThat( + meterRegistryCounterValue(basePath, Tags.of("deserialization_type", "headerSchemaId"))) + .isEqualTo(usingHeaderSchemaId); + + assertThat( + meterRegistryCounterValue( + basePath, Tags.of("deserialization_type", "schemaVersionTruncation"))) + .isEqualTo(usingSchemaVersionTruncation); + } + + private int meterRegistryCounterValue(String metricName, Tags tags) { + return MicrometerUtils.metricValue( + meterRegistry, metricName, tags, Search::counter, Counter::count) + .orElse(0.0d) + .intValue(); + } + + private Topic createTopic() { + return topic("group", "topic").build(); + } + + private Topic createTopicWithSchemaIdAwarePayload() { + return topic("group", "topic-idAware").withSchemaIdAwareSerialization().build(); + } + + private AvroUser createAvroUser(SchemaVersion schemaVersion, Topic topic) { + CompiledSchema schema = + schemaRepository.getKnownAvroSchemaVersion(topic, schemaVersion); + return new AvroUser(schema, "user-1", 15, "colour-1"); + } + + private AvroUser createAvroUser(SchemaId id, Topic topic) { + CompiledSchema schema = schemaRepository.getAvroSchema(topic, id); + return new AvroUser(schema, "user-1", 15, "colour-1"); + } + + private static SchemaVersion createSchemaVersion(int schemaVersionValue) { + return SchemaVersion.valueOf(schemaVersionValue); + } + + private static SchemaId createSchemaId(int schemaIdValue) { + return SchemaId.valueOf(schemaIdValue); + } + + private static byte[] serializeWithSchemaVersionInPayload( + SchemaVersion schemaVersion, byte[] data) { + final int HEADER_SIZE = 5; + final byte MAGIC_BYTE_VALUE = 0; + ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE + data.length); + buffer.put(MAGIC_BYTE_VALUE); + buffer.putInt(schemaVersion.value()); + buffer.put(data); + return buffer.array(); + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDeTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDeTest.java index 3ffa2ba9e2..8efa9d7212 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDeTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/message/wrapper/SchemaAwareSerDeTest.java @@ -1,46 +1,44 @@ package pl.allegro.tech.hermes.common.message.wrapper; -import org.testng.annotations.Test; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; + +import org.junit.Test; import pl.allegro.tech.hermes.schema.SchemaId; import pl.allegro.tech.hermes.test.helper.avro.AvroUser; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - public class SchemaAwareSerDeTest { - static final AvroUser avro = new AvroUser("bob", 10, "red"); - - @Test - public void shouldSerialize() throws IOException { - // given - SchemaId id = SchemaId.valueOf(8); - - // when - byte[] serialized = SchemaAwareSerDe.serialize(id, avro.asBytes()); - - // then - assertThat(serialized).startsWith((byte) 0); - } - - @Test - public void shouldDeserialize() throws IOException { - // given - byte[] serialized = SchemaAwareSerDe.serialize(SchemaId.valueOf(8), avro.asBytes()); - - // when - SchemaAwarePayload deserialized = SchemaAwareSerDe.deserialize(serialized); - - // then - assertThat(deserialized.getSchemaId().value()).isEqualTo(8); - assertThat(deserialized.getPayload()).isEqualTo(avro.asBytes()); - } - - @Test(expectedExceptions = { DeserializationException.class }) - public void shouldThrowExceptionWhenDeserializingWithoutMagicByte() throws IOException { - // when - SchemaAwareSerDe.deserialize(new byte[]{1, 2, 3}); - - // then exception is thrown - } + static final AvroUser avro = new AvroUser("bob", 10, "red"); + + @Test + public void shouldSerialize() { + // given + SchemaId id = SchemaId.valueOf(8); + + // when + byte[] serialized = SchemaAwareSerDe.serialize(id, avro.asBytes()); + + // then + assertThat(serialized).startsWith((byte) 0); + } + + @Test + public void shouldDeserialize() { + // given + byte[] serialized = SchemaAwareSerDe.serialize(SchemaId.valueOf(8), avro.asBytes()); + + // when + SchemaAwarePayload deserialized = SchemaAwareSerDe.deserialize(serialized); + + // then + assertThat(deserialized.getSchemaId().value()).isEqualTo(8); + assertThat(deserialized.getPayload()).isEqualTo(avro.asBytes()); + } + + @Test + public void shouldThrowExceptionWhenDeserializingWithoutMagicByte() { + // when + assertThrows( + DeserializationException.class, () -> SchemaAwareSerDe.deserialize(new byte[] {1, 2, 3})); + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculatorTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculatorTest.java index 57a40c7988..cffce230aa 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculatorTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/MetricsDeltaCalculatorTest.java @@ -1,53 +1,53 @@ package pl.allegro.tech.hermes.common.metric.counter; -import org.junit.Test; - import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Test; + public class MetricsDeltaCalculatorTest { - private final MetricsDeltaCalculator calculator = new MetricsDeltaCalculator(); + private final MetricsDeltaCalculator calculator = new MetricsDeltaCalculator(); - @Test - public void shouldReturnCurrentValueWhenThereIsNoPreviousStateForMetric() { - // given when - long delta = calculator.calculateDelta("metricWithoutHistory", 13L); + @Test + public void shouldReturnCurrentValueWhenThereIsNoPreviousStateForMetric() { + // given when + long delta = calculator.calculateDelta("metricWithoutHistory", 13L); - // then - assertThat(delta).isEqualTo(13); - } + // then + assertThat(delta).isEqualTo(13); + } - @Test - public void shouldReturnDeltaBetweenCurrentAndPreviousState() { - // given - calculator.calculateDelta("metric", 13L); + @Test + public void shouldReturnDeltaBetweenCurrentAndPreviousState() { + // given + calculator.calculateDelta("metric", 13L); - // when - long delta = calculator.calculateDelta("metric", 20L); + // when + long delta = calculator.calculateDelta("metric", 20L); - // then - assertThat(delta).isEqualTo(7); - } + // then + assertThat(delta).isEqualTo(7); + } - @Test - public void shouldRevertDelta() { - //given - String metricName = "metricToRevert"; - calculator.calculateDelta(metricName, 15L); - long delta = calculator.calculateDelta(metricName, 20L); + @Test + public void shouldRevertDelta() { + // given + String metricName = "metricToRevert"; + calculator.calculateDelta(metricName, 15L); + long delta = calculator.calculateDelta(metricName, 20L); - //when - calculator.revertDelta(metricName, delta); + // when + calculator.revertDelta(metricName, delta); - assertThat(calculator.calculateDelta(metricName, 20L)).isEqualTo(5L); - } + assertThat(calculator.calculateDelta(metricName, 20L)).isEqualTo(5L); + } - @Test - public void shouldNotRevertDeltaForNonExistingMetric() { - // when - calculator.revertDelta("emptyMetric", 10L); + @Test + public void shouldNotRevertDeltaForNonExistingMetric() { + // when + calculator.revertDelta("emptyMetric", 10L); - // then - assertThat(calculator.calculateDelta("emptyMetric", 10L)).isEqualTo(10L); - } + // then + assertThat(calculator.calculateDelta("emptyMetric", 10L)).isEqualTo(10L); + } } diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporterTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporterTest.java index 3788cfb46e..8e009bd91d 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporterTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterReporterTest.java @@ -1,110 +1,103 @@ package pl.allegro.tech.hermes.common.metric.counter.zookeeper; -import com.codahale.metrics.MetricRegistry; +import static org.mockito.Mockito.verify; + import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import pl.allegro.tech.hermes.api.SubscriptionName; import pl.allegro.tech.hermes.api.TopicName; -import pl.allegro.tech.hermes.common.metric.HermesMetrics; import pl.allegro.tech.hermes.common.metric.MetricsFacade; import pl.allegro.tech.hermes.common.metric.counter.CounterStorage; import pl.allegro.tech.hermes.common.util.InstanceIdResolver; -import pl.allegro.tech.hermes.metrics.PathsCompiler; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class ZookeeperCounterReporterTest { - public static final String GROUP_NAME = "pl.allegro.tech.skylab"; - public static final String TOPIC_NAME_UNDERSCORE = "topic_1"; - public static final String SUBSCRIPTION_NAME = "subscription.name"; - public static final TopicName topic = new TopicName(GROUP_NAME, TOPIC_NAME_UNDERSCORE); - public static final SubscriptionName subscription = new SubscriptionName(SUBSCRIPTION_NAME, topic); + public static final String GROUP_NAME = "pl.allegro.tech.skylab"; + public static final String TOPIC_NAME_UNDERSCORE = "topic_1"; + public static final String SUBSCRIPTION_NAME = "subscription.name"; + public static final TopicName topic = new TopicName(GROUP_NAME, TOPIC_NAME_UNDERSCORE); + public static final SubscriptionName subscription = + new SubscriptionName(SUBSCRIPTION_NAME, topic); - public static final long COUNT = 100L; + public static final long COUNT = 100L; - @Mock - private CounterStorage counterStorage; + @Mock private CounterStorage counterStorage; - private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); + private final MeterRegistry meterRegistry = new SimpleMeterRegistry(); - private final MetricsFacade metricsFacade = new MetricsFacade( - meterRegistry, new HermesMetrics(new MetricRegistry(), new PathsCompiler("localhost"))); + private final MetricsFacade metricsFacade = new MetricsFacade(meterRegistry); - @Mock - private InstanceIdResolver instanceIdResolver; + @Mock private InstanceIdResolver instanceIdResolver; - private ZookeeperCounterReporter zookeeperCounterReporter; + private ZookeeperCounterReporter zookeeperCounterReporter; - @Before - public void before() { - when(instanceIdResolver.resolve()).thenReturn("localhost.domain"); - zookeeperCounterReporter = new ZookeeperCounterReporter(meterRegistry, counterStorage, ""); - } + @Before + public void before() { + zookeeperCounterReporter = new ZookeeperCounterReporter(meterRegistry, counterStorage, ""); + } - @Test - public void shouldReportPublishedMessages() { - // given - metricsFacade.topics().topicPublished(topic).increment(COUNT); + @Test + public void shouldReportPublishedMessages() { + // given + metricsFacade.topics().topicPublished(topic, "dc1").increment(COUNT); - // when - zookeeperCounterReporter.report(); + // when + zookeeperCounterReporter.report(); - // then - verify(counterStorage).setTopicPublishedCounter(topic, COUNT); - } + // then + verify(counterStorage).setTopicPublishedCounter(topic, COUNT); + } - @Test - public void shouldReportDeliveredMessages() { - // given - metricsFacade.subscriptions().successes(subscription).increment(COUNT); + @Test + public void shouldReportDeliveredMessages() { + // given + metricsFacade.subscriptions().successes(subscription).increment(COUNT); - // when - zookeeperCounterReporter.report(); + // when + zookeeperCounterReporter.report(); - // then - verify(counterStorage).setSubscriptionDeliveredCounter(topic, SUBSCRIPTION_NAME, COUNT); - } + // then + verify(counterStorage).setSubscriptionDeliveredCounter(topic, SUBSCRIPTION_NAME, COUNT); + } - @Test - public void shouldReportDiscardedMessages() { - // given - metricsFacade.subscriptions().discarded(subscription).increment(COUNT); + @Test + public void shouldReportDiscardedMessages() { + // given + metricsFacade.subscriptions().discarded(subscription).increment(COUNT); - // when - zookeeperCounterReporter.report(); + // when + zookeeperCounterReporter.report(); - // then - verify(counterStorage).setSubscriptionDiscardedCounter(topic, SUBSCRIPTION_NAME, COUNT); - } + // then + verify(counterStorage).setSubscriptionDiscardedCounter(topic, SUBSCRIPTION_NAME, COUNT); + } - @Test - public void shouldReportSubscriptionVolumeCounter() { - // given - metricsFacade.subscriptions().throughputInBytes(subscription).increment(COUNT); + @Test + public void shouldReportSubscriptionVolumeCounter() { + // given + metricsFacade.subscriptions().throughputInBytes(subscription).increment(COUNT); - // when - zookeeperCounterReporter.report(); + // when + zookeeperCounterReporter.report(); - // then - verify(counterStorage).incrementVolumeCounter(topic, SUBSCRIPTION_NAME, COUNT); - } + // then + verify(counterStorage).incrementVolumeCounter(topic, SUBSCRIPTION_NAME, COUNT); + } - @Test - public void shouldReportTopicVolumeCounter() { - // given - metricsFacade.topics().topicThroughputBytes(topic).increment(COUNT); + @Test + public void shouldReportTopicVolumeCounter() { + // given + metricsFacade.topics().topicThroughputBytes(topic).increment(COUNT); - // when - zookeeperCounterReporter.report(); + // when + zookeeperCounterReporter.report(); - // then - verify(counterStorage).incrementVolumeCounter(topic, COUNT); - } -} \ No newline at end of file + // then + verify(counterStorage).incrementVolumeCounter(topic, COUNT); + } +} diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorageTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorageTest.java index 4434989d2b..78bc2faa65 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorageTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/common/metric/counter/zookeeper/ZookeeperCounterStorageTest.java @@ -1,93 +1,98 @@ package pl.allegro.tech.hermes.common.metric.counter.zookeeper; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; import pl.allegro.tech.hermes.api.TopicName; import pl.allegro.tech.hermes.domain.subscription.SubscriptionNotExistsException; import pl.allegro.tech.hermes.domain.subscription.SubscriptionRepository; import pl.allegro.tech.hermes.infrastructure.zookeeper.counter.SharedCounter; import pl.allegro.tech.hermes.metrics.PathsCompiler; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - @RunWith(MockitoJUnitRunner.class) public class ZookeeperCounterStorageTest { - @Mock - private SharedCounter sharedCounter; - - @Mock - private SubscriptionRepository subscriptionRepository; - - private ZookeeperCounterStorage storage; - - @Before - public void initialize() { - PathsCompiler pathCompiler = new PathsCompiler("my-host-example.net"); - storage = new ZookeeperCounterStorage(sharedCounter, subscriptionRepository, pathCompiler, "/hermes"); - } - - @Test - public void shouldIncrementTopicMetricUsingSharedCounter() { - //when - storage.setTopicPublishedCounter(TopicName.fromQualifiedName("test.topic"), 10); - - // then - verify(sharedCounter).increment("/hermes/groups/test/topics/topic/metrics/published", 10); - } - - @Test - public void shouldReadValueFromTopicMetric() { - // given - when(sharedCounter.getValue("/hermes/groups/test/topics/topic/metrics/published")).thenReturn(10L); - - // when - long value = storage.getTopicPublishedCounter(TopicName.fromQualifiedName("test.topic")); - - // then - assertThat(value).isEqualTo(10); - } - - @Test - public void shouldIncrementSubscriptionMetricUsingSharedCounter() { - // given when - storage.setSubscriptionDeliveredCounter(TopicName.fromQualifiedName("test.topic"), "sub", 10); - - // then - verify(sharedCounter).increment("/hermes/groups/test/topics/topic/subscriptions/sub/metrics/delivered", 10); - } - - @Test - public void shouldReadValueFromSubscriptionMetric() { - // given - when(sharedCounter.getValue("/hermes/groups/test/topics/topic/subscriptions/sub/metrics/delivered")).thenReturn(10L); - - // when - long value = storage.getSubscriptionDeliveredCounter(TopicName.fromQualifiedName("test.topic"), "sub"); - - // then - assertThat(value).isEqualTo(10); - } - - @Test - public void shouldNotIncrementSharedCounterForNonExistingSubscription() { - //given - TopicName topicName = TopicName.fromQualifiedName("test.topic"); - String subscriptionName = "sub"; - doThrow(new SubscriptionNotExistsException(topicName, subscriptionName)) - .when(subscriptionRepository).ensureSubscriptionExists(topicName, subscriptionName); - - //when - storage.setSubscriptionDeliveredCounter(topicName, subscriptionName, 1L); - - //then - verifyZeroInteractions(sharedCounter); - } -} \ No newline at end of file + @Mock private SharedCounter sharedCounter; + + @Mock private SubscriptionRepository subscriptionRepository; + + private ZookeeperCounterStorage storage; + + @Before + public void initialize() { + PathsCompiler pathCompiler = new PathsCompiler("my-host-example.net"); + storage = + new ZookeeperCounterStorage(sharedCounter, subscriptionRepository, pathCompiler, "/hermes"); + } + + @Test + public void shouldIncrementTopicMetricUsingSharedCounter() { + // when + storage.setTopicPublishedCounter(TopicName.fromQualifiedName("test.topic"), 10); + + // then + verify(sharedCounter).increment("/hermes/groups/test/topics/topic/metrics/published", 10); + } + + @Test + public void shouldReadValueFromTopicMetric() { + // given + when(sharedCounter.getValue("/hermes/groups/test/topics/topic/metrics/published")) + .thenReturn(10L); + + // when + long value = storage.getTopicPublishedCounter(TopicName.fromQualifiedName("test.topic")); + + // then + assertThat(value).isEqualTo(10); + } + + @Test + public void shouldIncrementSubscriptionMetricUsingSharedCounter() { + // given when + storage.setSubscriptionDeliveredCounter(TopicName.fromQualifiedName("test.topic"), "sub", 10); + + // then + verify(sharedCounter) + .increment("/hermes/groups/test/topics/topic/subscriptions/sub/metrics/delivered", 10); + } + + @Test + public void shouldReadValueFromSubscriptionMetric() { + // given + when(sharedCounter.getValue( + "/hermes/groups/test/topics/topic/subscriptions/sub/metrics/delivered")) + .thenReturn(10L); + + // when + long value = + storage.getSubscriptionDeliveredCounter(TopicName.fromQualifiedName("test.topic"), "sub"); + + // then + assertThat(value).isEqualTo(10); + } + + @Test + public void shouldNotIncrementSharedCounterForNonExistingSubscription() { + // given + TopicName topicName = TopicName.fromQualifiedName("test.topic"); + String subscriptionName = "sub"; + doThrow(new SubscriptionNotExistsException(topicName, subscriptionName)) + .when(subscriptionRepository) + .ensureSubscriptionExists(topicName, subscriptionName); + + // when + storage.setSubscriptionDeliveredCounter(topicName, subscriptionName, 1L); + + // then + Mockito.verifyNoInteractions(sharedCounter); + } +} diff --git a/hermes-common/src/test/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounterTest.java b/hermes-common/src/test/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounterTest.java index f3ab02804f..4f2996f757 100644 --- a/hermes-common/src/test/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounterTest.java +++ b/hermes-common/src/test/java/pl/allegro/tech/hermes/infrastructure/zookeeper/counter/SharedCounterTest.java @@ -1,44 +1,44 @@ package pl.allegro.tech.hermes.infrastructure.zookeeper.counter; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; import org.junit.Before; import org.junit.Test; import pl.allegro.tech.hermes.test.helper.zookeeper.ZookeeperBaseTest; -import java.time.Duration; - -import static org.assertj.core.api.Assertions.assertThat; - public class SharedCounterTest extends ZookeeperBaseTest { - private SharedCounter counter; - - @Before - public void initialize() { - this.counter = new SharedCounter(zookeeperClient, Duration.ofHours(72), Duration.ofSeconds(1), 3); - } - - @Test - public void shouldIncrementAndRetrieveCounterForGivenPath() { - // given when - counter.increment("/increment", 10); - wait.untilZookeeperPathIsCreated("/increment"); - - // then - assertThat(counter.getValue("/increment")).isEqualTo(10); - } - - @Test - public void shouldIncrementCounterAtomicallyWhenIncrementedConcurrently() { - // given - SharedCounter otherCounter = new SharedCounter(zookeeperClient, Duration.ofHours(72), Duration.ofSeconds(1), 3); - - // when - counter.increment("/sharedIncrement", 10); - otherCounter.increment("/sharedIncrement", 15); - wait.untilZookeeperPathIsCreated("/sharedIncrement"); - - // then - assertThat(counter.getValue("/sharedIncrement")).isEqualTo(25); - } - + private SharedCounter counter; + + @Before + public void initialize() { + this.counter = + new SharedCounter(zookeeperClient, Duration.ofHours(72), Duration.ofSeconds(1), 3); + } + + @Test + public void shouldIncrementAndRetrieveCounterForGivenPath() { + // given when + counter.increment("/increment", 10); + wait.untilZookeeperPathIsCreated("/increment"); + + // then + assertThat(counter.getValue("/increment")).isEqualTo(10); + } + + @Test + public void shouldIncrementCounterAtomicallyWhenIncrementedConcurrently() { + // given + SharedCounter otherCounter = + new SharedCounter(zookeeperClient, Duration.ofHours(72), Duration.ofSeconds(1), 3); + + // when + counter.increment("/sharedIncrement", 10); + otherCounter.increment("/sharedIncrement", 15); + wait.untilZookeeperPathIsCreated("/sharedIncrement"); + + // then + assertThat(counter.getValue("/sharedIncrement")).isEqualTo(25); + } } diff --git a/hermes-common/src/test/resources/allure.properties b/hermes-common/src/test/resources/allure.properties new file mode 100644 index 0000000000..8c70baec64 --- /dev/null +++ b/hermes-common/src/test/resources/allure.properties @@ -0,0 +1 @@ +allure.results.directory=../build/allure-results diff --git a/hermes-console-vue/README.md b/hermes-console-vue/README.md deleted file mode 100644 index 51dc8ee89a..0000000000 --- a/hermes-console-vue/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# hermes-console-vue - -Hermes console migrated from AngularJS to Vue 3. - -## Requirements - -* node >=18.0.0 -* yarn - -## Project Setup - -```sh -yarn -``` - -### Run mocked backend server - -```sh -yarn dev-server -``` - -### Compile and Hot-Reload for Development - -```sh -yarn dev -``` - -### Type-Check, Compile and Minify for Production - -```sh -yarn build -``` - -### Run Unit Tests with [Vitest](https://vitest.dev/) - -```sh -yarn test:unit -``` - -### Lint with [ESLint](https://eslint.org/) - -```sh -yarn lint -``` - -### Lint with [ESLint](https://eslint.org/) and fix - -```sh -yarn lint:fix -``` diff --git a/hermes-console-vue/package.json b/hermes-console-vue/package.json deleted file mode 100644 index 385c7e31de..0000000000 --- a/hermes-console-vue/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "hermes-console-vue", - "description": "Console for Hermes Management", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - }, - "scripts": { - "dev": "vite", - "build": "run-p type-check build-only", - "preview": "vite preview", - "test:unit": "vitest --environment jsdom", - "build-only": "vite build", - "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .gitignore", - "lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", - "dev-server": "node json-server/server.ts" - }, - "dependencies": { - "ace-builds": "1.28.0", - "axios": "1.4.0", - "base64-arraybuffer": "1.0.2", - "jwt-decode": "3.1.2", - "pinia": "2.1.4", - "pinia-plugin-persistedstate": "3.2.0", - "query-string": "8.1.0", - "uuid": "9.0.0", - "vue": "3.3.4", - "vue-i18n": "9.2.2", - "vue-router": "4.2.2", - "vue3-ace-editor": "2.2.3", - "vuetify": "3.3.2" - }, - "devDependencies": { - "@mdi/font": "7.1.96", - "@pinia/testing": "0.1.3", - "@rushstack/eslint-patch": "1.2.0", - "@testing-library/jest-dom": "5.16.5", - "@testing-library/user-event": "14.4.3", - "@testing-library/vue": "7.0.0", - "@types/jsdom": "21.1.0", - "@types/node": "18.13.0", - "@types/uuid": "9.0.2", - "@vitejs/plugin-vue": "4.0.0", - "@vue/eslint-config-prettier": "7.0.0", - "@vue/eslint-config-typescript": "11.0.2", - "@vue/test-utils": "2.3.2", - "@vue/tsconfig": "0.1.3", - "eslint": "8.34.0", - "eslint-plugin-sort-imports-es6-autofix": "0.6.0", - "eslint-plugin-vue": "9.9.0", - "jsdom": "22.1.0", - "json-server": "0.17.1", - "msw": "1.2.2", - "npm-run-all": "4.1.5", - "prettier": "2.8.4", - "sass": "1.58.1", - "typescript": "4.9.5", - "vite": "4.1.1", - "vite-plugin-rewrite-all": "1.0.1", - "vite-plugin-vuetify": "1.0.2", - "vitest": "0.31.4", - "vue-tsc": "1.0.24" - } -} diff --git a/hermes-console-vue/src/utils/date-formatter/date-formatter.ts b/hermes-console-vue/src/utils/date-formatter/date-formatter.ts deleted file mode 100644 index b88d6d6c18..0000000000 --- a/hermes-console-vue/src/utils/date-formatter/date-formatter.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function formatTimestamp(timestamp: number): string { - return new Date(timestamp * 1000) - .toISOString() - .replace('T', ' ') - .replace('Z', '') - .slice(0, 19); -} - -export function formatTimestampMillis(timestamp: number): string { - return new Date(timestamp) - .toISOString() - .replace('T', ' ') - .replace('Z', '') - .slice(0, 19); -} diff --git a/hermes-console-vue/src/views/admin/consistency/inconsistent-metadata/InconsistentMetadata.spec.ts b/hermes-console-vue/src/views/admin/consistency/inconsistent-metadata/InconsistentMetadata.spec.ts deleted file mode 100644 index 797360fc80..0000000000 --- a/hermes-console-vue/src/views/admin/consistency/inconsistent-metadata/InconsistentMetadata.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, expect } from 'vitest'; -import { inconsistentMetadata } from '@/dummy/inconsistentMetadata'; -import { render } from '@/utils/test-utils'; -import InconsistentMetadata from '@/views/admin/consistency/inconsistent-metadata/InconsistentMetadata.vue'; - -describe('InconsistentMetadataView', () => { - it('should render metadata inconsistencies', () => { - // when - const { getByText } = render(InconsistentMetadata, { - props: { - metadata: inconsistentMetadata, - }, - }); - - // then - expect( - getByText('consistency.inconsistentGroup.metadata.inconsistent'), - ).toBeVisible(); - }); - - it('should render banner with info that metadata are consistent', () => { - // when - const { getByText } = render(InconsistentMetadata, { - props: { - metadata: [], - }, - }); - - // then - expect( - getByText('consistency.inconsistentGroup.metadata.consistent'), - ).toBeVisible(); - }); -}); diff --git a/hermes-console-vue/src/views/search/subscription-search-results/SubscriptionSearchResults.vue b/hermes-console-vue/src/views/search/subscription-search-results/SubscriptionSearchResults.vue deleted file mode 100644 index 8889cf8975..0000000000 --- a/hermes-console-vue/src/views/search/subscription-search-results/SubscriptionSearchResults.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - diff --git a/hermes-console-vue/src/views/search/topic-search-results/TopicSearchResults.vue b/hermes-console-vue/src/views/search/topic-search-results/TopicSearchResults.vue deleted file mode 100644 index 8669dcaa06..0000000000 --- a/hermes-console-vue/src/views/search/topic-search-results/TopicSearchResults.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - diff --git a/hermes-console/.bowerrc b/hermes-console/.bowerrc deleted file mode 100644 index a3a2355531..0000000000 --- a/hermes-console/.bowerrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "directory" : "static/components", - "registry": "https://bower.herokuapp.com" -} \ No newline at end of file diff --git a/hermes-console-vue/.eslintrc.cjs b/hermes-console/.eslintrc.cjs similarity index 100% rename from hermes-console-vue/.eslintrc.cjs rename to hermes-console/.eslintrc.cjs diff --git a/hermes-console-vue/.gitignore b/hermes-console/.gitignore similarity index 95% rename from hermes-console-vue/.gitignore rename to hermes-console/.gitignore index 38adffa64e..b583b23b8b 100644 --- a/hermes-console-vue/.gitignore +++ b/hermes-console/.gitignore @@ -8,6 +8,7 @@ pnpm-debug.log* lerna-debug.log* node_modules +allure-results .DS_Store dist dist-ssr @@ -20,7 +21,6 @@ coverage # Editor directories and files .vscode/* !.vscode/extensions.json -.idea *.suo *.ntvs* *.njsproj diff --git a/hermes-console/.jshintrc b/hermes-console/.jshintrc deleted file mode 100644 index 53b202cb9f..0000000000 --- a/hermes-console/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "esversion": 6 -} \ No newline at end of file diff --git a/hermes-console-vue/.prettierrc.json b/hermes-console/.prettierrc.json similarity index 100% rename from hermes-console-vue/.prettierrc.json rename to hermes-console/.prettierrc.json diff --git a/hermes-console-vue/.vscode/extensions.json b/hermes-console/.vscode/extensions.json similarity index 100% rename from hermes-console-vue/.vscode/extensions.json rename to hermes-console/.vscode/extensions.json diff --git a/hermes-console/Gruntfile.js b/hermes-console/Gruntfile.js deleted file mode 100644 index a6e9e0fb43..0000000000 --- a/hermes-console/Gruntfile.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = function(grunt) { - - grunt.initConfig({ - jshint: { - files: ['Gruntfile.js', 'static/js/**/*.js', 'test/**/*.js'], - options: { - 'esnext': 6, - } }, - watch: { - files: ['<%= jshint.files %>'], - tasks: ['jshint', 'karma'] - }, - karma: { - unit: { - configFile: 'karma.config.js', - browsers: ['ChromeHeadless'] - } - } - }); - - - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-karma'); - grunt.loadNpmTasks('grunt-contrib-watch'); - - grunt.registerTask('test', ['jshint', 'karma']); - - grunt.registerTask('default', ['test']); - -}; diff --git a/hermes-console/README.md b/hermes-console/README.md index 1cbc2a23b1..c3a8fd8b63 100644 --- a/hermes-console/README.md +++ b/hermes-console/README.md @@ -1,62 +1,50 @@ -Hermes Console -==== +# hermes-console -Hermes admin console - UI for Hermes Management API. +Hermes console written in Vue 3. -## Build and install +## Requirements -Install backend and frontend dependencies: +* node >=20.0.0 +* yarn -```bash -npm install -bower install -``` - -## Configuration - -Hermes Console has two initial configuration options, which can be specified using either -command line or environment variables: +## Project Setup -* `-p` or `HERMES_CONSOLE_PORT`: specify port (default: `8000`) -* `-c` or `HERMES_CONSOLE_CONFIG`: specify configuration source: local file or http resource (default: `./config.json`) +```sh +yarn +``` -For example to run Hermes Console at port 8001 and fetch configuration from remote source: +### Run mocked backend server -```bash -node serve.js -p 8001 -c http://configuration-source +```sh +yarn dev-server ``` -Other configuration can be found in `config.json.example` file. +### Compile and Hot-Reload for Development -## Run +```sh +yarn dev +``` -Use provided node script: +### Type-Check, Compile and Minify for Production -``` -./serve.js +```sh +yarn build ``` -Or explicitly via node: +### Run Unit Tests with [Vitest](https://vitest.dev/) -``` -node serve.js +```sh +yarn test:unit ``` -For development purposes to have hermes console autoreload changed files it can be run with `nodemon` +### Lint with [ESLint](https://eslint.org/) -Install: -``` -npm install nodemon -g -``` -Run: +```sh +yarn lint ``` -nodemon serve.js -``` - -## Tests -In order to run all tests and `jshint` code analysis run `grunt` tasks: +### Lint with [ESLint](https://eslint.org/) and fix -``` -grunt test +```sh +yarn lint:fix ``` diff --git a/hermes-console/bower.json b/hermes-console/bower.json deleted file mode 100644 index 82f162af4b..0000000000 --- a/hermes-console/bower.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "hermes-console", - "version": "1.0.0", - "description": "Console for Hermes project", - "main": [], - "private": true, - "dependencies": { - "bootstrap": "~3.3.5", - "angular": "~1.4.9", - "angular-resource": "~1.4.9", - "angular-ui-router": "~0.2.15", - "angular-bootstrap": "~0.14.2", - "angular-animate": "~1.4.9", - "angular-sanitize": "~1.4.9", - "angularjs-toaster": "0.4.15", - "lodash": "2.4.1", - "font-awesome": "4.2.0", - "json-formatter": "0.2.2", - "hello": "~1.3.7", - "jquery": "2.1.4", - "angular-deferred-bootstrap": "~0.1.9", - "utf8": "3.0.0", - "angular-ui-ace": "0.2.3", - "eonasdan-bootstrap-datetimepicker": "~4.17.47" - }, - "resolutions": { - "angular": "1.4.14" - } -} diff --git a/hermes-console/config.json.example b/hermes-console/config.json.example deleted file mode 100644 index 9f58136c63..0000000000 --- a/hermes-console/config.json.example +++ /dev/null @@ -1,96 +0,0 @@ -{ - "console": { - "title": "hermes console" // page title - "footer": "hermes.allegro.tech" // page footer - "criticalEnvironment": false // should env alert be alert-danger - "environmentName": "LOCAL" // console environment displayed in navbar - }, - "dashboard": { - "metrics": "", // link to metrics dashboard - "docs": "http://hermes-pubsub.rtfd.org" // link to documentation - }, - "hermes": { - "discovery": { - "type": "simple", - "simple": { - "url": "http://localhost:8080" - }, - "consul": { - "serviceName": "hermes-management", - "agentUrl": "http://localhost:8500" - } - } - }, - "metrics": { - "type": "graphite", // active metric store type - "graphite": { - "url": "localhost:8091", - "prefix": "hermes" - } - }, - "auth": { - "oauth": { - "enabled": false, - "url": "localhost:8092/auth", - "clientId": "hermes", - "scope": "hermes" - }, - "headers": { - "enabled": false, - "adminHeader": "Hermes-Admin-Password" - } - }, - "topic": { - "messagePreviewEnabled": false, - "authEnabled": false, - "defaults": { - "ack": "LEADER", - "contentType": "JSON", - "retentionTime": { - "duration": 1 - } - }, - "contentTypes": { - {"value": "AVRO", "label": "AVRO"}, - {"value": "JSON", "label": "JSON"} - }, - "removeSchema": false, - "buttonsExtension": "Migrate to AVRO", - "offlineClientsEnabled": false - }, - "group": { - "nonAdminCreationEnabled": false - }, - "subscription": { - "endpointAddressResolverMetadata": { - "exampleEntryEnabled": { - "title": "Example boolean entry", - "type": "boolean" - }, - "exampleTextEntry": { - "title": "Example text entry", - "type": "text", - "placeholder": "You should write something here", - "hint": "This should help somehow..." - }, - "exampleSelectEntry": { - "title": "Example select entry", - "type": "select", - "options": { - "": "", - "a": "An option", - "b": "Another option" - } - } - }, - "deliveryTypes": [ - {"value": "SERIAL", "label": "SERIAL"}, - {"value": "BATCH", "label": "BATCH"} - ] - }, - "owner": { - "sources": [ - {"name": "Crowd", "placeholder": "Crowd group (or groups separated by ',')"} - ] - } -} diff --git a/hermes-console/config_default.json b/hermes-console/config_default.json deleted file mode 100644 index 687d5536f5..0000000000 --- a/hermes-console/config_default.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "console": { - "title": "hermes console", - "criticalEnvironment": false, - "environmentName": "LOCAL" - }, - "dashboard": { - "docs": "http://hermes-pubsub.rtfd.org" - }, - "auth": { - "oauth": { - "enabled": false - }, - "headers": { - "enabled": false - } - }, - "topic": { - "messagePreviewEnabled": false, - "contentTypes": [ - {"value": "AVRO", "label": "AVRO"}, - {"value": "JSON", "label": "JSON"} - ], - "defaults": { - "contentType": "AVRO" - }, - "offlineClientsEnabled": false - }, - "group": { - "nonAdminCreationEnabled": true - }, - "subscription": { - "deliveryTypes": [ - {"value": "SERIAL", "label": "SERIAL"}, - {"value": "BATCH", "label": "BATCH"} - ] - }, - "owner": { - "sources": [ - {"name": "Crowd", "placeholder": "Crowd group (or groups separated by ',')"} - ] - }, - "consistency": { - "maxGroupBatchSize": 10 - } -} diff --git a/hermes-console-vue/env.d.ts b/hermes-console/env.d.ts similarity index 100% rename from hermes-console-vue/env.d.ts rename to hermes-console/env.d.ts diff --git a/hermes-console-vue/index.html b/hermes-console/index.html similarity index 100% rename from hermes-console-vue/index.html rename to hermes-console/index.html diff --git a/hermes-console-vue/json-server/db.json b/hermes-console/json-server/db.json similarity index 89% rename from hermes-console-vue/json-server/db.json rename to hermes-console/json-server/db.json index b1a3f0ecb7..23dab13ee0 100644 --- a/hermes-console-vue/json-server/db.json +++ b/hermes-console/json-server/db.json @@ -91,6 +91,32 @@ ] } ], + "inconsistentGroups3":[ + { + "name": "pl.allegro.public.group", + "inconsistentMetadata": [], + "inconsistentTopics": [ + { + "name": "pl.allegro.public.group.DummyEvent", + "inconsistentMetadata": [], + "inconsistentSubscriptions": [ + { + "name": "pl.allegro.public.group.DummyEvent$foobar-service", + "inconsistentMetadata": [ + { + "datacenter": "DC1" + }, + { + "datacenter": "DC2", + "content": "{\n \"id\": \"foobar-service\",\n \"topicName\": \"pl.allegro.public.group.DummyEvent\",\n \"name\": \"foobar-service\",\n \"endpoint\": \"service://foobar-service/events/dummy-event\",\n \"state\": \"ACTIVE\",\n \"description\": \"Test Hermes endpoint\",\n \"subscriptionPolicy\": {\n \"rate\": 10,\n \"messageTtl\": 60,\n \"messageBackoff\": 100,\n \"requestTimeout\": 1000,\n \"socketTimeout\": 0,\n \"sendingDelay\": 0,\n \"backoffMultiplier\": 1.0,\n \"backoffMaxIntervalInSec\": 600,\n \"retryClientErrors\": true,\n \"backoffMaxIntervalMillis\": 600000\n },\n \"trackingEnabled\": false,\n \"trackingMode\": \"trackingOff\",\n \"owner\": {\n \"source\": \"Service Catalog\",\n \"id\": \"42\"\n },\n \"monitoringDetails\": {\n \"severity\": \"NON_IMPORTANT\",\n \"reaction\": \"\"\n },\n \"contentType\": \"JSON\",\n \"deliveryType\": \"SERIAL\",\n \"filters\": [\n {\n \"type\": \"avropath\",\n \"path\": \"foobar\",\n \"matcher\": \"^FOO_BAR$|^BAZ_BAR$\",\n \"matchingStrategy\": \"any\"\n },\n {\n \"type\": \"avropath\",\n \"path\": \".foo.bar.baz\",\n \"matcher\": \"true\",\n \"matchingStrategy\": \"all\"\n }\n ],\n \"mode\": \"ANYCAST\",\n \"headers\": [\n {\n \"name\": \"X-My-Header\",\n \"value\": \"boobar\"\n },\n {\n \"name\": \"X-Another-Header\",\n \"value\": \"foobar\"\n }\n ],\n \"endpointAddressResolverMetadata\": {\n \"additionalMetadata\": false,\n \"nonSupportedProperty\": 2\n },\n \"http2Enabled\": false,\n \"subscriptionIdentityHeadersEnabled\": false,\n \"autoDeleteWithTopicEnabled\": false,\n \"createdAt\": 1579507131.238,\n \"modifiedAt\": 1672140855.813\n}" + } + ] + } + ] + } + ] + } + ], "topicNames": [ "pl.allegro.public.offer.product.ProductEventV1", "pl.allegro.public.offer.product.ProductEventV2", @@ -128,7 +154,7 @@ }, { "datacenter": "DC3", - "status": "UNDEFINED" + "status": "READY" } ], "constraints": { @@ -155,11 +181,6 @@ "autocomplete": true, "deprecated": false }, - { - "name": "Crowd Catalog", - "autocomplete": false, - "deprecated": false - }, { "name": "Deprecated catalog", "autocomplete": true, @@ -501,12 +522,13 @@ "delivered": 39099, "discarded": 2137086, "volume": 1288032256, - "timeouts": "0.0", - "otherErrors": "0.0", - "codes2xx": "0", - "codes4xx": "0.0", - "codes5xx": "0.01", - "rate": "0", + "timeouts": "12.3028857479387", + "otherErrors": "16.3028857479387", + "codes2xx": "1236.3028857479387", + "codes4xx": "123.3028857479387", + "codes5xx": "6.3028857479387", + "retries": "24.3028857479387", + "rate": "1319.6064543974392", "throughput": "8.31", "batchRate": "0.0", "lag": "9055513" @@ -521,6 +543,7 @@ "codes2xx": "0", "codes4xx": "0.0", "codes5xx": "0.01", + "retries": "0.01", "rate": "0", "throughput": "8.36", "batchRate": "0.0", @@ -591,10 +614,6 @@ }, "owner": { "sources": [ - { - "name": "Crowd Catalog", - "placeholder": "Crowd group (or groups separated by ',')" - }, { "name": "Service Catalog", "placeholder": "Service name from Service Catalog" @@ -679,6 +698,14 @@ }, "group": { "nonAdminCreationEnabled": false + }, + "costs": { + "enabled": true, + "globalDetailsUrl": "", + "topicIframeUrl": "", + "topicDetailsUrl": "", + "subscriptionIframeUrl": "", + "subscriptionDetailsUrl": "" } }, "stats": { diff --git a/hermes-console-vue/json-server/filter-debug.json b/hermes-console/json-server/filter-debug.json similarity index 100% rename from hermes-console-vue/json-server/filter-debug.json rename to hermes-console/json-server/filter-debug.json diff --git a/hermes-console-vue/json-server/routes.json b/hermes-console/json-server/routes.json similarity index 94% rename from hermes-console-vue/json-server/routes.json rename to hermes-console/json-server/routes.json index eb0b5ae948..0a741b6490 100644 --- a/hermes-console-vue/json-server/routes.json +++ b/hermes-console/json-server/routes.json @@ -3,6 +3,7 @@ "/consistency/groups": "/consistencyGroups", "/consistency/inconsistencies/groups?groupNames=pl.allegro.public.offer*": "/inconsistentGroups", "/consistency/inconsistencies/groups?groupNames=pl.allegro.public.group2*": "/inconsistentGroups2", + "/consistency/inconsistencies/groups?groupNames=pl.allegro.public.group": "/inconsistentGroups3", "/groups": "/groups", "/owners/sources/Service%20Catalog/:id": "/topicsOwners/:id", "/readiness/datacenters": "/readinessDatacenters", diff --git a/hermes-console-vue/json-server/server.ts b/hermes-console/json-server/server.ts similarity index 85% rename from hermes-console-vue/json-server/server.ts rename to hermes-console/json-server/server.ts index 6c1daab3d7..0a47cb6361 100644 --- a/hermes-console-vue/json-server/server.ts +++ b/hermes-console/json-server/server.ts @@ -87,6 +87,22 @@ server.put( }, ); +server.post( + '/consistency/sync/topics/pl.allegro.public.group.DummyEvent/subscriptions/barbaz-service*', + (req, res) => { + res.sendStatus(200); + }, +); + +server.post( + '/consistency/sync/topics/pl.allegro.public.group.DummyEvent*', + (req, res) => { + res.status(404).jsonp({ + message: 'Group pl.allegro.public.group not found', + }); + }, +); + server.post('/filters/:topic', (req, res) => { res.jsonp(filterDebug); }); diff --git a/hermes-console-vue/json-server/subscriptions.json b/hermes-console/json-server/subscriptions.json similarity index 100% rename from hermes-console-vue/json-server/subscriptions.json rename to hermes-console/json-server/subscriptions.json diff --git a/hermes-console-vue/json-server/topics.json b/hermes-console/json-server/topics.json similarity index 100% rename from hermes-console-vue/json-server/topics.json rename to hermes-console/json-server/topics.json diff --git a/hermes-console/karma.config.js b/hermes-console/karma.config.js deleted file mode 100644 index 4222bdb6c7..0000000000 --- a/hermes-console/karma.config.js +++ /dev/null @@ -1,110 +0,0 @@ -// Karma configuration -// Generated on Wed Oct 15 2014 23:49:21 GMT+0200 (CEST) - -module.exports = function(config) { - config.set({ - - plugins:[ - 'karma-jasmine', - 'karma-requirejs', - 'karma-coverage', - 'karma-junit-reporter', - 'karma-chrome-launcher', - 'karma-ng-html2js-preprocessor' - ], - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], - - - // list of files / patterns to load in the browser - files: [ - "test/fixture/config.js", - "static/components/angular/angular.js", - "static/components/angular-ui-router/release/angular-ui-router.min.js", - "static/components/angular-bootstrap/ui-bootstrap-tpls.min.js", - "static/components/angular-resource/angular-resource.js", - "static/components/angular-sanitize/angular-sanitize.js", - "static/components/angular-animate/angular-animate.min.js", - "static/components/lodash/dist/lodash.min.js", - "static/components/angularjs-toaster/toaster.js", - "static/components/json-formatter/dist/json-formatter.min.js.js", - "static/components/hello/dist/hello.min.js", - "static/components/jquery/dist/jquery.min.js", - "static/js/console/owner/OwnerRepository.js", - "static/js/**/*.js", - "static/partials/**/*.html", - "node_modules/angular-mocks/angular-mocks.js", - "test/unit/**/*.js" - ], - - junitReporter : { - outputFile: 'test_out/unit.xml', - suite: 'unit' - }, - - // list of files to exclude - exclude: [ - "static/js/bootstrap.js", - ], - - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - // source files, that you wanna generate coverage for - // do not include tests or libraries - // (these files will be instrumented by Istanbul) - 'js/**/*.js': ['coverage'], - 'static/partials/**/*.html': ['ng-html2js'] - }, - - ngHtml2JsPreprocessor: { - stripPrefix: 'static/', - moduleName: 'templates' - }, - - // optionally, configure the reporter - coverageReporter: { - type : 'html', - dir : 'coverage/' - }, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress', 'junit', 'coverage'], - - - // web server port - port: 9876, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: false, - - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['ChromeHeadless'], - - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true - }); -}; diff --git a/hermes-console/package.json b/hermes-console/package.json index 6d9980ff88..39a78a0357 100644 --- a/hermes-console/package.json +++ b/hermes-console/package.json @@ -1,31 +1,66 @@ { "name": "hermes-console", "description": "Console for Hermes Management", - "repository": "https://github.com/allegro/hermes", - "main": "serve.js", "license": "Apache-2.0", + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "dev": "vite", + "build": "run-p type-check build-only", + "preview": "vite preview", + "test:unit": "vitest --silent --environment jsdom", + "build-only": "vite build", + "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .gitignore", + "lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "dev-server": "node json-server/server.ts" + }, "dependencies": { - "bower": "^1.7.2", - "lodash": "^4.17.4", - "node-static": "^0.7.6", - "request": "^2.67.0", - "yargs": "^3.32.0" + "ace-builds": "1.33.0", + "axios": "1.6.8", + "base64-arraybuffer": "1.0.2", + "jwt-decode": "4.0.0", + "pinia": "2.1.7", + "pinia-plugin-persistedstate": "3.2.1", + "query-string": "9.0.0", + "uuid": "9.0.1", + "vue": "3.4.23", + "vue-i18n": "9.13.0", + "vue-router": "4.3.2", + "vue3-ace-editor": "2.2.4", + "vuetify": "3.5.16" }, "devDependencies": { - "angular-mocks": "~1.4.7", - "grunt": "^0.4.5", - "grunt-cli": "~0.1.13", - "grunt-contrib-jshint": "0.11.3", - "grunt-contrib-watch": "~0.6.1", - "grunt-karma": "~0.12.1", - "jasmine-core": "~2.1.3", - "karma": "^6.3.4", - "karma-chrome-launcher": "^3.1.0", - "karma-coverage": "^2.0.3", - "karma-jasmine": "^4.0.1", - "karma-junit-reporter": "^2.0.1", - "karma-ng-html2js-preprocessor": "^1.0.0", - "karma-requirejs": "^1.1.0", - "phantomjs": "~1.9.18" + "@mdi/font": "7.4.47", + "@pinia/testing": "0.1.3", + "@rushstack/eslint-patch": "1.10.2", + "@testing-library/jest-dom": "6.4.2", + "@testing-library/user-event": "14.5.2", + "@testing-library/vue": "8.0.3", + "@types/jsdom": "21.1.6", + "@types/node": "20.12.7", + "@types/uuid": "9.0.8", + "@vitejs/plugin-vue": "5.0.4", + "@vue/eslint-config-prettier": "9.0.0", + "@vue/eslint-config-typescript": "13.0.0", + "@vue/test-utils": "2.4.5", + "@vue/tsconfig": "0.1.3", + "allure-commandline": "2.30.0", + "allure-vitest": "2.15.1", + "eslint": "8.57.0", + "eslint-plugin-sort-imports-es6-autofix": "0.6.0", + "eslint-plugin-vue": "9.25.0", + "jsdom": "24.0.0", + "json-server": "0.17.4", + "msw": "2.2.14", + "npm-run-all": "4.1.5", + "prettier": "3.2.5", + "sass": "1.75.0", + "typescript": "4.9.5", + "vite": "5.2.9", + "vite-plugin-vuetify": "2.0.3", + "vitest": "1.5.0", + "vue-tsc": "2.0.13" } } diff --git a/hermes-console/package.sh b/hermes-console/package.sh deleted file mode 100755 index fb6e08b3f7..0000000000 --- a/hermes-console/package.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -NAME=hermes-console - -if [ -z ${1+x} ]; then - ARCHIVE_NAME=hermes-console -else - ARCHIVE_NAME=$1 -fi - -UNAME="$(uname -s)" -case "${UNAME}" in - Darwin*) DISTRO=darwin;; - *) DISTRO=linux -esac - -NODE_VERSION="v6.11.4" -NODE_DIST="node-$NODE_VERSION-$DISTRO-x64" - -printf "Packaging Hermes Console\n" - -if [ ! -e dist ]; then - mkdir -p dist -fi - -if [ ! -e dist/$NODE_DIST.tar.gz ]; then - printf "Downloading NodeJS distribution version $NODE_DIST\n" - wget --quiet --no-clobber "https://nodejs.org/dist/$NODE_VERSION/$NODE_DIST.tar.gz" --directory-prefix dist - mkdir -p dist/node - tar --extract --keep-old-files --strip 1 --file dist/$NODE_DIST.tar.gz -C dist/node -fi - -export PATH=$(pwd)/dist/node/bin:$PATH - -printf "Running NPM and bower\n" - -npm install --production --yes -node_modules/.bin/bower install --allow-root -F - -printf "Creating directory: dist/static\n" - -# copy static contents -cp -r static dist diff --git a/hermes-console-vue/public/favicon.ico b/hermes-console/public/favicon.ico similarity index 100% rename from hermes-console-vue/public/favicon.ico rename to hermes-console/public/favicon.ico diff --git a/hermes-console/run.sh b/hermes-console/run.sh deleted file mode 100755 index ebf7ac6fcd..0000000000 --- a/hermes-console/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -export PATH=$(pwd)/node/bin:$PATH - -node serve.js "$@" \ No newline at end of file diff --git a/hermes-console/serve.js b/hermes-console/serve.js deleted file mode 100755 index 079732eef7..0000000000 --- a/hermes-console/serve.js +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env node - -var static = require('node-static'); - -var request = require('request'); -var fs = require('fs'); - -var _ = require('lodash'); - -var DEFAULT_CONFIG = './config_default.json'; - -var yargs = require('yargs') - .env('HERMES_CONSOLE') - .option('p', { - alias: 'port', - default: 8000, - demand: true - }) - .option('c', { - alias: 'config', - default: './config.json', - demand: true - }) - .argv; - -var port = yargs.port; - -readConfiguration(yargs.config, startServer); - -function startServer(config) { - console.log('Starting Hermes Console at port: ' + port); - console.log('Config: ' + JSON.stringify(config, null, 2)); - - var file = new static.Server('./static'); - require('http').createServer((request, response) => { - response.setHeader('Pragma', 'no-cache'); - if(request.url == '/console') { - response.setHeader('Content-Type', 'application/json'); - response.end('var config = ' + JSON.stringify(config) + ';', 'UTF-8'); - } - else if(request.url == '/status/ping') { - response.end('pong', 'UTF-8'); - } - else { - request.addListener('end', function () { - file.serve(request, response); - }).resume(); - } - }).listen(port); -} - -function readConfiguration(source, callback) { - console.log('Reading default configuration from file: ' + DEFAULT_CONFIG); - var defaultConfig = JSON.parse(fs.readFileSync(DEFAULT_CONFIG, 'utf8')); - - if (source.indexOf('htt') === 0) { - console.log('Reading configuration from remote source: ' + source); - request.get(source, (error, res, body) => { callback(mergeConfig(defaultConfig, JSON.parse(body))); }); - } - else { - console.log('Reading configuration from file: ' + source); - var config = JSON.parse(fs.readFileSync(source, 'utf8')); - callback(mergeConfig(defaultConfig, config)); - } -} - -function mergeConfig(defaultConfig, config) { - return _.mergeWith(defaultConfig, config, customizer) -} - -function customizer(objValue, srcValue) { - if (_.isArray(objValue)) { - return srcValue - } -} \ No newline at end of file diff --git a/hermes-console-vue/src/App.vue b/hermes-console/src/App.vue similarity index 100% rename from hermes-console-vue/src/App.vue rename to hermes-console/src/App.vue diff --git a/hermes-console-vue/src/api/OffsetRetransmissionDate.ts b/hermes-console/src/api/OffsetRetransmissionDate.ts similarity index 100% rename from hermes-console-vue/src/api/OffsetRetransmissionDate.ts rename to hermes-console/src/api/OffsetRetransmissionDate.ts diff --git a/hermes-console-vue/src/api/access-token-response.ts b/hermes-console/src/api/access-token-response.ts similarity index 100% rename from hermes-console-vue/src/api/access-token-response.ts rename to hermes-console/src/api/access-token-response.ts diff --git a/hermes-console-vue/src/api/app-configuration.ts b/hermes-console/src/api/app-configuration.ts similarity index 93% rename from hermes-console-vue/src/api/app-configuration.ts rename to hermes-console/src/api/app-configuration.ts index 72924a90ee..e1eb50b32f 100644 --- a/hermes-console-vue/src/api/app-configuration.ts +++ b/hermes-console/src/api/app-configuration.ts @@ -9,6 +9,7 @@ export interface AppConfiguration { subscription: SubscriptionViewConfiguration; consistency: ConsistencyViewConfiguration; group: GroupViewConfiguration; + costs: CostsConfiguration; } export interface ConsoleConfiguration { @@ -153,3 +154,12 @@ export interface ConsistencyViewConfiguration { export interface GroupViewConfiguration { nonAdminCreationEnabled: boolean; } + +export interface CostsConfiguration { + enabled: boolean; + globalDetailsUrl: string; + topicIframeUrl: string; + topicDetailsUrl: string; + subscriptionIframeUrl: string; + subscriptionDetailsUrl: string; +} diff --git a/hermes-console-vue/src/api/constraints.ts b/hermes-console/src/api/constraints.ts similarity index 100% rename from hermes-console-vue/src/api/constraints.ts rename to hermes-console/src/api/constraints.ts diff --git a/hermes-console-vue/src/api/consumer-group.ts b/hermes-console/src/api/consumer-group.ts similarity index 100% rename from hermes-console-vue/src/api/consumer-group.ts rename to hermes-console/src/api/consumer-group.ts diff --git a/hermes-console-vue/src/api/content-type.ts b/hermes-console/src/api/content-type.ts similarity index 100% rename from hermes-console-vue/src/api/content-type.ts rename to hermes-console/src/api/content-type.ts diff --git a/hermes-console-vue/src/api/datacenter-readiness.ts b/hermes-console/src/api/datacenter-readiness.ts similarity index 70% rename from hermes-console-vue/src/api/datacenter-readiness.ts rename to hermes-console/src/api/datacenter-readiness.ts index e6acf35caa..cedad9c849 100644 --- a/hermes-console-vue/src/api/datacenter-readiness.ts +++ b/hermes-console/src/api/datacenter-readiness.ts @@ -1,6 +1,6 @@ export interface DatacenterReadiness { datacenter: string; - status: 'READY' | 'NOT_READY' | 'UNDEFINED'; + status: 'READY' | 'NOT_READY'; } export interface Readiness { diff --git a/hermes-console-vue/src/api/group.ts b/hermes-console/src/api/group.ts similarity index 100% rename from hermes-console-vue/src/api/group.ts rename to hermes-console/src/api/group.ts diff --git a/hermes-console-vue/src/api/hermes-client/index.ts b/hermes-console/src/api/hermes-client/index.ts similarity index 93% rename from hermes-console-vue/src/api/hermes-client/index.ts rename to hermes-console/src/api/hermes-client/index.ts index 4c4f6f89a4..23b7ca55ef 100644 --- a/hermes-console-vue/src/api/hermes-client/index.ts +++ b/hermes-console/src/api/hermes-client/index.ts @@ -464,3 +464,45 @@ export function verifyFilters( }, ); } + +export function syncGroup( + groupName: string, + primaryDatacenter: string, +): ResponsePromise { + return axios.post(`/consistency/sync/groups/${groupName}`, null, { + params: { + primaryDatacenter: primaryDatacenter, + }, + }); +} + +export function syncTopic( + topicQualifiedName: string, + primaryDatacenter: string, +): ResponsePromise { + return axios.post( + `/consistency/sync/topics/${topicQualifiedName}`, + null, + { + params: { + primaryDatacenter: primaryDatacenter, + }, + }, + ); +} + +export function syncSubscription( + topicQualifiedName: string, + subscriptionName: string, + primaryDatacenter: string, +): ResponsePromise { + return axios.post( + `/consistency/sync/topics/${topicQualifiedName}/subscriptions/${subscriptionName}`, + null, + { + params: { + primaryDatacenter: primaryDatacenter, + }, + }, + ); +} diff --git a/hermes-console-vue/src/api/inconsistent-group.ts b/hermes-console/src/api/inconsistent-group.ts similarity index 93% rename from hermes-console-vue/src/api/inconsistent-group.ts rename to hermes-console/src/api/inconsistent-group.ts index 7ee039677b..7f27d1d4f1 100644 --- a/hermes-console-vue/src/api/inconsistent-group.ts +++ b/hermes-console/src/api/inconsistent-group.ts @@ -17,5 +17,5 @@ export interface InconsistentSubscription { export interface InconsistentMedata { datacenter: string; - content: string | undefined; + content?: string; } diff --git a/hermes-console-vue/src/api/message-filters-verification.ts b/hermes-console/src/api/message-filters-verification.ts similarity index 100% rename from hermes-console-vue/src/api/message-filters-verification.ts rename to hermes-console/src/api/message-filters-verification.ts diff --git a/hermes-console-vue/src/api/offline-clients-source.ts b/hermes-console/src/api/offline-clients-source.ts similarity index 100% rename from hermes-console-vue/src/api/offline-clients-source.ts rename to hermes-console/src/api/offline-clients-source.ts diff --git a/hermes-console-vue/src/api/offline-retransmission.ts b/hermes-console/src/api/offline-retransmission.ts similarity index 100% rename from hermes-console-vue/src/api/offline-retransmission.ts rename to hermes-console/src/api/offline-retransmission.ts diff --git a/hermes-console-vue/src/api/owner-id.ts b/hermes-console/src/api/owner-id.ts similarity index 100% rename from hermes-console-vue/src/api/owner-id.ts rename to hermes-console/src/api/owner-id.ts diff --git a/hermes-console-vue/src/api/owner.ts b/hermes-console/src/api/owner.ts similarity index 100% rename from hermes-console-vue/src/api/owner.ts rename to hermes-console/src/api/owner.ts diff --git a/hermes-console-vue/src/api/role.ts b/hermes-console/src/api/role.ts similarity index 100% rename from hermes-console-vue/src/api/role.ts rename to hermes-console/src/api/role.ts diff --git a/hermes-console-vue/src/api/stats.ts b/hermes-console/src/api/stats.ts similarity index 100% rename from hermes-console-vue/src/api/stats.ts rename to hermes-console/src/api/stats.ts diff --git a/hermes-console-vue/src/api/subscription-health.ts b/hermes-console/src/api/subscription-health.ts similarity index 100% rename from hermes-console-vue/src/api/subscription-health.ts rename to hermes-console/src/api/subscription-health.ts diff --git a/hermes-console-vue/src/api/subscription-metrics.ts b/hermes-console/src/api/subscription-metrics.ts similarity index 93% rename from hermes-console-vue/src/api/subscription-metrics.ts rename to hermes-console/src/api/subscription-metrics.ts index 13fec3351d..6a0b5fe737 100644 --- a/hermes-console-vue/src/api/subscription-metrics.ts +++ b/hermes-console/src/api/subscription-metrics.ts @@ -7,6 +7,7 @@ export interface SubscriptionMetrics { codes2xx: string; codes4xx: string; codes5xx: string; + retries: string; lag: string; rate: string; throughput: string; diff --git a/hermes-console-vue/src/api/subscription-undelivered.ts b/hermes-console/src/api/subscription-undelivered.ts similarity index 100% rename from hermes-console-vue/src/api/subscription-undelivered.ts rename to hermes-console/src/api/subscription-undelivered.ts diff --git a/hermes-console-vue/src/api/subscription.ts b/hermes-console/src/api/subscription.ts similarity index 98% rename from hermes-console-vue/src/api/subscription.ts rename to hermes-console/src/api/subscription.ts index cf892a3006..3df41b4e79 100644 --- a/hermes-console-vue/src/api/subscription.ts +++ b/hermes-console/src/api/subscription.ts @@ -83,7 +83,7 @@ export interface CreateSubscriptionFormRequestBody { contentType: string; deliveryType: string; description: string; - endpoint: string; + endpoint?: string; filters: SubscriptionFilterJson[]; headers: SubscriptionHeaderJson[]; http2Enabled: boolean; @@ -128,6 +128,7 @@ export interface SerialSubscriptionPolicyJson { backoffMultiplier: number; messageBackoff: number; messageTtl: number; + inflightSize: number; rate: number; requestTimeout: number; sendingDelay: number; diff --git a/hermes-console-vue/src/api/topic.ts b/hermes-console/src/api/topic.ts similarity index 100% rename from hermes-console-vue/src/api/topic.ts rename to hermes-console/src/api/topic.ts diff --git a/hermes-console-vue/src/assets/hermes-logo-dark-theme.png b/hermes-console/src/assets/hermes-logo-dark-theme.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo-dark-theme.png rename to hermes-console/src/assets/hermes-logo-dark-theme.png diff --git a/hermes-console-vue/src/assets/hermes-logo-full-dark-theme.png b/hermes-console/src/assets/hermes-logo-full-dark-theme.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo-full-dark-theme.png rename to hermes-console/src/assets/hermes-logo-full-dark-theme.png diff --git a/hermes-console-vue/src/assets/hermes-logo-full.png b/hermes-console/src/assets/hermes-logo-full.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo-full.png rename to hermes-console/src/assets/hermes-logo-full.png diff --git a/hermes-console-vue/src/assets/hermes-logo-header-dark-theme.png b/hermes-console/src/assets/hermes-logo-header-dark-theme.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo-header-dark-theme.png rename to hermes-console/src/assets/hermes-logo-header-dark-theme.png diff --git a/hermes-console-vue/src/assets/hermes-logo-header.png b/hermes-console/src/assets/hermes-logo-header.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo-header.png rename to hermes-console/src/assets/hermes-logo-header.png diff --git a/hermes-console-vue/src/assets/hermes-logo.png b/hermes-console/src/assets/hermes-logo.png similarity index 100% rename from hermes-console-vue/src/assets/hermes-logo.png rename to hermes-console/src/assets/hermes-logo.png diff --git a/hermes-console-vue/src/components/ace-editor/AceEditor.vue b/hermes-console/src/components/ace-editor/AceEditor.vue similarity index 100% rename from hermes-console-vue/src/components/ace-editor/AceEditor.vue rename to hermes-console/src/components/ace-editor/AceEditor.vue diff --git a/hermes-console-vue/src/components/ace-editor/ace-config.ts b/hermes-console/src/components/ace-editor/ace-config.ts similarity index 100% rename from hermes-console-vue/src/components/ace-editor/ace-config.ts rename to hermes-console/src/components/ace-editor/ace-config.ts diff --git a/hermes-console-vue/src/components/app-notification/AppNotification.vue b/hermes-console/src/components/app-notification/AppNotification.vue similarity index 100% rename from hermes-console-vue/src/components/app-notification/AppNotification.vue rename to hermes-console/src/components/app-notification/AppNotification.vue diff --git a/hermes-console-vue/src/components/app-notification/AppNotificationProvider.vue b/hermes-console/src/components/app-notification/AppNotificationProvider.vue similarity index 100% rename from hermes-console-vue/src/components/app-notification/AppNotificationProvider.vue rename to hermes-console/src/components/app-notification/AppNotificationProvider.vue diff --git a/hermes-console-vue/src/components/confirmation-dialog/ConfirmationDialog.spec.ts b/hermes-console/src/components/confirmation-dialog/ConfirmationDialog.spec.ts similarity index 92% rename from hermes-console-vue/src/components/confirmation-dialog/ConfirmationDialog.spec.ts rename to hermes-console/src/components/confirmation-dialog/ConfirmationDialog.spec.ts index 5ac517f014..b32945bdf5 100644 --- a/hermes-console-vue/src/components/confirmation-dialog/ConfirmationDialog.spec.ts +++ b/hermes-console/src/components/confirmation-dialog/ConfirmationDialog.spec.ts @@ -126,20 +126,20 @@ describe('ConfirmationDialog', () => { ).toBeEnabled(); //when - await userEvent.type(getAllByRole('textbox')[1], 'not matching text'); + await userEvent.type(getAllByRole('textbox')[0], 'not matching text'); //then - expect(getAllByRole('textbox')[1]).toHaveValue('not matching text'); + expect(getAllByRole('textbox')[0]).toHaveValue('not matching text'); expect( getByText('confirmationDialog.confirm').closest('button'), ).toBeDisabled(); //when - await userEvent.clear(getAllByRole('textbox')[1]); - await userEvent.type(getAllByRole('textbox')[1], 'prod'); + await userEvent.clear(getAllByRole('textbox')[0]); + await userEvent.type(getAllByRole('textbox')[0], 'prod'); //then - expect(getAllByRole('textbox')[1]).toHaveValue('prod'); + expect(getAllByRole('textbox')[0]).toHaveValue('prod'); expect( getByText('confirmationDialog.confirm').closest('button'), ).toBeEnabled(); diff --git a/hermes-console-vue/src/components/confirmation-dialog/ConfirmationDialog.vue b/hermes-console/src/components/confirmation-dialog/ConfirmationDialog.vue similarity index 100% rename from hermes-console-vue/src/components/confirmation-dialog/ConfirmationDialog.vue rename to hermes-console/src/components/confirmation-dialog/ConfirmationDialog.vue diff --git a/hermes-console-vue/src/components/console-alert/ConsoleAlert.vue b/hermes-console/src/components/console-alert/ConsoleAlert.vue similarity index 66% rename from hermes-console-vue/src/components/console-alert/ConsoleAlert.vue rename to hermes-console/src/components/console-alert/ConsoleAlert.vue index 98ab23e19e..8b71ad7936 100644 --- a/hermes-console-vue/src/components/console-alert/ConsoleAlert.vue +++ b/hermes-console/src/components/console-alert/ConsoleAlert.vue @@ -4,6 +4,8 @@ title?: string; text: string; type: 'error' | 'success' | 'warning' | 'info'; + link?: string; + linkDescription?: string; }>(); @@ -15,7 +17,14 @@ :type="props.type" border="start" :icon="icon ?? `\$${type}`" - > + > + {{ linkDescription }} + diff --git a/hermes-console-vue/src/components/console-footer/ConsoleFooter.spec.ts b/hermes-console/src/components/console-footer/ConsoleFooter.spec.ts similarity index 100% rename from hermes-console-vue/src/components/console-footer/ConsoleFooter.spec.ts rename to hermes-console/src/components/console-footer/ConsoleFooter.spec.ts diff --git a/hermes-console-vue/src/components/console-footer/ConsoleFooter.vue b/hermes-console/src/components/console-footer/ConsoleFooter.vue similarity index 100% rename from hermes-console-vue/src/components/console-footer/ConsoleFooter.vue rename to hermes-console/src/components/console-footer/ConsoleFooter.vue diff --git a/hermes-console-vue/src/components/console-header/ConsoleHeader.spec.ts b/hermes-console/src/components/console-header/ConsoleHeader.spec.ts similarity index 100% rename from hermes-console-vue/src/components/console-header/ConsoleHeader.spec.ts rename to hermes-console/src/components/console-header/ConsoleHeader.spec.ts diff --git a/hermes-console-vue/src/components/console-header/ConsoleHeader.vue b/hermes-console/src/components/console-header/ConsoleHeader.vue similarity index 100% rename from hermes-console-vue/src/components/console-header/ConsoleHeader.vue rename to hermes-console/src/components/console-header/ConsoleHeader.vue diff --git a/hermes-console/src/components/costs-card/CostsCard.spec.ts b/hermes-console/src/components/costs-card/CostsCard.spec.ts new file mode 100644 index 0000000000..15174af94e --- /dev/null +++ b/hermes-console/src/components/costs-card/CostsCard.spec.ts @@ -0,0 +1,30 @@ +import { describe } from 'vitest'; +import { render } from '@/utils/test-utils'; +import CostsCard from '@/components/costs-card/CostsCard.vue'; + +describe('CostsCard', () => { + const props = { + iframeUrl: + 'https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik', + detailsUrl: 'https://www.openstreetmap.org', + }; + + it('should render title properly', () => { + // when + const { getByText } = render(CostsCard, { props }); + + // then + const row = getByText('costsCard.title'); + expect(row).toBeVisible(); + }); + + it('should render iframe properly', async () => { + // when + const { container } = render(CostsCard, { props }); + const element = container.querySelector('iframe')!!; + + // then + expect(element).toBeVisible(); + expect(element).toHaveAttribute('src', props.iframeUrl); + }); +}); diff --git a/hermes-console/src/components/costs-card/CostsCard.vue b/hermes-console/src/components/costs-card/CostsCard.vue new file mode 100644 index 0000000000..0526ba9d62 --- /dev/null +++ b/hermes-console/src/components/costs-card/CostsCard.vue @@ -0,0 +1,38 @@ + + +