diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
new file mode 100644
index 0000000..375f3bd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -0,0 +1,41 @@
+name: Bug
+description: File a bug report
+title: "[BUG]: "
+labels: ["Type: Bug", "Status: Triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: What happened?
+ description: What did you do? What happened? What did you expect to happen?
+ placeholder: Put your description of the bug here.
+ validations:
+ required: true
+ - type: textarea
+ id: versions
+ attributes:
+ label: Versions
+ description: What versions of the relevant software are you running?
+ placeholder: 1.0.0
+ validations:
+ required: true
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: |
+ Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+ Please check your logs before submission to ensure sensitive information is redacted.
+ render: shell
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this issue, you agree to follow our [Code of Conduct](./CODE_OF_CONDUCT.md)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml
new file mode 100644
index 0000000..1990961
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation.yml
@@ -0,0 +1,41 @@
+name: Documentation
+description: Update or add documentation
+title: "[DOCS]: "
+labels: ["Type: Documentation", "Status: Triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill this out!
+ - type: textarea
+ id: describe-need
+ attributes:
+ label: Describe the need
+ description: What do you wish was different about our docs?
+ placeholder: Describe the need for documentation updates here.
+ validations:
+ required: true
+ - type: input
+ id: library_version
+ attributes:
+ label: Version
+ description: Do these docs apply to a specific version?
+ placeholder: 1.1.1
+ validations:
+ required: false
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: |
+ Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+ Please check your logs before submission to ensure sensitive information is redacted.
+ render: shell
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this documentation issue, you agree to follow our [Code of Conduct](CODE_OF_CONDUCT.md)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml
new file mode 100644
index 0000000..3bf43c6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature.yml
@@ -0,0 +1,41 @@
+name: Feature
+description: Suggest an idea for a new feature or enhancement
+title: "[FEAT]: "
+labels: ["Type: Feature", "Status: Triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill this out!
+ - type: textarea
+ id: describe-need
+ attributes:
+ label: Describe the need
+ description: What do you want to happen? What problem are you trying to solve?
+ placeholder: Describe the need for the feature.
+ validations:
+ required: true
+ - type: input
+ id: library_version
+ attributes:
+ label: Library Version
+ description: Does this feature suggestion apply to a specific version?
+ placeholder: 1.0.0
+ validations:
+ required: false
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: |
+ Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+ Please check your logs before submission to ensure sensitive information is redacted.
+ render: shell
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this feature request, you agree to follow our [Code of Conduct](CODE_OF_CONDUCT.md)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/maintenance.yml b/.github/ISSUE_TEMPLATE/maintenance.yml
new file mode 100644
index 0000000..5eff93c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/maintenance.yml
@@ -0,0 +1,41 @@
+name: Maintenance
+description: Dependencies, cleanup, refactoring, reworking of code
+title: "[MAINT]: "
+labels: ["Type: Maintenance", "Status: Triage"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill this out!
+ - type: textarea
+ id: describe-need
+ attributes:
+ label: Describe the need
+ description: What do you want to happen?
+ placeholder: Describe the maintenance need here.
+ validations:
+ required: true
+ - type: input
+ id: library_version
+ attributes:
+ label: Library Version
+ description: Does this maintenance apply to a specific version?
+ placeholder: v1.0.0
+ validations:
+ required: false
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: |
+ Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+ Please check your logs before submission to ensure sensitive information is redacted.
+ render: shell
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this request, you agree to follow our [Code of Conduct](CODE_OF_CONDUCT.md)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..2512348
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,32 @@
+Please refer to our [contributing docs](./CONTRIBUTING.md) for any questions on submitting a pull request.
+Issues are required for both bug fixes and features.
+
+Resolves #ISSUE_NUMBER
+
+
+----
+### Describe behaviour before the change
+
+
+*
+
+### Describe behaviour after the change
+
+
+*
+
+### Pull request checklist
+- [ ] I have read the [CONTRIBUTING.md](./CONTRIBUTING.md)
+- [ ] My code follows the code style of this project
+- [ ] Tests for the changes have been added (for bug fixes / features)
+- [ ] All new and existing tests passed.
+- [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
+
+### Does this introduce a breaking change?
+
+
+- [ ] Yes
+- [ ] No
+
+----
+
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 0000000..b0f48a3
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,24 @@
+# .github/release.yml
+
+changelog:
+ exclude:
+ labels:
+ - ignore-for-release
+ authors:
+ - HARMAN-Automotive
+ categories:
+ - title: Breaking Changes
+ labels:
+ - breaking-change
+ - title: Features
+ labels:
+ - feature
+ - title: Bug Fixes
+ labels:
+ - bug
+ - title: Other Changes
+ labels:
+ - "*"
+ exclude:
+ labels:
+ - dependencies
diff --git a/.github/workflows/dependencies-update.yaml b/.github/workflows/dependencies-update.yaml
new file mode 100644
index 0000000..da9bb76
--- /dev/null
+++ b/.github/workflows/dependencies-update.yaml
@@ -0,0 +1,60 @@
+name: "Update DEPENDENCIES file"
+
+on:
+ push:
+ branches: [ "*" ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ contents: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'zulu'
+ cache: maven
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Generate Dependencies file
+ run: mvn org.eclipse.dash:license-tool-plugin:license-check -Ddash.summary=DEPENDENCIES -P dash
+
+ - name: Check if file was changed
+ run: |
+ if git diff --name-only ${{ github.base_ref }}...${{ github.sha }} | grep -e 'DEPENDENCIES'; then
+ echo "The file was changed"
+ echo "was_file_changed=true" >> "$GITHUB_ENV"
+ git
+ else
+ echo "The file was not changed"
+ echo "was_file_changed=false" >> "$GITHUB_ENV"
+ fi
+
+ - name: Configure Git
+ if: ${{ env.was_file_changed }} == 'true'
+ run: |
+ git config user.name "$GITHUB_ACTOR"
+ git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
+
+ - name: Create pull request
+ if: ${{ env.was_file_changed }} == 'true'
+ uses: peter-evans/create-pull-request@v6
+ with:
+ add-paths: |
+ DEPENDENCIES
+ token: ${{ secrets.GITHUB_TOKEN }}
+ branch: chore/update-DEPENDENCIES
+ commit-message: "chore(dependencies): Update DEPENDENCIES"
+ delete-branch: true
+ title: Update DEPENDENCIES
+ body: |
+ This PR updates the DEPENDENCIES
\ No newline at end of file
diff --git a/.github/workflows/license-compliance.yml b/.github/workflows/license-compliance.yml
new file mode 100644
index 0000000..40366b3
--- /dev/null
+++ b/.github/workflows/license-compliance.yml
@@ -0,0 +1,48 @@
+name: License Compliance
+
+on:
+ push:
+ branches: [ "*" ]
+ paths-ignore:
+ - '**/*.md'
+ - '**/*.txt'
+ pull_request:
+ branches: [ "*" ]
+ paths:
+ - '**/pom.xml'
+ - 'pom.xml'
+ workflow_dispatch:
+
+permissions:
+ pull-requests: read
+ contents: write
+
+jobs:
+ check-licenses:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'zulu'
+ cache: maven
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Build with Maven and check dependencies with dash
+ run: |
+ mvn --batch-mode --update-snapshots verify -P dash
+
+ - name: Ensure DEPENDENCIES file is reflecting the current state
+ run: |
+ mvn org.eclipse.dash:license-tool-plugin:license-check -Ddash.summary=DEPENDENCIES-gen -P dash
+ diff DEPENDENCIES DEPENDENCIES-gen
+
+ - name: upload results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ path: 'target/dash/summary'
\ No newline at end of file
diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
new file mode 100644
index 0000000..00d64ad
--- /dev/null
+++ b/.github/workflows/maven-publish.yml
@@ -0,0 +1,36 @@
+# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
+# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
+
+name: Maven Package
+
+on:
+ release:
+ types: [created]
+ push:
+ branches: [ "main" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'zulu'
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Build with Maven
+ run: mvn clean -B package --file pom.xml
+
+ - name: Publish to GitHub Packages Apache Maven
+ run: mvn -DskipTests=true deploy -s $GITHUB_WORKSPACE/settings.xml
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 0000000..7284194
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,67 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+# This workflow helps you trigger a SonarCloud analysis of your code and populates
+# GitHub Code Scanning alerts with the vulnerabilities found.
+# Free for open source project.
+
+# 1. Login to SonarCloud.io using your GitHub account
+
+# 2. Import your project on SonarCloud
+# * Add your GitHub organization first, then add your repository as a new project.
+# * Please note that many languages are eligible for automatic analysis,
+# which means that the analysis will start automatically without the need to set up GitHub Actions.
+# * This behavior can be changed in Administration > Analysis Method.
+#
+# 3. Follow the SonarCloud in-product tutorial
+# * a. Copy/paste the Project Key and the Organization Key into the args parameter below
+# (You'll find this information in SonarCloud. Click on "Information" at the bottom left)
+#
+# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN
+# (On SonarCloud, click on your avatar on top-right > My account > Security
+# or go directly to https://sonarcloud.io/account/security/)
+
+# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/)
+# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9)
+
+name: SonarCloud analysis
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+ workflow_dispatch:
+
+permissions:
+ pull-requests: read # allows SonarCloud to decorate PRs with analysis results
+
+jobs:
+ Analysis:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Analyze with SonarCloud
+
+ # You can pin the exact commit or the version.
+ # uses: SonarSource/sonarcloud-github-action@v2.2.0
+ uses: SonarSource/sonarcloud-github-action@4006f663ecaf1f8093e8e4abb9227f6041f52216
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret)
+ with:
+ # Additional arguments for the SonarScanner CLI
+ args:
+ # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu)
+ # mandatory
+ -Dsonar.projectKey=eclipse-ecsp_utils
+ -Dsonar.organization=eclipse-ecsp
+ # Comma-separated paths to directories containing main source files.
+ #-Dsonar.sources= # optional, default is project base directory
+ # Comma-separated paths to directories containing test source files.
+ #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/
+ # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing.
+ #-Dsonar.verbose= # optional, default is false
+ # When you need the analysis to take place in a directory other than the one from which it was launched, default is .
+ projectBaseDir: .
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..951dd81
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,3 @@
+# Eclipse Foundation Community Code of Conduct
+
+This project has adopted the [Eclipse Foundation Community Code of Conduct](https://raw.githubusercontent.com/eclipse/.github/master/CODE_OF_CONDUCT.md).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ed62cf5
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,42 @@
+# How to contribute
+
+Support and contributions from the open source community are essential for keeping
+`HARMAN-Automotive/utils` up to date and always improving! There are a few guidelines that we need
+contributors to follow to keep the project consistent, as well as allow us to keep
+maintaining `HARMAN-Automotive/utils` in a reasonable amount of time.
+
+Please note that this project is released with a [Contributor Code of Conduct][coc].
+
+By participating in this project you agree to abide by its terms.
+
+[coc]: ./CODE_OF_CONDUCT.template
+
+## Creating an Issue
+
+Before you create a new Issue:
+
+1. Please make sure there is no [open issue](https://github.com/HARMAN-Automotive/utils/issues) yet.
+2. If it is a bug report, include the steps to reproduce the issue and please create a reproducible test case.
+3. If it is a feature request, please share the motivation for the new feature and how you would implement it.
+4. Please include links to the corresponding GitHub documentation.
+
+## Making Changes
+
+- Create a topic branch from the main branch.
+- Check for unnecessary whitespace / changes with `git diff --check` before committing.
+- Keep git commit messages clear and appropriate. Ideally follow commit conventions described below.
+
+## Submitting the Pull Request
+
+- Push your changes to your topic branch on your fork of the repo.
+- Submit a pull request from your topic branch to the [main](https://github.com/HARMAN-Automotive/utils) branch on the `HARMAN-Automotive/utils` repository.
+- Be sure to tag any issues your pull request is taking care of / contributing to. \* Adding "Closes #123"
+to a pull request description will auto close the issue once the pull request is merged in.
+
+
+## Merging a PR and Shipping a release (maintainers only)
+
+- A PR can only be merged into main branch by a maintainer if: CI is passing, approved by another maintainer and is up-to-date with the default branch.
+- Ensure that the PR is tagged with related [issue](https://github.com/HARMAN-Automotive/utils/issues) it intends to resolve.
+- Change log for all the PRs merged since the last release should be included in the release notes.
+- Automatically generated release notes is configured for the repo and must be used while creating a new release tag.
diff --git a/DEPENDENCIES b/DEPENDENCIES
new file mode 100644
index 0000000..295e015
--- /dev/null
+++ b/DEPENDENCIES
@@ -0,0 +1,34 @@
+maven/mavencentral/ch.qos.logback/logback-classic/1.5.5, EPL-1.0 AND LGPL-2.1-only, approved, #15279
+maven/mavencentral/ch.qos.logback/logback-core/1.5.5, EPL-1.0 AND LGPL-2.1-only, approved, #15210
+maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.3, Apache-2.0, approved, #15260
+maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.15.3, , approved, #15194
+maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.15.3, Apache-2.0, approved, #15199
+maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.15.3, Apache-2.0, approved, #15189
+maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0 and CC-BY-2.5, approved, #15220
+maven/mavencentral/com.google.errorprone/error_prone_annotations/2.26.1, Apache-2.0, approved, #13657
+maven/mavencentral/com.google.guava/failureaccess/1.0.2, Apache-2.0, approved, CQ22654
+maven/mavencentral/com.google.guava/guava/33.1.0-jre, Apache-2.0 AND CC0-1.0, approved, #13675
+maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, Apache-2.0, approved, CQ22657
+maven/mavencentral/com.google.j2objc/j2objc-annotations/3.0.0, Apache-2.0, approved, #13676
+maven/mavencentral/com.openpojo/openpojo/0.9.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/dev.morphia.morphia/morphia-core/2.2.3, Apache-2.0, approved, clearlydefined
+maven/mavencentral/io.github.classgraph/classgraph/4.8.78, MIT, approved, CQ22530
+maven/mavencentral/javax.el/javax.el-api/3.0.0, CDDL-1.0 or Apache-2.0, approved, CQ7249
+maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636
+maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.11.13, Apache-2.0, approved, clearlydefined
+maven/mavencentral/net.bytebuddy/byte-buddy/1.11.13, Apache-2.0 AND BSD-3-Clause, approved, #2712
+maven/mavencentral/org.apache.commons/commons-lang3/3.9, Apache-2.0, approved, CQ21762
+maven/mavencentral/org.checkerframework/checker-qual/3.42.0, MIT, approved, clearlydefined
+maven/mavencentral/org.glassfish.web/javax.el/2.2.6, CDDL-1.0 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, #1654
+maven/mavencentral/org.hamcrest/hamcrest-core/1.3, BSD-2-Clause, approved, CQ11429
+maven/mavencentral/org.javassist/javassist/3.21.0-GA, MPL-1.1 OR LGPL-2.1-or-later OR Apache-2.0, approved, CQ14999
+maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.mockito/mockito-core/3.12.4, MIT, approved, clearlydefined
+maven/mavencentral/org.mongodb/bson/4.5.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.mongodb/mongodb-driver-core/4.5.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.mongodb/mongodb-driver-legacy/4.5.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.mongodb/mongodb-driver-sync/4.5.1, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.objenesis/objenesis/3.2, Apache-2.0, approved, clearlydefined
+maven/mavencentral/org.projectlombok/lombok/1.18.30, MIT, approved, #15192
+maven/mavencentral/org.reflections/reflections/0.9.11, MIT, approved, CQ16665
+maven/mavencentral/org.slf4j/slf4j-api/2.0.13, MIT, approved, #5915
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..612c4db
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2023 Harman Connected Services Pvt. Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/NOTICE.md b/NOTICE.md
new file mode 100644
index 0000000..fb37fcd
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,44 @@
+# Notices for HARMAN-Automotive/utils
+
+This content is produced and maintained by the Eclipse Connected Services Platform Project.
+
+* Project home: [https://projects.eclipse.org/projects/automotive.ecsp](https://projects.eclipse.org/projects/automotive.ecsp)
+
+## Trademarks
+
+HARMAN-Automotive/utils is a trademark of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Apache License, Version 2.0 which is available at
+https://www.apache.org/licenses/LICENSE-2.0.
+
+SPDX-License-Identifier: Apache-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+https://github.com/HARMAN-Automotive/utils
+
+## Third-party Content
+
+This project leverages the following third party content.
+
+ignite-utils(1.0-SNAPSHOT) License: Apache-2.0 Project: https://github.com/HARMAN-Automotive/utils
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
diff --git a/README.md b/README.md
index 75db3f6..5ff1ee5 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,182 @@
-# utils
-ECSP utils
+[](./images/logo.png)
+
+# Utils
+`Utils` project consists frequently used utilities for ECSP project and provides the below functionalities to a service.
+[](https://github.com/eclipse-ecsp/utils/actions/workflows/maven-publish.yml)
+[](https://github.com/eclipse-ecsp/utils/actions/workflows/license-compliance.yml)
+
+1. Centralized logging.
+2. Health checks.
+3. Diagnostic data reporting.
+4. Application Metrics - counters, gauges and histograms.
+
+# Table of Contents
+* [Getting Started](#getting-started)
+* [Usage](#usage)
+* [How to contribute](#how-to-contribute)
+* [Built with Dependencies](#built-with-dependencies)
+* [Code of Conduct](#code-of-conduct)
+* [Authors](#authors)
+* [Security Contact Information](#security-contact-information)
+* [Support](#support)
+* [Troubleshooting](#troubleshooting)
+* [License](#license)
+* [Announcements](#announcements)
+
+
+## Getting Started
+
+To build the project in the local working directory after the project has been cloned/forked, run:
+
+```mvn clean install```
+
+from the command line interface.
+
+### Prerequisites
+
+1. Maven
+2. Java 17
+
+### Installation
+
+[How to set up maven](https://maven.apache.org/install.html)
+
+[Install Java](https://adoptium.net/installation/)
+
+### Running the tests
+
+```mvn test```
+
+### Deployment
+
+`utils` project serves as a library for the services. It is not meant to be deployed as a service in any cloud environment.
+
+## Usage
+Add the following dependency in the target project
+```
+
+ org.eclipse.ecsp
+ utils
+ 1.X.X
+
+```
+
+### Logging
+
+To implement logging in the services, the services need to get an instance of `IgniteLogger` class using the `IgniteLoggerFactory` factory class.
+
+Example:
+```java
+private static IgniteLogger logger = IgniteLoggerFactory.getLogger(ProtocolTranslatorPreProcessor.class);
+```
+
+The `IgniteLogger` is an extension of slf4j logger. All the concepts related to logging an error message, exception, stack traces, etc. alongwith the different log levels remain the same.
+
+### Health Check
+
+`utils` provides the services with Health Check capabilities by implementing a number of different `HealthMonitor` instances.
+`HealthService` listens to the health state changes in all the `HealthMonitor` instances implemented by the service and maintains Gauge data for the healthy and unhealthy health monitors along with the overall health of the service for service health monitoring.
+
+
+### Diagnostic Reporting
+
+`utils` provides the service with Diagnostic reporting capabilities by implementing a number of `DiagnosticReporter` instances which in turn maintain `DiagnosticData` for the metrics and the corresponding `DiagnosticResult`.
+`DiagnosticService` listens to all the enabled diagnostic reporters and do the reporting in Graylog besides publishing the metrics in prometheus about diagnostic data of a service.
+
+### Implementing metrics
+`utils` provides the service with metrics - Counters, Gauge and Histogram.
+Any service can extend the above metrics for reporting and monitoring purpose.
+
+Examples:
+
+ Implementing a Counter
+
+Custom `Counter` classes need to extend the abstract class `AbstractIgniteCounter`
+
+```java
+public class GenericIgniteCounter extends AbstractIgniteCounter {
+ public GenericIgniteCounter(String name, String help, String... labels) {
+ createCounter(name, help, labels);
+ }
+}
+```
+
+ Implementing a Gauge
+
+Custom `Gauge` classes need to extend the abstract class `AbstractIgniteGauge`
+
+```java
+public class IgniteDiagnosticGuage extends IgniteGuage {
+ public IgniteDiagnosticGuage() {
+ createGuage("diagnostic_metric", "node", "diagnostic_reporter_name", "diagnostic_reporter_sublabel");
+ }
+}
+```
+
+ Implementing a Histogram
+
+Custom `Histogram` classes need to extend the abstract class `AbstractIgniteHistogram`
+
+```java
+public class GenericIgniteHistogram extends AbstractIgniteHistogram {
+ public GenericIgniteHistogram(String name, String help, double[] buckets, String... labelNames) {
+ createHistogram(name, help, buckets, labelNames);
+ }
+}
+```
+
+## Built With Dependencies
+
+| Dependency | Purpose |
+|:---------------------------------------------------------------:|:-------------------------------------------|
+| [Entities](https://github.com/eclipse-ecsp/entities) | The library to implement database entities |
+| [Spring Framework](https://spring.io/projects/spring-framework) | The core spring support |
+| [Spring Boot](https://spring.io/projects/spring-boot/) | The web framework used |
+| [Maven](https://maven.apache.org/) | Dependency Management |
+| [Junit](https://junit.org/junit5/) | Testing framework |
+| [Mockito](https://site.mockito.org/) | Test Mocking framework |
+
+## How to contribute
+
+Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our contribution guidelines, and the process for submitting pull requests to us.
+
+## Code of Conduct
+
+Please read [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) for details on our code of conduct.
+
+## Authors
+
+
+
+
+
+
+See also the list of [contributors](https://github.com/HARMAN-Automotive/utils/graphs/contributors) who participated in this project.
+
+## Security Contact Information
+
+Please read [SECURITY.md](./SECURITY.md) to raise any security related issues.
+
+## Support
+Please write to us at [csp@harman.com](mailto:csp@harman.com)
+
+## Troubleshooting
+
+Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on how to raise an issue and submit a pull request to us.
+
+## License
+
+This project is licensed under the Apache-2.0 License - see the [LICENSE](./LICENSE) file for details.
+
+## Announcements
+All updates to this library are documented in our [Release Notes](./release_notes.txt) and [releases](https://github.com/eclipse-ecsp/utils/releases).
+For the versions available, see the [tags on this repository](https://github.com/eclipse-ecsp/utils/tags).
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..4773bf0
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,21 @@
+## Security Policy
+
+Thanks for helping make GitHub Open Source Software safe for everyone.
+
+GitHub takes the security of our software products and services seriously, including all the open source code repositories managed through our GitHub organizations, such as [HARMAN-Automotive](https://github.com/HARMAN-Automotive).
+
+Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we want to make sure that your finding gets passed along to the maintainers of this project for remediation.
+
+## Reporting a Vulnerability
+
+Since this source is part of [HARMAN-Automotive](https://github.com/HARMAN-Automotive) (a GitHub organization) we ask that you follow the guidelines [here](https://github.com/github/.github/blob/master/SECURITY.md#reporting-security-issues) to report anything that you might've found.
+
+## Dependency Security Management
+
+This project uses Dependabot tool to monitor (and fix) vulnerabilities in this project's dependencies.
+
+### Dependabot
+
+* [Dependabot](https://docs.github.com/en/free-pro-team@latest/github/managing-security-vulnerabilities/about-dependabot-security-updates) is a GitHub Security Feature. It tracks vulnerabilities in several languages including JavaScript.
+* When Dependabot detects any vulnerabilities in the [GitHub Advisory Database](https://docs.github.com/en/free-pro-team@latest/github/managing-security-vulnerabilities/browsing-security-vulnerabilities-in-the-github-advisory-database), it sends a notification and may also open a pull request to fix the vulnerability.
+* Only project maintainers can see Dependabot alerts
\ No newline at end of file
diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml
new file mode 100644
index 0000000..42ee9f5
--- /dev/null
+++ b/checkstyle-suppressions.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/harman_checks.xml b/harman_checks.xml
new file mode 100644
index 0000000..fa7cd59
--- /dev/null
+++ b/harman_checks.xml
@@ -0,0 +1,420 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/images/logo.png b/images/logo.png
new file mode 100644
index 0000000..d97dc01
Binary files /dev/null and b/images/logo.png differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9bd2883
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,455 @@
+
+
+
+ 4.0.0
+ org.eclipse.ecsp
+ utils
+ 1.0-SNAPSHOT
+
+ Utils
+ Utility library for ECSP project
+ https://github.com/eclipse-ecsp/utils
+
+
+
+ Apache-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
+
+
+
+
+ scm:git:https://github.com/eclipse-ecsp/utils.git
+ https://github.com/eclipse-ecsp/utils
+ HEAD
+
+
+
+
+ kaushalaroraharman
+ Kaushal Arora
+ kaushal.arora@harman.com
+
+
+
+
+ GitHub
+ https://github.com/eclipse-ecsp/utils/issues
+
+
+
+ https://github.com/eclipse-ecsp
+ eclipse-ecsp
+
+
+
+ UTF-8
+ https://artifactory-fr.harman.com/artifactory
+
+ 10.13.0
+ 1.1.0
+ 3.3.1
+ ${project.basedir}/harman_checks.xml
+ ${project.basedir}/checkstyle-suppressions.xml
+
+ ${project.build.directory}/checkstyle-result.xml
+
+ 3.0.3
+
+ jacoco
+ 17
+ 17
+ ${project.build.directory}/site/jacoco-ut/jacoco.xml
+
+ java
+ ${project.build.directory}/coverage-reports/jacoco-ut.exec
+
+ 1.5.5
+ 6.0.1
+ 0.6.0
+ 2.18.1
+ 6.1.14
+ 3.12.4
+ 3.3.3
+ 3.3.3
+ 2.0
+
+ src/main/java/com/harman/ignite/utils/logger/IgniteCallerDataConverter.java,
+ src/main/java/com/harman/ignite/utils/logger/IgniteThrowableProxyConverter.java
+
+
+
+
+
+ github
+ GitHub OWNER Apache Maven Packages
+ https://maven.pkg.github.com/eclipse-ecsp/utils
+
+
+
+
+
+ github
+ https://maven.pkg.github.com/harman-automotive/csp-central-repo
+
+ true
+
+
+
+
+
+
+ dash-licenses-releases
+ https://repo.eclipse.org/content/repositories/dash-licenses-releases/
+
+ false
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring.boot.autoconfigure}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${springboot.version}
+
+
+ org.springframework
+ spring-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.yaml
+ snakeyaml
+
+
+
+
+ org.yaml
+ snakeyaml
+ ${snakeyaml.version}
+
+
+ com.harman.ignite
+ ignite-entities
+ ${ignite.entities.version}
+
+
+ de.siegmar
+ logback-gelf
+ ${logback.gelf.version}
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+
+ io.prometheus
+ simpleclient
+ ${prometheus.client.version}
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+ org.springframework
+ spring-core
+ ${spring.version}
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+ org.springframework
+ spring-test
+ ${spring.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.6.2
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+
+
+
+
+ org.cyclonedx
+ cyclonedx-maven-plugin
+ 2.7.10
+
+ application
+ 1.5
+ true
+ true
+ true
+ true
+ true
+ true
+ true
+ all
+ ${project.basedir}/sbom
+ false
+
+
+
+ package
+
+ makeAggregateBom
+
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 2.5
+
+
+
+ ${basedir}
+
+ **/jar/
+
+ false
+
+
+
+
+
+ org.cyclonedx
+ cyclonedx-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.1
+
+
+ package
+
+ shade
+
+
+ true
+ false
+
+
+
+
+
+
+ maven-surefire-plugin
+ ${maven.surefire.version}
+
+
+ -Xmx2G
+ ${surefireArgLine}
+
+ pertest
+ true
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+
+ pre-unit-test
+
+ prepare-agent
+
+
+
+ ${jacoco.ut.execution.data.file}
+
+ surefireArgLine
+
+
+
+
+ post-unit-test
+ test
+
+ report
+
+
+
+ ${jacoco.ut.execution.data.file}
+
+ ${project.reporting.outputDirectory}/jacoco-ut
+
+ com/harman/ignite/utils/logger/IgniteCallerDataConverter.*
+ com/harman/ignite/utils/logger/IgniteThrowableProxyConverter.*
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.3
+
+
+
+ org.sonarsource.scanner.maven
+ sonar-maven-plugin
+ 3.10.0.2594
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ ${maven.checkstyle.version}
+
+
+ validate
+ validate
+
+ true
+ true
+ xml
+ true
+ true
+ warning
+ true
+ true
+ true
+
+
+ check
+
+
+
+
+
+ com.puppycrawl.tools
+ checkstyle
+ ${checkstyle.version}
+
+
+
+
+ org.eclipse.dash
+ license-tool-plugin
+ ${license-tool-plugin.version}
+
+ test
+
+
+
+ license-check
+
+ license-check
+
+
+
+
+
+
+
+
+ dash
+
+
+
+ org.eclipse.dash
+ license-tool-plugin
+
+ false
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ true
+
+
+
+
+
+
+
diff --git a/release_notes.txt b/release_notes.txt
new file mode 100644
index 0000000..581fd82
--- /dev/null
+++ b/release_notes.txt
@@ -0,0 +1,2 @@
+==== 1.0-SNAPSHOT ================================================================
+Open source SNAPSHOT release for utils
\ No newline at end of file
diff --git a/settings.xml b/settings.xml
new file mode 100644
index 0000000..3183838
--- /dev/null
+++ b/settings.xml
@@ -0,0 +1,11 @@
+
+
+
+ github
+ ${env.GITHUB_ACTOR}
+ ${env.GITHUB_TOKEN}
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/harman/ignite/diagnostic/DiagnosticConstants.java b/src/main/java/com/harman/ignite/diagnostic/DiagnosticConstants.java
new file mode 100644
index 0000000..6c72293
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/DiagnosticConstants.java
@@ -0,0 +1,46 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+/** Constants for diagnostic module. */
+public class DiagnosticConstants {
+
+ public static final String DIAGNOSTIC_KEYWORD = "diagnostic.";
+ public static final String PROPERTY_DIAGNOSTIC_REPORTER_ENABLED = "property.diagnostic.reporter.enabled";
+
+ private DiagnosticConstants() {}
+}
diff --git a/src/main/java/com/harman/ignite/diagnostic/DiagnosticData.java b/src/main/java/com/harman/ignite/diagnostic/DiagnosticData.java
new file mode 100644
index 0000000..b4ce106
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/DiagnosticData.java
@@ -0,0 +1,44 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import java.util.HashMap;
+
+/** This class is used to store the diagnostic data. */
+public class DiagnosticData extends HashMap {
+
+}
diff --git a/src/main/java/com/harman/ignite/diagnostic/DiagnosticReporter.java b/src/main/java/com/harman/ignite/diagnostic/DiagnosticReporter.java
new file mode 100644
index 0000000..0dcdc51
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/DiagnosticReporter.java
@@ -0,0 +1,69 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+/**
+ * This interface is used to report the diagnostic data.
+ *
+ * @author avadakkootko
+ */
+public interface DiagnosticReporter {
+
+ public DiagnosticData getDiagnosticData();
+
+ /**
+ * This method is used to name of the DiagnosticReporter.
+ *
+ * @return name of the DiagnosticReporter
+ */
+ public String getDiagnosticReporterName();
+
+ /**
+ * This method is used to return the name of the DiagnosticMetric.
+ *
+ * @return name of the DiagnosticMetric
+ */
+ public String getDiagnosticMetricName();
+
+ /**
+ * This method is used to check if the DiagnosticReporter is enabled.
+ *
+ * @return is DiagnosticReporter enabled or not
+ */
+ public boolean isDiagnosticReporterEnabled();
+
+}
diff --git a/src/main/java/com/harman/ignite/diagnostic/DiagnosticResult.java b/src/main/java/com/harman/ignite/diagnostic/DiagnosticResult.java
new file mode 100644
index 0000000..68c2cba
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/DiagnosticResult.java
@@ -0,0 +1,65 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+/**
+ * This enum is used to report the diagnostic result.
+ *
+ * @author avadakkootko
+ */
+public enum DiagnosticResult {
+ /**
+ * Used by diagnostic metric to report success.
+ */
+ PASS {
+ @Override
+ public double getValue() {
+ return 1.0;
+ }
+ },
+ /**
+ * Used by diagnostic metric to report an issue.
+ */
+ FAIL {
+ @Override
+ public double getValue() {
+ return 0.0;
+ }
+ };
+
+ public abstract double getValue();
+}
diff --git a/src/main/java/com/harman/ignite/diagnostic/DiagnosticService.java b/src/main/java/com/harman/ignite/diagnostic/DiagnosticService.java
new file mode 100644
index 0000000..45ad1b8
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/DiagnosticService.java
@@ -0,0 +1,134 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import com.harman.ignite.healthcheck.InvalidMetricNamingException;
+import com.harman.ignite.healthcheck.InvalidMetricNamingException;
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import com.harman.ignite.utils.metrics.IgniteDiagnosticGuage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The objective of DiagnosticService is to do reporting in Graylog,
+ * and publish metrics in prometheus about diagnostic data of a service.
+ *
+ * @author avadakkootko
+ */
+@Component
+public class DiagnosticService {
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(DiagnosticService.class);
+
+ @Autowired(required = false)
+ private List reporters;
+
+ @Value("${NODE_NAME:localhost}")
+ private String nodeName;
+
+ @Autowired
+ private IgniteDiagnosticGuage serviceDiagnosticGuage;
+
+ /**
+ * This method is used to trigger the diagnosis.
+ */
+ public void triggerDiagnosis() {
+ List enabledDiagnosticReporters = getEnabledReporters();
+ for (DiagnosticReporter reporter : enabledDiagnosticReporters) {
+ String metricName = reporter.getDiagnosticMetricName();
+ DiagnosticData data = reporter.getDiagnosticData();
+ for (Map.Entry entry : data.entrySet()) {
+ String key = entry.getKey();
+ DiagnosticResult result = data.get(key);
+ serviceDiagnosticGuage.set(result.getValue(), nodeName, metricName, key);
+ if (result == DiagnosticResult.PASS) {
+ LOGGER.info("Diagnosis for metric name {} and key {} is {}", metricName, key, result);
+ } else {
+ LOGGER.warn("Diagnosis for metric name {} and key {} is {}", metricName, key, result);
+ }
+ }
+ }
+ }
+
+ protected List getEnabledReporters() {
+ Map metricToReporterMapping = new HashMap<>();
+ List enabledDiagnosticReporters = new ArrayList<>();
+ if (reporters != null) {
+ for (DiagnosticReporter reporter : reporters) {
+ if (reporter.isDiagnosticReporterEnabled()) {
+ String reporterName = reporter.getDiagnosticReporterName();
+ String metricName = reporter.getDiagnosticMetricName();
+ // If two reporters have metrics with same name throw
+ // exception
+ if (metricToReporterMapping.containsKey(metricName)) {
+ LOGGER.error("Two diagnostic reporters {} and {} cannot have same MetricName :", reporterName,
+ metricToReporterMapping.get(metricName), metricName);
+ throw new InvalidMetricNamingException("Two diagnostic reporters "
+ + reporterName + "and " + metricToReporterMapping.get(metricName)
+ + "cannot have same MetricName " + metricName);
+ }
+ LOGGER.info("Diagnostic reporter {}, is enabled.", reporterName);
+ metricToReporterMapping.put(metricName, reporterName);
+ enabledDiagnosticReporters.add(reporter);
+ } else {
+ LOGGER.info("Diagnostic reporter {}, is disabled.", reporter.getDiagnosticReporterName());
+ }
+ }
+ }
+ return enabledDiagnosticReporters;
+ }
+
+ // for testing
+ void setReporters(List reporters) {
+ this.reporters = reporters;
+ }
+
+ void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ void setServiceDiagnosticGuage(IgniteDiagnosticGuage serviceDiagnosticGuage) {
+ this.serviceDiagnosticGuage = serviceDiagnosticGuage;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterImpl.java b/src/main/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterImpl.java
new file mode 100644
index 0000000..c66547f
--- /dev/null
+++ b/src/main/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterImpl.java
@@ -0,0 +1,121 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.env.AbstractEnvironment;
+import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Objects;
+import java.util.stream.StreamSupport;
+
+/**
+ * Performs the validation of the values of the properties configured against the expected diagnostic value provided.
+ *
+ * @author MaKumari
+ */
+@Component("propertyDiagnostic")
+public class PropertyDiagnosticReporterImpl implements DiagnosticReporter {
+
+ private static IgniteLogger igniteLogger = IgniteLoggerFactory.getLogger(PropertyDiagnosticReporterImpl.class);
+
+ private static final String DIAGNOSTIC_PROPERTY_METRIC_NAME = "DIAGNOSTIC_PROPERTY_METRIC";
+ private static final String DIAGNOSTIC_PROPERTY_REPORTER_NAME = "DIAGNOSTIC_PROPERTY_REPORTER";
+
+ HashMap configurationMap = new HashMap<>();
+
+ @Autowired
+ ApplicationContext ctx;
+ @Value("${" + DiagnosticConstants.PROPERTY_DIAGNOSTIC_REPORTER_ENABLED + ": true }")
+ private boolean diagnosticPropertyReporterEnabled;
+
+ @Override
+ public DiagnosticData getDiagnosticData() {
+ StringBuilder passedParms = new StringBuilder().append('\n');
+ StringBuilder failedParms = new StringBuilder().append('\n');
+ DiagnosticData diagnosticData = new DiagnosticData();
+ MutablePropertySources propSources = ((AbstractEnvironment) ctx.getEnvironment()).getPropertySources();
+ StreamSupport.stream(propSources.spliterator(), false)
+
+ .filter(ps -> ps instanceof EnumerablePropertySource)
+ .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames())
+ .flatMap(Arrays::stream)
+ .filter(diagnosticPropertyName ->
+ diagnosticPropertyName.startsWith(DiagnosticConstants.DIAGNOSTIC_KEYWORD))
+ .forEach(diagnosticPropertyName -> {
+ String propertyToBeValidated = diagnosticPropertyName
+ .substring(DiagnosticConstants.DIAGNOSTIC_KEYWORD.length());
+ // Check if the property to be validated is present in the environment
+ if (Objects.equals(((AbstractEnvironment) ctx.getEnvironment()).getProperty(diagnosticPropertyName),
+ ((AbstractEnvironment) ctx.getEnvironment())
+ .getProperty(propertyToBeValidated))) {
+ diagnosticData.put(propertyToBeValidated, DiagnosticResult.PASS);
+ passedParms.append(propertyToBeValidated).append('\n');
+ } else {
+ diagnosticData.put(propertyToBeValidated, DiagnosticResult.FAIL);
+ failedParms.append(propertyToBeValidated).append('\n');
+ }
+ });
+ igniteLogger.debug("Properties that matches diagnostic criteria are : {}", passedParms.toString());
+ igniteLogger.warn("Properties that don't matches diagnostic criteria are : {}", failedParms.toString());
+ return diagnosticData;
+ }
+
+ @Override
+ public boolean isDiagnosticReporterEnabled() {
+ return diagnosticPropertyReporterEnabled;
+ }
+
+ @Override
+ public String getDiagnosticReporterName() {
+ return DIAGNOSTIC_PROPERTY_REPORTER_NAME;
+ }
+
+ @Override
+ public String getDiagnosticMetricName() {
+ return DIAGNOSTIC_PROPERTY_METRIC_NAME;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/HealthMonitor.java b/src/main/java/com/harman/ignite/healthcheck/HealthMonitor.java
new file mode 100644
index 0000000..f9574e3
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/HealthMonitor.java
@@ -0,0 +1,82 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+/**
+ * Contract for health monitors that intend to publish health to the HealthService.
+ *
+ * @author avadakkootko
+ */
+public interface HealthMonitor {
+
+ /**
+ * This method is used to check if the health monitor is healthy.
+ *
+ * @param forceHealthCheck if true, then it needs to trigger a forced health check
+ * @return true if health monitor is healthy. If forceHealthCheck is true, trigger a forced health check.
+ */
+ public boolean isHealthy(boolean forceHealthCheck);
+
+ /**
+ * This method is used to get name of the health monitor.
+ *
+ * @return name of the health monitor
+ */
+ public String monitorName();
+
+ /**
+ * This method is used to check if the health monitor is unhealthy and hence service should be restarted.
+ *
+ * @return true if the health monitor is unhealthy and hence service should be restarted.
+ */
+ public boolean needsRestartOnFailure();
+
+ /**
+ * This method is used to get the name of the ignite gauge.
+ *
+ * @return name of the ignite gauge
+ */
+ public String metricName();
+
+ /**
+ * This method is used to check if the service is enabled or not.
+ *
+ * @return if the service is enabled or not
+ */
+ public boolean isEnabled();
+
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/HealthService.java b/src/main/java/com/harman/ignite/healthcheck/HealthService.java
new file mode 100644
index 0000000..e9f82e6
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/HealthService.java
@@ -0,0 +1,340 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import com.harman.ignite.utils.metrics.IgniteHealthGuage;
+import jakarta.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The objective of IgniteHealthMonitor is to publish the health of stream processor to prometheus.
+ * This can also be used for readiness and liveliness probe.
+ *
+ * @author avadakkootko
+ */
+@Component
+public class HealthService {
+
+ static final String SERVICE_HEALTH = "SERVICE_HEALTH";
+ static final String ISHEALTHY = " is healthy; ";
+ static final String ISUNHEALTHY = " is unhealthy;";
+ static final double HEALTHY = 0;
+ static final double UNHEALTHY = 1;
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(HealthService.class);
+ private final AtomicBoolean startedExecutor = new AtomicBoolean(false);
+ @Autowired(required = false)
+ private List healthMonitors;
+ @Value("${NODE_NAME:localhost}")
+ private String nodeName;
+ @Value("${health.service.failure.retry.thrshold:10}")
+ private int failureRetryThreshold;
+ @Value("${health.service.failure.retry.interval.millis:50}")
+ private int failureRetryInterval;
+ @Value("${health.service.retry.interval.millis:100}")
+ private int retryInterval;
+ @Value("${health.service.executor.shutdown.millis:2000}")
+ private int shutdownBuffer;
+ @Value("${health.service.executor.initial.delay:300000}")
+ private long initialDelay;
+ private HealthServiceCallBack callback;
+ // Here we are using a single guage with multiple labels for each monitor.
+ @Autowired
+ private IgniteHealthGuage serviceHealthGuage;
+ private ScheduledExecutorService healthServiceExecutor = null;
+ private final HealthServiceState previousState = new HealthServiceState();
+ private final HealthServiceState currentState = new HealthServiceState();
+
+ /**
+ * It accepts two parameters boolean value force and list of health-monitor to be checked for health status.
+ * If force is true, it triggers a forced health check for all health monitors.
+ * It returns a list of failed monitors which can be retried in case of failures.
+ *
+ * @param force if true, then it needs to trigger a forced health check
+ * @param hms list of health monitors to be checked for health status
+ * @return list of failed monitors
+ */
+ protected synchronized List checkHealthAndGetFailedMonitors(boolean force, List hms) {
+ List failedHealthMonitors = new ArrayList<>();
+ boolean spHealthy = true;
+ StringBuilder status = new StringBuilder();
+ for (HealthMonitor healthMonitor : hms) {
+ String monitorName = healthMonitor.monitorName();
+ String metricName = healthMonitor.metricName();
+ if (healthMonitor.isHealthy(force)) {
+ status.append(healthMonitor.monitorName()).append(ISHEALTHY);
+ serviceHealthGuage.set(HEALTHY, nodeName, metricName);
+ } else {
+ status.append(monitorName).append(ISUNHEALTHY);
+ serviceHealthGuage.set(UNHEALTHY, nodeName, metricName);
+ failedHealthMonitors.add(healthMonitor);
+ spHealthy = false;
+ }
+ }
+
+ String statusMsg = status.toString();
+ double state = 0;
+ if (spHealthy) {
+ state = HEALTHY;
+ serviceHealthGuage.set(HEALTHY, nodeName, SERVICE_HEALTH);
+ } else {
+ state = UNHEALTHY;
+ serviceHealthGuage.set(UNHEALTHY, nodeName, SERVICE_HEALTH);
+ }
+ currentState.setState(state);
+ currentState.setMessage(statusMsg);
+ if (!currentState.equals(previousState)) {
+ printStatus(spHealthy, statusMsg);
+ previousState.setState(currentState.getState());
+ previousState.setMessage(currentState.getMessage());
+ }
+ return failedHealthMonitors;
+ }
+
+ private void printStatus(boolean spHealthy, String statusMsg) {
+ if (spHealthy) {
+ LOGGER.info("Health status :: healthy; desc: {}", statusMsg);
+ } else {
+ LOGGER.error("Health status :: unhealthy; desc: {}", statusMsg);
+ }
+ }
+
+ /**
+ * This method can be used:
+ * For the initial forced health check by Stream base Launcher or API without starting the scheduled executor.
+ * Why this approach?
+ * Because there may be certain monitors which is bound to return unhealthy unless the process starts.
+ * For example:
+ * Kafka state listener health monitor will return unhealthy unless stream processor starts.
+ * Hence, this may have to be ignored for the initial health check.
+ * Another approach is to come up with a new contract that says:
+ * initialCheckDisabled
+ * This can be achieved in the next step.
+ *
+ * @return list of failed health monitors
+ */
+ public List triggerInitialCheck() {
+ List failedHealthMonitors;
+ boolean force = true;
+ failedHealthMonitors = checkHealthAndGetFailedMonitors(force, healthMonitors);
+ int failedMonitorsSize = failedHealthMonitors.size();
+ if (failedMonitorsSize > 0) {
+ LOGGER.error("Initial health check failed with {} health monitors", failedMonitorsSize);
+ } else {
+ LOGGER.info("Initial health check has passed");
+ }
+ return failedHealthMonitors;
+ }
+
+ /**
+ * Invoked by the scheduled executor. It wraps the retry strategy in case of failure scenario.
+ *
+ * @return true if service needs to be restarted
+ * @throws InterruptedException if the thread is interrupted
+ */
+ protected boolean needsRestart() throws InterruptedException {
+ // For the first health check default states of variable unHealthy will
+ // be true and force will be force.
+ startedExecutor.set(true);
+ boolean restart = true;
+ boolean force = false;
+ List hms = new ArrayList<>(healthMonitors);
+ int counter = 0;
+ do {
+ List failedHms = checkHealthAndGetFailedMonitors(force, hms);
+ hms.clear();
+ for (HealthMonitor hm : failedHms) {
+ if (hm.needsRestartOnFailure()) {
+ hms.add(hm);
+ }
+ }
+ if (!hms.isEmpty()) {
+ restart = true;
+ force = true;
+ } else {
+ restart = false;
+ force = false;
+ }
+ counter++;
+ Thread.sleep(failureRetryInterval);
+ } while (counter <= (failureRetryThreshold) && restart);
+ return restart;
+ }
+
+ /**
+ * Create a list of health monitors that are enabled and if two monitors have same name throw exception.
+ */
+ @PostConstruct
+ public void init() {
+ Map metricToMonitorMapping = new HashMap<>();
+ List enabledHealthMonitors = new ArrayList<>();
+ if (healthMonitors != null) {
+ for (HealthMonitor healthMonitor : healthMonitors) {
+ if (healthMonitor.isEnabled()) {
+ String metricName = healthMonitor.metricName();
+ String monitorName = healthMonitor.monitorName();
+ // If two monitors have metrics with same name throw
+ // exception
+ if (metricToMonitorMapping.containsKey(metricName)) {
+ LOGGER.error("Two health monitors {} and {} cannot have same MetricName :", monitorName,
+ metricToMonitorMapping.get(metricName), metricName);
+ throw new InvalidMetricNamingException(
+ "Two health monitors " + monitorName + "and " + metricToMonitorMapping.get(metricName)
+ + "cannot have same MetricName " + metricName);
+ }
+ LOGGER.info("Health monitor {}, is enabled.", healthMonitor.monitorName());
+ metricToMonitorMapping.put(metricName, healthMonitor.monitorName());
+ enabledHealthMonitors.add(healthMonitor);
+ } else {
+ LOGGER.info("Health monitor {}, is disabled.", healthMonitor.monitorName());
+ }
+ }
+ }
+ healthMonitors = new ArrayList<>(enabledHealthMonitors);
+ createhHealthServiceExecutor();
+ }
+
+ private void createhHealthServiceExecutor() {
+ healthServiceExecutor = Executors.newSingleThreadScheduledExecutor(runnable -> {
+ Thread t = Executors.defaultThreadFactory().newThread(runnable);
+ t.setDaemon(true);
+ t.setUncaughtExceptionHandler(new HealthServiceUncaughtExceptionHandler());
+ t.setName(Thread.currentThread().getName() + ":" + "HealthService");
+ return t;
+ });
+ }
+
+ /**
+ * Start the scheduled executor which will periodically check health of HealthMonitors.
+ */
+ public synchronized void startHealthServiceExecutor() {
+ if (!startedExecutor.get()) {
+ healthServiceExecutor.scheduleWithFixedDelay(() -> {
+ try {
+ checkCallback(callback);
+ } catch (InterruptedException e) {
+ LOGGER.error("Error occurred while executing health service scheduled thread {}", e);
+ Thread.currentThread().interrupt();
+ } catch (Exception e) {
+ LOGGER.error("Error occurred while executing health service scheduled thread {}", e);
+ }
+
+ }, initialDelay, retryInterval, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void checkCallback(HealthServiceCallBack callback) throws InterruptedException {
+ boolean restart = needsRestart();
+ if (restart) {
+ if (callback != null && callback.performRestart()) {
+ close();
+ } else {
+ LOGGER.trace("Service is unhealthy. Continuing health check without restart");
+ }
+ }
+ }
+
+ private class HealthServiceUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
+ @Override
+ public void uncaughtException(Thread thread, Throwable t) {
+ LOGGER.error("Uncaught exception in thread {} ", thread.getName(), t);
+ }
+ }
+
+ protected void close() {
+ if (healthServiceExecutor != null && !healthServiceExecutor.isShutdown()) {
+ LOGGER.info("Shutting the SingleThreadScheduledExecutor for health service!");
+ ThreadUtils.shutdownExecutor(healthServiceExecutor, shutdownBuffer, false);
+ startedExecutor.set(false);
+ }
+ }
+
+ public void registerCallBack(HealthServiceCallBack callback) {
+ this.callback = callback;
+ LOGGER.info("Registered HealthService callback");
+ }
+
+ boolean isStartedExecutor() {
+ return startedExecutor.get();
+ }
+
+ List getHealthMonitors() {
+ return this.healthMonitors;
+ }
+
+ // Below are for test case support
+ void setHealthMonitors(List healthMonitors) {
+ this.healthMonitors = healthMonitors;
+ }
+
+ void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ void setFailureRetryThreshold(int failureRetryThreshold) {
+ this.failureRetryThreshold = failureRetryThreshold;
+ }
+
+ void setFailureRetryInterval(int failureRetryInterval) {
+ this.failureRetryInterval = failureRetryInterval;
+ }
+
+ void setRetryInterval(int retryInterval) {
+ this.retryInterval = retryInterval;
+ }
+
+ void setServiceHealthGuage(IgniteHealthGuage serviceHealthGuage) {
+ this.serviceHealthGuage = serviceHealthGuage;
+ }
+
+ HealthServiceCallBack getCallback() {
+ return this.callback;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/HealthServiceCallBack.java b/src/main/java/com/harman/ignite/healthcheck/HealthServiceCallBack.java
new file mode 100644
index 0000000..2ebe9ce
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/HealthServiceCallBack.java
@@ -0,0 +1,53 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+/**
+ * Enables the application implementing health service to take appropriate action on critical health monitor failure.
+ *
+ * @author avadakkootko
+ */
+public interface HealthServiceCallBack {
+
+ /**
+ * This method is used to perform a restart of the service.
+ *
+ * @return true if a restart is required, else false
+ */
+ public boolean performRestart();
+
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/HealthServiceState.java b/src/main/java/com/harman/ignite/healthcheck/HealthServiceState.java
new file mode 100644
index 0000000..61ee026
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/HealthServiceState.java
@@ -0,0 +1,102 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+/**
+ * This class is used to store the health service state.
+ */
+public class HealthServiceState {
+
+ private static final int BITS = 32;
+
+ private double state;
+ private String message;
+
+ public double getState() {
+ return state;
+ }
+
+ public void setState(double state) {
+ this.state = state;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public HealthServiceState() {
+ //default constructor
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((message != null) ? message.hashCode() : 0);
+ long temp;
+ temp = Double.doubleToLongBits(state);
+ result = prime * result + (int) (temp ^ (temp >>> BITS));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ HealthServiceState other = (HealthServiceState) obj;
+ if (message == null) {
+ if (other.message != null) {
+ return false;
+ }
+ } else if (!message.equals(other.message)) {
+ return false;
+ }
+ return (Double.doubleToLongBits(state) == Double.doubleToLongBits(other.state));
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/InvalidMetricNamingException.java b/src/main/java/com/harman/ignite/healthcheck/InvalidMetricNamingException.java
new file mode 100644
index 0000000..f02e08b
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/InvalidMetricNamingException.java
@@ -0,0 +1,12 @@
+package com.harman.ignite.healthcheck;
+
+/**
+ * Custom exception for duplicate metric name.
+ */
+public class InvalidMetricNamingException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidMetricNamingException(String s) {
+ super(s);
+ }
+}
diff --git a/src/main/java/com/harman/ignite/healthcheck/ThreadUtils.java b/src/main/java/com/harman/ignite/healthcheck/ThreadUtils.java
new file mode 100644
index 0000000..bd710e0
--- /dev/null
+++ b/src/main/java/com/harman/ignite/healthcheck/ThreadUtils.java
@@ -0,0 +1,106 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility class for thread related operations.
+ */
+public abstract class ThreadUtils {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(ThreadUtils.class);
+
+ private ThreadUtils() {
+ }
+
+ /**
+ * Shuts down an executor reliably. Optionally allows shutting down the JVM if executor doesn't shutdown
+ *
+ * @param exec Executor service to shut down
+ * @param waitTimeMs Time to wait for executor to shut down
+ * @param exitOnFailure Exit the JVM if executor doesn't shut down
+ */
+ public static void shutdownExecutor(ExecutorService exec, int waitTimeMs, boolean exitOnFailure) {
+ if (exec != null && !exec.isShutdown()) {
+ LOGGER.info("Shutting down executor service");
+ exec.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!exec.awaitTermination(waitTimeMs, TimeUnit.MILLISECONDS)) {
+ LOGGER.info("Shutting down executor service forcefully,"
+ + " as it has not responded to graceful shutdown");
+ exec.shutdownNow(); // Cancel currently executing tasks
+ // Wait a while for tasks to respond to being cancelled
+ execWithAwaitTermination(exec, waitTimeMs, exitOnFailure);
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ exec.shutdownNow();
+ try {
+ execWithAwaitTermination(exec, waitTimeMs, exitOnFailure);
+ } catch (InterruptedException e) {
+ logErrorAndExit(exitOnFailure);
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ private static void execWithAwaitTermination(ExecutorService exec, int waitTimeMs, boolean exitOnFailure)
+ throws InterruptedException {
+ if (!exec.awaitTermination(waitTimeMs, TimeUnit.MILLISECONDS)) {
+ LOGGER.error("Executor service not closed after waiting {} ms", waitTimeMs);
+ if (exitOnFailure) {
+ LOGGER.error("Executor service not closed after waiting {} ms . Exiting application", waitTimeMs);
+ System.exit(1);
+ }
+ }
+ }
+
+ private static void logErrorAndExit(boolean exitOnFailure) {
+ LOGGER.error("Interrupted when waiting on executor");
+ if (exitOnFailure) {
+ LOGGER.error("Executor service shutdown failed. Interrupted. Exiting application");
+ System.exit(1);
+ }
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/filter/DuplicateExceptionFilter.java b/src/main/java/com/harman/ignite/utils/filter/DuplicateExceptionFilter.java
new file mode 100644
index 0000000..2f3e2de
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/filter/DuplicateExceptionFilter.java
@@ -0,0 +1,166 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.filter;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.spi.FilterReply;
+import com.harman.ignite.utils.logger.LoggerUtils;
+import org.slf4j.Marker;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class will suppress repeating exception for a configurable amount of time in millis (typically for minutes).
+ * It will help non-pollute the log files and reduce disk pressure.
+ * The implementation is based on TurboFilter (logback).
+ *
+ * @author vkoul
+ */
+public class DuplicateExceptionFilter extends TurboFilter {
+
+ private static ConcurrentHashMap exceptionCache = null;
+
+ static {
+ exceptionCache = new ConcurrentHashMap<>();
+ }
+
+ // provide suppress time in milliseconds
+ private long suppressTimeInMs = 10L * 60 * 1000;
+
+ @Override
+ public void start() {
+ super.start();
+ }
+
+ /**
+ * This method is invoked in deciding if log statement would be logged or not.
+ * The filter is configured in logback.xml, where we are providing "suppressTimeInMs" like below:
+ *
+ * <turboFilter class="com.harman.ignite.utils.filter.DuplicateExceptionFilter">
+ * <suppressTimeInMs>60000</suppressTimeInMs>
+ * </turboFilter>>
+ *
+ */
+ @Override
+ public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) {
+
+ FilterReply reply = FilterReply.NEUTRAL;
+
+ // Throwable is coming from last argument of decide()
+ if (null != t) {
+ reply = decide(logger, t);
+ } else if (LoggerUtils.hasThrowableObject(format, params)) { // Check if we get Throwable from var-args.
+ reply = decide(logger, (Throwable) params[params.length - 1]);
+ }
+
+ return reply;
+ }
+
+ /**
+ * Internally, this method maintains ConcurrentHashMap which keeps:
+ * Throwable as key and values at which point-in-time it was logged in.
+ * Based on setSuppressTimeInMs value multiple logs would be filtered out,
+ * that is at most one log statement would be logged-in within setSuppressTimeInMs time.
+ *
+ * @param logger Logger from which log statement is being invoked.
+ * @param t Throwable object.
+ * @return decision if a log statement would be logged-in or not.
+ */
+
+ private FilterReply decide(Logger logger, Throwable t) {
+ FilterReply reply;
+ long currTime = System.currentTimeMillis();
+
+ // The exceptionKey need to have both:
+ // 1) Exception-Name and 2) Logger from which it came from.
+ //
+ // Else, we may have a scenario in which if we have entry for an
+ // exception in cache,
+ // even though we may be logging it for first time, it would get
+ // discarded.
+
+ StringBuilder exceptionKey = new StringBuilder().append(t.getClass().getName()).append(logger.toString());
+
+ if (!exceptionCache.containsKey(exceptionKey.toString())) {
+ exceptionCache.put(exceptionKey.toString(), currTime);
+
+ // We are returning NEUTRAL to propagate
+ // it thru next filter chains.
+ reply = FilterReply.NEUTRAL;
+ } else {
+ // It means we have entry inside our map, hence we need to check last time when exception came in.
+ // We need to keep a gap of 10 minutes (configurable)
+ // so that we can allow it to pass through, else DENY.
+ long previousTime = exceptionCache.get(exceptionKey.toString());
+
+ // We are returning NEUTRAL to propagate
+ // it through next filter chains.
+ if ((currTime - previousTime) >= suppressTimeInMs) {
+ // Update the time-stamp in cache
+ exceptionCache.put(exceptionKey.toString(), currTime);
+ reply = FilterReply.NEUTRAL;
+ } else {
+ reply = FilterReply.DENY;
+ }
+ }
+
+ return reply;
+
+ }
+
+ public long getSuppressTimeInMs() {
+ return suppressTimeInMs;
+ }
+
+ /**
+ * The value of suppressTimeInMs will be taken from logback.xml. Refer below for an example:
+ * <turboFilter class="com.harman.ignite.utils.filter.DuplicateExceptionFilter">
+ * <suppressTimeInMs>60000</suppressTimeInMs>
+ * </turboFilter>
+ *
+ * @param suppressTimeInMs - time in milliseconds.
+ */
+ public void setSuppressTimeInMs(long suppressTimeInMs) {
+ this.suppressTimeInMs = suppressTimeInMs;
+ }
+
+ static void setExceptionCache(ConcurrentHashMap exceptionCache) {
+ DuplicateExceptionFilter.exceptionCache = exceptionCache;
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/logger/IgniteCallerDataConverter.java b/src/main/java/com/harman/ignite/utils/logger/IgniteCallerDataConverter.java
new file mode 100644
index 0000000..28ce84e
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/IgniteCallerDataConverter.java
@@ -0,0 +1,133 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import ch.qos.logback.classic.pattern.CallerDataConverter;
+import ch.qos.logback.classic.spi.CallerData;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.boolex.EvaluationException;
+import ch.qos.logback.core.boolex.EventEvaluator;
+import ch.qos.logback.core.status.ErrorStatus;
+
+import java.util.List;
+
+/**
+ * This is used to customize log messages.
+ * This class overrides methods from CallerDataConverter and customize log messages.
+ * Customizations include:
+ * 1. removing of "caller" from log message.
+ * 2. Printing short-form of fully qualified package
+ * name(eg:com.harman.ignite.utils.logger.IgniteCallerDataConverter
+ * will be replaced as c.h.i.u.l.IgniteCallerDataConverter)
+ *
+ * @author vishnu.k;
+ */
+public class IgniteCallerDataConverter extends CallerDataConverter {
+
+ private int depthStart = 1;
+ static final int MAX_ERR_COUNT = 4;
+ private int errCount = 0;
+ List> eventEvaluatorList = null;
+
+ @Override
+ public String convert(ILoggingEvent logEvent) {
+ if (eventEvaluatorList != null) {
+ boolean printCallerData = false;
+
+ for (int i = 0; i < eventEvaluatorList.size(); i++) {
+ EventEvaluator eventEvaluator = eventEvaluatorList.get(i);
+ if (canEvaluatorProcessLogEvent(logEvent, eventEvaluator)) {
+ printCallerData = true;
+ break;
+ }
+ }
+
+ // no evaluator can process the logging event
+ if (!printCallerData) {
+ return CoreConstants.EMPTY_STRING;
+ }
+ }
+
+ return convertToCallerData(logEvent);
+ }
+
+ private String convertToCallerData(ILoggingEvent le) {
+ StringBuilder buf = new StringBuilder();
+ StackTraceElement[] cda = le.getCallerData();
+ if (cda != null && cda.length > depthStart) {
+ buf.append(getCallerLinePrefix());
+ buf.append(cda[depthStart]);
+ buf.append(" ");
+
+ return buf.toString();
+ } else {
+ return CallerData.CALLER_DATA_NA;
+ }
+ }
+
+ @Override
+ protected String getCallerLinePrefix() {
+ return "";
+ }
+
+ private boolean canEvaluatorProcessLogEvent(ILoggingEvent logEvent, EventEvaluator eventEvaluator) {
+ try {
+ if (eventEvaluator.evaluate(logEvent)) {
+ return true;
+ }
+ } catch (EvaluationException eex) {
+ errCount++;
+ processErrorCount(eventEvaluator, eex);
+ }
+ return false;
+ }
+
+ private void processErrorCount(EventEvaluator ee, EvaluationException eex) {
+ if (errCount < MAX_ERR_COUNT) {
+ addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex);
+ } else if (errCount == MAX_ERR_COUNT) {
+ ErrorStatus errorStatus = new ErrorStatus(
+ "Exception thrown for evaluator named [" + ee.getName() + "].",
+ this, eex);
+ errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors."
+ + "We don't want the StatusManager to get flooded.", this));
+ addStatus(errorStatus);
+ }
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/logger/IgniteLogger.java b/src/main/java/com/harman/ignite/utils/logger/IgniteLogger.java
new file mode 100644
index 0000000..0c026b0
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/IgniteLogger.java
@@ -0,0 +1,147 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import com.harman.ignite.entities.IgniteEvent;
+
+/**
+ * MDC (Mapped Diagnostic Context) will help produce customized logs for Ignite
+ * product. It will bring together the core parameters like the TimeStamp,
+ * RequestId, MessageId, BizTransactionId and CorrelationId as a wrapper for all
+ * the events being logged.
+ *
+ * @author AKumar
+ *
+ */
+public interface IgniteLogger {
+
+ /**
+ * Method to know if Trace level log is enable or not.
+ *
+ * @return - true - if trace level enabled, false - otherwise.
+ */
+ public boolean isTraceEnabled();
+
+ /**
+ * Method to know if Debug level log is enable or not.
+ *
+ * @return - true - if Debug level enabled, false - otherwise.
+ */
+ public boolean isDebugEnabled();
+
+ /**
+ * Method to know if Info level log is enable or not.
+ *
+ * @return - true - if Info level enabled, false - otherwise.
+ */
+ public boolean isInfoEnabled();
+
+ /**
+ * Method to know if Warn level log is enable or not.
+ *
+ * @return - true - if Warn level enabled, false - otherwise.
+ */
+ public boolean isWarnEnabled();
+
+ /**
+ * Method to know if Error level log is enable or not.
+ *
+ * @return - true - if Error level enabled, false - otherwise.
+ */
+ public boolean isErrorEnabled();
+
+ public void trace(IgniteEvent event, String msg);
+
+ public void trace(IgniteEvent event, String format, Object... arguments);
+
+ public void trace(IgniteEvent event, String msg, Throwable t);
+
+ public void trace(String msg);
+
+ public void trace(String format, Object... arguments);
+
+ public void trace(String msg, Throwable t);
+
+ public void debug(IgniteEvent event, String msg);
+
+ public void debug(IgniteEvent event, String format, Object... arguments);
+
+ public void debug(IgniteEvent event, String msg, Throwable t);
+
+ public void debug(String msg);
+
+ public void debug(String format, Object... arguments);
+
+ public void debug(String msg, Throwable t);
+
+ public void info(IgniteEvent event, String msg);
+
+ public void info(IgniteEvent event, String format, Object... arguments);
+
+ public void info(IgniteEvent event, String msg, Throwable t);
+
+ public void info(String msg);
+
+ public void info(String format, Object... arguments);
+
+ public void info(String msg, Throwable t);
+
+ public void warn(IgniteEvent event, String msg);
+
+ public void warn(IgniteEvent event, String format, Object... arguments);
+
+ public void warn(IgniteEvent event, String msg, Throwable t);
+
+ public void warn(String msg);
+
+ public void warn(String format, Object... arguments);
+
+ public void warn(String msg, Throwable t);
+
+ public void error(IgniteEvent event, String msg);
+
+ public void error(IgniteEvent event, String format, Object... arguments);
+
+ public void error(IgniteEvent event, String msg, Throwable t);
+
+ public void error(String msg);
+
+ public void error(String format, Object... arguments);
+
+ public void error(String msg, Throwable t);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerFactory.java b/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerFactory.java
new file mode 100644
index 0000000..d0c8ed1
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerFactory.java
@@ -0,0 +1,52 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+/**
+ * Factory class which gives a logger instance for the requested type.
+ *
+ * @author AKumar
+ */
+public class IgniteLoggerFactory {
+
+ private IgniteLoggerFactory() {
+ }
+
+ public static IgniteLogger getLogger(Class clazz) {
+ return IgniteLoggerImpl.getIgniteLoggerInstance(clazz);
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerImpl.java b/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerImpl.java
new file mode 100644
index 0000000..0ebcba8
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/IgniteLoggerImpl.java
@@ -0,0 +1,337 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import ch.qos.logback.classic.PatternLayout;
+import com.harman.ignite.entities.IgniteEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation class for IgniteLogger interface.
+ * This class is used to customize log messages.
+ *
+ * @author AKumar
+ *
+ */
+public class IgniteLoggerImpl implements IgniteLogger {
+
+ private Logger logger;
+ private static final String MESSAGE = "message";
+ private static Map igniteLoggersMap = new ConcurrentHashMap<>();
+
+ private IgniteLoggerImpl(Class> clazz) {
+ PatternLayout.defaultConverterMap
+ .put("caller", com.harman.ignite.utils.logger.IgniteCallerDataConverter.class.getName());
+ PatternLayout.defaultConverterMap
+ .put("ex", com.harman.ignite.utils.logger.IgniteThrowableProxyConverter.class.getName());
+ PatternLayout.defaultConverterMap
+ .put("exception", com.harman.ignite.utils.logger.IgniteThrowableProxyConverter.class.getName());
+ PatternLayout.defaultConverterMap
+ .put("throwable", com.harman.ignite.utils.logger.IgniteThrowableProxyConverter.class.getName());
+ logger = LoggerFactory.getLogger(clazz);
+ }
+
+ public void setLogger(Logger logger) {
+ this.logger = logger;
+ }
+
+ protected static IgniteLogger getIgniteLoggerInstance(Class> clazz) {
+ igniteLoggersMap.putIfAbsent(clazz.getName(), new IgniteLoggerImpl(clazz));
+ return (IgniteLogger) igniteLoggersMap.get(clazz.getName());
+ }
+
+ /*
+ * Added for JUnit test purpose only.
+ */
+ static IgniteLoggerImpl getIgniteLoggerImplInstance(Class> clazz) {
+ igniteLoggersMap.putIfAbsent(clazz.getName(), new IgniteLoggerImpl(clazz));
+ return igniteLoggersMap.get(clazz.getName());
+ }
+
+ @Override
+ public boolean isTraceEnabled() {
+ return logger.isTraceEnabled();
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
+ }
+
+ @Override
+ public boolean isInfoEnabled() {
+ return logger.isInfoEnabled();
+ }
+
+ @Override
+ public boolean isWarnEnabled() {
+ return logger.isWarnEnabled();
+ }
+
+ @Override
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
+ }
+
+ @Override
+ public void trace(IgniteEvent event, String msg) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(getMessageWithHeader(event, msg));
+ }
+ }
+
+ @Override
+ public void trace(IgniteEvent event, String format, Object... arguments) {
+ if (isTraceEnabled()) {
+ logger.trace(getMessageWithHeader(event, format), arguments);
+ }
+ }
+
+ @Override
+ public void trace(IgniteEvent event, String msg, Throwable t) {
+ if (isTraceEnabled()) {
+ logger.trace(getMessageWithHeader(event, msg), t);
+ }
+ }
+
+ @Override
+ public void trace(String msg) {
+ if (isTraceEnabled()) {
+ logger.trace(msg);
+ }
+ }
+
+ @Override
+ public void trace(String format, Object... arguments) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(format, arguments);
+ }
+ }
+
+ @Override
+ public void trace(String msg, Throwable t) {
+ if (logger.isTraceEnabled()) {
+ logger.trace(msg, t);
+ }
+ }
+
+ @Override
+ public void debug(IgniteEvent event, String msg) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(getMessageWithHeader(event, msg));
+ }
+ }
+
+ @Override
+ public void debug(IgniteEvent event, String format, Object... arguments) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(getMessageWithHeader(event, format), arguments);
+ }
+ }
+
+ @Override
+ public void debug(IgniteEvent event, String msg, Throwable t) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(getMessageWithHeader(event, msg), t);
+ }
+ }
+
+ @Override
+ public void debug(String msg) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(msg);
+ }
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(format, arguments);
+ }
+ }
+
+ @Override
+ public void debug(String msg, Throwable t) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(msg, t);
+ }
+ }
+
+ @Override
+ public void info(IgniteEvent event, String msg) {
+ if (logger.isInfoEnabled()) {
+ logger.info(getMessageWithHeader(event, msg));
+ }
+ }
+
+ @Override
+ public void info(IgniteEvent event, String format, Object... arguments) {
+ if (logger.isInfoEnabled()) {
+ logger.info(getMessageWithHeader(event, format), arguments);
+ }
+ }
+
+ @Override
+ public void info(IgniteEvent event, String msg, Throwable t) {
+ if (logger.isInfoEnabled()) {
+ logger.info(getMessageWithHeader(event, msg), t);
+ }
+ }
+
+ @Override
+ public void info(String msg) {
+ if (logger.isInfoEnabled()) {
+ logger.info(msg);
+ }
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ if (logger.isInfoEnabled()) {
+ logger.info(format, arguments);
+ }
+ }
+
+ @Override
+ public void info(String msg, Throwable t) {
+ if (isInfoEnabled()) {
+ logger.info(msg, t);
+ }
+ }
+
+ @Override
+ public void warn(IgniteEvent event, String msg) {
+ logger.warn(getMessageWithHeader(event, msg));
+ }
+
+ @Override
+ public void warn(IgniteEvent event, String format, Object... arguments) {
+ logger.warn(getMessageWithHeader(event, format), arguments);
+ }
+
+ @Override
+ public void warn(IgniteEvent event, String msg, Throwable t) {
+ logger.warn(getMessageWithHeader(event, msg), t);
+ }
+
+ @Override
+ public void warn(String msg) {
+ logger.warn(msg);
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ logger.warn(format, arguments);
+ }
+
+ @Override
+ public void warn(String msg, Throwable t) {
+ logger.warn(msg, t);
+ }
+
+ @Override
+ public void error(IgniteEvent event, String msg) {
+ logger.error(getMessageWithHeader(event, msg));
+ }
+
+ @Override
+ public void error(IgniteEvent event, String format, Object... arguments) {
+ logger.error(getMessageWithHeader(event, format), arguments);
+ }
+
+ @Override
+ public void error(IgniteEvent event, String msg, Throwable t) {
+ logger.error(getMessageWithHeader(event, msg), t);
+ }
+
+ @Override
+ public void error(String msg) {
+ logger.error(msg);
+ }
+
+ @Override
+ public void error(String format, Object... arguments) {
+ logger.error(format, arguments);
+ }
+
+ @Override
+ public void error(String msg, Throwable t) {
+ logger.error(msg, t);
+ }
+
+ private String getMessageWithHeader(IgniteEvent event, String format) {
+ StringBuilder formatBuilder = new StringBuilder();
+ long timeStamp = event.getTimestamp();
+ formatBuilder.append("Timestamp:").append(timeStamp);
+
+ String requestId = event.getRequestId();
+ formatBuilder.append(" , RequestId:").append(requestId);
+
+ String messageId = event.getMessageId();
+ formatBuilder.append(" , MessageId:").append(messageId);
+
+ String bizTransactionId = event.getBizTransactionId();
+ formatBuilder.append(" , BizTransactionId:").append(bizTransactionId);
+
+ formatBuilder.append(" , VehicleID:").append(event.getVehicleId());
+
+ formatBuilder.append(" , EventID:").append(event.getEventId());
+
+ formatBuilder.append(" , Version:").append(event.getSchemaVersion());
+
+ formatBuilder.append(" , SourceDeviceID:").append(event.getSourceDeviceId());
+
+ Optional correlateionId = Optional.ofNullable(event.getCorrelationId());
+ if (correlateionId.isPresent()) {
+ formatBuilder.append(" , CorrelationId:").append(correlateionId);
+ }
+ formatBuilder.append(" ," + MESSAGE + ":").append(format);
+ return formatBuilder.toString();
+ }
+
+ /*
+ * Added for JUnit test purpose only.
+ */
+ Map getIgniteLoggersMap() {
+ return igniteLoggersMap;
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/logger/IgniteThrowableProxyConverter.java b/src/main/java/com/harman/ignite/utils/logger/IgniteThrowableProxyConverter.java
new file mode 100644
index 0000000..93cb53c
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/IgniteThrowableProxyConverter.java
@@ -0,0 +1,298 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.StackTraceElementProxy;
+import ch.qos.logback.classic.spi.ThrowableProxyUtil;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.CoreConstants;
+import ch.qos.logback.core.boolex.EvaluationException;
+import ch.qos.logback.core.boolex.EventEvaluator;
+import ch.qos.logback.core.status.ErrorStatus;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Add a stack trace in case the event contains a Throwable. This is used to customize error message.
+ * Customizations include:
+ * 1. removing of "at" from stack trace.
+ * 2. printing full stack-trace in single line separated by comma(,)
+ *
+ * @author vishnu.k
+ */
+public class IgniteThrowableProxyConverter extends ThrowableHandlingConverter {
+
+ protected static final int BUILDER_CAPACITY = 2048;
+
+ int lengthOption;
+ List> evaluatorList = null;
+ List ignoredStackTraceLines = null;
+
+ int errorCount = 0;
+
+
+ /**
+ * This method is used to start the converter.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public void start() {
+
+ String lengthStr = getFirstOption();
+ createLengthOption(lengthStr);
+
+ final List optionList = getOptionList();
+ if (optionList != null && optionList.size() > 1) {
+ final int optionListSize = optionList.size();
+ for (int i = 1; i < optionListSize; i++) {
+ String evaluatorOrIgnoredStackTraceLine = (String) optionList.get(i);
+ Context context = getContext();
+ Map> evaluatorMap = (Map>) context
+ .getObject(CoreConstants.EVALUATOR_MAP);
+ EventEvaluator ee = (EventEvaluator) evaluatorMap
+ .get(evaluatorOrIgnoredStackTraceLine);
+ if (ee != null) {
+ addEvaluator(ee);
+ } else {
+ addIgnoreStackTraceLine(evaluatorOrIgnoredStackTraceLine);
+ }
+ }
+ }
+ super.start();
+ }
+
+
+ private void createLengthOption(String lengthStr) {
+ if (lengthStr == null) {
+ lengthOption = Integer.MAX_VALUE;
+ } else {
+ lengthStr = lengthStr.toLowerCase();
+ if ("full".equals(lengthStr)) {
+ lengthOption = Integer.MAX_VALUE;
+ } else if ("short".equals(lengthStr)) {
+ lengthOption = 1;
+ } else {
+ try {
+ lengthOption = Integer.parseInt(lengthStr);
+ } catch (NumberFormatException nfe) {
+ addError("Could not parse [" + lengthStr + "] as an integer");
+ lengthOption = Integer.MAX_VALUE;
+ }
+ }
+ }
+ }
+
+ private void addEvaluator(EventEvaluator ee) {
+ if (evaluatorList == null) {
+ evaluatorList = new ArrayList<>();
+ }
+ evaluatorList.add(ee);
+ }
+
+ private void addIgnoreStackTraceLine(String ignoredStackTraceLine) {
+ if (ignoredStackTraceLines == null) {
+ ignoredStackTraceLines = new ArrayList<>();
+ }
+ ignoredStackTraceLines.add(ignoredStackTraceLine);
+ }
+
+ @Override
+ public void stop() {
+ evaluatorList = null;
+ super.stop();
+ }
+
+ protected void extraData(StringBuilder builder, StackTraceElementProxy step) {
+ // nop
+ }
+
+ /**
+ * Convert the event to a string.
+ *
+ * @param event the log event
+ * @return the string representation of the event
+ */
+ public String convert(ILoggingEvent event) {
+
+ IThrowableProxy tp = event.getThrowableProxy();
+ if (tp == null) {
+ return CoreConstants.EMPTY_STRING;
+ }
+ // an evaluator match will cause stack printing to be skipped
+ if (evaluatorList != null) {
+ boolean printStack = true;
+ for (EventEvaluator ee : evaluatorList) {
+ try {
+ if (ee.evaluate(event)) {
+ printStack = false;
+ break;
+ }
+ } catch (EvaluationException eex) {
+ errorCount++;
+ processErrorCount(ee, eex);
+ }
+ }
+
+ if (!printStack) {
+ return CoreConstants.EMPTY_STRING;
+ }
+ }
+
+ return throwableProxyToString(tp);
+ }
+
+ protected String throwableProxyToString(IThrowableProxy tp) {
+ StringBuilder sb = new StringBuilder(BUILDER_CAPACITY);
+
+ recursiveAppend(sb, null, ThrowableProxyUtil.REGULAR_EXCEPTION_INDENT, tp);
+ return sb.toString().replace("\t", ", ");
+ }
+
+ private void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrowableProxy tp) {
+ if (tp == null) {
+ return;
+ }
+ subjoinFirstLine(sb, prefix, indent, tp);
+ subjoinStepArray(sb, indent, tp);
+ IThrowableProxy[] suppressed = tp.getSuppressed();
+ if (suppressed != null) {
+ for (IThrowableProxy current : suppressed) {
+ recursiveAppend(sb,
+ CoreConstants.SUPPRESSED,
+ indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT,
+ current);
+ }
+ }
+ recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause());
+ }
+
+ private void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThrowableProxy tp) {
+ ThrowableProxyUtil.indent(buf, indent - 1);
+ if (prefix != null) {
+ buf.append(prefix);
+ }
+ subjoinExceptionMessage(buf, tp);
+ }
+
+ private void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) {
+ buf.append(tp.getClassName()).append(": ").append(tp.getMessage());
+ }
+
+ protected void subjoinStepArray(StringBuilder buf, int indent, IThrowableProxy tp) {
+ StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
+ int commonFrames = tp.getCommonFrames();
+
+ boolean unrestrictedPrinting = lengthOption > stepArray.length;
+
+ int maxIndex = (unrestrictedPrinting) ? stepArray.length : lengthOption;
+ if (commonFrames > 0 && unrestrictedPrinting) {
+ maxIndex -= commonFrames;
+ }
+
+ int ignoredCount = 0;
+ for (int i = 0; i < maxIndex; i++) {
+ StackTraceElementProxy element = stepArray[i];
+ if (!isIgnoredStackTraceLine(element.toString())) {
+ ThrowableProxyUtil.indent(buf, indent);
+ printStackLine(buf, ignoredCount, element);
+ ignoredCount = 0;
+ buf.append(CoreConstants.LINE_SEPARATOR);
+ } else {
+ ++ignoredCount;
+ if (maxIndex < stepArray.length) {
+ ++maxIndex;
+ }
+ }
+ }
+ if (ignoredCount > 0) {
+ printIgnoredCount(buf, ignoredCount);
+ buf.append(CoreConstants.LINE_SEPARATOR);
+ }
+
+ if (commonFrames > 0 && unrestrictedPrinting) {
+ ThrowableProxyUtil.indent(buf, indent);
+ buf
+ .append("... ")
+ .append(tp.getCommonFrames())
+ .append(" common frames omitted")
+ .append(CoreConstants.LINE_SEPARATOR);
+ }
+ }
+
+ private void printStackLine(StringBuilder buf, int ignoredCount, StackTraceElementProxy element) {
+ buf.append(element);
+ extraData(buf, element); // allow other data to be added
+ if (ignoredCount > 0) {
+ printIgnoredCount(buf, ignoredCount);
+ }
+ }
+
+ private void printIgnoredCount(StringBuilder buf, int ignoredCount) {
+ buf.append(" [").append(ignoredCount).append(" skipped]");
+ }
+
+ private boolean isIgnoredStackTraceLine(String line) {
+ if (ignoredStackTraceLines != null) {
+ for (String ignoredStackTraceLine : ignoredStackTraceLines) {
+ if (line.contains(ignoredStackTraceLine)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void processErrorCount(EventEvaluator ee, EvaluationException eex) {
+ if (errorCount < CoreConstants.MAX_ERROR_COUNT) {
+ addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex);
+ } else if (errorCount == CoreConstants.MAX_ERROR_COUNT) {
+ ErrorStatus errorStatus = new ErrorStatus(
+ "Exception thrown for evaluator named [" + ee.getName() + "].",
+ this,
+ eex);
+ errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors."
+ + "We don't want the StatusManager to get flooded.", this));
+ addStatus(errorStatus);
+ }
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/logger/LoggerUtils.java b/src/main/java/com/harman/ignite/utils/logger/LoggerUtils.java
new file mode 100644
index 0000000..97231c8
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/logger/LoggerUtils.java
@@ -0,0 +1,101 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class has utility methods which helps in deciding if Object[] args (AKS var-args) has Throwable.
+ * The methods of this class is used in case decide()'s Throwable arg is null,
+ * but still we can get Throwable as part of var-args.
+ *
+ * @author vkoul
+ */
+public class LoggerUtils {
+
+ private LoggerUtils() {
+
+ }
+
+ private static final String CURLYBRACES_REGEX = "\\{\\}";
+ private static final Pattern CURLYBRACES_PATTERN = Pattern.compile(CURLYBRACES_REGEX);
+
+ /**
+ * Helper method to identify if the last element of object[] is throwable or not.
+ *
+ * @param format log message format
+ * @param args var-args
+ * @return true if last element of args is Throwable, else false.
+ */
+ public static boolean hasThrowableObject(String format, Object[] args) {
+
+ boolean hasThrowable = false;
+ if (null == args || args.length < 1) {
+ return false;
+ }
+ int curlyBracesCount = getCurlyBracesCount(format);
+ // check if the counts are unequal and curly brace count should exactly
+ // be 1 less than arguments length
+ if (curlyBracesCount != args.length
+ && (curlyBracesCount == (args.length - 1))
+ && (args[args.length - 1] instanceof Throwable)) {
+ hasThrowable = true;
+ }
+ return hasThrowable;
+ }
+
+ /**
+ * Helper method to retrieve the number of curly braces from the format.
+ *
+ * @param format log message format
+ * @return count of curly braces
+ */
+ private static int getCurlyBracesCount(String format) {
+ Matcher m = CURLYBRACES_PATTERN.matcher(format);
+ // we are trying to find the pattern {} and group count will always give
+ // 0.
+ // Hence, we will iterate and get the count
+ int count = 0;
+
+ while (m.find()) {
+ count++;
+ }
+ return count;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteCounter.java b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteCounter.java
new file mode 100644
index 0000000..5c6f3af
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteCounter.java
@@ -0,0 +1,131 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import io.prometheus.client.Counter;
+
+/**
+ * Abstract class for creating prometheus counter metric.
+ */
+public abstract class AbstractIgniteCounter {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(AbstractIgniteCounter.class);
+
+ private Counter counter;
+
+ private boolean isInitialized;
+
+ protected void createCounter(String name, String help, String... labels) {
+
+ if (null == counter) {
+ synchronized (this) {
+ counter = Counter.build().name(name).help(help).labelNames(labels).register();
+ }
+ }
+
+ if (null != counter) {
+ isInitialized = true;
+ LOGGER.info("Created prometheus counter metric with name : {}", name);
+ } else {
+ LOGGER.warn("Error creating prometheus counter metric with name : {}", name);
+ }
+ }
+
+ /**
+ * Increment the counter by 1.
+ *
+ * @param labelValues label values
+ */
+ public void inc(String... labelValues) {
+ if (isInitialized) {
+ synchronized (counter) {
+ counter.labels(labelValues).inc();
+ }
+ }
+ }
+
+ /** Increment the counter by the given amount.
+ *
+ * @param value The value to increment the counter by.
+ * @param label values
+ */
+ public void inc(double value, String ... label) {
+ if (isInitialized) {
+ synchronized (counter) {
+ counter.labels(label).inc(value);
+ }
+ }
+ }
+
+ /** Get the value of the counter.
+ *
+ * @param labelValues label values
+ * @return The value of the counter.
+ */
+ public double get(String... labelValues) {
+ double val = 0;
+ if (isInitialized) {
+ synchronized (counter) {
+ val = counter.labels(labelValues).get();
+ }
+ }
+ return val;
+ }
+
+ /**
+ * Remove the counter from the registry it was registered with.
+ */
+ public void clear() {
+ if (isInitialized) {
+ synchronized (counter) {
+ counter.clear();
+ }
+ }
+ }
+
+ //Below setters/getters are just for test cases
+ public Counter getCounter() {
+ return this.counter;
+ }
+
+ public boolean isInitialized() {
+ return isInitialized;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteGauge.java b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteGauge.java
new file mode 100644
index 0000000..0f71d38
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteGauge.java
@@ -0,0 +1,170 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Gauge;
+
+/**
+ * Abstract class for creating prometheus gauge metric.
+ */
+public abstract class AbstractIgniteGauge {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(AbstractIgniteGauge.class);
+
+ private Gauge guage;
+
+ private boolean isInitialized;
+
+ protected void createGauge(String name, String help, String... labels) {
+ if (null == guage) {
+ synchronized (this) {
+ guage = Gauge.build(name, name)
+ .labelNames(labels)
+ .help(help)
+ .register(CollectorRegistry.defaultRegistry);
+ LOGGER.info("Created ignite guage with name : {} and labels {}", name, labels);
+ }
+ }
+
+ if (null != guage) {
+ isInitialized = true;
+ LOGGER.info("Created prometheus gauge metric with name : {}", name);
+ } else {
+ LOGGER.warn("Error creating prometheus guage metric with name : {}", name);
+ }
+ }
+
+ /** Increment the gauge by 1.
+ *
+ * @param labelValues label values
+ */
+ public void inc(String... labelValues) {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.labels(labelValues).inc();
+ }
+ }
+ }
+
+ /** Increment the gauge by the given amount.
+ *
+ * @param value The value to increment the gauge by.
+ * @param labelValues label values
+ */
+ public void inc(double value, String... labelValues) {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.labels(labelValues).inc(value);
+ }
+ }
+ }
+
+ /** Decrement the gauge by 1.
+ *
+ * @param labelValues label values
+ */
+ public void dec(String... labelValues) {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.labels(labelValues).dec();
+ }
+ }
+ }
+
+ /** Decrement the gauge by the given amount.
+ *
+ * @param value The value to decrement the gauge by.
+ * @param labelValues label values
+ */
+ public void dec(double value, String... labelValues) {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.labels(labelValues).dec(value);
+ }
+ }
+ }
+
+ /** Get the value of the gauge.
+ *
+ * @param labelValues label values
+ * @return The value of the gauge.
+ */
+ public double get(String... labelValues) {
+ double val = 0;
+ if (isInitialized) {
+ synchronized (guage) {
+ val = guage.labels(labelValues).get();
+ }
+ }
+ return val;
+ }
+
+ /** Set the gauge to the given value.
+ *
+ * @param value The value to set the gauge to.
+ * @param labelValues label values
+ */
+ public void set(double value, String... labelValues) {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.labels(labelValues).set(value);
+ }
+ }
+ }
+
+ /**
+ * Remove the gauge from the registry it was registered with.
+ */
+ public void clear() {
+ if (isInitialized) {
+ synchronized (guage) {
+ guage.clear();
+ }
+ }
+ }
+
+ Gauge getGuage() {
+ return this.guage;
+ }
+
+ boolean getIsInitialized() {
+ return this.isInitialized;
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteHistogram.java b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteHistogram.java
new file mode 100644
index 0000000..a801a14
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/AbstractIgniteHistogram.java
@@ -0,0 +1,150 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Histogram;
+
+import java.util.concurrent.Callable;
+import java.util.function.Supplier;
+
+/**
+ * Base histogram for Ignite.
+ *
+ * @author ssasidharan
+ */
+public abstract class AbstractIgniteHistogram {
+
+ private static final double TIMER_DIVISOR = 1E9D;
+ private Histogram histogram;
+
+ protected void createHistogram(String name, String help, double[] buckets, String... labelNames) {
+ histogram = Histogram
+ .build(name, help)
+ .labelNames(labelNames)
+ .buckets(buckets)
+ .register(CollectorRegistry.defaultRegistry);
+ }
+
+ public IgniteTimer start() {
+ return new IgniteTimer(this);
+ }
+
+ public void observe(double amt, String... labels) {
+ histogram.labels(labels).observe(amt);
+ }
+
+ /**
+ * Observe the time taken for a function to execute.
+ *
+ * @param f supplier function to execute
+ * @param labels labels to be observed
+ * @param type of the result
+ * @return result of the function
+ */
+ public T observe(Supplier f, String... labels) {
+ IgniteTimer timer = start();
+ try {
+ return f.get();
+ } finally {
+ timer.observe(labels);
+ }
+ }
+
+ /**
+ * Observe the time taken for a function to execute.
+ *
+ * @param f runnable function to execute
+ * @param labels labels to be observed
+ */
+ public void observe(Runnable f, String... labels) {
+ IgniteTimer timer = start();
+ try {
+ f.run();
+ } finally {
+ timer.observe(labels);
+ }
+ }
+
+ /**
+ * Allows exception to be thrown unlike observe(Supplier).
+ *
+ * @param f function to execute
+ * @param labels labels to be observed
+ * @return result of the function
+ * @throws Exception the exception thrown by the function
+ */
+ public V observeExtended(Callable f, String... labels) throws Exception {
+ IgniteTimer timer = start();
+ try {
+ return f.call();
+ } finally {
+ timer.observe(labels);
+ }
+ }
+
+
+
+ /**
+ * Timer for Ignite.
+ */
+ public static class IgniteTimer {
+ private AbstractIgniteHistogram histo = null;
+ private long start = 0L;
+
+ public IgniteTimer(AbstractIgniteHistogram histo) {
+ this.histo = histo;
+ this.start = System.nanoTime();
+ }
+
+ /**
+ * Observe the time taken for the function to execute.
+ *
+ * @param labels labels to be observed
+ * @return time taken
+ */
+ public double observe(String... labels) {
+ double amt = (System.nanoTime() - start) / TIMER_DIVISOR;
+ histo.observe(amt, labels);
+ return amt;
+ }
+ }
+
+ Histogram getHistogram() {
+ return histogram;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteCounter.java b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteCounter.java
new file mode 100644
index 0000000..09b7ad7
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteCounter.java
@@ -0,0 +1,50 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+/**
+ * The default Ignite Counter implementation.
+ *
+ * @author avadakkootko
+ */
+public class GenericIgniteCounter extends AbstractIgniteCounter {
+
+ public GenericIgniteCounter(String name, String help, String... labels) {
+ createCounter(name, help, labels);
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteGauge.java b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteGauge.java
new file mode 100644
index 0000000..154bd3c
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteGauge.java
@@ -0,0 +1,48 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+/**
+ * Gauge for Ignite.
+ *
+ * @author ssasidharan
+ */
+public class GenericIgniteGauge extends AbstractIgniteGauge {
+ public GenericIgniteGauge(String name, String help, String... labels) {
+ createGauge(name, help, labels);
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteHistogram.java b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteHistogram.java
new file mode 100644
index 0000000..afef655
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/GenericIgniteHistogram.java
@@ -0,0 +1,50 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+/**
+ * Histogram for Ignite.
+ *
+ * @author ssasidharan
+ */
+public class GenericIgniteHistogram extends AbstractIgniteHistogram {
+
+ public GenericIgniteHistogram(String name, String help, double[] buckets, String... labelNames) {
+ createHistogram(name, help, buckets, labelNames);
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteCounter.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteCounter.java
new file mode 100644
index 0000000..b329c6f
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteCounter.java
@@ -0,0 +1,95 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Counter;
+
+import java.util.Objects;
+
+/**
+ * Wrapper around Prometheus Counter.
+ *
+ * @author sanketadhikari
+ */
+public abstract class IgniteCounter {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(IgniteCounter.class);
+
+ private Counter counter;
+
+ /**
+ * Increment the counter metric's value by 1 for given labels.
+ *
+ * @param labelValues Values of the labels
+ */
+ public void inc(String... labelValues) {
+ Objects.requireNonNull(counter, "IgniteCounter is not initialized");
+ synchronized (this.counter) {
+ counter.labels(labelValues).inc();
+ }
+ }
+
+ /**
+ * Get the metric's value for given labels.
+ *
+ * @param labelValues label values
+ */
+ public double get(String... labelValues) {
+ double value = 0;
+ Objects.requireNonNull(counter, "IgniteCounter is not initialized");
+ value = counter.labels(labelValues).get();
+ return value;
+ }
+
+ protected void createCounter(String name, String... labels) {
+ if (null == counter) {
+ synchronized (this) {
+ counter = Counter.build(name, name).labelNames(labels).register(CollectorRegistry.defaultRegistry);
+ LOGGER.info("Created ignite counter with name : {} and labels {}", name, labels);
+ }
+ } else {
+ LOGGER.warn("Ignite counter with name : {} and labels {}, already created", name, labels);
+ }
+ }
+
+ Counter getCounter() {
+ return this.counter;
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteDiagnosticGuage.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteDiagnosticGuage.java
new file mode 100644
index 0000000..1c69d30
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteDiagnosticGuage.java
@@ -0,0 +1,52 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * To be used by various Diagnostic reporters for publishing their respective report to prometheus.
+ *
+ * @author avadakkootko
+ */
+@Component
+public class IgniteDiagnosticGuage extends IgniteGuage {
+
+ public IgniteDiagnosticGuage() {
+ createGuage("diagnostic_metric", "node", "diagnostic_reporter_name", "diagnostic_reporter_sublabel");
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteErrorCounter.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteErrorCounter.java
new file mode 100644
index 0000000..fa8f4c2
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteErrorCounter.java
@@ -0,0 +1,120 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+/**
+ * The default implementation of error counter for ignite platform.
+ * If prometheus is enabled all base framework and services can inject
+ * this bean to register error counter metric and continuously publish values.
+ *
+ * @author avadakkootko
+ */
+@Component
+public class IgniteErrorCounter extends GenericIgniteCounter {
+
+ public static final String NA = "N/A";
+ private static final String ERROR_COUNT = "error_count";
+ private static final String NODE = "node";
+ private static final String TASKID = "tid";
+ private static final String EXCEPTION_CLASS_NAME = "ecn";
+ @Value("${NODE_NAME:localhost}")
+ private String nodeName;
+
+ public IgniteErrorCounter() {
+ super(ERROR_COUNT, ERROR_COUNT, NODE, TASKID, EXCEPTION_CLASS_NAME);
+ }
+
+ /**
+ * Increments the counter by 1.
+ *
+ * @param taskId Optional and refers to the stream processor's current task id. Defaults to N/A
+ * @param exceptionClassName The exception class name
+ */
+ public void incErrorCounter(Optional taskId, Class exceptionClassName) {
+ String tid = null;
+ if (taskId.isPresent()) {
+ tid = taskId.get();
+ } else {
+ tid = NA;
+ }
+ inc(nodeName, tid, exceptionClassName.getName());
+ }
+
+ /**
+ * Increments the counter with value specified.
+ *
+ * @param value The value to increment the counter by.
+ * @param taskId Optional and refers to the stream processor's current task id. Defaults to N/A
+ * @param exceptionClassName The exception class name
+ */
+ public void incErrorCounter(double value, Optional taskId, Class exceptionClassName) {
+ String tid = null;
+ if (taskId.isPresent()) {
+ tid = taskId.get();
+ } else {
+ tid = NA;
+ }
+ inc(value, nodeName, tid, exceptionClassName.getName());
+ }
+
+ /**
+ * This returns the error count grouped by nodename, taskId if present and exceptionClassName.
+ *
+ * @param taskId Optional and refers to the stream processor's current task id. Defaults to N/A
+ * @param exceptionClassName The exception class name
+ * @return The value of the counter
+ */
+ public double getErrorCounterValue(Optional taskId, Class exceptionClassName) {
+ String tid = null;
+ if (taskId.isPresent()) {
+ tid = taskId.get();
+ } else {
+ tid = NA;
+ }
+ return get(nodeName, tid, exceptionClassName.getName());
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteGuage.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteGuage.java
new file mode 100644
index 0000000..cb3b1b6
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteGuage.java
@@ -0,0 +1,104 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Gauge;
+
+import java.util.Objects;
+
+/**
+ * Wrapper around Prometheus Guage.
+ *
+ * @author avadakkootko
+ */
+public abstract class IgniteGuage {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(IgniteGuage.class);
+
+ private Gauge igniteGuageMetric;
+
+ /**
+ * Set the metric's value.
+
+ * @param value The value to set.
+ * @param labelValues Label values to set the value for.
+ */
+ public void set(double value, String... labelValues) {
+ Objects.requireNonNull(igniteGuageMetric, "IgniteGuage is not initialized");
+ synchronized (this.igniteGuageMetric) {
+ igniteGuageMetric.labels(labelValues).set(value);
+ }
+ }
+
+ /**
+ * Get the value of the guage.
+ *
+ * @param labelValues label values
+ * @return The value of the guage.
+ */
+ public double get(String... labelValues) {
+ double value = 0;
+ Objects.requireNonNull(igniteGuageMetric, "IgniteGuage is not initialized");
+ value = igniteGuageMetric.labels(labelValues).get();
+ return value;
+ }
+
+ protected void createGuage(String name, String... labels) {
+ if (null == igniteGuageMetric) {
+ synchronized (this) {
+ igniteGuageMetric = Gauge.build(name, name)
+ .labelNames(labels)
+ .register(CollectorRegistry.defaultRegistry);
+ LOGGER.info("Created ignite guage with name : {} and labels {}", name, labels);
+ }
+ } else {
+ LOGGER.warn("Ignite guage with name : {} and labels {}, already created", name, labels);
+ }
+ }
+
+ // setter and getter for unit tests
+ Gauge getIgniteGuageMetric() {
+ return this.igniteGuageMetric;
+ }
+
+ void setIgniteGuageMetric(Gauge igniteGuageMetric) {
+ this.igniteGuageMetric = igniteGuageMetric;
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteHealthGuage.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteHealthGuage.java
new file mode 100644
index 0000000..eb82ae6
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteHealthGuage.java
@@ -0,0 +1,52 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * IgniteHealthGuage will be used by various HealthMonitors for publishing their respective health status to prometheus.
+ *
+ * @author avadakkootko
+ */
+@Component
+public class IgniteHealthGuage extends IgniteGuage {
+
+ public IgniteHealthGuage() {
+ createGuage("service_health_metric", "node", "monitorname");
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/IgniteRocksDBGuage.java b/src/main/java/com/harman/ignite/utils/metrics/IgniteRocksDBGuage.java
new file mode 100644
index 0000000..5d461db
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/IgniteRocksDBGuage.java
@@ -0,0 +1,59 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Creates and registers Guage metric in Prometheus for each one of the property-based metrics in RocksDB.
+ * with labels = serviceName
+ * and metricName(the actual name of the RocksDB metric property)
+ *
+ * @author hbadshah
+ */
+@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
+@Component
+public class IgniteRocksDBGuage extends IgniteGuage {
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(IgniteRocksDBGuage.class);
+
+ public void setup() {
+ createGuage("rocksdb_metric", "metric_name", "svc", "node");
+ LOGGER.debug("rocksdb_metric guage successfully created.");
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/metrics/InternalCacheGuage.java b/src/main/java/com/harman/ignite/utils/metrics/InternalCacheGuage.java
new file mode 100644
index 0000000..d1480be
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/metrics/InternalCacheGuage.java
@@ -0,0 +1,81 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.metrics;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import jakarta.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+
+/**
+ * Guage for internal cache(s) metric.
+ *
+ * @author hbadshah
+ */
+@Component
+public class InternalCacheGuage extends IgniteGuage {
+
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(InternalCacheGuage.class);
+
+ @Value("${internal.metrics.enabled:false}")
+ private boolean internalMetricsEnabled;
+
+ @Value("${metrics.prometheus.enabled:true}")
+ private boolean prometheusEnabled;
+
+ /**
+ * Setup the guage metric for internal cache.
+ */
+ @PostConstruct
+ public void setup() {
+ if (prometheusEnabled && internalMetricsEnabled) {
+ createGuage("internal_cache_size_metric", "cache_type", "svc", "node", "task_id");
+ LOGGER.info("Guage metric for internal cache created.");
+ }
+ }
+
+ @Override
+ public void set(double value, String... labels) {
+ if (prometheusEnabled && internalMetricsEnabled) {
+ super.set(value, labels);
+ LOGGER.debug("Published metrics for labels: {} with value: {}", Arrays.asList(labels), value);
+ }
+ }
+}
diff --git a/src/main/java/com/harman/ignite/utils/test/NightlyBuildTestCase.java b/src/main/java/com/harman/ignite/utils/test/NightlyBuildTestCase.java
new file mode 100644
index 0000000..3af114c
--- /dev/null
+++ b/src/main/java/com/harman/ignite/utils/test/NightlyBuildTestCase.java
@@ -0,0 +1,54 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.test;
+
+/**
+ * Marker interface to tag a JUnit4 test case to run for nightly build.
+ * Typically, a long-running test case is tagged for nightly build process.
+ *
This interface is specified in Maven pom.xml's <excludeGroups/> XML tag to exclude the test cases.
+ *
Usage: Tag a test method or class,
+ *
+ *
@org.junit.Test @org.junit.experimental.categories.Category(NightlyBuildTestCase.class)
+ * public void testFoo(){...}
+ *
@org.junit.experimental.categories.Category(NightlyBuildTestCase.class)
+ * public class Foo{...}
+ *
+ *
+ * @author KJalawadi
+ */
+public interface NightlyBuildTestCase {
+}
diff --git a/src/test/java/com/harman/ignite/diagnostic/DiagnosticResultTest.java b/src/test/java/com/harman/ignite/diagnostic/DiagnosticResultTest.java
new file mode 100644
index 0000000..90cc8ab
--- /dev/null
+++ b/src/test/java/com/harman/ignite/diagnostic/DiagnosticResultTest.java
@@ -0,0 +1,55 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test class for DiagnosticResult.
+ *
+ * @see DiagnosticResult
+ */
+public class DiagnosticResultTest {
+ private DiagnosticResult res;
+
+ @Test
+ public void testPassAndFail() {
+ Assert.assertEquals(1.0, DiagnosticResult.PASS.getValue(), 0);
+ Assert.assertEquals(0.0, DiagnosticResult.FAIL.getValue(), 0);
+ }
+}
diff --git a/src/test/java/com/harman/ignite/diagnostic/DiagnosticUnitTest.java b/src/test/java/com/harman/ignite/diagnostic/DiagnosticUnitTest.java
new file mode 100644
index 0000000..38eb671
--- /dev/null
+++ b/src/test/java/com/harman/ignite/diagnostic/DiagnosticUnitTest.java
@@ -0,0 +1,185 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import com.harman.ignite.utils.metrics.IgniteDiagnosticGuage;
+import io.prometheus.client.Collector.MetricFamilySamples;
+import io.prometheus.client.CollectorRegistry;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Test class for DiagnosticService.
+ *
+ * @see DiagnosticService
+ */
+public class DiagnosticUnitTest {
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(DiagnosticUnitTest.class);
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @InjectMocks
+ private DiagnosticService diagnosticService = new DiagnosticService();
+
+ @Mock
+ private IgniteDiagnosticGuage diagnosticGuage;
+
+ /**
+ * Setup the test.
+ */
+ @Before
+ public void setUp() {
+ clearMetrics();
+ MockitoAnnotations.initMocks(this);
+ diagnosticService.setServiceDiagnosticGuage(diagnosticGuage);
+ diagnosticService.setNodeName("localhost");
+
+ }
+
+ /**
+ * If two DiagnosticReporters have same name it should throw runtime exception.
+ */
+ @Test(expected = RuntimeException.class)
+ public void testInitTwoReportersWithSameName() {
+ final List reporters = new ArrayList<>();
+ TestDiagnosticReporter reporter1 = new TestDiagnosticReporter();
+ reporter1.setEnabled(true);
+ reporter1.setMetricName("Metric1");
+ reporter1.setReporterName("Reporter1");
+ reporters.add(reporter1);
+
+ TestDiagnosticReporter reporter2 = new TestDiagnosticReporter();
+ reporter2.setEnabled(true);
+ reporter2.setMetricName("Metric1");
+ reporter2.setReporterName("Reporter2");
+ reporters.add(reporter2);
+
+ diagnosticService.setReporters(reporters);
+ diagnosticService.getEnabledReporters();
+ }
+
+ /**
+ * If a DiagnosticReporter is disabled it should not be added in the list of DiagnosticReporters.
+ */
+ @Test
+ public void testInitWithDisabledReporter() {
+ final List reporters = new ArrayList<>();
+ TestDiagnosticReporter reporter1 = new TestDiagnosticReporter();
+ reporter1.setEnabled(true);
+ reporter1.setMetricName("Metric1");
+ reporter1.setReporterName("Reporter1");
+ reporters.add(reporter1);
+
+ TestDiagnosticReporter reporter2 = new TestDiagnosticReporter();
+ reporter2.setEnabled(false);
+ reporter2.setMetricName("Metric1");
+ reporter2.setReporterName("Reporter2");
+ reporters.add(reporter2);
+
+ diagnosticService.setReporters(reporters);
+
+ List enabledReporters = diagnosticService.getEnabledReporters();
+ Assert.assertEquals(1, enabledReporters.size());
+ Assert.assertEquals("Metric1", enabledReporters.get(0).getDiagnosticMetricName());
+ Assert.assertEquals("Reporter1", enabledReporters.get(0).getDiagnosticReporterName());
+ }
+
+ @Test
+ public void testMetricPublishing() {
+ final List reporters = new ArrayList<>();
+ TestDiagnosticReporter reporter1 = new TestDiagnosticReporter();
+ reporter1.setEnabled(true);
+ reporter1.setMetricName("Metric1");
+ reporter1.setReporterName("Reporter1");
+ DiagnosticData data = new DiagnosticData();
+ data.put("Metric1Key1", DiagnosticResult.FAIL);
+ data.put("Metric1Key2", DiagnosticResult.PASS);
+ data.put("Metric1Key3", DiagnosticResult.FAIL);
+ reporter1.setData(data);
+ reporters.add(reporter1);
+
+ TestDiagnosticReporter reporter2 = new TestDiagnosticReporter();
+ reporter2.setEnabled(true);
+ reporter2.setMetricName("Metric2");
+ reporter2.setReporterName("Reporter2");
+ DiagnosticData data2 = new DiagnosticData();
+ data2.put("Metric2Key1", DiagnosticResult.PASS);
+ reporter2.setData(data2);
+ reporters.add(reporter2);
+
+ diagnosticService.setReporters(reporters);
+ diagnosticService.triggerDiagnosis();
+
+ Mockito.verify(diagnosticGuage, Mockito.times(1))
+ .set(DiagnosticResult.FAIL.getValue(), "localhost", "Metric1", "Metric1Key1");
+ Mockito.verify(diagnosticGuage, Mockito.times(1))
+ .set(DiagnosticResult.PASS.getValue(), "localhost", "Metric1", "Metric1Key2");
+ Mockito.verify(diagnosticGuage, Mockito.times(1))
+ .set(DiagnosticResult.FAIL.getValue(), "localhost", "Metric1", "Metric1Key3");
+ Mockito.verify(diagnosticGuage, Mockito.times(1))
+ .set(DiagnosticResult.PASS.getValue(), "localhost", "Metric2", "Metric2Key1");
+
+ }
+
+ /**
+ * Clear all the metrics.
+ */
+ public void clearMetrics() {
+ Enumeration mfsEnumerator = CollectorRegistry.defaultRegistry.metricFamilySamples();
+ while (mfsEnumerator.hasMoreElements()) {
+ MetricFamilySamples mfs = mfsEnumerator.nextElement();
+ if (mfs.samples != null) {
+ mfs.samples.clear();
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterTest.java b/src/test/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterTest.java
new file mode 100644
index 0000000..be8f138
--- /dev/null
+++ b/src/test/java/com/harman/ignite/diagnostic/PropertyDiagnosticReporterTest.java
@@ -0,0 +1,107 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * Test class for DiagnosticReporter.
+ *
+ * @see DiagnosticReporter
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = { TestDiagnosticReporterConfig.class })
+@TestPropertySource(
+ locations = {"classpath:property-diagnostic-test.properties", "classpath:property-diagnostic-test-2.properties"}
+)
+public class PropertyDiagnosticReporterTest {
+ @Autowired
+ @Qualifier("propertyDiagnostic")
+ DiagnosticReporter propertyDiagnosticReporterImpl;
+
+ @Before
+ public void setUp() {
+
+ }
+
+ @Test
+ public void testDiagnosticReporterName() {
+ Assert.assertEquals("DIAGNOSTIC_PROPERTY_REPORTER", propertyDiagnosticReporterImpl.getDiagnosticReporterName());
+ }
+
+ @Test
+ public void testDiagnosticMetricName() {
+ Assert.assertEquals("DIAGNOSTIC_PROPERTY_METRIC", propertyDiagnosticReporterImpl.getDiagnosticMetricName());
+ }
+
+ @Test
+ public void isDiagnosticReporterEnabled() {
+ Assert.assertTrue(propertyDiagnosticReporterImpl.isDiagnosticReporterEnabled());
+
+ }
+
+ @Test
+ public void testPropertyDiagnosticReporter() {
+ DiagnosticData diagnosticData = propertyDiagnosticReporterImpl.getDiagnosticData();
+
+ diagnosticData.forEach((key, value) -> System.out.println("property: " + key + "value: " + value));
+
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("mongodb.hosts"));
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("mongodb.port"));
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("mongodb.username"));
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("mongodb.hosts"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.pool.max.size"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.max.wait.time.ms"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.socket.timeout.ms"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.max.connections.per.host"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.block.threads.allowed.multiplier"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.read.preference"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("morphia.map.packages"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("mongodb.socket.timeout.ms"));
+ Assert.assertEquals(DiagnosticResult.PASS, diagnosticData.get("morphia.converters.fqn"));
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("vault.secret.folder"));
+ Assert.assertEquals(DiagnosticResult.FAIL, diagnosticData.get("vault.environment"));
+ }
+}
diff --git a/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporter.java b/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporter.java
new file mode 100644
index 0000000..52a9d7c
--- /dev/null
+++ b/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporter.java
@@ -0,0 +1,85 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+/**
+ * Test POJO for DiagnosticReporter.
+ */
+public class TestDiagnosticReporter implements DiagnosticReporter {
+
+ private boolean enabled;
+ private String reporterName = "TestDiagnosticReporter";
+ private String metricName = "TestDiagnosticReporterGuage";
+ private DiagnosticData data = new DiagnosticData();
+
+ @Override
+ public boolean isDiagnosticReporterEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public String getDiagnosticReporterName() {
+ return reporterName;
+ }
+
+ @Override
+ public String getDiagnosticMetricName() {
+ return metricName;
+ }
+
+ public void setReporterName(String reporterName) {
+ this.reporterName = reporterName;
+ }
+
+ public void setMetricName(String metricName) {
+ this.metricName = metricName;
+ }
+
+ @Override
+ public DiagnosticData getDiagnosticData() {
+ return data;
+ }
+
+ public void setData(DiagnosticData data) {
+ this.data = data;
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporterConfig.java b/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporterConfig.java
new file mode 100644
index 0000000..492ed50
--- /dev/null
+++ b/src/test/java/com/harman/ignite/diagnostic/TestDiagnosticReporterConfig.java
@@ -0,0 +1,56 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.diagnostic;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Test configuration for initializing a diagnostic reporter and testing it as PropertyDiagnosticReporterTest.
+ *
+ * @see PropertyDiagnosticReporterTest
+ */
+@Configuration
+@ComponentScan(basePackages = { "com.harman.ignite" })
+public class TestDiagnosticReporterConfig {
+
+ @Bean
+ public PropertyDiagnosticReporterTest testPropertyDiagnosticReporter() {
+ return new PropertyDiagnosticReporterTest();
+ }
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/HealthServiceIntegrationTestCase.java b/src/test/java/com/harman/ignite/healthcheck/HealthServiceIntegrationTestCase.java
new file mode 100644
index 0000000..ea123cd
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/HealthServiceIntegrationTestCase.java
@@ -0,0 +1,161 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import io.prometheus.client.Collector.MetricFamilySamples;
+import io.prometheus.client.Collector.MetricFamilySamples.Sample;
+import io.prometheus.client.CollectorRegistry;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Integration test for HealthService.
+ *
+ * @see HealthService
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = { TestHealthServiceConfig.class })
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class HealthServiceIntegrationTestCase {
+
+ static final double HEALTHY = 0.0;
+ static final double UNHEALTHY = 1.0;
+ static final int TWO = 2;
+ static final String SERVICE_HEALTH = "SERVICE_HEALTH";
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(HealthServiceIntegrationTestCase.class);
+ @Autowired
+ private HealthService healthService;
+
+ @Before
+ public void setup() {
+ clearMetrics();
+ }
+
+ /**
+ * Post constructor should have autowired the health monitors and not started the executor.
+ */
+ @Test
+ public void testInit() {
+ List monitors = healthService.getHealthMonitors();
+ // Although 3 monitors are present in Configuration class one is
+ // disabled hence only 2 monitors should be present
+ Assert.assertEquals(TWO, monitors.size());
+ }
+
+ @Test
+ public void testNeedsRestartWithHealthyMonitor() throws InterruptedException {
+ TestHealthMonitor overrideMonitor = new TestHealthMonitor();
+ overrideMonitor.setEnabled(true);
+ overrideMonitor.setHealthy(true);
+ overrideMonitor.setNeedsRestartOnFailure(true);
+
+ List monitors = healthService.getHealthMonitors();
+
+ monitors.clear();
+ monitors.add(overrideMonitor);
+
+ healthService.setHealthMonitors(monitors);
+
+ Assert.assertFalse(healthService.needsRestart());
+
+ Enumeration allSamples = CollectorRegistry.defaultRegistry.metricFamilySamples();
+
+ while (allSamples.hasMoreElements()) {
+ MetricFamilySamples samples = allSamples.nextElement();
+ LOGGER.info("Metric name {}", samples.name);
+
+ for (Sample sample : samples.samples) {
+ Assert.assertEquals(HEALTHY, sample.value, 0.0D);
+ }
+ }
+
+ }
+
+ @Test
+ public void testNeedsRestartWithUnHealthyMonitor() throws InterruptedException {
+ TestHealthMonitor overrideMonitor = new TestHealthMonitor();
+ overrideMonitor.setEnabled(true);
+ overrideMonitor.setHealthy(false);
+ overrideMonitor.setNeedsRestartOnFailure(true);
+
+ List monitors = healthService.getHealthMonitors();
+
+ monitors.clear();
+ monitors.add(overrideMonitor);
+
+ healthService.setHealthMonitors(monitors);
+
+ Assert.assertTrue(healthService.needsRestart());
+
+ Enumeration allSamples = CollectorRegistry.defaultRegistry.metricFamilySamples();
+
+ while (allSamples.hasMoreElements()) {
+ MetricFamilySamples samples = allSamples.nextElement();
+ LOGGER.info("Metric name {}", samples.name);
+
+ for (Sample sample : samples.samples) {
+ Assert.assertEquals(UNHEALTHY, sample.value, 1.0D);
+ }
+ }
+ }
+
+ /**
+ * Clear all the metrics.
+ */
+ public void clearMetrics() {
+ Enumeration mfsEnumerator = CollectorRegistry.defaultRegistry.metricFamilySamples();
+ while (mfsEnumerator.hasMoreElements()) {
+ MetricFamilySamples mfs = mfsEnumerator.nextElement();
+ if (mfs.samples != null) {
+ mfs.samples.clear();
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/HealthServiceStateTest.java b/src/test/java/com/harman/ignite/healthcheck/HealthServiceStateTest.java
new file mode 100644
index 0000000..6b0a6cf
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/HealthServiceStateTest.java
@@ -0,0 +1,110 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/** Test class for HealthServiceState.
+ *
+ * @see HealthServiceState
+ */
+public class HealthServiceStateTest {
+
+ @InjectMocks
+ private HealthServiceState serviceState;
+
+ private double state;
+ private String message;
+
+ /**
+ * Setup the test.
+ */
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ state = 0;
+ message = "Test message";
+ }
+
+ @Test
+ public void testSetState() {
+ serviceState.setState(state);
+ Assert.assertEquals(0, serviceState.getState(), 0);
+ }
+
+ @Test
+ public void testSetMessage() {
+ serviceState.setMessage(message);
+ Assert.assertEquals("Test message", serviceState.getMessage());
+ }
+
+ @Test
+ public void testEquals() {
+ HealthServiceState serviceState1 = new HealthServiceState();
+ serviceState1.setState(1);
+ serviceState1.setMessage("Unhealthy");
+ HealthServiceState serviceState2 = new HealthServiceState();
+ serviceState2.setState(1);
+ serviceState2.setMessage("Unhealthy");
+ Assert.assertEquals(serviceState1, (serviceState2));
+ serviceState1.setState(0);
+ Assert.assertNotEquals(serviceState1, (serviceState2));
+ serviceState2.setMessage("test");
+ Assert.assertNotNull(String.valueOf(serviceState1), (serviceState2));
+ serviceState2.setMessage(null);
+ Assert.assertNotEquals(serviceState1, (serviceState2));
+ Assert.assertNotNull(serviceState1);
+ Assert.assertNotEquals(serviceState1, (new ArrayList()));
+ serviceState1.setMessage(null);
+ serviceState2.setMessage("test");
+ Assert.assertNotEquals(serviceState1, (serviceState2));
+ }
+
+
+ @Test
+ public void testHashCode() {
+ serviceState.setState(0);
+ serviceState.setMessage(message);
+ Assert.assertNotEquals(serviceState.hashCode(), 0);
+ }
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/HealthServiceUnitTest.java b/src/test/java/com/harman/ignite/healthcheck/HealthServiceUnitTest.java
new file mode 100644
index 0000000..4533603
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/HealthServiceUnitTest.java
@@ -0,0 +1,678 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import com.harman.ignite.utils.logger.IgniteLogger;
+import com.harman.ignite.utils.logger.IgniteLoggerFactory;
+import com.harman.ignite.utils.metrics.IgniteHealthGuage;
+import io.prometheus.client.Collector.MetricFamilySamples;
+import io.prometheus.client.CollectorRegistry;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Test class for HealthService.
+ *
+ * @see HealthService
+ */
+public class HealthServiceUnitTest {
+ static final String SERVICE_HEALTH = "SERVICE_HEALTH";
+ static final double HEALTHY = 0;
+ static final double UNHEALTHY = 1;
+ static final int TWO = 2;
+ static final int THREE = 3;
+ static final int FOUR = 4;
+ static final int FIVE = 5;
+ static final int SEVEN = 7;
+ static final int HUNDRED = 100;
+ static final long THOUSAND = 1000L;
+ private static final IgniteLogger LOGGER = IgniteLoggerFactory.getLogger(HealthServiceUnitTest.class);
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+ @InjectMocks
+ private HealthService healthService = new HealthService();
+
+ @Mock
+ private IgniteHealthGuage serviceHealthGuage;
+ @Mock
+ private ThreadUtils threadUtils;
+
+ /**
+ * Setup the test.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ healthService.setServiceHealthGuage(serviceHealthGuage);
+ healthService.setFailureRetryInterval(HUNDRED);
+ healthService.setFailureRetryThreshold(TWO);
+ healthService.setRetryInterval(HUNDRED);
+ healthService.setNodeName("localhost");
+ }
+
+ /**
+ * HealthMonitor used simple TestHealthMonitor;
+ * Count = 2; Both healthMonitors are enabled; Both healthMonitors are set healthy ; Force
+ * is set to false;
+ * Expecting success.
+ */
+ @Test
+ public void testCheckHealthAndGetFailedMonitorsWithForceFalse() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ boolean force = false;
+
+ // Validate Failed hms should be 0
+ List failedHms = healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Assert.assertEquals(0, failedHms.size());
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+ }
+
+ /**
+ * HealthMonitor used simple TestHealthMonitor;
+ * Count = 2; Both healthMonitors are enabled; Both healthMonitors are set healthy ; Force
+ * is set to false;
+ * Expecting success.
+ */
+ @Test
+ public void testCheckHealthAndGetFailedMonitorsWithForceFalseWithPromethus() {
+ Enumeration allSamples = CollectorRegistry.defaultRegistry.metricFamilySamples();
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ boolean force = false;
+
+ // Validate Failed hms should be 0
+ List failedHms = healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Assert.assertEquals(0, failedHms.size());
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+
+ }
+
+ /**
+ * HealthMonitor used simple TestHealthMonitor;
+ * Count = 2; Both healthMonitors are enabled; HealthMonitor 1 is set healthy ;
+ * HealthMonitor 2 is set to unhealthy ; Force is set to false;
+ * As one health monitor is not healthy we are expecting one failed health monitor
+ * Expecting failure.
+ */
+ @Test
+ public void testCheckHealthAndGetFailedMonitorsWithForceFalseWhenOneMonitorIsUnHealthy() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(false);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ boolean force = false;
+
+ // Validate Failed hms should be 1
+ List failedHms = healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Assert.assertEquals(1, failedHms.size());
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+ }
+
+ /**
+ * HealthMonitor used simple TestHealthMonitor;
+ * Count = 2; Both healthMonitors are enabled; Both HealthMonitors are set healthy ; Force
+ * is set to true;
+ * As both health monitors are healthy setting force to true or false should not have any impact
+ * Expecting success.
+ */
+ @Test
+ public void testCheckHealthAndGetFailedMonitorsWithForceTrueWhenHealthMonitorsAreHealthy() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ boolean force = true;
+
+ // Validate Failed hms should be 0
+ List failedHms = healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Assert.assertEquals(0, failedHms.size());
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+ }
+
+ /**
+ * Here we are using two types of health monitors TestHealthMonitor and TestHealthMonitorWithForce.
+ * Both healthMonitors are enabled;
+ * Both HealthMonitors are set healthy ; Force is set to true;
+ * As one is unhealthy and force is true we will be retrying.
+ * Expecting success after retrying.
+ */
+ @Test
+ public void testCheckHealthAndGetFailedMonitorsWithForceTrueWhenHealthMonitorsAreUnHealthy() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitorWithForce hm = new TestHealthMonitorWithForce();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ boolean force = true;
+ healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+
+ healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Mockito.verify(serviceHealthGuage, Mockito.times(TWO))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(TWO))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(TWO))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+
+ healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Mockito.verify(serviceHealthGuage, Mockito.times(THREE))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(THREE))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(THREE))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+
+ healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Mockito.verify(serviceHealthGuage, Mockito.times(FOUR))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(FOUR))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(FOUR))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+
+ healthService.checkHealthAndGetFailedMonitors(force, hms);
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(FIVE))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+
+ }
+
+ /**
+ * HealthMonitor used simple TestHealthMonitor;
+ * Count = 2; Both healthMonitors are enabled; HealthMonitor 1 is set healthy ;
+ * HealthMonitor 2 is set to unhealthy ;
+ *
+ * As one health monitor is unhealthy we are expecting failure not have any impact
+ *
+ * Expecting failure.
+ */
+ @Test
+ public void testTriggerInitialCheck() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(false);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+
+ List failedList = healthService.triggerInitialCheck();
+ Assert.assertEquals(1, failedList.size());
+
+ Assert.assertEquals("Metric2", failedList.get(0).metricName());
+ Assert.assertEquals("Monitor2", failedList.get(0).monitorName());
+
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+
+ hms.clear();
+ hm2.setHealthy(true);
+ hms.add(hm2);
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List list = healthService.triggerInitialCheck();
+ Assert.assertEquals(0, list.size());
+ }
+
+ @Test
+ public void testClose() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(false);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ healthService.close();
+
+ Assert.assertFalse(healthService.isStartedExecutor());
+ }
+
+ @Test
+ public void testRegisterCallback() {
+ TestHealthMonitor hm = new TestHealthMonitor();
+ healthService.registerCallBack(hm);
+
+ Assert.assertNotNull(healthService.getCallback());
+ }
+
+ /**
+ * Here we are using two types of health monitors TestHealthMonitor and TestHealthMonitorWithForce.
+ * Both healthMonitors are enabled;
+ * Both HealthMonitors are set healthy ;
+ *
+ * As both are healthy it is a success scenario
+ *
+ * Expecting success.
+ */
+ @Test
+ public void testNeedsRestartFalseScenario() throws InterruptedException {
+ healthService.setFailureRetryThreshold(SEVEN);
+ final List hms = new ArrayList();
+ TestHealthMonitorWithForce hm = new TestHealthMonitorWithForce();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hm.setNeedsRestartOnFailure(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hm2.setNeedsRestartOnFailure(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+
+ boolean restart = healthService.needsRestart();
+ Assert.assertFalse(restart);
+
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+
+ }
+
+ /**
+ * Objective is to test the retry threshold of health service.
+ * The method needsRestart will repeat in a loop until the retry threshold is exceeded;
+ * or before that the service becomes healthy. Testing with retry threshold 5.
+ *
+ * The health monitor TestHealthMonitorWithForce will give true after 3 attempts with force true
+ * As both are healthy it is a success scenario.
+ * Hence, for retry threshold > 4 we should get healthy
+ *
+ *
+ * Expecting success after retry.
+ */
+ @Test
+ public void testNeedsRestartFalseScenarioWithRetry() throws InterruptedException {
+ healthService.setFailureRetryThreshold(FIVE);
+ final List hms = new ArrayList<>();
+ TestHealthMonitorWithForce hm = new TestHealthMonitorWithForce();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setNeedsRestartOnFailure(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hm2.setNeedsRestartOnFailure(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+
+ boolean restart = healthService.needsRestart();
+ Assert.assertFalse(restart);
+
+ Mockito.verify(serviceHealthGuage, Mockito.times(FIVE))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(FIVE))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", SERVICE_HEALTH);
+
+ }
+
+ /**
+ * Objective is to test the retry threshold of health service.
+ * The method needsRestart will repeat in a loop until the retry threshold is exceeded;
+ * or before that the service becomes healthy. Testing with retry threshold 5.
+ *
+ * The health monitor TestHealthMonitorWithForce will give true after 3 attempts with force true
+ * As both are healthy it is a success scenario.
+ * Hence, for retry threshold > 4 we should get healthy
+ *
+ *
+ * Expecting failure as retry threshold == 4
+ */
+
+ @Test
+ public void testNeedsRestartTrueScenario() throws InterruptedException {
+ healthService.setFailureRetryThreshold(FOUR);
+ final List hms = new ArrayList<>();
+ TestHealthMonitorWithForce hm = new TestHealthMonitorWithForce();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setNeedsRestartOnFailure(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hm2.setNeedsRestartOnFailure(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+
+ boolean restart = healthService.needsRestart();
+ Assert.assertTrue(restart);
+
+ Mockito.verify(serviceHealthGuage, Mockito.times(FIVE))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(FIVE))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+ }
+
+ /**
+ * If a health monitor needs restart is false.
+ * Even if it is unhealthy the service should return restart false.
+ */
+ @Test
+ public void testNeedsRestartFalseScenarioWithMonitorRestartFalse() throws InterruptedException {
+ healthService.setFailureRetryThreshold(FOUR);
+ final List hms = new ArrayList<>();
+ TestHealthMonitorWithForce hm = new TestHealthMonitorWithForce();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setNeedsRestartOnFailure(false);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hm2.setNeedsRestartOnFailure(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+
+ boolean restart = healthService.needsRestart();
+ Assert.assertFalse(restart);
+
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", "Metric1");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(HEALTHY, "localhost", "Metric2");
+ Mockito.verify(serviceHealthGuage, Mockito.times(1))
+ .set(UNHEALTHY, "localhost", SERVICE_HEALTH);
+ }
+
+ /**
+ * If two health monitors have same name it should throw runtime exception.
+ */
+ @Test(expected = RuntimeException.class)
+ public void testInitTwoMonitorsWithSameName() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric1");
+ hm2.setMonitorName("Monitor2");
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ }
+
+ /**
+ * If a health monitor is disabled, it should not be added in the list of health monitors.
+ */
+ @Test
+ public void testInitWithDisabledMonitor() {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(false);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(1, enabledHms.size());
+ Assert.assertEquals("Metric1", enabledHms.get(0).metricName());
+ Assert.assertEquals("Monitor1", enabledHms.get(0).monitorName());
+ }
+
+ @Test
+ public void testStartHealthServiceExecutor() throws InterruptedException {
+ final List hms = new ArrayList<>();
+ TestHealthMonitor hm = new TestHealthMonitor();
+ hm.setEnabled(true);
+ hm.setMetricName("Metric1");
+ hm.setMonitorName("Monitor1");
+ hm.setHealthy(true);
+ hms.add(hm);
+
+ TestHealthMonitor hm2 = new TestHealthMonitor();
+ hm2.setEnabled(true);
+ hm2.setMetricName("Metric2");
+ hm2.setMonitorName("Monitor2");
+ hm2.setHealthy(true);
+ hms.add(hm2);
+
+ healthService.setHealthMonitors(hms);
+ healthService.init();
+ healthService.startHealthServiceExecutor();
+ LOGGER.info("Started health service");
+ Thread.sleep(THOUSAND);
+ healthService.startHealthServiceExecutor();
+ List enabledHms = healthService.getHealthMonitors();
+ Assert.assertEquals(TWO, enabledHms.size());
+ LOGGER.info("Started again health service");
+ }
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitor.java b/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitor.java
new file mode 100644
index 0000000..1fffe88
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitor.java
@@ -0,0 +1,102 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+/**
+ * Test POJO class for HealthMonitor.
+ *
+ * @see HealthMonitor
+ */
+public class TestHealthMonitor implements HealthMonitor, HealthServiceCallBack {
+
+ private boolean healthy;
+ private boolean needsRestartOnFailure;
+ private boolean enabled;
+ private String monitorName = "TestHealthMonitor";
+ private String metricName = "TestHealthMonitorGuage";
+
+ @Override
+ public boolean isHealthy(boolean forceHealthCheck) {
+ return healthy;
+ }
+
+ @Override
+ public boolean needsRestartOnFailure() {
+ return needsRestartOnFailure;
+ }
+
+ @Override
+ public boolean performRestart() {
+ return false;
+ }
+
+ public void setNeedsRestartOnFailure(boolean needsRestartOnFailure) {
+ this.needsRestartOnFailure = needsRestartOnFailure;
+ }
+
+ public void setHealthy(boolean healthy) {
+ this.healthy = healthy;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public String monitorName() {
+ return monitorName;
+ }
+
+ public void setMonitorName(String monitorName) {
+ this.monitorName = monitorName;
+ }
+
+ @Override
+ public String metricName() {
+ return metricName;
+ }
+
+ public void setMetricName(String metricName) {
+ this.metricName = metricName;
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitorWithForce.java b/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitorWithForce.java
new file mode 100644
index 0000000..ebe2001
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/TestHealthMonitorWithForce.java
@@ -0,0 +1,105 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+/** Test POJO class for HealthMonitor with force health check.
+ *
+ * @see HealthMonitor
+ */
+public class TestHealthMonitorWithForce implements HealthMonitor {
+
+ private static final int THREE = 3;
+ private boolean healthy;
+ private boolean needsRestartOnFailure;
+ private boolean enabled;
+ private String monitorName = "TestHealthMonitorWithForce";
+ private String metricName = "TestHealthMonitorWithForceGuage";
+ @SuppressWarnings("checkstyle:MemberName")
+ private int i = 0;
+
+ @Override
+ public boolean isHealthy(boolean forceHealthCheck) {
+ if (i > THREE) {
+ return true;
+ }
+ if (forceHealthCheck) {
+ i++;
+ }
+ return false;
+ }
+
+ @Override
+ public String monitorName() {
+ return monitorName;
+ }
+
+ @Override
+ public boolean needsRestartOnFailure() {
+ return needsRestartOnFailure;
+ }
+
+ @Override
+ public String metricName() {
+ return metricName;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public void setHealthy(boolean healthy) {
+ this.healthy = healthy;
+ }
+
+ public void setNeedsRestartOnFailure(boolean needsRestartOnFailure) {
+ this.needsRestartOnFailure = needsRestartOnFailure;
+ }
+
+ public void setMonitorName(String monitorName) {
+ this.monitorName = monitorName;
+ }
+
+ public void setMetricName(String metricName) {
+ this.metricName = metricName;
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/healthcheck/TestHealthServiceConfig.java b/src/test/java/com/harman/ignite/healthcheck/TestHealthServiceConfig.java
new file mode 100644
index 0000000..5eaa9af
--- /dev/null
+++ b/src/test/java/com/harman/ignite/healthcheck/TestHealthServiceConfig.java
@@ -0,0 +1,86 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.healthcheck;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/** Test configuration for initializing TestHealthMonitor and TestHealthMonitorWithForce beans.
+ *
+ * @see HealthServiceStateTest
+ */
+@Configuration
+@ComponentScan(basePackages = { "com.harman.ignite" })
+public class TestHealthServiceConfig {
+
+ /** Bean for TestHealthMonitor.
+ *
+ * @return TestHealthMonitor
+ */
+ @Bean
+ public TestHealthMonitor testHealthMonitor() {
+ TestHealthMonitor monitor1 = new TestHealthMonitor();
+ monitor1.setEnabled(true);
+ return monitor1;
+ }
+
+ /** Bean for TestHealthMonitor.
+ * This should be skipped at the time of init of healthservice as enabled is false.
+ *
+ * @return TestHealthMonitor
+ */
+ @Bean
+ public TestHealthMonitor testHealthMonitor2() {
+ TestHealthMonitor monitor1 = new TestHealthMonitor();
+ monitor1.setEnabled(false);
+ return monitor1;
+ }
+
+ /**
+ * Bean for TestHealthMonitorWithForce.
+ *
+ * @return TestHealthMonitorWithForce
+ */
+ @Bean
+ public TestHealthMonitorWithForce testHealthMonitorWithForce() {
+ TestHealthMonitorWithForce monitor1 = new TestHealthMonitorWithForce();
+ monitor1.setEnabled(true);
+ return monitor1;
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/utils/filter/TestDuplicateExceptionFilter.java b/src/test/java/com/harman/ignite/utils/filter/TestDuplicateExceptionFilter.java
new file mode 100644
index 0000000..f6eeba0
--- /dev/null
+++ b/src/test/java/com/harman/ignite/utils/filter/TestDuplicateExceptionFilter.java
@@ -0,0 +1,118 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.filter;
+
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.spi.FilterReply;
+import com.harman.ignite.utils.logger.LoggerUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * This test case is used to test the filter DuplicateExceptionFilter.
+ *
+ * @author vkoul
+ */
+public class TestDuplicateExceptionFilter {
+
+ private static final long SIXTY_THOUSAND = 60000L;
+ private static final long LONG_VALUE_FOR_LOGGER = 2628077220L;
+ private static final Logger LOGGER = LoggerFactory.getLogger(TestDuplicateExceptionFilter.class);
+ private DuplicateExceptionFilter def = null;
+ @Mock
+ private LoggerUtils loggerUtils;
+
+ /**
+ * Setup the test.
+ */
+ @Before
+ public void setUp() {
+ def = new DuplicateExceptionFilter();
+ def.setSuppressTimeInMs(SIXTY_THOUSAND);
+ def.start();
+ }
+
+ /**
+ * This test case calls decide() twice within a minute.
+ *
+ * First invocation to decide() should return NEUTRAL whereas the next invocation should return DENY,
+ * as it is occurring within a minute, for which suppression should happen.
+ */
+ @Test
+ public void testExceptionSuppressed() {
+
+ assertThat(logMessage(def, new IOException())).isEqualTo(FilterReply.NEUTRAL);
+
+ // Now, next log is within one minute, so it should be DENY
+ assertThat(logMessage(def, new IOException())).isEqualTo(FilterReply.DENY);
+ assertThat(def.decide(null, (ch.qos.logback.classic.Logger) LOGGER,
+ null,
+ "",
+ new Object[] { new IOException() }, null))
+ .isEqualTo(FilterReply.DENY);
+
+ ConcurrentHashMap exceptionCache = new ConcurrentHashMap<>();
+ exceptionCache.put(IOException.class.getName() + LOGGER.toString(), LONG_VALUE_FOR_LOGGER);
+ def.setExceptionCache(exceptionCache);
+
+ assertThat(logMessage(def, new IOException())).isEqualTo(FilterReply.NEUTRAL);
+ }
+
+ @Test
+ public void testGetSuppressTimeInMs() {
+ Assert.assertEquals(SIXTY_THOUSAND, def.getSuppressTimeInMs());
+ }
+
+ @After
+ public void tearDown() {
+ def.stop();
+ }
+
+ private FilterReply logMessage(final TurboFilter def, Throwable t) {
+ return def.decide(null, (ch.qos.logback.classic.Logger) LOGGER, null, null, null, t);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/harman/ignite/utils/logger/EventLogger1.java b/src/test/java/com/harman/ignite/utils/logger/EventLogger1.java
new file mode 100644
index 0000000..54b21d1
--- /dev/null
+++ b/src/test/java/com/harman/ignite/utils/logger/EventLogger1.java
@@ -0,0 +1,57 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+/** Thread class for EventLogger1.
+ *
+ * @author AKumar
+ */
+public class EventLogger1 implements Runnable {
+ private static IgniteLogger igniteLogger = IgniteLoggerFactory.getLogger(EventLogger1.class);
+
+ public static IgniteLogger getIgniteLogger() {
+ return igniteLogger;
+ }
+
+ @Override
+ public void run() {
+ igniteLogger.info("Info message from EventLogger1");
+ igniteLogger.trace("Info message from EventLogger1");
+ igniteLogger.debug("Info message from EventLogger1");
+ igniteLogger.error("Error message from EventLogger1", new Exception("exception occurred"));
+ }
+}
diff --git a/src/test/java/com/harman/ignite/utils/logger/EventLogger2.java b/src/test/java/com/harman/ignite/utils/logger/EventLogger2.java
new file mode 100644
index 0000000..d9eb8f7
--- /dev/null
+++ b/src/test/java/com/harman/ignite/utils/logger/EventLogger2.java
@@ -0,0 +1,55 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+/** Thread class for EventLogger2.
+ *
+ * @author AKumar
+ */
+public class EventLogger2 implements Runnable {
+
+ private static IgniteLogger igniteLogger = IgniteLoggerFactory.getLogger(EventLogger2.class);
+
+ @Override
+ public void run() {
+ igniteLogger.info("Info message from EventLogger2");
+ igniteLogger.trace("Info message from EventLogger2");
+ igniteLogger.debug("Info message from EventLogger2");
+ igniteLogger.error("Error message from EventLogger2", new Exception("exception occurred"));
+ }
+
+}
diff --git a/src/test/java/com/harman/ignite/utils/logger/IgniteLoggerFactoryTest.java b/src/test/java/com/harman/ignite/utils/logger/IgniteLoggerFactoryTest.java
new file mode 100644
index 0000000..51d25ca
--- /dev/null
+++ b/src/test/java/com/harman/ignite/utils/logger/IgniteLoggerFactoryTest.java
@@ -0,0 +1,55 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+/** Test class for IgniteLoggerFactory.
+ *
+ * @see IgniteLoggerFactory
+ */
+public class IgniteLoggerFactoryTest {
+
+ @Test
+ public void testGetLogger() {
+ MockitoAnnotations.initMocks(this);
+ IgniteLogger logger = IgniteLoggerFactory.getLogger(EventLogger1.class);
+ Assert.assertTrue(logger instanceof IgniteLoggerImpl);
+ }
+}
diff --git a/src/test/java/com/harman/ignite/utils/logger/TestIgniteLogger.java b/src/test/java/com/harman/ignite/utils/logger/TestIgniteLogger.java
new file mode 100644
index 0000000..860bffc
--- /dev/null
+++ b/src/test/java/com/harman/ignite/utils/logger/TestIgniteLogger.java
@@ -0,0 +1,358 @@
+/*
+ * *******************************************************************************
+ *
+ * Copyright (c) 2023-24 Harman International
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ *
+ * you may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *      
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ *
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ *
+ * limitations under the License.
+ *
+ *
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * *******************************************************************************
+ */
+
+package com.harman.ignite.utils.logger;
+
+import com.harman.ignite.entities.IgniteEvent;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test class for IgniteLogger.
+ *
+ * @author AKumar
+ */
+
+public class TestIgniteLogger {
+ private static final int TWO = 2;
+ private static IgniteLoggerImpl igniteLogger =
+ IgniteLoggerImpl.getIgniteLoggerImplInstance(EventLogger2.class);
+ Logger logger;
+
+ /**
+ * Setup method.
+ */
+ @Before
+ public void setup() {
+ logger = Mockito.mock(Logger.class);
+ when(logger.isTraceEnabled()).thenReturn(true);
+ when(logger.isDebugEnabled()).thenReturn(true);
+ when(logger.isInfoEnabled()).thenReturn(true);
+ igniteLogger.setLogger(logger);
+ }
+
+ @Test
+ public void testIgniteLoggersInMap() {
+ Thread igniteEventTh1 = new Thread(new EventLogger1(), "EventLogger1");
+ Thread igniteEventTh2 = new Thread(new EventLogger2(), "EventLogger2");
+ igniteEventTh1.start();
+ igniteEventTh2.start();
+
+ Map igniteLoggersMap = igniteLogger.getIgniteLoggersMap();
+ assertEquals(TWO, igniteLoggersMap.size());
+ Assert.assertTrue(igniteLoggersMap.containsKey(EventLogger1.class.getName()));
+ Assert.assertTrue(igniteLoggersMap.containsKey(EventLogger2.class.getName()));
+ }
+
+ @Test
+ public void testInfo() {
+ ArgumentCaptor arg = ArgumentCaptor.forClass(String.class);
+ igniteLogger.info("InfoWithMsg");
+ Mockito.verify(logger).info(arg.capture());
+ assertEquals("InfoWithMsg", arg.getValue());
+ }
+
+ @Test
+ public void testInfoWithMsgAndThrowable() {
+ igniteLogger.info("InfoWithMsgAndThrowable", new Throwable());
+ Mockito.verify(logger).info(Mockito.eq("InfoWithMsgAndThrowable"), Mockito.any());
+ }
+
+ @Test
+ public void testInfoWithFormatNArgs() {
+ igniteLogger.info("Display message as: parameter1={}", "value1");
+ Mockito.verify(logger)
+ .info(
+ ArgumentMatchers.contains("Display message as: parameter1={}"),
+ ArgumentMatchers.