diff --git a/.github/maven/settings.xml b/.github/maven/settings.xml index cd4437f8acd31..fe6684806b49d 100644 --- a/.github/maven/settings.xml +++ b/.github/maven/settings.xml @@ -1,6 +1,18 @@ - + + + + + central + ${env.MAVEN_USER} + ${env.MAVEN_PASSWORD} + ossrh ${env.MAVEN_USER} @@ -12,4 +24,35 @@ default + + + default + + + central + Maven Central + + true + + + false + + https://repo.maven.apache.org/maven2/ + default + + + sonatype + Sonatype Snapshots + + false + + + true + + https://central.sonatype.com/repository/maven-snapshots/ + default + + + + diff --git a/.github/workflows/build-examples.yaml b/.github/workflows/build-examples.yaml new file mode 100644 index 0000000000000..c1d02fd9b0170 --- /dev/null +++ b/.github/workflows/build-examples.yaml @@ -0,0 +1,99 @@ +# Copyright 2020, 2025, Oracle Corporation and/or its affiliates. +# +# Licensed under the Universal Permissive License v 1.0 as shown at +# https://oss.oracle.com/licenses/upl. + +# --------------------------------------------------------------------------- +# Coherence CE GitHub Actions Examples - Build. +# --------------------------------------------------------------------------- + +name: Examples Build + +on: + workflow_dispatch: + inputs: + runner: + default: ubuntu-latest + required: true + type: string + workflow_call: + inputs: + runner: + default: ubuntu-latest + required: true + type: string + +env: + MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false + +jobs: + build: + runs-on: ${{ inputs.runner }} + strategy: + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + + - name: Print Maven & Java version + run: mvn -version + + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2- + + # Build and Test examples + - name: build-test-examples + timeout-minutes: 180 + shell: bash + env: + MAVEN_USER: ${{ secrets.MavenUser }} + MAVEN_PASSWORD: ${{ secrets.MavenPassword }} + run: | + echo "Building and Running - Examples" + export DEV_ROOT=$(pwd) + mvn --file prj/pom.xml --batch-mode -U -e -s .github/maven/settings.xml -P-modules -DskipTests clean install + mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -Pmodules,-coherence -nsu -DskipTests clean install + mvn --file prj/pom.xml --batch-mode -U -e -s .github/maven/settings.xml -Pexamples -nsu clean install + + # Upload build artifacts for diagnosing failures + - name: Build Artifacts test logs + uses: actions/upload-artifact@v4 + if: failure() + with: + name: test-output + path: prj/examples/**/target/test-output/**/* + if-no-files-found: ignore + + - name: Build Artifacts test reports + uses: actions/upload-artifact@v4 + if: failure() + with: + name: failsafe-surefire-test-reports + path: prj/examples/**/target/*-reports/**/* + if-no-files-found: ignore + + - name: Build Artifacts core dumps + uses: actions/upload-artifact@v4 + if: failure() + with: + name: core-dumps + path: prj/examples/**/core.* + if-no-files-found: ignore + + - name: Build Artifacts compiler replays + uses: actions/upload-artifact@v4 + if: failure() + with: + name: compiler-replay-logs + path: prj/examples/**/replay_pid*.log + if-no-files-found: ignore diff --git a/.github/workflows/build-tde-sources.yaml b/.github/workflows/build-tde-sources.yaml deleted file mode 100644 index 6205f729b709c..0000000000000 --- a/.github/workflows/build-tde-sources.yaml +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2022 Oracle Corporation and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at -# http://oss.oracle.com/licenses/upl. - -# --------------------------------------------------------------------------- -# Coherence CE GitHub Actions TDE Sources build. -# --------------------------------------------------------------------------- - -name: TDE Sources - -on: - workflow_dispatch: - push: - branches-ignore: - - gh-pages - - p4-integ* - - last-p4-* - pull_request: - types: - - opened - - committed - branches: - - '*' - -env: - MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false - -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - stage: - - stage1 - - stage2 - - stage3 - - stage4 - - stage5 - - stage6 - - stage7 - - stage8 - - stage9 - - stage10 - - stage11 - - stage12 - - stage13 - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up JDK - uses: actions/setup-java@v1 - with: - java-version: 11 - - - name: Print Maven & Java version - run: mvn -version - - - name: Cache Maven packages - uses: actions/cache@v2 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2- - - # First run a simple compile without tests - - name: Build - shell: bash - env: - MAVEN_USER: ${{ secrets.MavenUser }} - MAVEN_PASSWORD: ${{ secrets.MavenPassword }} - run: | - export DEV_ROOT=$(pwd) - mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -U -P-modules,tde.sources -DskipTests clean install - mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -Pmodules,-coherence,tde.sources -nsu -DskipTests clean install - - # If the build job runs successfully then run the verify stages in parallel - - name: Verify - timeout-minutes: 120 - shell: bash - env: - MAVEN_USER: ${{ secrets.MavenUser }} - MAVEN_PASSWORD: ${{ secrets.MavenPassword }} - run: | - echo "Running verify ${{ matrix.stage }}" - export DEV_ROOT=$(pwd) - mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -P-modules,${{ matrix.stage }},-javadoc,tde.sources -Doptional -Dcoherence.SkipLargeMemoryTests=true clean verify - mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -Pmodules,-coherence,${{ matrix.stage }},-javadoc,tde.sources -nsu -Doptional -Dcoherence.SkipLargeMemoryTests=true clean verify - - # Upload build artifacts for diagnosing failures - - name: Build Artifacts test logs - uses: actions/upload-artifact@v2 - if: failure() - with: - name: test-output - path: prj/**/target/test-output/**/* - - - name: Build Artifacts test reports - uses: actions/upload-artifact@v2 - if: failure() - with: - name: test-reports - path: prj/**/target/*-reports/**/* diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e2cfc0da2cc10..c2585f1a01f8e 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,4 +1,4 @@ -# Copyright 2020, 2022, Oracle Corporation and/or its affiliates. +# Copyright 2020, 2025, Oracle Corporation and/or its affiliates. # # Licensed under the Universal Permissive License v 1.0 as shown at # http://oss.oracle.com/licenses/upl. @@ -7,28 +7,22 @@ # Coherence CE GitHub Actions CI build. # --------------------------------------------------------------------------- -name: CI Build +name: Parallel CI Build on: - workflow_dispatch: - push: - branches-ignore: - - gh-pages - - p4-integ* - - last-p4-* - pull_request: - types: - - opened - - committed - branches: - - '*' + workflow_call: + inputs: + runner: + default: ubuntu-latest + required: true + type: string env: MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} strategy: fail-fast: false matrix: @@ -49,18 +43,19 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up JDK - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: - java-version: 11 + distribution: 'temurin' + java-version: '11' - name: Print Maven & Java version run: mvn -version - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -68,7 +63,7 @@ jobs: # Run the Verify stages in parallel - name: Verify - timeout-minutes: 120 + timeout-minutes: 180 shell: bash env: MAVEN_USER: ${{ secrets.MavenUser }} @@ -76,20 +71,39 @@ jobs: run: | echo "Building and running tests in ${{ matrix.stage }}" export DEV_ROOT=$(pwd) - mvn --file prj/pom.xml --batch-mode -U -e -s .github/maven/settings.xml -P-modules,${{ matrix.stage }} -Doptional -Dcoherence.SkipLargeMemoryTests=true clean install - mvn --file prj/pom.xml --batch-mode -e -s .github/maven/settings.xml -Pmodules,-coherence,${{ matrix.stage }} -nsu -Doptional -Dcoherence.SkipLargeMemoryTests=true clean verify + export SETTINGS_XML=${DEV_ROOT}/.github/maven/settings.xml + mvn --file prj/pom.xml --batch-mode -U -e -s ${SETTINGS_XML} -P-modules,${{ matrix.stage }} -Doptional -Dcoherence.SkipLargeMemoryTests=true clean install + mvn --file prj/pom.xml --batch-mode -e -s ${SETTINGS_XML} -Pmodules,-coherence,${{ matrix.stage }} -nsu -Doptional -Dcoherence.SkipLargeMemoryTests=true -Dcoherence.compatability.settings=${SETTINGS_XML} clean install # Upload build artifacts for diagnosing failures - name: Build Artifacts test logs - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: failure() with: - name: test-output + name: test-output-${{ matrix.stage }} path: prj/**/target/test-output/**/* + if-no-files-found: ignore - name: Build Artifacts test reports - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: failure() with: - name: test-reports - path: prj/**/target/*-reports/**/* \ No newline at end of file + name: failsafe-surefire-test-reports-${{ matrix.stage }} + path: prj/**/target/*-reports/**/* + if-no-files-found: ignore + + - name: Build Artifacts core dumps + uses: actions/upload-artifact@v4 + if: failure() + with: + name: core-dumps-${{ matrix.stage }} + path: prj/**/core.* + if-no-files-found: ignore + + - name: Build Artifacts compiler replays + uses: actions/upload-artifact@v4 + if: failure() + with: + name: compiler-replay-logs-${{ matrix.stage }} + path: prj/**/replay_pid*.log + if-no-files-found: ignore diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000000000..7c44f2b95a56b --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,51 @@ +# Copyright 2020, 2025, Oracle Corporation and/or its affiliates. +# +# Licensed under the Universal Permissive License v 1.0 as shown at +# http://oss.oracle.com/licenses/upl. + +# --------------------------------------------------------------------------- +# Coherence CE GitHub Actions CI build. +# --------------------------------------------------------------------------- + +name: CI Pipeline + +on: + workflow_dispatch: + push: + branches-ignore: + - gh-pages + - p4-integ* + - last-p4-* + +jobs: + build: + name: Parallel Build - Linux + uses: ./.github/workflows/build.yaml + secrets: inherit + with: + runner: ubuntu-latest + +# windows: +# name: Parallel Build - Windows +# uses: ./.github/workflows/ci.yaml +# secrets: inherit +# with: +# runner: windows-latest + + examples: + name: Examples Build + uses: ./.github/workflows/build-examples.yaml + secrets: inherit + with: + runner: ubuntu-latest + + deploy: + if: github.repository == 'oracle/coherence' && (github.ref_name == 'main' || startsWith(github.ref_name, 'v1') || startsWith(github.ref_name, 'v2') || startsWith(github.ref_name, 'v3')) + name: Deploy Snapshot + uses: ./.github/workflows/deploy-snapshots.yaml + with: + runner: ubuntu-latest + secrets: inherit + needs: + - build + - examples diff --git a/.github/workflows/deploy-snapshots.yaml b/.github/workflows/deploy-snapshots.yaml index 61f9b89247d51..e7a0b9dffc19b 100644 --- a/.github/workflows/deploy-snapshots.yaml +++ b/.github/workflows/deploy-snapshots.yaml @@ -1,3 +1,8 @@ +# Copyright 2025, Oracle Corporation and/or its affiliates. +# +# Licensed under the Universal Permissive License v 1.0 as shown at +# http://oss.oracle.com/licenses/upl. + # This workflow will build a Java project with Maven and deploy snapshot # artifacts to Maven Central # @@ -8,11 +13,12 @@ name: Deploy to Maven Central on: - workflow_run: - workflows: ["CI Build"] - branches: [ master, v20.12, v21.06, v21.12, v14.1.1.0 ] - types: - - completed + workflow_call: + inputs: + runner: + default: ubuntu-latest + required: true + type: string env: GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }} @@ -22,22 +28,20 @@ env: jobs: build: - if: ${{ github.event.workflow_run.conclusion == 'success' }} - - runs-on: ubuntu-latest + if: github.run_attempt == 1 + runs-on: ${{ inputs.runner }} steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 + - uses: actions/checkout@v4 - - name: Set up Java 11 - uses: actions/setup-java@v1 + - name: Set up JDK + uses: actions/setup-java@v4 with: - java-version: 11 + distribution: 'temurin' + java-version: '11' - name: Cache Maven packages - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} @@ -47,11 +51,22 @@ jobs: env: MAVEN_USER: ${{ secrets.MavenUser }} MAVEN_PASSWORD: ${{ secrets.MavenPassword }} - GIT_COMMIT: ${{github.event.workflow_run.head_commit.id}} - HEAD_BRANCH: ${{github.event.workflow_run.head_branch}} run: | - echo "Checking out branch ${HEAD_BRANCH} commit ${GIT_COMMIT}" - git checkout "${GIT_COMMIT}" git config --local user.name "${GIT_USER}" git config --local user.email "${GIT_EMAIL}" - sh tools/bin/github-deploy-snapshots.sh \ No newline at end of file + sh tools/bin/github-deploy-snapshots.sh + + - name: Build Docs + env: + MAVEN_USER: ${{ secrets.MavenUser }} + MAVEN_PASSWORD: ${{ secrets.MavenPassword }} + run: | + cd prj + make docs + + - name: Upload Docs + uses: actions/upload-artifact@v4 + with: + name: docs.zip + path: prj/docs/target/docs.zip + if-no-files-found: ignore diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000000000..b3fe9496debe1 --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,35 @@ +# Copyright 2020, 2025, Oracle Corporation and/or its affiliates. +# +# Licensed under the Universal Permissive License v 1.0 as shown at +# http://oss.oracle.com/licenses/upl. + +# --------------------------------------------------------------------------- +# Coherence CE GitHub Actions CI build. +# --------------------------------------------------------------------------- + +name: Pull Request Build + +on: + pull_request: + types: + - opened + - committed + branches-ignore: + - gh-pages + - p4-integ* + - last-p4-* + +jobs: + build: + name: Parallel Build - Linux + uses: ./.github/workflows/build.yaml + secrets: inherit + with: + runner: ubuntu-latest + + examples: + name: Examples Build + uses: ./.github/workflows/build-examples.yaml + secrets: inherit + with: + runner: ubuntu-latest diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000..53a78fa23fa80 Binary files /dev/null and b/.idea/icon.png differ diff --git a/LICENSE.txt b/LICENSE.txt index 6e0307d0e28a2..f4f0717f8ec01 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2000, 2020, Oracle and/or its affiliates. +Copyright (c) 2000, 2025, Oracle and/or its affiliates. The Universal Permissive License (UPL), Version 1.0 diff --git a/README.md b/README.md index f19e9419b0ac5..215706fb719cb 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ the natural place to consume this dependency is from Maven: com.oracle.coherence.ce coherence - 21.12 + 22.06 ``` @@ -161,10 +161,8 @@ our own [operator](https://github.com/oracle/coherence-operator). ### Prerequisites - 1. Java - JDK 8 or higher - 2. Maven - 3.6.3 or higher - -> **Note:** When _building_ Coherence, JDK 11 is required. + 1. Java - JDK 11 or higher + 2. Maven - 3.8.5 or higher ### CLI Hello Coherence @@ -186,9 +184,9 @@ and data is once again retrieved to illustrate the permanence of the data. ```shell -$> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=21.12 dependency:get +$> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=22.06 dependency:get -$> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/21.12/coherence-21.12.jar +$> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/22.06/coherence-22.06.jar $> java -jar $COH_JAR & @@ -225,9 +223,9 @@ $> kill %1 #### Coherence Console ```shell -$> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=21.12 dependency:get +$> mvn -DgroupId=com.oracle.coherence.ce -DartifactId=coherence -Dversion=22.06 dependency:get -$> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/21.12/coherence-21.12.jar +$> export COH_JAR=~/.m2/repository/com/oracle/coherence/ce/coherence/22.06/coherence-22.06.jar $> java -jar $COH_JAR & @@ -285,7 +283,7 @@ inserts and retrieves data from the Coherence server. com.oracle.coherence.ce coherence - 21.12 + 22.06 ``` 3. Copy and paste the following source to a file named src/main/java/HelloCoherence.java: @@ -340,24 +338,22 @@ inserts and retrieves data from the Coherence server. ## Building -In order to **build** Coherence CE, please ensure that you have **JDK 11** installed. - -> **Note:** When using Coherence merely as a dependency in a project, without intending -> to build the project from source code, then an installed JDK 8 version is sufficient. - ```shell $> git clone git@github.com:oracle/coherence.git $> cd coherence/prj -# build all modules +# build Coherence module $> mvn clean install -# build all modules skipping tests +# build Coherence module skipping tests $> mvn clean install -DskipTests -# build a specific module, including all dependent modules and run tests -$> mvn -am -pl test/functional/persistence clean verify +# build all other modules skipping tests +$> mvn -Pmodules clean install -DskipTests + +# build specific module, including all dependent modules and run tests +$> mvn -Pmodules -am -pl test/functional/persistence clean verify # only build coherence.jar without running tests $> mvn -am -pl coherence clean install -DskipTests diff --git a/bin/cfgcommon.sh b/bin/cfgcommon.sh index e03a2dbdf8381..52635fa99620c 100644 --- a/bin/cfgcommon.sh +++ b/bin/cfgcommon.sh @@ -1,9 +1,9 @@ #!/bin/bash -# Copyright (c) 2000, 2022, Oracle and/or its affiliates. +# Copyright (c) 2000, 2025, Oracle and/or its affiliates. # # Licensed under the Universal Permissive License v 1.0 as shown at -# http://oss.oracle.com/licenses/upl. +# https://oss.oracle.com/licenses/upl. # This script sets all environment variables necessary to build Coherence, # however should be sourced by other scripts. These scripts should define @@ -16,11 +16,9 @@ # JAVA_HOME e.g. /usr/java/jdk1.6 # MAVEN_HOME e.g. /dev/tools/maven # P4IGNORE e.g. /dev/.p4ignore -# TDE_HOME e.g. /dev/tools/tde # CLASSPATH e.g. # # _P4IGNORE saved P4IGNORE -# _TDE_HOME saved TDE_HOME # _CLASSPATH saved CLASSPATH # _PATH saved PATH # @@ -53,12 +51,6 @@ function reset export P4IGNORE=$_P4IGNORE fi - if [ -z $_TDE_HOME ]; then - unset TDE_HOME - else - export TDE_HOME=$_TDE_HOME - fi - if [ -z $_CLASSPATH ]; then unset CLASSPATH else @@ -75,7 +67,6 @@ function reset unset _JAVA_HOME unset _MAVEN_HOME unset _P4IGNORE - unset _TDE_HOME unset _CLASSPATH unset _PATH @@ -173,15 +164,6 @@ function setup echo P4IGNORE = $P4IGNORE fi - # - # Set the TDE_HOME environment variable - # - _TDE_HOME=$TDE_HOME - TDE_HOME=$DEV_ROOT/tools/tde - PATH=$TDE_HOME/bin:$PATH - export TDE_HOME - echo TDE_HOME = $TDE_HOME - # # Add the RQ executables to the PATH environment variable # diff --git a/bin/cfglocal.sh b/bin/cfglocal.sh index 5fa87338edea1..62405a28bd18b 100644 --- a/bin/cfglocal.sh +++ b/bin/cfglocal.sh @@ -1,19 +1,19 @@ -# Copyright (c) 2000, 2022, Oracle and/or its affiliates. +# Copyright (c) 2000, 2023, Oracle and/or its affiliates. # # Licensed under the Universal Permissive License v 1.0 as shown at -# http://oss.oracle.com/licenses/upl. +# https://oss.oracle.com/licenses/upl. # # This script by default uses JDK 11 and set JAVA_HOME pointing to # that JDK version. # -# To use JDK 17 locally, use command: RBT_JV=17 ./bin/cfglocal.sh +# To use JDK 17 locally, use command: JV=17 ./bin/cfglocal.sh # # And to run on RQ with JDK 17, specify -j option as shown below # enqueue -j 17 [-c changelist] SCRIPT_PATH="${BASH_SOURCE[0]}" -JAVA_VERSION_TO_USE=${RBT_JV:-11} +JAVA_VERSION_TO_USE=${JV:-11} while [ -h "${SCRIPT_PATH}" ]; do LS=`ls -ld "${SCRIPT_PATH}"` diff --git a/bin/cfgwindows.cmd b/bin/cfgwindows.cmd index 29d31a5f8963a..c5afff7a9f36d 100755 --- a/bin/cfgwindows.cmd +++ b/bin/cfgwindows.cmd @@ -1,8 +1,8 @@ @echo off -rem Copyright (c) 2000, 2020, Oracle and/or its affiliates. +rem Copyright (c) 2000, 2024, Oracle and/or its affiliates. rem Licensed under the Universal Permissive License v 1.0 as shown at -rem http://oss.oracle.com/licenses/upl. +rem https://oss.oracle.com/licenses/upl. rem rem This script sets all environment variables necessary to build Coherence. @@ -14,11 +14,9 @@ rem This script is responsible for the following environment variables: rem rem DEV_ROOT e.g. c:\dev rem MAVEN_HOME e.g. c:\dev\tools\maven -rem TDE_HOME e.g. c:\dev\tools\tde rem CLASSPATH e.g. rem PATH e.g. %ANT_HOME%\bin;%MAVEN_HOME%\bin;%PATH% rem -rem _TDE_HOME saved TDE_HOME rem _CLASSPATH saved CLASSPATH rem _PATH saved PATH rem @@ -33,13 +31,11 @@ if "%1"=="-reset" ( ) set MAVEN_HOME=%_MAVEN_HOME% - set TDE_HOME=%_TDE_HOME% set CLASSPATH=%_CLASSPATH% set PATH=%_PATH% set DEV_ROOT= set _MAVEN_HOME= - set _TDE_HOME= set _CLASSPATH= set _PATH= @@ -79,13 +75,6 @@ if exist %DEV_ROOT%\tools\maven ( echo MAVEN_HOME = %MAVEN_HOME% ) -rem -rem Set the TDE_HOME environment variable -rem -set _TDE_HOME=%TDE_HOME% -set TDE_HOME=%DEV_ROOT%\tools\tde -echo TDE_HOME = %TDE_HOME% - rem rem Set the CLASSPATH environment variable rem @@ -97,7 +86,6 @@ rem rem Set the PATH environment variable rem set _PATH=%PATH% -set PATH=%TDE_HOME%\bin;%PATH% echo PATH = %PATH% echo Build environment set. diff --git a/build-images.sh b/build-images.sh new file mode 100755 index 0000000000000..27fc85534cc82 --- /dev/null +++ b/build-images.sh @@ -0,0 +1,246 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2000, 2022, Oracle and/or its affiliates. +# +# Licensed under the Universal Permissive License v 1.0 as shown at +# https://oss.oracle.com/licenses/upl. +# + +# --------------------------------------------------------------------------- +# This script uses Buildah to build a multi-architecture Coherence image. +# The architectures built are linux/amd64 and linux/arm64. +# The images are pushed to the local Docker daemon unless NO_DAEMON=true. +# --------------------------------------------------------------------------- +set -x + +BASEDIR=$(dirname "$0") + +# Ensure the IMAGE_NAME has been set - this is the name of the image to build +if [ "${IMAGE_NAME}" == "" ] +then + echo "ERROR: No IMAGE_NAME environment variable has been set" + exit 1 +fi +# Ensure the AMD_BASE_IMAGE has been set - this is the name of the base image for amd64 +if [ "${AMD_BASE_IMAGE}" == "" ] +then + echo "ERROR: No AMD_BASE_IMAGE environment variable has been set" + exit 1 +fi +# Ensure the ARM_BASE_IMAGE has been set - this is the name of the base image for arm64 +if [ "${ARM_BASE_IMAGE}" == "" ] +then + echo "ERROR: No ARM_BASE_IMAGE environment variable has been set" + exit 1 +fi +# Ensure the GRAAL_AMD_BASE_IMAGE has been set - this is the name of the base image for amd64 +if [ "${GRAAL_AMD_BASE_IMAGE}" == "" ] +then + echo "ERROR: No GRAAL_AMD_BASE_IMAGE environment variable has been set" + exit 1 +fi +# Ensure the GRAAL_ARM_BASE_IMAGE has been set - this is the name of the base image for arm64 +if [ "${GRAAL_ARM_BASE_IMAGE}" == "" ] +then + echo "ERROR: No ARM_BASE_IMAGE environment variable has been set" + exit 1 +fi + +# Ensure there is a default architecture - if not set we assume amd64 +if [ "${IMAGE_ARCH}" == "" ] +then + IMAGE_ARCH="amd64" +fi + +# Ensure there is a main class set +if [ "${MAIN_CLASS}" == "" ] +then + MAIN_CLASS=com.tangosol.net.Coherence +fi + +# Ensure there is an extend port set +if [ "${PORT_EXTEND}" == "" ] +then + PORT_EXTEND=20000 +fi + +# Ensure there is a concurrent extend port set +if [ "${PORT_CONCURRENT_EXTEND}" == "" ] +then + PORT_CONCURRENT_EXTEND=20001 +fi + +# Ensure there is a gRPC port set +if [ "${PORT_GRPC}" == "" ] +then + PORT_GRPC=1408 +fi + +# Ensure there is a management port set +if [ "${PORT_MANAGEMENT}" == "" ] +then + PORT_MANAGEMENT=30000 +fi + +# Ensure there is a metrics port set +if [ "${PORT_METRICS}" == "" ] +then + PORT_METRICS=9612 +fi + +# Ensure there is a health port set +if [ "${PORT_HEALTH}" == "" ] +then + PORT_HEALTH=6676 +fi + +# we must use docker format to use health checks +export BUILDAH_FORMAT=docker + +# Build the entrypoint command line. +ENTRY_POINT="java" + +CLASSPATH="/coherence/ext/conf:/coherence/ext/lib/*:/app/resources:/app/classes:/app/libs/*" + +# The command line +CMD="" +CMD="${CMD} -cp ${CLASSPATH}" +CMD="${CMD} -XshowSettings:all" +CMD="${CMD} -XX:+PrintCommandLineFlags" +CMD="${CMD} -XX:+PrintFlagsFinal" +CMD="${CMD} -Djava.net.preferIPv4Stack=true" +CMD="${CMD} @/args/jvm-args.txt" + +# The health check command line +HEALTH_CMD="" +HEALTH_CMD="${HEALTH_CMD} -cp ${CLASSPATH}" +HEALTH_CMD="${HEALTH_CMD} com.tangosol.util.HealthCheckClient" +HEALTH_CMD="${HEALTH_CMD} http://127.0.0.1:${PORT_HEALTH}/ready" + +# Build the environment variable options +ENV_VARS="" +ENV_VARS="${ENV_VARS} -e COHERENCE_WKA=localhost" +ENV_VARS="${ENV_VARS} -e COHERENCE_EXTEND_PORT=${PORT_EXTEND}" +ENV_VARS="${ENV_VARS} -e COHERENCE_CONCURRENT_EXTEND_PORT=${PORT_CONCURRENT_EXTEND}" +ENV_VARS="${ENV_VARS} -e COHERENCE_GRPC_ENABLED=true" +ENV_VARS="${ENV_VARS} -e COHERENCE_GRPC_SERVER_PORT=${PORT_GRPC}" +ENV_VARS="${ENV_VARS} -e COHERENCE_MANAGEMENT_HTTP=all" +ENV_VARS="${ENV_VARS} -e COHERENCE_MANAGEMENT_HTTP_PORT=${PORT_MANAGEMENT}" +ENV_VARS="${ENV_VARS} -e COHERENCE_METRICS_HTTP_ENABLED=true" +ENV_VARS="${ENV_VARS} -e COHERENCE_METRICS_HTTP_PORT=${PORT_METRICS}" +ENV_VARS="${ENV_VARS} -e COHERENCE_HEALTH_HTTP_PORT=${PORT_HEALTH}" +ENV_VARS="${ENV_VARS} -e COHERENCE_TTL=0" +ENV_VARS="${ENV_VARS} -e COH_MAIN_CLASS=${MAIN_CLASS}" +ENV_VARS="${ENV_VARS} -e JAEGER_SAMPLER_TYPE=const" +ENV_VARS="${ENV_VARS} -e JAEGER_SAMPLER_PARAM=0" +ENV_VARS="${ENV_VARS} -e JAEGER_SERVICE_NAME=coherence" + +# Build the exposed port list +PORT_LIST="" +PORT_LIST="${PORT_LIST} -p ${PORT_EXTEND}" +PORT_LIST="${PORT_LIST} -p ${PORT_CONCURRENT_EXTEND}" +PORT_LIST="${PORT_LIST} -p ${PORT_GRPC}" +PORT_LIST="${PORT_LIST} -p ${PORT_MANAGEMENT}" +PORT_LIST="${PORT_LIST} -p ${PORT_METRICS}" +PORT_LIST="${PORT_LIST} -p ${PORT_HEALTH}" + +# The image creation date +CREATED=$(date) + +# Common image builder function +# param 1: the image architecture, e.g. amd64 or arm64 +# param 2: the image o/s e.g. linux +# param 3: the base image +# param 4: the image name +common_image(){ + # make sure the old container is removed + buildah rm "container-${1}" || true + buildah rmi "coherence:${1}" || true + + # Create the container from the base image, setting the architecture and O/S + buildah from --arch "${1}" --os "${2}" --name "container-${1}" "${3}" + + # Add the configuration, entrypoint, ports, env-vars etc... + buildah config --healthcheck-start-period 10s --healthcheck-interval 10s --healthcheck "CMD ${ENTRY_POINT} ${HEALTH_CMD}" "container-${1}" + + buildah config --arch "${1}" --os "${2}" \ + --entrypoint "[\"${ENTRY_POINT}\"]" --cmd "${CMD} ${MAIN_CLASS}" \ + ${ENV_VARS} ${PORT_LIST} \ + --annotation "org.opencontainers.image.created=${CREATED}" \ + --annotation "org.opencontainers.image.url=https://github.com/oracle/coherence/pkgs/container/coherence-ce" \ + --annotation "org.opencontainers.image.version=${COHERENCE_VERSION}" \ + --annotation "org.opencontainers.image.source=http://github.com/oracle/coherence" \ + --annotation "org.opencontainers.image.vendor=${PROJECT_VENDOR}" \ + --annotation "org.opencontainers.image.title=${PROJECT_DESCRIPTION} ${COHERENCE_VERSION}" \ + --label "org.opencontainers.image.url=https://github.com/oracle/coherence/pkgs/container/coherence-ce" \ + --label "org.opencontainers.image.version=${COHERENCE_VERSION}" \ + --label "org.opencontainers.image.source=http://github.com/oracle/coherence" \ + --label "org.opencontainers.image.vendor=${PROJECT_VENDOR}" \ + --label "org.opencontainers.image.title=${PROJECT_DESCRIPTION} ${COHERENCE_VERSION}" \ + "container-${1}" + + # Copy files into the container + buildah copy "container-${1}" "${BASEDIR}/target/docker" / + buildah copy "container-${1}" "${BASEDIR}/target/*.jar" /app/libs + + # Commit the container to an image + buildah commit "container-${1}" "coherence:${1}" + + # Export the image to the Docker daemon unless NO_DAEMON is true + if [ "${NO_DAEMON}" != "true" ] + then + buildah push -f v2s2 "coherence:${1}" "docker-daemon:${4}" + echo "Pushed ${2}/${1} image ${4} to Docker daemon" + fi +} + +buildah version + +if [ "${DOCKER_HUB_USERNAME}" != "" ] && [ "${DOCKER_HUB_PASSWORD}" != "" ] +then + buildah login -u "${DOCKER_HUB_USERNAME}" -p "${DOCKER_HUB_PASSWORD}" "docker.io" +fi + +if [ "${DOCKER_REGISTRY}" != "" ] && [ "${DOCKER_USERNAME}" != "" ] && [ "${DOCKER_PASSWORD}" != "" ] +then + buildah login -u "${DOCKER_USERNAME}" -p "${DOCKER_PASSWORD}" "${DOCKER_REGISTRY}" +fi + +if [ "${OCR_DOCKER_USERNAME}" != "" ] && [ "${OCR_DOCKER_USERNAME}" != "" ] +then + buildah login -u "${OCR_DOCKER_USERNAME}" -p "${OCR_DOCKER_PASSWORD}" "${OCR_DOCKER_SERVER}" +fi + +# Build the amd64 image +common_image amd64 linux "${AMD_BASE_IMAGE}" "${IMAGE_NAME}-amd64" + +# Build the arm64 image +common_image arm64 linux "${ARM_BASE_IMAGE}" "${IMAGE_NAME}-arm64" + +# Push the relevant image to the docker daemon base on the build machine's o/s architecture +if [ "${NO_DAEMON}" != "true" ] +then + buildah push -f v2s2 "coherence:${IMAGE_ARCH}" "docker-daemon:${IMAGE_NAME}" + echo "Pushed linux/${IMAGE_ARCH} image ${IMAGE_NAME} to Docker daemon" +fi + +# Build the amd64 Graal image +common_image amd64 linux "${GRAAL_AMD_BASE_IMAGE}" "${IMAGE_NAME}-graal-amd64" + +# Build the arm64 Graal image +common_image arm64 linux "${GRAAL_ARM_BASE_IMAGE}" "${IMAGE_NAME}-graal-arm64" + +# Push the relevant Graal image to the docker daemon base on the build machine's o/s architecture +if [ "${NO_DAEMON}" != "true" ] +then + buildah push -f v2s2 "coherence:${IMAGE_ARCH}" "docker-daemon:${IMAGE_NAME}-graal" + echo "Pushed linux/${IMAGE_ARCH} image ${IMAGE_NAME} to Docker daemon" +fi + +# Clean-up +buildah rm "container-amd64" || true +buildah rmi "coherence:amd64" || true +buildah rm "container-arm64" || true +buildah rmi "coherence:arm64" || true + + diff --git a/prj/.idea/icon.png b/prj/.idea/icon.png new file mode 100644 index 0000000000000..53a78fa23fa80 Binary files /dev/null and b/prj/.idea/icon.png differ diff --git a/prj/Makefile b/prj/Makefile index 2c79f1c22c335..7b87284a46e65 100644 --- a/prj/Makefile +++ b/prj/Makefile @@ -1,5 +1,5 @@ # ---------------------------------------------------------------------------------------------------------------------- -# Copyright (c) 2022, Oracle and/or its affiliates. +# Copyright (c) 2022, 2023, Oracle and/or its affiliates. # # Licensed under the Universal Permissive License v 1.0 as shown at # https://oss.oracle.com/licenses/upl. @@ -11,9 +11,8 @@ # # ---------------------------------------------------------------------------------------------------------------------- -ARGS = -NO_TDE = -Dtde.compile.not.required=true -CURRENT_VERSION = $(shell mvn help:evaluate -Dexpression=project.version -q -DforceStdout -nsu) +ARGS ?= +CURRENT_VERSION ?= $(shell mvn help:evaluate -Dexpression=project.version -q -DforceStdout -nsu) # ====================================================================================================================== # Makefile targets start here @@ -35,41 +34,43 @@ help: ## Display this help. .PHONY: all all: coherence modules ## A full build of Coherence with all modules -.PHONY: all-no-tde -all-no-tde: coherence-no-tde modules-no-tde ## A full build of Coherence with all modules (without compiling TDE) - .PHONY: coherence coherence: ## Build coherence.jar $(call mvn_build_coherence) -.PHONY: coherence-no-tde -coherence-no-tde: ## Build coherence.jar (without compiling TDE) - $(call mvn_build_coherence,$(NO_TDE)) - .PHONY: modules modules: ## Build Coherence modules $(call mvn_build_modules) -.PHONY: modules-no-tde -modules-no-tde: ## Build Coherence modules (without compiling TDE) - $(call mvn_build_modules,$(NO_TDE)) - .PHONY: docker docker: ## Build the Coherence image (does not rebuild Coherence jars) - mvn clean install -Pmodules,-coherence,docker -pl coherence-docker/ -nsu + mvn clean install -Pmodules,-coherence,docker -pl coherence-docker/ -nsu $(ARGS) + +.PHONY: docker-no-graal +docker-no-graal: ## Build the Coherence image (does not rebuild Coherence jars) + mvn clean install -Pmodules,-coherence,docker -pl coherence-docker/ -nsu -Dgraal.image.skip=true $(ARGS) .PHONY: docker-test docker-test: ## Build the Coherence image (does not rebuild Coherence image) - mvn verify -Pmodules,-coherence,docker-test -pl coherence-docker/ -nsu + mvn verify -Pmodules,-coherence,docker-test -pl coherence-docker/ -nsu $(ARGS) + +.PHONY: docker-push +docker-push: ## Push the Coherence image (does not rebuild Coherence image) + mvn -DskipTests install -Pmodules,-coherence,docker-push -pl coherence-docker -nsu $(ARGS) define mvn_build_coherence - mvn clean install -U -DskipTests $(1) $(ARGS) + mvn clean install -T 1.5C -U -DskipTests $(1) $(ARGS) endef define mvn_build_modules - mvn clean install -nsu -Pmodules,-coherence -DskipTests $(1) $(ARGS) + mvn clean install -T 1.5C -nsu -Pmodules,-coherence -DskipTests $(1) $(ARGS) endef +.PHONY: docs +docs: ## Build the documentation + mvn clean install -DskipTests -pl docs -P docs -nsu + cd docs/target/docs && zip -r ../docs.zip * + # ====================================================================================================================== # Run targets # ====================================================================================================================== diff --git a/prj/build-import.xml b/prj/build-import.xml deleted file mode 100644 index 31f12c349d84a..0000000000000 --- a/prj/build-import.xml +++ /dev/null @@ -1,771 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ****************************************************************************** - | - | ${banner.message} - | - ****************************************************************************** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/pom.xml b/prj/coherence-bedrock/coherence-bedrock-testing-support/pom.xml index 27cf34136f685..9d853abb40f5a 100755 --- a/prj/coherence-bedrock/coherence-bedrock-testing-support/pom.xml +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/pom.xml @@ -1,6 +1,6 @@ + + org.apache.felix + maven-bundle-plugin + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + org.apache.maven.plugins maven-deploy-plugin - ${maven.deploy.plugin.version} false @@ -102,7 +117,7 @@ - release-coherence + release-modules @@ -112,32 +127,17 @@ false - - - sign-artifacts - verify - - sign - - - org.sonatype.plugins nexus-staging-maven-plugin - ${nexus.staging.maven.plugin.version} - true - ossrh - https://oss.sonatype.org/ - true false - true - \ No newline at end of file + diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/AbstractCoherenceBuilder.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/AbstractCoherenceBuilder.java new file mode 100644 index 0000000000000..5dc166311a725 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/AbstractCoherenceBuilder.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.junit; + +import com.oracle.bedrock.Bedrock; +import com.oracle.bedrock.OptionsByType; + +import com.oracle.bedrock.options.Timeout; + +import com.oracle.bedrock.runtime.LocalPlatform; +import com.oracle.bedrock.runtime.MetaClass; +import com.oracle.bedrock.runtime.Profile; + +import com.oracle.bedrock.runtime.coherence.CoherenceCluster; +import com.oracle.bedrock.runtime.coherence.CoherenceClusterMember; + +import com.oracle.bedrock.runtime.coherence.options.CacheConfig; +import com.oracle.bedrock.runtime.coherence.options.LocalStorage; +import com.oracle.bedrock.runtime.coherence.options.RoleName; + +import com.oracle.bedrock.table.Cell; +import com.oracle.bedrock.table.Table; + +import com.oracle.coherence.common.base.Exceptions; + +import com.tangosol.net.Coherence; +import com.tangosol.net.CoherenceConfiguration; +import com.tangosol.net.SessionConfiguration; + +import java.util.Properties; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A base class for {@link CoherenceBuilder} implementations. + * + * @author Jonathan Knight 2022.06.25 + * @since 22.06 + */ +public abstract class AbstractCoherenceBuilder + implements CoherenceBuilder + { + /** + * The {@link Logger} for this class. + */ + private static final Logger LOGGER = Logger.getLogger(AbstractCoherenceBuilder.class.getName()); + + @Override + @SuppressWarnings("resource") + public Coherence build(LocalPlatform platform, CoherenceCluster cluster, OptionsByType optionsByType) + { + // ----- establish the diagnostics output table ----- + + Table diagnosticsTable = new Table(); + + diagnosticsTable.getOptions().add(Table.orderByColumn(0)); + + // establish a new set of options based on those provided + optionsByType = OptionsByType.of(optionsByType); + + // ----- establish the options for launching a local storage-disabled member ----- + optionsByType.add(RoleName.of("client")); + optionsByType.add(LocalStorage.disabled()); + optionsByType.addIfAbsent(CacheConfig.of("coherence-cache-config.xml")); + + // ----- notify the Profiles that we're about to launch an application ----- + + MetaClass metaClass = new CoherenceClusterMember.MetaClass(); + + for (Profile profile : optionsByType.getInstancesOf(Profile.class)) + { + profile.onLaunching(platform, metaClass, optionsByType); + } + + // ----- create local system properties based on those defined by the launch options ----- + + // modify the current system properties to include/override those in the schema + com.oracle.bedrock.runtime.java.options.SystemProperties systemProperties = + optionsByType.get(com.oracle.bedrock.runtime.java.options.SystemProperties.class); + + Properties properties = systemProperties.resolve(platform, optionsByType); + + Table systemPropertiesTable = new Table(); + + systemPropertiesTable.getOptions().add(Table.orderByColumn(0)); + systemPropertiesTable.getOptions().add(Cell.Separator.of("")); + systemPropertiesTable.getOptions().add(Cell.DisplayNull.asEmptyString()); + + Coherence.Mode mode = getMode(); + + SessionConfiguration.Builder sessionBuilder = SessionConfiguration.builder() + .withMode(mode) + .withConfigUri(optionsByType.get(CacheConfig.class).getUri()); + + for (String propertyName : properties.stringPropertyNames()) + { + String propertyValue = properties.getProperty(propertyName); + + systemPropertiesTable.addRow(propertyName + (System.getProperties().containsKey(propertyName) ? "*" : ""), + propertyValue); + + System.setProperty(propertyName, propertyValue.isEmpty() ? "" : propertyValue); + sessionBuilder.withParameter(propertyName, propertyValue.isEmpty() ? "" : propertyValue); + } + + diagnosticsTable.addRow("System Properties", systemPropertiesTable.toString()); + + // ----- output the diagnostics ----- + + if (LOGGER.isLoggable(Level.INFO)) + { + LOGGER.log(Level.INFO, + "Oracle Bedrock " + Bedrock.getVersion() + ": Starting Storage Disabled Member...\n" + + "------------------------------------------------------------------------\n" + + diagnosticsTable + "\n" + + "------------------------------------------------------------------------\n"); + } + + CoherenceConfiguration configuration = CoherenceConfiguration.builder() + .withSession(sessionBuilder.build()) + .build(); + + Coherence coherence; + + Timeout timeout = optionsByType.get(Timeout.class); + try + { + coherence = Coherence.builder(configuration, mode).build() + .start() + .get(timeout.to(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); + } + catch (InterruptedException | ExecutionException | TimeoutException e) + { + throw Exceptions.ensureRuntimeException(e, "Failed to start Coherence"); + } + + return coherence; + } + + /** + * Return the mode the Coherence instance and default session will run in. + * + * @return the mode the Coherence instance and default session will run in + */ + protected abstract Coherence.Mode getMode(); + + @Override + public boolean equals(Object other) + { + return other instanceof AbstractCoherenceBuilder; + } + + + @Override + public int hashCode() + { + return AbstractCoherenceBuilder.class.hashCode(); + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherence.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherence.java new file mode 100644 index 0000000000000..9b07447ec15e4 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherence.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.junit; + +import com.tangosol.net.Coherence; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A JUnit 5 extension annotation that bootstraps a local Coherence cluster member + * in the{@link BeforeAllCallback#beforeAll(ExtensionContext)} method and shuts-down + * Coherence in the {@link AfterAllCallback#afterAll(ExtensionContext)} method. + * + * @author Jonathan Knight 2022.06.23 + * @since 22.06 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(BootstrapCoherenceExtension.class) +public @interface BootstrapCoherence + { + /** + * The cache configuration to use. + * + * @return the cache configuration to use + */ + String config() default ""; + + /** + * The mode to bootstrap Coherence in. + * + * @return the mode to bootstrap Coherence in + */ + Coherence.Mode mode() default Coherence.Mode.ClusterMember; + + /** + * Properties to pass to the default session builder, + * as key value pairs in the format {code "key=value"}. + * + * @return properties to pass to the default session builder + */ + String[] properties() default {}; + + /** + * The maximum amount of time to wait for Coherence to start. + * + * @return the maximum amount of time to wait for Coherence to start + */ + int timeout() default DEFAULT_TIMEOUT; + + /** + * The default maximum amount of time to wait for Coherence to start. + */ + int DEFAULT_TIMEOUT = 300000; + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + @interface Session + { + } + + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + @interface Cache + { + String name(); + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherenceExtension.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherenceExtension.java new file mode 100644 index 0000000000000..fdd30c64a927b --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/BootstrapCoherenceExtension.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.junit; + +import com.tangosol.net.Coherence; + +import com.tangosol.net.CoherenceConfiguration; +import com.tangosol.net.Session; +import com.tangosol.net.SessionConfiguration; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import java.util.concurrent.TimeUnit; + +/** + * A JUnit 5 extension that bootstraps a local Coherence cluster member in the + * {@link BeforeAllCallback#beforeAll(ExtensionContext)} method and shuts down + * Coherence in the {@link AfterAllCallback#afterAll(ExtensionContext)} method. + * + * @author Jonathan Knight 2022.06.23 + * @since 22.06 + */ +public class BootstrapCoherenceExtension + implements BeforeAllCallback, AfterAllCallback + { + @Override + @SuppressWarnings("resource") + public void beforeAll(ExtensionContext context) throws Exception + { + Class clz = context.getTestClass().orElse(null); + BootstrapCoherence annotation = clz == null ? null : clz.getAnnotation(BootstrapCoherence.class); + SessionConfiguration sessionConfiguration = createSessionConfiguration(annotation); + + CoherenceConfiguration configuration = CoherenceConfiguration.builder() + .withSession(sessionConfiguration) + .build(); + + int cMillis = annotation == null ? BootstrapCoherence.DEFAULT_TIMEOUT : annotation.timeout(); + Coherence coherence = Coherence.builder(configuration) + .build() + .start() + .get(cMillis, TimeUnit.MILLISECONDS); + + if (clz != null) + { + Session session = coherence.getSession(); + for (Field field : clz.getDeclaredFields()) + { + int nMods = field.getModifiers(); + + BootstrapCoherence.Session fieldSession = field.getAnnotation(BootstrapCoherence.Session.class); + if (fieldSession != null) + { + if (Modifier.isPublic(nMods) && Modifier.isStatic(nMods)) + { + field.set(null, session); + } + else + { + throw new RuntimeException("Cannot inject Session into field " + field.getName() + " - must be public static"); + } + } + BootstrapCoherence.Cache fieldCache = field.getAnnotation(BootstrapCoherence.Cache.class); + if (fieldCache != null) + { + String sName = fieldCache.name(); + if (!sName.isBlank()) + { + if (Modifier.isPublic(nMods) && Modifier.isStatic(nMods)) + { + field.set(null, session.getCache(sName)); + } + else + { + throw new RuntimeException("Cannot inject cache \"" + sName + "\" into field \"" + field.getName() + "\" - must be public static"); + } + } + } + } + } + } + + @Override + public void afterAll(ExtensionContext context) + { + Coherence.closeAll(); + } + + private SessionConfiguration createSessionConfiguration(BootstrapCoherence annotation) + { + SessionConfiguration.Builder builder = SessionConfiguration.builder(); + + if (annotation != null) + { + builder.withMode(annotation.mode()); + + String sConfig = annotation.config(); + if (!sConfig.isBlank()) + { + builder.withConfigUri(sConfig); + } + + for (String sProperty : annotation.properties()) + { + int nIndex = sProperty.indexOf('='); + if (nIndex > 0) + { + String sKey = sProperty.substring(0, nIndex); + String sValue = sProperty.substring(nIndex + 1); + builder.withParameter(sKey, sValue); + } + } + } + + return builder.build(); + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceBuilder.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceBuilder.java new file mode 100644 index 0000000000000..8ccf1cc07dcde --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceBuilder.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.junit; + +import com.oracle.bedrock.Option; +import com.oracle.bedrock.OptionsByType; +import com.oracle.bedrock.runtime.LocalPlatform; +import com.oracle.bedrock.runtime.coherence.CoherenceCluster; +import com.oracle.bedrock.runtime.coherence.CoherenceClusterMember; +import com.tangosol.net.Coherence; +import com.tangosol.net.ConfigurableCacheFactory; + +/** + * A builder that produces {@link Coherence} instances. + * + * @author Jonathan Knight 2022.06.25 + * @since 22.06 + */ +public interface CoherenceBuilder + { + /** + * Creates a {@link Coherence} instance with a single default {@link com.tangosol.net.Session}. + * + * @param platform the {@link LocalPlatform} on which the {@link ConfigurableCacheFactory} will be established + * @param cluster the {@link CoherenceCluster} for which the session will be created + * @param optionsByType the {@link OptionsByType}s provided to all of the {@link CoherenceClusterMember}s + * when establishing the {@link CoherenceCluster} + * @return a {@link Coherence} instance + */ + Coherence build(LocalPlatform platform, CoherenceCluster cluster, OptionsByType optionsByType); + + /** + * Creates a {@link Coherence} instance with a single default {@link com.tangosol.net.Session}. + * + * @param platform the {@link LocalPlatform} on which the {@link ConfigurableCacheFactory} will be established + * @param cluster the {@link CoherenceCluster} for which the session will be created + * @param options the {@link OptionsByType}s provided to all of the {@link CoherenceClusterMember}s + * when establishing the {@link CoherenceCluster} + * @return a {@link Coherence} instance + */ + default Coherence build(LocalPlatform platform, CoherenceCluster cluster, Option... options) + { + return build(platform, cluster, OptionsByType.of(options)); + } + + /** + * Returns a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a cluster member. + * + * @return a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a cluster member + */ + static CoherenceBuilder clusterMember() + { + return withMode(Coherence.Mode.ClusterMember); + } + + /** + * Returns a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a client. + * + * @return a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a client + */ + static CoherenceBuilder client() + { + return withMode(Coherence.Mode.Client); + } + + /** + * Returns a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a client. + * + * @return a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a client + */ + static CoherenceBuilder fixedClient() + { + return withMode(Coherence.Mode.ClientFixed); + } + + /** + * Returns a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a cluster member. + * + * @return a {@link CoherenceBuilder} that builds a {@link Coherence} instance + * that is a cluster member + */ + static CoherenceBuilder withMode(Coherence.Mode mode) + { + if (mode == null) + { + throw new IllegalArgumentException("The mode parameter cannot be null"); + } + + return new AbstractCoherenceBuilder() + { + @Override + protected Coherence.Mode getMode() + { + return mode; + } + }; + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterExtension.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterExtension.java index 23dafd85083ca..24c2a68c59539 100644 --- a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterExtension.java +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterExtension.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; @@ -58,9 +58,8 @@ public CoherenceClusterExtension() this.sessions = new HashMap<>(); // establish default java process options - this.commonOptionsByType.add(Headless.enabled()); - this.commonOptionsByType.add(HotSpot.Mode.SERVER); - this.commonOptionsByType.add(HeapSize.of(256, HeapSize.Units.MB, 1024, HeapSize.Units.MB)); + commonOptionsByType.add(Headless.enabled()); + commonOptionsByType.add(HotSpot.Mode.SERVER); // establish default bedrock options this.commonOptionsByType.add(Console.system()); @@ -182,4 +181,14 @@ public synchronized ConfigurableCacheFactory createSession(SessionBuilder builde return session; } + + /** + * Return the common options for this {@link CoherenceClusterExtension}. + * + * @return the common options for this {@link CoherenceClusterExtension} + */ + public Option[] getCommonOptions() + { + return commonOptionsByType.asArray(); + } } diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterResource.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterResource.java index 3520ff24b4b81..88330447c298f 100644 --- a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterResource.java +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/CoherenceClusterResource.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; @@ -10,7 +10,6 @@ import com.oracle.bedrock.Option; import com.oracle.bedrock.OptionsByType; import com.oracle.bedrock.runtime.Application; -import com.oracle.bedrock.runtime.AssemblyBuilder; import com.oracle.bedrock.runtime.LocalPlatform; import com.oracle.bedrock.runtime.Platform; import com.oracle.bedrock.runtime.coherence.CoherenceCluster; @@ -25,54 +24,47 @@ import com.oracle.bedrock.runtime.options.Console; import com.oracle.bedrock.runtime.options.PlatformPredicate; import com.oracle.bedrock.testsupport.junit.AbstractAssemblyResource; +import com.oracle.bedrock.util.SystemProperties; +import com.oracle.coherence.common.collections.ConcurrentHashMap; +import com.tangosol.internal.net.ConfigurableCacheFactorySession; import com.tangosol.net.CacheFactory; import com.tangosol.net.ConfigurableCacheFactory; +import com.tangosol.net.Session; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.util.HashMap; +import java.util.Map; import java.util.Properties; public class CoherenceClusterResource - extends AbstractAssemblyResource -{ - /** - * The system properties prior to the creation of the {@link CoherenceClusterResource }. - */ - private Properties systemProperties; - - /** - * The {@link ConfigurableCacheFactory} sessions that have been locally created against the - * {@link CoherenceCluster} using this {@link CoherenceClusterResource}. - */ - private HashMap sessions; - - + extends AbstractAssemblyResource + { /** * Constructs a {@link CoherenceClusterResource}. */ public CoherenceClusterResource() - { + { super(); - // by default we have no sessions - this.sessions = new HashMap<>(); + // by default, we have no sessions + f_mapCCF = new HashMap<>(); + f_mapSession = new ConcurrentHashMap<>(); // establish default java process options this.commonOptionsByType.add(Headless.enabled()); this.commonOptionsByType.add(HotSpot.Mode.SERVER); - this.commonOptionsByType.add(HeapSize.of(256, HeapSize.Units.MB, 1024, HeapSize.Units.MB)); // establish default bedrock options this.commonOptionsByType.add(Console.system()); - } + } @Override protected CoherenceClusterBuilder createBuilder() - { + { return new CoherenceClusterBuilder(); - } + } /** @@ -81,43 +73,43 @@ protected CoherenceClusterBuilder createBuilder() * @return the {@link CoherenceCluster} */ public CoherenceCluster getCluster() - { + { return assembly; - } + } @Override - protected void before() throws Throwable - { - if (launchDefinitions.isEmpty()) + public void before() throws Throwable { + if (launchDefinitions.isEmpty()) + { throw new IllegalStateException("CoherenceClusterResource fails to define members to include when launching"); - } + } - // take a snapshot of the current system properties so we can restore them when cleaning up the resource - this.systemProperties = com.oracle.bedrock.util.SystemProperties.createSnapshot(); + // take a snapshot of the current system properties, so we can restore them when cleaning up the resource + this.m_systemProperties = com.oracle.bedrock.util.SystemProperties.createSnapshot(); // let's ensure that we don't have a local cluster member CacheFactory.setCacheFactoryBuilder(null); CacheFactory.shutdown(); - // let the super-class perform it's initialization + // let the super-class perform its initialization super.before(); - } + } + - @Override - protected void after() - { - // clean up the sessions - synchronized (sessions) + public void after() { - for (ConfigurableCacheFactory session : sessions.values()) + // clean up the sessions + synchronized (f_mapCCF) { + for (ConfigurableCacheFactory session : f_mapCCF.values()) + { CacheFactory.getCacheFactoryBuilder().release(session); + } } - } // let's ensure that we don't have a local cluster member CacheFactory.setCacheFactoryBuilder(null); @@ -128,20 +120,21 @@ protected void after() super.after(); // restore the system properties - com.oracle.bedrock.util.SystemProperties.replaceWith(systemProperties); - } + com.oracle.bedrock.util.SystemProperties.replaceWith(m_systemProperties); + } @Override - public Statement apply(Statement base, - Description description) - { + public Statement apply( + Statement base, + Description description) + { // automatically set the cluster name to the test class name // if the cluster name isn't configured commonOptionsByType.addIfAbsent(ClusterName.of(description.getClassName())); return super.apply(base, description); - } + } /** @@ -149,7 +142,7 @@ public Statement apply(Statement base, * as part of the {@link CoherenceCluster} when the {@link CoherenceClusterResource} is established. *

* The {@link Platform} on which the {@link CoherenceClusterMember}s are launched is based on the - * {@link PlatformPredicate} specified as an {@link Option}. By default this is {@link PlatformPredicate#any()}. + * {@link PlatformPredicate} specified as an {@link Option}. By default, this is {@link PlatformPredicate#any()}. *

* Multiple calls to this method are permitted, allowing a {@link CoherenceCluster} to be created containing * multiple different types of {@link CoherenceCluster}s. @@ -157,52 +150,124 @@ public Statement apply(Statement base, * This is equivalent to calling {@link #include(int, Class, Option...)} using a {@link CoherenceClusterMember} * class as the {@link Application} class. * - * @param count the number of instances of the {@link CoherenceCluster} that should be launched for - * the {@link CoherenceCluster} - * @param options the {@link Option}s to use for launching the {@link CoherenceCluster}s - * + * @param count the number of instances of the {@link CoherenceCluster} that should be launched for + * the {@link CoherenceCluster} + * @param options the {@link Option}s to use for launching the {@link CoherenceCluster}s * @return the {@link CoherenceClusterResource} to permit fluent-style method calls */ - public CoherenceClusterResource include(int count, - Option... options) - { + public CoherenceClusterResource include( + int count, + Option... options) + { return include(count, CoherenceClusterMember.class, options); - } + } /** * Obtains a session, represented as a {@link ConfigurableCacheFactory}, against the {@link CoherenceCluster}. *

- * Only a single session may be created by a {@link CoherenceClusterResource} against the {@link CoherenceCluster}. - *

- * Attempts to request a session multiple times with the same {@link SessionBuilder} will return the same session. + * Attempts to request a {@link ConfigurableCacheFactory} multiple times with the same {@link SessionBuilder} + * will return the same {@link ConfigurableCacheFactory}. * - * @param builder the builder for the specific type of session + * @param builder the builder for the specific type of session * - * @return a {@link ConfigurableCacheFactory} representing the Coherence Session. - * - * @throws IllegalStateException when an attempt to request sessions for - * different {@link SessionBuilder}s is made + * @return a {@link ConfigurableCacheFactory} representing the Coherence Session */ public synchronized ConfigurableCacheFactory createSession(SessionBuilder builder) - { - // restore the system properties (as the session needs to start cleanly) - com.oracle.bedrock.util.SystemProperties.replaceWith(systemProperties); + { + return f_mapCCF.compute(builder, (k, ccf) -> + { + if (ccf != null && !ccf.isDisposed()) + { + return ccf; + } - ConfigurableCacheFactory session = sessions.get(builder); + // restore the system properties (as the session needs to start cleanly) + SystemProperties.replaceWith(m_systemProperties); - if (session == null) - { OptionsByType optionsByType = OptionsByType.of(commonOptionsByType); optionsByType.add(RoleName.of("client")); optionsByType.add(LocalStorage.disabled()); - session = builder.build(LocalPlatform.get(), getCluster(), optionsByType); + return builder.build(LocalPlatform.get(), getCluster(), optionsByType); + }); + } - sessions.put(builder, session); + /** + * Obtains a {@link Session}, against the {@link CoherenceCluster}. + *

+ * Attempts to request a session multiple times with the same {@link SessionBuilder} will return the same session. + * + * @param builder the builder for the specific type of session + * + * @return a {@link Session} + */ + public synchronized Session buildSession(SessionBuilder builder) + { + return f_mapSession.compute(builder, (k, session) -> + { + if (session != null && session.isActive()) + { + return session; + } + ConfigurableCacheFactory ccf = createSession(builder); + return new ClosingConfigurableCacheFactorySession(ccf, builder.getClass().getClassLoader()); + }); } - return session; + /** + * Return the common options for this {@link CoherenceClusterResource}. + * + * @return the common options for this {@link CoherenceClusterResource} + */ + public Option[] getCommonOptions() + { + return commonOptionsByType.asArray(); + } + + // ----- ClosingConfigurableCacheFactorySession ------------------------- + + protected static class ClosingConfigurableCacheFactorySession + extends ConfigurableCacheFactorySession + { + public ClosingConfigurableCacheFactorySession(ConfigurableCacheFactory ccf, ClassLoader loader) + { + super(ccf, loader); + } + + @Override + public synchronized void close() throws Exception + { + if (isClosed()) + { + return; + } + super.close(); + ConfigurableCacheFactory ccf = getConfigurableCacheFactory(); + if (!ccf.isDisposed()) + { + ccf.dispose(); + } + } + } + + // ----- data members --------------------------------------------------- + + /** + * The system properties prior to the creation of the {@link CoherenceClusterResource }. + */ + private Properties m_systemProperties; + + /** + * The {@link ConfigurableCacheFactory} sessions that have been locally created against the + * {@link CoherenceCluster} using this {@link CoherenceClusterResource}. + */ + private final Map f_mapCCF; + + /** + * The {@link ConfigurableCacheFactory} sessions that have been locally created against the + * {@link CoherenceCluster} using this {@link CoherenceClusterResource}. + */ + private final Map f_mapSession; } -} diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SessionBuilders.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SessionBuilders.java index 5e47d0e465edd..1aa6d72b1bea6 100644 --- a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SessionBuilders.java +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SessionBuilders.java @@ -2,7 +2,7 @@ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; @@ -10,41 +10,49 @@ import com.oracle.bedrock.Option; public class SessionBuilders -{ + { /** * Constructs a {@link SessionBuilder} for a Storage Disabled Member. * * @return a {@link SessionBuilder} */ public static SessionBuilder storageDisabledMember() - { + { return new StorageDisabledMember(); - } + } + /** + * Constructs a {@link SessionBuilder} for a Storage Disabled Member. + * + * @param options the additional options to use to create the session + * + * @return a {@link SessionBuilder} + */ + public static SessionBuilder storageDisabledMember(Option... options) + { + return new StorageDisabledMember(options); + } /** * Constructs a {@link SessionBuilder} for a *Extend Client. * - * @param cacheConfigURI the Cache Configuration URI - * + * @param cacheConfigURI the Cache Configuration URI * @return a {@link SessionBuilder} */ public static SessionBuilder extendClient(String cacheConfigURI) - { + { return new ExtendClient(cacheConfigURI); - } - + } /** * Constructs a {@link SessionBuilder} for a *Extend Client. * - * @param cacheConfigURI the Cache Configuration URI - * @param options additional options to configure the client - * + * @param cacheConfigURI the Cache Configuration URI + * @param options additional options to configure the client * @return a {@link SessionBuilder} */ public static SessionBuilder extendClient(String cacheConfigURI, Option... options) - { + { return new ExtendClient(cacheConfigURI, options); + } } -} diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/StorageDisabledMember.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/StorageDisabledMember.java index 4821b1300aea4..715e509396da9 100644 --- a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/StorageDisabledMember.java +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/StorageDisabledMember.java @@ -2,12 +2,13 @@ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; import com.oracle.bedrock.Bedrock; +import com.oracle.bedrock.Option; import com.oracle.bedrock.OptionsByType; import com.oracle.bedrock.runtime.LocalPlatform; import com.oracle.bedrock.runtime.MetaClass; @@ -27,19 +28,22 @@ import java.util.logging.Level; import java.util.logging.Logger; -public class StorageDisabledMember implements SessionBuilder -{ - /** - * The {@link Logger} for this class. - */ - private static final Logger LOGGER = Logger.getLogger(StorageDisabledMember.class.getName()); +public class StorageDisabledMember + implements SessionBuilder + { + public StorageDisabledMember() + { + this(new Option[0]); + } + public StorageDisabledMember(Option[] aOption) + { + m_optionsByType = OptionsByType.of(aOption); + } @Override - public ConfigurableCacheFactory build(LocalPlatform platform, - CoherenceCluster cluster, - OptionsByType optionsByType) - { + public ConfigurableCacheFactory build(LocalPlatform platform, CoherenceCluster cluster, OptionsByType optionsByType) + { // ----- establish the diagnostics output table ----- Table diagnosticsTable = new Table(); @@ -52,6 +56,9 @@ public ConfigurableCacheFactory build(LocalPlatform platform, // ----- establish the options for launching a local storage-disabled member ----- optionsByType.add(RoleName.of("client")); optionsByType.add(LocalStorage.disabled()); + + optionsByType.addAll(m_optionsByType); + optionsByType.addIfAbsent(CacheConfig.of("coherence-cache-config.xml")); // ----- notify the Profiles that we're about to launch an application ----- @@ -59,71 +66,79 @@ public ConfigurableCacheFactory build(LocalPlatform platform, MetaClass metaClass = new CoherenceClusterMember.MetaClass(); for (Profile profile : optionsByType.getInstancesOf(Profile.class)) - { + { profile.onLaunching(platform, metaClass, optionsByType); - } + } // ----- create local system properties based on those defined by the launch options ----- // modify the current system properties to include/override those in the schema com.oracle.bedrock.runtime.java.options.SystemProperties systemProperties = - optionsByType.get(com.oracle.bedrock.runtime.java.options.SystemProperties.class); + optionsByType.get(com.oracle.bedrock.runtime.java.options.SystemProperties.class); - Properties properties = systemProperties.resolve(platform, optionsByType); + Properties properties = systemProperties.resolve(platform, optionsByType); - Table systemPropertiesTable = new Table(); + Table systemPropertiesTable = new Table(); systemPropertiesTable.getOptions().add(Table.orderByColumn(0)); systemPropertiesTable.getOptions().add(Cell.Separator.of("")); systemPropertiesTable.getOptions().add(Cell.DisplayNull.asEmptyString()); - for (String propertyName : properties.stringPropertyNames()) - { - String propertyValue = properties.getProperty(propertyName); - - systemPropertiesTable.addRow(propertyName + (System.getProperties().containsKey(propertyName) ? "*" : ""), - propertyValue); + for (String sName : properties.stringPropertyNames()) + { + String sValue = properties.getProperty(sName); + String sSuffix = System.getProperties().containsKey(sName) ? "*" : ""; + systemPropertiesTable.addRow(sName + sSuffix, sValue); - System.setProperty(propertyName, propertyValue.isEmpty() ? "" : propertyValue); - } + System.setProperty(sName, sValue); + } diagnosticsTable.addRow("System Properties", systemPropertiesTable.toString()); // ----- output the diagnostics ----- if (LOGGER.isLoggable(Level.INFO)) - { + { LOGGER.log(Level.INFO, "Oracle Bedrock " + Bedrock.getVersion() + ": Starting Storage Disabled Member...\n" - + "------------------------------------------------------------------------\n" - + diagnosticsTable.toString() + "\n" - + "------------------------------------------------------------------------\n"); - } - + + "------------------------------------------------------------------------\n" + + diagnosticsTable + "\n" + + "------------------------------------------------------------------------\n"); + } + // ----- establish the session ----- // create the session - ConfigurableCacheFactory session = - new ScopedCacheFactoryBuilder().getConfigurableCacheFactory(optionsByType.get(CacheConfig.class).getUri(), - getClass().getClassLoader()); + String uriConfig = optionsByType.get(CacheConfig.class).getUri(); + ClassLoader loader = getClass().getClassLoader(); + ConfigurableCacheFactory session = new ScopedCacheFactoryBuilder().getConfigurableCacheFactory(uriConfig, loader); // as this is a cluster member we have to join the cluster CacheFactory.ensureCluster(); return session; - } + } @Override public boolean equals(Object other) - { + { return other instanceof StorageDisabledMember; - } + } @Override public int hashCode() - { + { return StorageDisabledMember.class.hashCode(); + } + + // ----- data members --------------------------------------------------- + + /** + * The {@link Logger} for this class. + */ + private static final Logger LOGGER = Logger.getLogger(StorageDisabledMember.class.getName()); + + private final OptionsByType m_optionsByType; } -} diff --git a/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SystemPropertyExtension.java b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SystemPropertyExtension.java new file mode 100644 index 0000000000000..cb8b14fa28cb1 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock-testing-support/src/main/java/com/oracle/bedrock/junit/SystemPropertyExtension.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.junit; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * A JUnit 5 extension that sets System properties in the + * {@link BeforeAllCallback before all phase}. + *

+ * By using this extension with {@link org.junit.jupiter.api.Order @Order} + * after other extensions, it is possible to configures properties based on + * the state of other extensions before tests run. + * + * @author Jonathan Knight 2022.06.20 + * @since 22.06 + */ +public class SystemPropertyExtension + implements BeforeAllCallback + { + @Override + public void beforeAll(ExtensionContext context) throws Exception + { + properties.forEach((k, v) -> + { + Object o = v.get(); + if (o != null) + { + System.setProperty(k, String.valueOf(o)); + } + }); + } + + /** + * Add a property to set. + *

+ * The {@code toString()} of the value returned by the supplier will be used + * as the property value. + *

+ * If the {@link Supplier} returns {@code null} then no property will be set. + * + * @param key the key of the property + * @param value a supplier for the property value + * + * @return this builder + */ + public SystemPropertyExtension withProperty(String key, Supplier value) + { + properties.put(key, value); + return this; + } + + /** + * The {@link Map} of properties to set. + */ + private final Map> properties = new HashMap<>(); + } diff --git a/prj/coherence-bedrock/coherence-bedrock-tests-junit4/src/test/java/com/oracle/bedrock/junit/CoherenceClusterResourceTest.java b/prj/coherence-bedrock/coherence-bedrock-tests-junit4/src/test/java/com/oracle/bedrock/junit/CoherenceClusterResourceTest.java index 0248d383aa4b5..919d3ad36645e 100644 --- a/prj/coherence-bedrock/coherence-bedrock-tests-junit4/src/test/java/com/oracle/bedrock/junit/CoherenceClusterResourceTest.java +++ b/prj/coherence-bedrock/coherence-bedrock-tests-junit4/src/test/java/com/oracle/bedrock/junit/CoherenceClusterResourceTest.java @@ -2,7 +2,7 @@ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; @@ -44,6 +44,7 @@ public class CoherenceClusterResourceTest .with(LocalHost.only(), Multicast.ttl(0), ClusterPort.automatic(), + SystemProperty.of("coherence.mode", System.getProperty("coherence.mode", "dev")), SystemProperty.of("tangosol.coherence.extend.address", LocalPlatform.get().getLoopbackAddress().getHostAddress()), SystemProperty.of("tangosol.coherence.extend.port", Capture.of(LocalPlatform.get().getAvailablePorts()))) .include(2, diff --git a/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/junit/CoherenceClusterExtensionTest.java b/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/junit/CoherenceClusterExtensionTest.java index fb3f1a5474685..3875187824ad7 100644 --- a/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/junit/CoherenceClusterExtensionTest.java +++ b/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/junit/CoherenceClusterExtensionTest.java @@ -2,7 +2,7 @@ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.junit; @@ -43,6 +43,7 @@ public class CoherenceClusterExtensionTest .with(LocalHost.only(), Multicast.ttl(0), ClusterPort.automatic(), + SystemProperty.of("coherence.mode", System.getProperty("coherence.mode", "dev")), SystemProperty.of("tangosol.coherence.extend.address", LocalPlatform.get().getLoopbackAddress().getHostAddress()), SystemProperty.of("tangosol.coherence.extend.port", Capture.of(LocalPlatform.get().getAvailablePorts()))) .include(2, diff --git a/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/runtime/coherence/CoherenceSessionIT.java b/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/runtime/coherence/CoherenceSessionIT.java index e993485460999..ad69ce2d12c27 100644 --- a/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/runtime/coherence/CoherenceSessionIT.java +++ b/prj/coherence-bedrock/coherence-bedrock-tests-junit5/src/test/java/com/oracle/bedrock/runtime/coherence/CoherenceSessionIT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -17,6 +17,7 @@ import com.oracle.bedrock.runtime.coherence.options.ClusterPort; import com.oracle.bedrock.runtime.coherence.options.LocalHost; +import com.oracle.bedrock.runtime.coherence.options.WellKnownAddress; import com.oracle.bedrock.testsupport.deferred.Eventually; import com.oracle.bedrock.testsupport.junit.AbstractTest; @@ -45,6 +46,7 @@ public void shouldGetDefaultSession() try (CoherenceCacheServer server = platform.launch(CoherenceCacheServer.class, ClusterPort.automatic(), LocalHost.only(), + WellKnownAddress.loopback(), m_testLogs)) { Eventually.assertThat(server, new GetLocalMemberId(), is(1)); @@ -66,6 +68,7 @@ public void shouldGetCacheFromDefaultSession() try (CoherenceCacheServer server = platform.launch(CoherenceCacheServer.class, ClusterPort.automatic(), LocalHost.only(), + WellKnownAddress.loopback(), m_testLogs)) { Eventually.assertThat(server, new GetLocalMemberId(), is(1)); @@ -94,6 +97,7 @@ public void shouldGetSystemSession() try (CoherenceCacheServer server = platform.launch(CoherenceCacheServer.class, ClusterPort.automatic(), LocalHost.only(), + WellKnownAddress.loopback(), m_testLogs)) { Eventually.assertThat(server, new GetLocalMemberId(), is(1)); @@ -116,11 +120,13 @@ public void shouldGetCacheFromSystemSession() try (CoherenceCacheServer server = platform.launch(CoherenceCacheServer.class, ClusterPort.automatic(), LocalHost.only(), + WellKnownAddress.loopback(), m_testLogs)) { Eventually.assertThat(server, new GetLocalMemberId(), is(1)); Eventually.assertThat(server, new GetClusterSize(), is(1)); Eventually.assertThat(server, new SessionExists(Coherence.SYSTEM_SESSION), is(true)); + Eventually.assertThat(server, new SessionExists(Coherence.DEFAULT_NAME), is(true)); Session session = server.getSession(Coherence.SYSTEM_SESSION); assertThat(session, is(notNullValue())); diff --git a/prj/coherence-bedrock/coherence-bedrock/pom.xml b/prj/coherence-bedrock/coherence-bedrock/pom.xml index 4ce39c3e8b95f..0ebf0371cafb8 100755 --- a/prj/coherence-bedrock/coherence-bedrock/pom.xml +++ b/prj/coherence-bedrock/coherence-bedrock/pom.xml @@ -1,6 +1,6 @@ + + org.apache.felix + maven-bundle-plugin + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + org.apache.maven.plugins @@ -113,42 +129,26 @@ - release-coherence + release-modules org.apache.maven.plugins maven-gpg-plugin - ${maven.gpg.plugin.version} false - - - sign-artifacts - verify - - sign - - - org.sonatype.plugins nexus-staging-maven-plugin - ${nexus.staging.maven.plugin.version} - true - ossrh - https://oss.sonatype.org/ - true false - true - \ No newline at end of file + diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/AbstractCoherenceClusterMember.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/AbstractCoherenceClusterMember.java index e6bb2ce2eb368..43cf8d32e184f 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/AbstractCoherenceClusterMember.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/AbstractCoherenceClusterMember.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -11,27 +11,7 @@ import com.oracle.bedrock.runtime.Application; import com.oracle.bedrock.runtime.ApplicationProcess; import com.oracle.bedrock.runtime.Platform; -import com.oracle.bedrock.runtime.coherence.callables.GetClusterMemberUIDs; -import com.oracle.bedrock.runtime.coherence.callables.GetClusterName; -import com.oracle.bedrock.runtime.coherence.callables.GetClusterSize; -import com.oracle.bedrock.runtime.coherence.callables.GetExtendConnectionCount; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberId; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberMachineName; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberName; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberRackName; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberRoleName; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberSiteName; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberUID; -import com.oracle.bedrock.runtime.coherence.callables.GetLocalMemberUUID; -import com.oracle.bedrock.runtime.coherence.callables.GetServiceStatus; -import com.oracle.bedrock.runtime.coherence.callables.GetSessionCache; -import com.oracle.bedrock.runtime.coherence.callables.HasExtendConnection; -import com.oracle.bedrock.runtime.coherence.callables.IsCoherenceRunning; -import com.oracle.bedrock.runtime.coherence.callables.IsReady; -import com.oracle.bedrock.runtime.coherence.callables.IsSafe; -import com.oracle.bedrock.runtime.coherence.callables.IsServiceRunning; -import com.oracle.bedrock.runtime.coherence.callables.IsServiceStorageEnabled; -import com.oracle.bedrock.runtime.coherence.callables.SessionExists; +import com.oracle.bedrock.runtime.coherence.callables.*; import com.oracle.bedrock.runtime.java.AbstractJavaApplication; import com.oracle.bedrock.runtime.java.JavaApplication; import com.oracle.bedrock.runtime.java.JavaApplicationProcess; @@ -118,6 +98,13 @@ public int getClusterSize() } + @Override + public int getLocalMemberClusterPort() + { + return invoke(new GetLocalMemberClusterPort()); + } + + @Override public int getLocalMemberId() { @@ -339,14 +326,14 @@ public boolean isCoherenceRunning(String sName) @Override public boolean isSafe() { - return invoke(new IsSafe()); + return invoke(IsSafe.INSTANCE); } @Override public boolean isReady() { - return invoke(new IsReady()); + return invoke(IsReady.INSTANCE); } @@ -357,6 +344,12 @@ public Trilean isStorageEnabled(String serviceName) } + @Override + public Set getServiceNames() + { + return invoke(new GetServiceNames()); + } + @Override public ServiceStatus getServiceStatus(String serviceName) { @@ -376,4 +369,10 @@ public boolean hasExtendConnection(String sProxyName, UUID uuid) { return invoke(new HasExtendConnection(sProxyName, uuid)); } + + @Override + public void threadDump() + { + submit(LogThreadDump.INSTANCE); + } } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceCluster.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceCluster.java index 33c1fd307f655..615d65fe813e9 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceCluster.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceCluster.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -9,24 +9,39 @@ import com.oracle.bedrock.Option; import com.oracle.bedrock.OptionsByType; +import com.oracle.bedrock.deferred.DeferredHelper; +import com.oracle.bedrock.deferred.DeferredPredicate; +import com.oracle.bedrock.deferred.PermanentlyUnavailableException; import com.oracle.bedrock.options.Decoration; import com.oracle.bedrock.options.Decorations; import com.oracle.bedrock.runtime.AbstractAssembly; import com.oracle.bedrock.runtime.Assembly; import com.oracle.bedrock.runtime.coherence.callables.GetAutoStartServiceNames; import com.oracle.bedrock.runtime.coherence.callables.GetServiceStatus; +import com.oracle.bedrock.runtime.coherence.callables.IsCoherenceRunning; import com.oracle.bedrock.runtime.coherence.callables.IsReady; import com.oracle.bedrock.runtime.coherence.callables.IsSafe; import com.oracle.bedrock.runtime.coherence.callables.IsServiceStorageEnabled; import com.oracle.bedrock.runtime.concurrent.options.Caching; +import com.oracle.bedrock.runtime.concurrent.runnable.ThreadDump; +import com.oracle.bedrock.runtime.options.StabilityPredicate; import com.oracle.bedrock.util.Trilean; +import com.oracle.coherence.common.base.Logger; +import com.oracle.coherence.common.internal.util.HeapDump; +import com.tangosol.net.CacheFactory; +import com.tangosol.net.Coherence; import com.tangosol.net.NamedCache; import com.tangosol.util.UID; +import com.tangosol.util.function.Remote; +import java.io.File; +import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Set; -import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import static com.oracle.bedrock.deferred.DeferredHelper.ensure; @@ -89,6 +104,7 @@ public boolean isSafe() * * @return the current number of {@link CoherenceClusterMember}s */ + @SuppressWarnings("resource") public int getClusterSize() { Iterator members = iterator(); @@ -102,11 +118,12 @@ public int getClusterSize() * * @return a {@link Set} of {@link UID}, one for each {@link CoherenceClusterMember} */ + @SuppressWarnings("resource") public Set getClusterMemberUIDs() { Iterator members = iterator(); - return members.hasNext() ? members.next().getClusterMemberUIDs() : new TreeSet(); + return members.hasNext() ? members.next().getClusterMemberUIDs() : Collections.emptySet(); } @@ -117,6 +134,7 @@ public Set getClusterMemberUIDs() * @param cacheName the name of the {@link NamedCache} * @return a proxy to the {@link NamedCache} */ + @SuppressWarnings({"resource", "rawtypes"}) public NamedCache getCache(String cacheName) { Iterator members = iterator(); @@ -136,6 +154,7 @@ public NamedCache getCache(String cacheName) * @param the type of the value class * @return a proxy to the {@link NamedCache} */ + @SuppressWarnings("resource") public NamedCache getCache( String cacheName, Class keyClass, @@ -147,6 +166,36 @@ public NamedCache getCache( } + /** + * Initiates a heap dump across all members of the cluster. + */ + public void heapdump() + { + for (CoherenceClusterMember member : this) + { + if (member != null && member.getLocalMemberId() > 0) + { + String sDir = System.getProperty("test.project.dir"); + + if (sDir == null || sDir.isEmpty()) + { + try + { + sDir = new java.io.File(".").getCanonicalPath(); + } + catch (IOException ioe) + { + Logger.err("Failed to get canonical path of " + sDir, ioe); + } + } + final String sPath = sDir; + member.submit(()-> CacheFactory.log("### Dumping heap for analysis here : \n" + + HeapDump.dumpHeap(sPath + File.separatorChar + "target", true))); + } + } + } + + @Override protected void onRelaunching( CoherenceClusterMember member, @@ -195,6 +244,47 @@ protected void onRelaunched( onChanged(optionsByType); } + @Override + @SuppressWarnings({"rawtypes", "unchecked"}) + protected void onChanged(OptionsByType options) { + try + { + StabilityPredicate> stabilityPredicate = options.getOrDefault(StabilityPredicate.class, null); + if (stabilityPredicate != null) + { + DeferredPredicate deferredPredicate = new DeferredPredicate(this, stabilityPredicate.get()); + DeferredHelper.ensure(DeferredHelper.eventually(deferredPredicate), com.oracle.bedrock.predicate.Predicates.is(true), options.asArray()); + } + } + catch (PermanentlyUnavailableException e) + { + CoherenceClusterMember[] aMember = this.applications.toArray(CoherenceClusterMember[]::new); + CompletableFuture[] aFuture = new CompletableFuture[aMember.length]; + + for (int i = 0; i < aMember.length; i++) + { + try + { + aFuture[i] = aMember[i].submit(ThreadDump.toStdErr()); + } + catch (Exception ignored) + { + // ignored + } + } + + try + { + CompletableFuture.allOf(aFuture).get(2, TimeUnit.MINUTES); + } + catch (Exception ex) + { + System.err.println("Caught exception waiting for thread dumps to complete " + ex.getMessage()); + } + throw e; + } + } + /** * Useful {@link Predicate}s for a {@link CoherenceCluster}. @@ -202,15 +292,86 @@ protected void onRelaunched( public interface Predicates { /** - * A {@link Predicate} to determine if all of the services of + * A {@link Predicate} to determine if all the services of * {@link CoherenceClusterMember}s are safe. * * @return a {@link Predicate} */ static Predicate autoStartServicesSafe() { - return (cluster) -> { + return IsAutoStartServicesSafePredicate.INSTANCE; + } + + /** + * A {@link Predicate} to determine if {@link CoherenceCluster} + * is running. + * + * @return a {@link Predicate} + */ + static Predicate isCoherenceRunning() + { + return new IsCoherenceRunningPredicate(Set.of(Coherence.DEFAULT_NAME)); + } + + /** + * A {@link Predicate} to determine if all health checks in the + * {@link CoherenceCluster} are ready. + * + * @return a {@link Predicate} + */ + static Predicate isReady() + { + return IsReadyPredicate.INSTANCE; + } + + /** + * A {@link Predicate} to determine if all health checks in the + * {@link CoherenceCluster} are ready. + * + * @param sHealthCheck the name of an additional health check to verify + * + * @return a {@link Predicate} + */ + static Predicate isReady(String sHealthCheck) + { + return isCoherenceRunning(Set.of(Coherence.DEFAULT_NAME)); + } + + /** + * A {@link Predicate} to determine if {@link CoherenceCluster} + * is running. + * + * @param asName the names of the Coherence instances to verify + * + * @return a {@link Predicate} + */ + static Predicate isCoherenceRunning(String... asName) + { + return isCoherenceRunning(Set.of(asName)); + } + /** + * A {@link Predicate} to determine if {@link CoherenceCluster} + * is running. + * + * @param setName the names of the Coherence instances to verify + * + * @return a {@link Predicate} + */ + static Predicate isCoherenceRunning(Set setName) + { + return new IsCoherenceRunningPredicate(setName); + } + } + + // ----- IsAutoStartServicesSafePredicate ------------------------------- + + static class IsAutoStartServicesSafePredicate + implements Remote.Predicate + { + @Override + public boolean test(CoherenceCluster cluster) + { // determine the number of each auto start service is defined by the cluster HashMap serviceCountMap = new HashMap<>(); @@ -271,7 +432,79 @@ else if (count == 1) } return true; - }; } + + @Override + public String toString() + { + return "IsReadyPredicate"; + } + + // ----- data members ----------------------------------------------- + + static final IsAutoStartServicesSafePredicate INSTANCE = new IsAutoStartServicesSafePredicate(); + } + + // ----- IsReadyPredicate ----------------------------------------------- + + static class IsReadyPredicate + implements Remote.Predicate + { + @Override + public boolean test(CoherenceCluster cluster) + { + for (CoherenceClusterMember member : cluster) + { + if (!member.invoke(IsReady.INSTANCE)) + { + return false; + } + } + return true; + } + + @Override + public String toString() + { + return "IsReadyPredicate"; + } + + // ----- data members ----------------------------------------------- + + static final IsReadyPredicate INSTANCE = new IsReadyPredicate(); + } + + // ----- IsCoherenceRunningPredicate ------------------------------------ + + static class IsCoherenceRunningPredicate + implements Remote.Predicate + { + public IsCoherenceRunningPredicate(Set setNames) + { + f_setNames = setNames; + } + + @Override + public boolean test(CoherenceCluster cluster) + { + for (CoherenceClusterMember member : cluster) + { + if (!member.invoke(new IsCoherenceRunning(f_setNames))) + { + return false; + } + } + return true; + } + + @Override + public String toString() + { + return "IsCoherenceRunningPredicate(coherence=" + f_setNames + ")"; + } + + // ----- data members ----------------------------------------------- + + private final Set f_setNames; } } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceClusterMember.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceClusterMember.java index 15dc7c78a2ac9..dd4f0a11d3acc 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceClusterMember.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceClusterMember.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -9,6 +9,10 @@ import com.oracle.bedrock.OptionsByType; import com.oracle.bedrock.runtime.Platform; +import com.oracle.bedrock.runtime.coherence.callables.FindExtendProxyPort; +import com.oracle.bedrock.runtime.coherence.callables.FindGrpcProxyPort; +import com.oracle.bedrock.runtime.coherence.callables.HasSession; +import com.oracle.bedrock.runtime.coherence.callables.HasSessionWithScope; import com.oracle.bedrock.runtime.coherence.options.LocalHost; import com.oracle.bedrock.runtime.coherence.options.MachineName; import com.oracle.bedrock.runtime.concurrent.RemoteCallable; @@ -49,6 +53,14 @@ public interface CoherenceClusterMember int getClusterSize(); + /** + * Obtains the cluster port for the {@link CoherenceClusterMember}. + * + * @return the cluster port + */ + int getLocalMemberClusterPort(); + + /** * Obtains the local member id for the {@link CoherenceClusterMember}. * @@ -399,6 +411,13 @@ default Trilean isStorageEnabled(String scopeName, String serviceName) } + /** + * Returns the service names known to the Coherence cluster. + * + * @return the service names known to the Coherence cluster + */ + Set getServiceNames(); + /** * Determines the status of a service being run by the {@link CoherenceClusterMember}. * @@ -442,6 +461,113 @@ default ServiceStatus getServiceStatus(String scopeName, String serviceName) return getServiceStatus(scopeName + ":" + serviceName); } + void threadDump(); + + /** + * Return the port the default Extend proxy is listening on. + * + * @return the port the default Extend proxy is listening on, + * or {@code -1} if the proxy is not running + */ + default int getExtendProxyPort() + { + return invoke(FindExtendProxyPort.INSTANCE); + } + + /** + * Return the port a specific Extend proxy is listening on. + * + * @param sServiceName the proxy service name + * + * @return the port the Extend proxy is listening on, + * or {@code -1} if the proxy is not running + */ + default int getExtendProxyPort(String sServiceName) + { + return invoke(new FindExtendProxyPort(sServiceName)); + } + + /** + * Return the port the default gRPC proxy is listening on. + * + * @return the port the default gRPC proxy is listening on, + * or {@code -1} if the proxy is not running + */ + default int getGrpcProxyPort() + { + return invoke(FindGrpcProxyPort.INSTANCE); + } + + /** + * Return the port a scoped gRPC proxy is listening on. + * + * @param sScope the scope prefix to use to locate the gRPC proxy + * + * @return the port the default gRPC proxy is listening on, + * or {@code -1} if the proxy is not running + */ + default int getGrpcProxyPort(String sScope) + { + return invoke(new FindGrpcProxyPort(sScope)); + } + + /** + * Return {@code true} if the cluster member has a named {@link Session} started by + * the Coherence bootstrap API. + * + * @param sName the name of the session + * + * @return {@code true} if the cluster member has a named {@link Session} started by + * the Coherence bootstrap API + */ + default boolean hasSession(String sName) + { + return invoke(new HasSession(sName)); + } + + /** + * Return {@code true} if the cluster member has a named {@link Session} started by + * the Coherence bootstrap API. + * + * @param sSessionName the name of the session + * @param sCoherenceName an optional {@link Coherence} instance name + * + * @return {@code true} if the cluster member has a named {@link Session} started by + * the Coherence bootstrap API + */ + default boolean hasSession(String sSessionName, String sCoherenceName) + { + return invoke(new HasSession(sSessionName, sCoherenceName)); + } + + /** + * Return {@code true} if the cluster member has a {@link Session} with a specific + * scope started by the Coherence bootstrap API. + * + * @param sName the session scope name + * + * @return {@code true} if the cluster member has a {@link Session} with a specific + * scope started by the Coherence bootstrap API + */ + default boolean hasSessionWithScope(String sName) + { + return invoke(new HasSessionWithScope(sName)); + } + + /** + * Return {@code true} if the cluster member has a {@link Session} with a specific + * scope started by the Coherence bootstrap API. + * + * @param sScopeName the session scope + * @param sCoherenceName an optional {@link Coherence} instance name + * + * @return {@code true} if the cluster member has a {@link Session} with a specific + * scope started by the Coherence bootstrap API. + */ + default boolean hasSessionWithScope(String sScopeName, String sCoherenceName) + { + return invoke(new HasSessionWithScope(sScopeName, sCoherenceName)); + } /** * The {@link com.oracle.bedrock.runtime.MetaClass} for {@link CoherenceClusterMember}s. diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceNamedCache.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceNamedCache.java index 7a39399fd2860..a15a9bd2c355a 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceNamedCache.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/CoherenceNamedCache.java @@ -1,24 +1,31 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.runtime.coherence; import com.oracle.bedrock.runtime.Assembly; + import com.oracle.bedrock.runtime.concurrent.RemoteCallable; + import com.oracle.bedrock.runtime.concurrent.callable.RemoteCallableStaticMethod; import com.oracle.bedrock.runtime.concurrent.callable.RemoteMethodInvocation; + import com.oracle.bedrock.util.ReflectionHelper; + import com.tangosol.net.CacheService; import com.tangosol.net.NamedCache; + import com.tangosol.util.Filter; import com.tangosol.util.MapListener; import java.io.Serializable; + import java.lang.reflect.Method; + import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; @@ -29,6 +36,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; + import java.util.concurrent.CompletableFuture; public class CoherenceNamedCache @@ -243,6 +251,13 @@ public boolean isActive() } + @Override + public boolean isReady() + { + return remotelyInvoke("isReady"); + } + + @Override public void release() { diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/AbstractHealthCheckCallable.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/AbstractHealthCheckCallable.java new file mode 100644 index 0000000000000..fab53e72341a9 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/AbstractHealthCheckCallable.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.oracle.coherence.common.base.Logger; +import com.tangosol.net.CacheFactory; +import com.tangosol.net.Cluster; +import com.tangosol.net.management.Registry; +import com.tangosol.util.HealthCheck; + +import java.util.Optional; + +/** + * A base class for {@link RemoteCallable} to determine the state of a + * Coherence member health check + * + * @author Jonathan Knight 2022.07.06 + * @since 22.06.1 + */ +public abstract class AbstractHealthCheckCallable + implements RemoteCallable + { + /** + * Create a {@link AbstractHealthCheckCallable}. + * + * @param sName an additional HealthCheck name to verify + */ + protected AbstractHealthCheckCallable(String sName) + { + m_sName = sName; + } + + @Override + public Boolean call() throws Exception + { + try + { + Cluster cluster = CacheFactory.getCluster(); + if (cluster != null) + { + Registry management = cluster.getManagement(); + boolean fCheck = check(management); + if (!fCheck) + { + Logger.info("Bedrock: " + getClass().getSimpleName() + " check failed"); + return false; + } + if (m_sName != null && !m_sName.isBlank()) + { + Optional optional = management.getHealthCheck(m_sName); + if (optional.isPresent()) + { + fCheck = check(optional.get()); + if (!fCheck) + { + Logger.info("Bedrock: " + getClass().getSimpleName() + " check failed for HealthCheck " + m_sName); + return false; + } + } + else + { + Logger.info("Bedrock: " + getClass().getSimpleName() + " check failed, HealthCheck " + m_sName + " is not registered"); + return false; + } + } + if (m_sName == null || m_sName.isBlank()) + { + Logger.finest("Bedrock: " + getClass().getSimpleName() + " check passed"); + } + else + { + Logger.finest("Bedrock: " + getClass().getSimpleName() + " check passed for " + m_sName); + } + return true; + } + else + { + Logger.info("Bedrock: " + getClass().getSimpleName() + " check - cluster is null"); + return false; + } + } + catch (Exception e) + { + Logger.err("Bedrock: " + getClass().getSimpleName() + " check failed", e); + return false; + } + } + + /** + * Perform the member health check. + * + * @param registry the management registry + * + * @return {@code true} if the check passed + */ + protected abstract boolean check(Registry registry); + + /** + * Perform a specific health check. + * + * @param healthCheck the {@link HealthCheck} to test + * + * @return {@code true} if the check passed + */ + protected abstract boolean check(HealthCheck healthCheck); + + /** + * The name of a specific health check to verify + */ + private final String m_sName; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindExtendProxyPort.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindExtendProxyPort.java new file mode 100644 index 0000000000000..04f4a6cc4cf29 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindExtendProxyPort.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.oracle.coherence.common.net.InetSocketAddress32; +import com.tangosol.coherence.component.util.SafeService; +import com.tangosol.coherence.component.util.daemon.queueProcessor.service.grid.ProxyService; +import com.tangosol.coherence.component.util.daemon.queueProcessor.service.peer.acceptor.TcpAcceptor; +import com.tangosol.net.CacheFactory; +import com.tangosol.net.Cluster; +import com.tangosol.net.Service; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +/** + * A {@link RemoteCallable} that returns the port that the Extend proxy is listening on. + */ +public class FindExtendProxyPort + implements RemoteCallable + { + /** + * Create an instance of {@link FindExtendProxyPort} to find the default proxy port. + */ + public FindExtendProxyPort() + { + this("Proxy"); + } + + /** + * Create an instance of {@link FindExtendProxyPort} to find the + * port for the Extend proxy with the specified service name. + * + * @param sServiceName the service name + */ + public FindExtendProxyPort(String sServiceName) + { + m_sServiceName = sServiceName; + } + + @Override + public Integer call() + { + return findPort(m_sServiceName); + } + + // ----- helper methods ------------------------------------------------- + + private int findPort(String sServiceName) + { + Cluster cluster = CacheFactory.getCluster(); + Service service = cluster.getService(sServiceName); + if (service instanceof SafeService) + { + service = ((SafeService) service).getService(); + } + + if (service instanceof ProxyService) + { + ProxyService proxyService = (ProxyService) service; + TcpAcceptor acceptor = (TcpAcceptor) proxyService.getAcceptor(); + SocketAddress address = acceptor.getLocalAddress(); + if (address instanceof InetSocketAddress) + { + return ((InetSocketAddress) address).getPort(); + } + if (address instanceof InetSocketAddress32) + { + return ((InetSocketAddress32) address).getPort(); + } + } + return -1; + } + + /** + * Find the locally running Extend proxy port. + * + * @return the local Extend proxy port + */ + public static int local() + { + return local("Proxy"); + } + + /** + * Find the locally running Extend proxy port. + * + * @param sServiceName the service name + * + * @return the local Extend proxy port + */ + public static int local(String sServiceName) + { + return INSTANCE.findPort(sServiceName); + } + + // ----- constants ------------------------------------------------------ + + /** + * A singleton instance of {@link FindExtendProxyPort} to find the default proxy port. + */ + public static final FindExtendProxyPort INSTANCE = new FindExtendProxyPort(); + + // ----- data members --------------------------------------------------- + + /** + * The proxy service name. + */ + private final String m_sServiceName; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindGrpcProxyPort.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindGrpcProxyPort.java new file mode 100644 index 0000000000000..a64d34a6a93b3 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/FindGrpcProxyPort.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.tangosol.internal.net.service.grid.ProxyServiceDependencies; +import com.tangosol.internal.net.service.peer.acceptor.GrpcAcceptorDependencies; +import com.tangosol.net.CacheFactory; +import com.tangosol.net.Cluster; +import com.tangosol.net.Coherence; +import com.tangosol.net.ProxyService; +import com.tangosol.net.grpc.GrpcAcceptorController; +import com.tangosol.net.grpc.GrpcDependencies; + +/** + * A {@link RemoteCallable} that returns the port that the gRPC proxy is listening on. + */ +public class FindGrpcProxyPort + implements RemoteCallable + { + /** + * Create an instance of {@link FindGrpcProxyPort} to find the default proxy port. + */ + public FindGrpcProxyPort() + { + this(Coherence.DEFAULT_SCOPE); + } + + /** + * Create an instance of {@link FindGrpcProxyPort} to find the + * port for the gRPC proxy with the specified scope prefix. + * + * @param sScope the scope prefix + */ + public FindGrpcProxyPort(String sScope) + { + m_sScope = sScope; + } + + @Override + public Integer call() + { + return findPort(m_sScope); + } + + // ----- helper methods ------------------------------------------------- + + private int findPort(String sScope) + { + Cluster cluster = CacheFactory.getCluster(); + ProxyService service = (ProxyService) cluster.getService(sScope + GrpcDependencies.SCOPED_PROXY_SERVICE_NAME); + if (service == null) + { + return -1; + } + ProxyServiceDependencies depsService = (ProxyServiceDependencies) service.getDependencies(); + GrpcAcceptorDependencies depsAcceptor = (GrpcAcceptorDependencies) depsService.getAcceptorDependencies(); + GrpcAcceptorController controller = depsAcceptor.getController(); + return controller.getLocalPort(); + } + + /** + * Find the locally running gRPC proxy port. + * + * @return the local gRPC proxy port + */ + public static int local() + { + return local(Coherence.DEFAULT_SCOPE); + } + + /** + * Find the locally running gRPC proxy port. + * + * @param sScope the scope prefix + * + * @return the local gRPC proxy port + */ + public static int local(String sScope) + { + return INSTANCE.findPort(sScope); + } + + // ----- constants ------------------------------------------------------ + + /** + * A singleton instance of {@link FindGrpcProxyPort} to find the default proxy port. + */ + public static final FindGrpcProxyPort INSTANCE = new FindGrpcProxyPort(); + + // ----- data members --------------------------------------------------- + + /** + * The scope prefix to use. + */ + private final String m_sScope; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetLocalMemberClusterPort.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetLocalMemberClusterPort.java new file mode 100644 index 0000000000000..89413b261e067 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetLocalMemberClusterPort.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.tangosol.net.CacheFactory; + +/** + * A {@link RemoteCallable} to obtain the cluster port. + * + * @author Jonathan Knight 2022.12.09 + * @since 22.06.4 + */ +public class GetLocalMemberClusterPort + implements RemoteCallable + { + @Override + public Integer call() throws Exception + { + // attempt to get the cluster + com.tangosol.net.Cluster cluster = CacheFactory.getCluster(); + + // when there's no cluster there's no result + return cluster == null ? -1 : cluster.getDependencies().getGroupPort(); + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetServiceNames.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetServiceNames.java new file mode 100644 index 0000000000000..d52ad72ecb8fe --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/GetServiceNames.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.tangosol.net.CacheFactory; + +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; + +/** + * A {@link RemoteCallable} to obtain the names of the services + * known to the Coherence cluster. + */ +public class GetServiceNames + implements RemoteCallable> + { + @Override + public Set call() throws Exception + { + // attempt to get the cluster + com.tangosol.net.Cluster cluster = CacheFactory.getCluster(); + Set set = new HashSet<>(); + + // when there's no cluster there's no result + if (cluster == null || !cluster.isRunning()) + { + return set; + } + + Enumeration en = cluster.getServiceNames(); + while (en.hasMoreElements()) + { + set.add(en.nextElement()); + } + return set; + } + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasRunningCluster.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasRunningCluster.java new file mode 100644 index 0000000000000..fe205a0542174 --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasRunningCluster.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.tangosol.net.CacheFactory; + +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; + +/** + * A {@link RemoteCallable} to determine whether the + * member has a running cluster. + */ +public class HasRunningCluster + implements RemoteCallable + { + @Override + public Boolean call() throws Exception + { + // attempt to get the cluster + com.tangosol.net.Cluster cluster = CacheFactory.getCluster(); + return cluster != null && cluster.isRunning(); + } + + // ----- constants ------------------------------------------------------ + + /** + * A singleton instance of {@link HasRunningCluster}. + */ + public static final HasRunningCluster INSTANCE = new HasRunningCluster(); + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSession.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSession.java new file mode 100644 index 0000000000000..37053d95aab6a --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSession.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; + +import com.tangosol.net.Coherence; + +import java.util.Collection; + +/** + * A {@link RemoteCallable} to determine whether a {@link com.tangosol.net.Session} has been + * started using the Coherence bootstrap API. + */ +public class HasSession + implements RemoteCallable + { + /** + * Create a {@link HasSession} to find the default {@link com.tangosol.net.Session} + * in any {@link Coherence} instance. + */ + public HasSession() + { + this(Coherence.DEFAULT_NAME, null); + } + + /** + * Create a {@link HasSession} to find a specific {@link com.tangosol.net.Session} + * in any {@link Coherence} instance. + * + * @param sSessionName the name of the {@link com.tangosol.net.Session} + */ + public HasSession(String sSessionName) + { + this(sSessionName, null); + } + + /** + * Create a {@link HasSession} to find a specific {@link com.tangosol.net.Session} + * (optionally in a specific {@link Coherence} instance). + * + * @param sSessionName the name of the {@link com.tangosol.net.Session} + * @param sCoherenceName the optional name of the {@link Coherence} instance + */ + public HasSession(String sSessionName, String sCoherenceName) + { + f_sSessionName = sSessionName == null ? Coherence.DEFAULT_NAME : sSessionName; + f_sCoherenceName = sCoherenceName; + } + + @Override + public Boolean call() throws Exception + { + Collection col = Coherence.getInstances(); + if (col.isEmpty()) + { + // no Coherence instances are running + return false; + } + + if (f_sCoherenceName == null) + { + return Coherence.findSession(f_sSessionName).isPresent(); + } + + Coherence coherence = Coherence.getInstance(f_sCoherenceName); + if (coherence == null) + { + return false; + } + + return coherence.hasSession(f_sSessionName); + } + + // ----- data members --------------------------------------------------- + + /** + * The name of the {@link Coherence} instance. + */ + private final String f_sCoherenceName; + + /** + * The name of the {@link com.tangosol.net.Session}. + */ + private final String f_sSessionName; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSessionWithScope.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSessionWithScope.java new file mode 100644 index 0000000000000..731893090390f --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/HasSessionWithScope.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; + +import com.tangosol.net.Coherence; + +import java.util.Collection; + +/** + * A {@link RemoteCallable} to determine whether a {@link com.tangosol.net.Session} has been + * started with a specific scope using the Coherence bootstrap API. + */ +public class HasSessionWithScope + implements RemoteCallable + { + /** + * Create a {@link HasSessionWithScope} to find a {@link com.tangosol.net.Session} + * with the default scope in any {@link Coherence} instance. + */ + public HasSessionWithScope() + { + this(Coherence.DEFAULT_SCOPE, null); + } + + /** + * Create a {@link HasSessionWithScope} to find a {@link com.tangosol.net.Session} + * with a specific scope in any {@link Coherence} instance. + * + * @param sSessionName the name of the {@link com.tangosol.net.Session} + */ + public HasSessionWithScope(String sSessionName) + { + this(sSessionName, null); + } + + /** + * Create a {@link HasSessionWithScope} to find a {@link com.tangosol.net.Session} + * with a specific scope (optionally in a specific {@link Coherence} instance). + * + * @param sScopeName the scope of the {@link com.tangosol.net.Session} + * @param sCoherenceName the optional name of the {@link Coherence} instance + */ + public HasSessionWithScope(String sScopeName, String sCoherenceName) + { + f_sScopeName = sScopeName == null ? Coherence.DEFAULT_SCOPE : sScopeName; + f_sCoherenceName = sCoherenceName; + } + + @Override + public Boolean call() throws Exception + { + Collection col = Coherence.getInstances(); + if (col.isEmpty()) + { + // no Coherence instances are running + return false; + } + + if (f_sCoherenceName == null) + { + return !Coherence.findSessionsByScope(f_sScopeName).isEmpty(); + } + + Coherence coherence = Coherence.getInstance(f_sCoherenceName); + if (coherence == null) + { + return false; + } + + return !coherence.getSessionsWithScope(f_sScopeName).isEmpty(); + } + + // ----- data members --------------------------------------------------- + + /** + * The name of the {@link Coherence} instance. + */ + private final String f_sCoherenceName; + + /** + * The {@link com.tangosol.net.Session} scope name. + */ + private final String f_sScopeName; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsCoherenceRunning.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsCoherenceRunning.java index 5d08d36e40c31..94c1e95ce42d0 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsCoherenceRunning.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsCoherenceRunning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -10,25 +10,24 @@ import com.oracle.bedrock.runtime.concurrent.RemoteCallable; import com.tangosol.net.Coherence; +import java.util.Set; + +/** + * A {@link RemoteCallable} to determine whether a + * {@link Coherence} instance is running. + */ public class IsCoherenceRunning implements RemoteCallable { - /** - * The name of the {@link Coherence} instance. - */ - private final String name; - - /** * Constructs an {@link IsCoherenceRunning} for the * default {@link Coherence} instance. */ public IsCoherenceRunning() { - this(Coherence.DEFAULT_NAME); + this(Set.of(Coherence.DEFAULT_NAME)); } - /** * Constructs an {@link IsCoherenceRunning} * @@ -36,19 +35,46 @@ public IsCoherenceRunning() */ public IsCoherenceRunning(String name) { - this.name = name == null ? Coherence.DEFAULT_NAME : name; + this(name == null ? Set.of() : Set.of(name)); + } + + /** + * Constructs an {@link IsCoherenceRunning} + * + * @param setName the optional names of the Coherence instances + */ + public IsCoherenceRunning(Set setName) + { + if (setName.isEmpty()) + { + m_setName = Set.of(Coherence.DEFAULT_NAME); + } + else + { + m_setName = setName; + } } @Override - public Boolean call() throws Exception + public Boolean call() { - return Coherence.getInstances() - .stream() - .filter(c -> name.equals(c.getName())) - .map(c -> c.whenStarted().isDone() && c.isStarted()) - .findFirst() - .orElse(false); + for (String sName : m_setName) + { + Coherence coherence = Coherence.getInstance(sName); + if (coherence != null) + { + if (!coherence.whenStarted().isDone() || !coherence.isStarted()) + { + return false; + } + } + else + { + return false; + } + } + return true; } // ----- helper methods ------------------------------------------------- @@ -69,4 +95,11 @@ public static IsCoherenceRunning named(String sName) * A singleton default instance. */ private static final IsCoherenceRunning s_fInstance = new IsCoherenceRunning(); + + // ----- data members --------------------------------------------------- + + /** + * The names of the {@link Coherence} instances. + */ + private final Set m_setName; } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsReady.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsReady.java index 63dfa9784edf1..b5c8ef30469d4 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsReady.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsReady.java @@ -8,13 +8,10 @@ import com.oracle.bedrock.runtime.concurrent.RemoteCallable; -import com.oracle.coherence.common.base.Logger; - -import com.tangosol.net.CacheFactory; -import com.tangosol.net.Cluster; - import com.tangosol.net.management.Registry; +import com.tangosol.util.HealthCheck; + /** * A {@link RemoteCallable} to determine whether a Coherence member is ready * by calling {@link Registry#allHealthChecksReady()}. @@ -23,38 +20,41 @@ * @since 22.06 */ public class IsReady + extends AbstractHealthCheckCallable implements RemoteCallable { + /** + * Create a {@link IsReady} to verify all member health checks are ready. + */ + public IsReady() + { + this(null); + } + + /** + * Create a {@link IsReady} to verify all member health checks are ready. + * + * @param sName the name of an additional HealthCheck to test + */ + public IsReady(String sName) + { + super(sName); + } + + @Override + protected boolean check(Registry registry) + { + return registry.allHealthChecksReady(); + } + @Override - public Boolean call() throws Exception + protected boolean check(HealthCheck healthCheck) { - try - { - Cluster cluster = CacheFactory.getCluster(); - if (cluster != null && cluster.isRunning()) - { - boolean fReady = cluster.getManagement().allHealthChecksReady(); - if (!fReady) - { - Logger.info("Bedrock: IsReady check failed"); - } - return true; - } - else - { - Logger.info("Bedrock: IsReady check - cluster is null or not running"); - return false; - } - } - catch (Exception e) - { - Logger.err("Bedrock: IsReady check failed", e); - return false; - } + return healthCheck.isReady(); } /** * A singleton instance of an {@link IsReady} callable. */ - public static final IsReady INSTANCE = new IsReady(); + public static final IsReady INSTANCE = new IsReady(null); } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsSafe.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsSafe.java index a47839e070dad..f5252df6448a2 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsSafe.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsSafe.java @@ -8,13 +8,10 @@ import com.oracle.bedrock.runtime.concurrent.RemoteCallable; -import com.oracle.coherence.common.base.Logger; - -import com.tangosol.net.CacheFactory; -import com.tangosol.net.Cluster; - import com.tangosol.net.management.Registry; +import com.tangosol.util.HealthCheck; + /** * A {@link RemoteCallable} to determine whether a Coherence member is "safe" * by calling {@link Registry#allHealthChecksSafe()} ()}. @@ -23,34 +20,37 @@ * @since 22.06 */ public class IsSafe + extends AbstractHealthCheckCallable implements RemoteCallable { + /** + * Create a {@link IsSafe} to verify all member health checks are safe. + */ + public IsSafe() + { + this(null); + } + + /** + * Create a {@link IsSafe} to verify all member health checks are safe. + * + * @param sName the name of an additional HealthCheck to test + */ + public IsSafe(String sName) + { + super(sName); + } + + @Override + protected boolean check(Registry registry) + { + return registry.allHealthChecksSafe(); + } + @Override - public Boolean call() throws Exception + protected boolean check(HealthCheck healthCheck) { - try - { - Cluster cluster = CacheFactory.getCluster(); - if (cluster != null && cluster.isRunning()) - { - boolean fReady = cluster.getManagement().allHealthChecksSafe(); - if (!fReady) - { - Logger.info("Bedrock: IsSafe check failed"); - } - return true; - } - else - { - Logger.info("Bedrock: IsSafe check - cluster is null or not running"); - return false; - } - } - catch (Exception e) - { - Logger.err("Bedrock: IsSafe check failed", e); - return false; - } + return healthCheck.isSafe(); } /** diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsServiceRunning.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsServiceRunning.java index 40a47a1aaeaea..e26f0179dbf6d 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsServiceRunning.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/IsServiceRunning.java @@ -2,7 +2,7 @@ * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.bedrock.runtime.coherence.callables; @@ -11,32 +11,50 @@ import com.tangosol.net.CacheFactory; import com.tangosol.net.Service; +/** + * A Bedrock {@link RemoteCallable} to determine whether + * a specific service is running. + */ public class IsServiceRunning implements RemoteCallable { - /** - * The name of the service. - */ - private String serviceName; - - /** * Constructs an {@link IsServiceRunning} * - * @param serviceName the name of the service + * @param sServiceName the name of the service */ - public IsServiceRunning(String serviceName) + public IsServiceRunning(String sServiceName) { - this.serviceName = serviceName; + m_sServiceName = sServiceName; } - @Override - public Boolean call() throws Exception + public Boolean call() { - com.tangosol.net.Cluster cluster = CacheFactory.getCluster(); - Service service = cluster == null ? null : cluster.getService(serviceName); - - return service == null ? false : service.isRunning(); + try + { + com.tangosol.net.Cluster cluster = CacheFactory.getCluster(); + if (cluster != null && cluster.isRunning()) + { + if ("Cluster".equals(m_sServiceName)) + { + return true; + } + Service service = cluster.getService(m_sServiceName); + return service != null && service.isRunning(); + } + } + catch (Throwable t) + { + t.printStackTrace(); + } + return false; } + + // ----- data members --------------------------------------------------- + + /** + * The name of the service. + */ + private final String m_sServiceName; } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogMessage.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogMessage.java new file mode 100644 index 0000000000000..b72149fb8893b --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogMessage.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ + +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.oracle.coherence.common.base.Logger; + +/** + * A {@link RemoteCallable} to log a message using the Coherence logger. + */ +public class LogMessage + implements RemoteCallable + { + /** + * Create a {@link LogMessage} task. + * + * @param sMsg the message to log + */ + public LogMessage(String sMsg) + { + this.sMsg = sMsg; + } + + @Override + public Void call() throws Exception + { + Logger.info(sMsg); + return null; + } + + // ----- data members --------------------------------------------------- + + private final String sMsg; + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogThreadDump.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogThreadDump.java new file mode 100644 index 0000000000000..dbc98fe7013fb --- /dev/null +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/callables/LogThreadDump.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2000, 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.bedrock.runtime.coherence.callables; + +import com.oracle.bedrock.runtime.concurrent.RemoteCallable; +import com.oracle.coherence.common.util.Threads; + +/** + * A {@link RemoteCallable} that writes a thread dump to standard error. + */ +public class LogThreadDump + implements RemoteCallable + { + @Override + public Void call() throws Exception + { + System.err.println(Threads.getThreadDump()); + return null; + } + + // ----- constants ------------------------------------------------------ + + /** + * The singleton instance of {@link LogThreadDump}. + */ + public static final LogThreadDump INSTANCE = new LogThreadDump(); + } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/LocalHost.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/LocalHost.java index f11ec14b894a5..4383d6cf28a2d 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/LocalHost.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/LocalHost.java @@ -22,35 +22,14 @@ import java.net.InetAddress; import java.util.Iterator; +import java.util.Objects; +/** + * An option to configure Coherence to run in localhost mode. + */ public class LocalHost implements Profile, Option { - /** - * The tangosol.coherence.localhost property. - */ - public static final String PROPERTY = "coherence.localhost"; - - /** - * The tangosol.coherence.localport property. - */ - public static final String PROPERTY_PORT = "coherence.localport"; - - /** - * The local address of an {@link CoherenceClusterMember}. - * - * null when not defined. - */ - private String address; - - /** - * The local address port for a {@link CoherenceClusterMember}. - * - * null when not defined. - */ - private Iterator ports; - - /** * Constructs a {@link LocalHost} for the specified address and ports. *

@@ -61,12 +40,10 @@ public class LocalHost * @param address the address of the {@link LocalHost} * @param ports the possible ports for the {@link LocalHost} */ - private LocalHost( - String address, - Iterator ports) + private LocalHost(String address, Iterator ports) { - this.address = address; - this.ports = ports; + m_sAddress = address; + m_ports = ports; } @@ -77,10 +54,9 @@ private LocalHost( */ public String getAddress() { - return address; + return m_sAddress; } - /** * Obtains the possible ports of the {@link LocalHost}, null if not defined. * @@ -88,9 +64,18 @@ public String getAddress() */ public Iterator getPorts() { - return ports; + return m_ports; } + /** + * Obtains a {@link LocalHost} using the loopback address. + * + * @return a {@link LocalHost} using the loopback address + */ + public static LocalHost loopback() + { + return new LocalHost("127.0.0.1", null); + } /** * Obtains a {@link LocalHost} for a specified address. @@ -103,7 +88,6 @@ public static LocalHost of(String address) return new LocalHost(address, null); } - /** * Obtains a {@link LocalHost} for a specified address and port. * @@ -118,7 +102,6 @@ public static LocalHost of( return new LocalHost(address, new PerpetualIterator<>(port)); } - /** * Obtains a {@link LocalHost} for a specified address and port. * @@ -133,7 +116,6 @@ public static LocalHost of( return new LocalHost(address, port); } - /** * Obtains a {@link LocalHost} for a specified address and ports. * @@ -148,7 +130,6 @@ public static LocalHost of( return new LocalHost(address, ports); } - /** * Obtains a {@link LocalHost} for a specified address and ports. * @@ -163,7 +144,6 @@ public static LocalHost of( return new LocalHost(address, ports); } - /** * Obtains a {@link LocalHost} configured for "local host only" mode. * @@ -174,14 +154,13 @@ public static LocalHost only() return new LocalHost(null, null); } - @Override public void onLaunching( Platform platform, MetaClass metaClass, OptionsByType optionsByType) { - if (ports != null && !ports.hasNext()) + if (m_ports != null && !m_ports.hasNext()) { throw new IllegalStateException("Exhausted the available ports for the LocalHost"); } @@ -191,7 +170,7 @@ public void onLaunching( if (systemProperties != null) { - if (address == null && ports == null) + if (m_sAddress == null && m_ports == null) { // setup local-only mode optionsByType.add(SystemProperty.of(PROPERTY, InetAddress.getLoopbackAddress().getHostAddress())); @@ -201,21 +180,20 @@ public void onLaunching( } else { - if (address != null) + if (m_sAddress != null) { - optionsByType.add(SystemProperty.of(PROPERTY, address)); + optionsByType.add(SystemProperty.of(PROPERTY, m_sAddress)); } - if (ports != null) + if (m_ports != null) { - optionsByType.add(SystemProperty.of(PROPERTY_PORT, ports.next())); + optionsByType.add(SystemProperty.of(PROPERTY_PORT, m_ports.next())); } } } } } - @Override public void onLaunched( Platform platform, @@ -224,7 +202,6 @@ public void onLaunched( { } - @Override public void onClosing( Platform platform, @@ -233,7 +210,6 @@ public void onClosing( { } - @Override public boolean equals(Object o) { @@ -241,41 +217,50 @@ public boolean equals(Object o) { return true; } - - if (!(o instanceof LocalHost)) + if (o == null || getClass() != o.getClass()) { return false; } - - LocalHost that = (LocalHost) o; - - if (address != null ? !address.equals(that.address) : that.address != null) - { - return false; - } - - return ports != null ? ports.equals(that.ports) : that.ports == null; - + LocalHost localHost = (LocalHost) o; + return Objects.equals(m_sAddress, localHost.m_sAddress) && Objects.equals(m_ports, localHost.m_ports); } - @Override public int hashCode() { - int result = address != null ? address.hashCode() : 0; - - result = 31 * result + (ports != null ? ports.hashCode() : 0); - - return result; + return Objects.hash(m_sAddress, m_ports); } - @Override public String toString() { return "LocalHost(" + - "address='" + address + '\'' + - ", ports=" + ports + + "address='" + m_sAddress + '\'' + + ", ports=" + m_ports + ')'; } + + // ----- data members --------------------------------------------------- + + /** + * The coherence.localhost property. + */ + public static final String PROPERTY = "coherence.localhost"; + + /** + * The coherence.localport property. + */ + public static final String PROPERTY_PORT = "coherence.localport"; + + /** + * The local address of a {@link CoherenceClusterMember}, or + * {@code null} when not defined. + */ + private final String m_sAddress; + + /** + * The local address port for a {@link CoherenceClusterMember}, or + * {@code null} when not defined. + */ + private final Iterator m_ports; } diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/MemberName.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/MemberName.java index bd25d927d6f29..783069fb5fdbb 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/MemberName.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/MemberName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2025, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -16,6 +16,7 @@ import com.oracle.bedrock.runtime.coherence.CoherenceClusterMember; import com.oracle.bedrock.runtime.java.options.SystemProperties; import com.oracle.bedrock.runtime.java.options.SystemProperty; +import com.oracle.bedrock.runtime.options.Discriminator; import java.util.UUID; @@ -38,13 +39,19 @@ public class MemberName */ private final String name; + /** + * A flag to indicate that the {@link Discriminator} + * should be used to make the member name unique. + */ + private final boolean useDiscriminator; /** * Constructs a {@link MemberName} for the specified name. * - * @param name the name + * @param name the name + * @param useDiscriminator {@code true} to use the {@link Discriminator} option to make the name unique */ - private MemberName(String name) + private MemberName(String name, boolean useDiscriminator) { if (name == null) { @@ -54,6 +61,7 @@ private MemberName(String name) { this.name = name; } + this.useDiscriminator = useDiscriminator; } @@ -76,7 +84,19 @@ public String get() */ public static MemberName of(String name) { - return new MemberName(name); + return new MemberName(name, false); + } + + + /** + * Obtains a {@link MemberName} for a specified name. + * + * @param name the name of the {@link MemberName} + * @return a {@link MemberName} for the specified name + */ + public static MemberName withDiscriminator(String name) + { + return new MemberName(name, true); } @@ -87,14 +107,23 @@ public void onLaunching( OptionsByType optionsByType) { SystemProperties systemProperties = optionsByType.get(SystemProperties.class); + String memberName = name; + + if (useDiscriminator) + { + Discriminator discriminator = optionsByType.get(Discriminator.class); + if (discriminator != null) + { + memberName = memberName + "-" + discriminator.getValue(); + } + } if (systemProperties != null) { - optionsByType.add(SystemProperty.of(PROPERTY, name)); + optionsByType.add(SystemProperty.of(PROPERTY, memberName)); } } - @Override public void onLaunched( Platform platform, diff --git a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/WellKnownAddress.java b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/WellKnownAddress.java index 90718caee871e..5a15ff878f68a 100644 --- a/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/WellKnownAddress.java +++ b/prj/coherence-bedrock/coherence-bedrock/src/main/java/com/oracle/bedrock/runtime/coherence/options/WellKnownAddress.java @@ -16,31 +16,15 @@ import com.oracle.bedrock.runtime.coherence.CoherenceClusterMember; import com.oracle.bedrock.runtime.java.options.SystemProperties; import com.oracle.bedrock.runtime.java.options.SystemProperty; -import com.oracle.bedrock.runtime.network.AvailablePortIterator; -import com.oracle.bedrock.util.Capture; -import com.oracle.bedrock.util.PerpetualIterator; -import java.util.Iterator; import java.util.Objects; +/** + * An option to configure Coherence well known addresses. + */ public class WellKnownAddress implements Profile, Option { - /** - * The tangosol.coherence.wka property. - */ - public static final String PROPERTY = "coherence.wka"; - - /** - * The tangosol.coherence.wka.port property. - */ - public static final String PROPERTY_PORT = "coherence.wka.port"; - - /** - * The well known address of an {@link CoherenceClusterMember}. - */ - private final String address; - /** * Constructs a {@link WellKnownAddress}. * @@ -48,7 +32,7 @@ public class WellKnownAddress */ private WellKnownAddress(String address) { - this.address = address; + m_sAddress = address; } /** @@ -56,9 +40,19 @@ private WellKnownAddress(String address) * * @return the address of the {@link WellKnownAddress} */ - public String getAddress() + public String getsAddress() + { + return m_sAddress; + } + + /** + * Obtains a {@link WellKnownAddress} that uses the loopback address. + * + * @return a {@link WellKnownAddress} that uses the loopback address + */ + public static WellKnownAddress loopback() { - return address; + return new WellKnownAddress("127.0.0.1"); } /** @@ -79,7 +73,7 @@ public void onLaunching(Platform platform, MetaClass metaClass, OptionsByType op if (systemProperties != null) { - optionsByType.add(SystemProperty.of(PROPERTY, address)); + optionsByType.add(SystemProperty.of(PROPERTY, m_sAddress)); } } @@ -100,28 +94,40 @@ public boolean equals(Object o) { return true; } - - if (!(o instanceof WellKnownAddress)) + if (o == null || getClass() != o.getClass()) { return false; } - WellKnownAddress that = (WellKnownAddress) o; - - return !Objects.equals(address, that.address); + return Objects.equals(m_sAddress, that.m_sAddress); } - @Override public int hashCode() { - return address != null ? address.hashCode() : 0; + return Objects.hash(m_sAddress); } - @Override public String toString() { - return "WellKnownAddress(" + address + "')"; + return "WellKnownAddress(" + m_sAddress + "')"; } + + // ----- data members --------------------------------------------------- + + /** + * The tangosol.coherence.wka property. + */ + public static final String PROPERTY = "coherence.wka"; + + /** + * The tangosol.coherence.wka.port property. + */ + public static final String PROPERTY_PORT = "coherence.wka.port"; + + /** + * The well known address of an {@link CoherenceClusterMember}. + */ + private final String m_sAddress; } diff --git a/prj/coherence-bom/pom.xml b/prj/coherence-bom/pom.xml index 653e668af1558..795536aa28eed 100644 --- a/prj/coherence-bom/pom.xml +++ b/prj/coherence-bom/pom.xml @@ -1,10 +1,10 @@ + --> @@ -35,20 +35,16 @@ ossrh Coherence Snapshot Repository - https://oss.sonatype.org/content/repositories/snapshots/ + https://central.sonatype.com/repository/maven-snapshots/ ossrh Coherence Release Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://central.sonatype.com/ - - Harvey Raja - https://github.com/harveyraja - Aleks Seovic https://github.com/aseovic @@ -58,24 +54,28 @@ https://github.com/thegridman - Patrick Fry - https://github.com/fryp + Joe Fialli + https://github.com/jfialli - Bin Chen - https://github.com/bbcadela + Patrick Fry + https://github.com/fryp - Ryan Lubke - https://github.com/rlubke + Maurice Gamanho + https://github.com/mgamanho Luk Ho https://github.com/lsho - Joe Fialli - https://github.com/jfialli + Ryan Lubke + https://github.com/rlubke + + + Kirk Lund + https://github.com/kirklund Tim Middleton @@ -85,6 +85,14 @@ Chinmay Patel https://github.com/chpatel3 + + Vaso Putica + https://github.com/vasac + + + Emily Rivas + https://github.com/ecrivas + @@ -95,15 +103,15 @@ Note that this property has to be named 'revision' and that it has special meaning for Maven (see http://maven.apache.org/maven-ci-friendly.html) --> - 22.06-SNAPSHOT + 22.06.15-SNAPSHOT com.oracle.coherence.ce - 2.8.2 + 3.1.1 1.2.2 - 2.5.1 + 3.1.1 - 1.6 - 1.6.7 + 3.1.0 + 1.6.13 @@ -123,21 +131,6 @@ coherence-concurrent ${project.version} - - ${coherence.group.id} - coherence-helidon-client - ${project.version} - - - ${coherence.group.id} - coherence-helidon-grpc - ${project.version} - - - ${coherence.group.id} - coherence-helidon-grpc-proxy - ${project.version} - ${coherence.group.id} coherence-cdi-server @@ -178,11 +171,6 @@ coherence-json ${project.version} - - ${coherence.group.id} - coherence-loadbalancer - ${project.version} - ${coherence.group.id} coherence-login @@ -205,12 +193,12 @@ ${coherence.group.id} - coherence-mock + coherence-mp-config ${project.version} ${coherence.group.id} - coherence-mp-config + coherence-mp-health ${project.version} @@ -300,7 +288,7 @@ http://oss.oracle.com/licenses/upl repo - Copyright (c) 2000, 2022, Oracle and/or its affiliates. + Copyright (c) 2000, 2025, Oracle and/or its affiliates. Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl. @@ -330,20 +318,6 @@ - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus.staging.maven.plugin.version} - true - - ossrh - https://oss.sonatype.org/ - true - false - true - - diff --git a/prj/coherence-cdi-server/pom.xml b/prj/coherence-cdi-server/pom.xml index 65b333a6d98d9..cdb19e9efd05c 100644 --- a/prj/coherence-cdi-server/pom.xml +++ b/prj/coherence-cdi-server/pom.xml @@ -1,6 +1,6 @@ + + org.apache.felix + maven-bundle-plugin + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + org.apache.maven.plugins diff --git a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/BeanBuilder.java b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/BeanBuilder.java index 5cfe605cc1338..885204ab97d4a 100644 --- a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/BeanBuilder.java +++ b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/BeanBuilder.java @@ -1,18 +1,18 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2024 Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.cdi.server; import com.tangosol.coherence.config.ParameterList; +import com.tangosol.coherence.config.ParameterMacroExpression; import com.tangosol.coherence.config.builder.ParameterizedBuilder; import com.tangosol.config.ConfigurationException; import com.tangosol.config.expression.Expression; -import com.tangosol.config.expression.LiteralExpression; import com.tangosol.config.expression.ParameterResolver; import javax.enterprise.inject.Instance; @@ -42,7 +42,7 @@ public class BeanBuilder BeanBuilder(CDI cdi, String exprBeanName) { f_cdi = Objects.requireNonNull(cdi); - f_exprBeanName = new LiteralExpression<>(exprBeanName); + f_exprBeanName = new ParameterMacroExpression<>(exprBeanName, String.class); } // ---- ParameterizedBuilder interface ---------------------------------- diff --git a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/LogConfig.java b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/LogConfig.java index bfaf2d271dd03..a4a288d648e35 100644 --- a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/LogConfig.java +++ b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/LogConfig.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.cdi.server; @@ -19,11 +19,11 @@ /** * Logging configuration utility. - *

+ *

* Eliminates the need to explicitly configure Java Util logging as long as a * file {@code logging.properties} is on the classpath or in the current * directory, or you configure logging explicitly using System properties. - *

+ *

* Both {@value #SYS_PROP_LOGGING_CLASS} and {@value #SYS_PROP_LOGGING_FILE} are * honored. If you wish to configure the logging system differently, just do not * include the file and/or system properties. diff --git a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/Server.java b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/Server.java index fc390cdd4b3b0..7e6cb16ed5e12 100644 --- a/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/Server.java +++ b/prj/coherence-cdi-server/src/main/java/com/oracle/coherence/cdi/server/Server.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2022 Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.cdi.server; @@ -14,7 +14,7 @@ /** * This class bootstraps the CDI container, which will in turn start Coherence * server via CDI extension. - *

+ *

* This class should only be used when Coherence CDI is used in a standalone * mode. When used with Helidon 2.0, Helidon's {@code io.helidon.microprofile.cdi.Main} * should be used instead and will ensure that both Helidon and Coherence diff --git a/prj/coherence-cdi-server/src/main/resources/coherence-config.xml b/prj/coherence-cdi-server/src/main/resources/coherence-config.xml deleted file mode 100644 index 9c085cfe5c437..0000000000000 --- a/prj/coherence-cdi-server/src/main/resources/coherence-config.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - * - default-storage - - - - - - * - default-topic - - - - - - - default-storage - ${coherence.service.prefix Storage}Service - - - BINARY - - - true - - - - - default-topic - ${coherence.service.prefix Default}TopicService - true - true - 0B - - - - diff --git a/prj/coherence-cdi-server/src/test/java/com/oracle/coherence/cdi/server/BeanBuilderTest.java b/prj/coherence-cdi-server/src/test/java/com/oracle/coherence/cdi/server/BeanBuilderTest.java index 0a9dabf0ff0bc..44856185d46ae 100644 --- a/prj/coherence-cdi-server/src/test/java/com/oracle/coherence/cdi/server/BeanBuilderTest.java +++ b/prj/coherence-cdi-server/src/test/java/com/oracle/coherence/cdi/server/BeanBuilderTest.java @@ -1,15 +1,19 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2024 Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.cdi.server; import com.oracle.coherence.inject.Injectable; +import com.tangosol.coherence.config.ResolvableParameterList; +import com.tangosol.config.expression.Parameter; + import javax.enterprise.inject.Instance; +import javax.enterprise.inject.literal.NamedLiteral; import javax.enterprise.inject.spi.CDI; import com.tangosol.config.ConfigurationException; @@ -57,6 +61,27 @@ void testRealizeSuccess() assertThat(result, is(sameInstance(bean))); } + @Test + void testRealizeMacroExpressionSuccess() + { + Object bean = new BeanX(); + CDI cdi = mock(CDI.class); + Instance instance = mock(Instance.class); + + when(cdi.select(NamedLiteral.of("fooStore"))).thenReturn(instance); + when(instance.isResolvable()).thenReturn(true); + when(instance.get()).thenReturn(bean); + + ResolvableParameterList resolver = new ResolvableParameterList(); + + resolver.add(new Parameter("cache-name", "foo")); + + BeanBuilder builder = new BeanBuilder(cdi, "{cache-name}Store"); + Object result = builder.realize(resolver, null, null); + assertThat(result, notNullValue()); + assertThat(result, is(sameInstance(bean))); + } + @Test void testRealizeMissingBean() { diff --git a/prj/coherence-cdi/pom.xml b/prj/coherence-cdi/pom.xml index 5c05ddbb8f59d..a471daec0e4b4 100644 --- a/prj/coherence-cdi/pom.xml +++ b/prj/coherence-cdi/pom.xml @@ -1,6 +1,6 @@ + + org.apache.felix + maven-bundle-plugin + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + org.apache.maven.plugins diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AbstractCacheInterceptor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AbstractCacheInterceptor.java new file mode 100644 index 0000000000000..6eb2d69e3e0b1 --- /dev/null +++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AbstractCacheInterceptor.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.coherence.cdi; + +import com.tangosol.net.Coherence; +import com.tangosol.net.Session; + +import javax.enterprise.inject.spi.DefinitionException; + +/** + * Abstract base class for caching CDI interceptors. + */ +public abstract class AbstractCacheInterceptor + { + // ----- constructors --------------------------------------------------- + + /** + * Construct cache interceptor. + * + * @param coherence the Coherence instance + * @param extension the Coherence extension + */ + public AbstractCacheInterceptor(Coherence coherence, CoherenceExtension extension) + { + f_coherence = coherence; + f_extension = extension; + } + + /** + * Obtains the named {@link Session} or the default one if session name + * was not specified. + * + * @param sName session name + * + * @return the Coherence session + */ + protected Session getSession(String sName) + { + String sSessionName; + if (sName == null || sName.trim().isEmpty()) + { + sSessionName = Coherence.DEFAULT_NAME; + } + else + { + sSessionName = sName; + } + + return getCoherence().getSessionIfPresent(sSessionName) + .orElseThrow(() -> new DefinitionException("No Session is configured with name " + sSessionName)); + } + + protected Coherence getCoherence() + { + return f_coherence; + } + + protected CoherenceExtension getExtension() + { + return f_extension; + } + + // ---- data members ---------------------------------------------------- + + /** + * Coherence instance. + */ + private final Coherence f_coherence; + + /** + * Coherence CDI extension. + */ + private final CoherenceExtension f_extension; + } diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AnnotationLiteral.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AnnotationLiteral.java index c8d01b13e7810..74298bd54bf1d 100644 --- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AnnotationLiteral.java +++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/AnnotationLiteral.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.cdi; @@ -24,7 +24,7 @@ /** * Supports inline instantiation of annotation type instances. *

- * An instance of an annotation type may be obtained by subclassing AnnotationLiteral. + * An instance of an annotation type may be obtained by subclassing {@code AnnotationLiteral}. *

  * public abstract class PayByQualifier
  *       extends AnnotationLiteral<PayBy>
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAdd.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAdd.java
new file mode 100644
index 0000000000000..0228164598501
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAdd.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Never get the value from cache, get it from method and cache the result.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheAdd
+    {
+    /**
+     * An annotation literal for the {@link CacheAdd} annotation.
+     */
+    class Literal
+            extends AnnotationLiteral
+            implements CacheAdd
+        {
+        public static final Literal INSTANCE = new Literal();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAddInterceptor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAddInterceptor.java
new file mode 100644
index 0000000000000..4768f27f62f91
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheAddInterceptor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import com.tangosol.net.Coherence;
+import com.tangosol.net.NamedCache;
+import com.tangosol.net.Session;
+
+import javax.annotation.Priority;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import javax.inject.Inject;
+
+/**
+ * A CDI interceptor that always invokes target method and then
+ * caches invocation result before returning it.
+ */
+@Interceptor
+@Priority(Interceptor.Priority.LIBRARY_BEFORE + 5)
+public class CacheAddInterceptor
+    extends AbstractCacheInterceptor
+    {
+    // ----- constructors ---------------------------------------------------
+
+    /**
+     * Create the {@link CacheAddInterceptor}.
+     *
+     * @param coherence  the Coherence instance
+     * @param extension  the Coherence CDI extension
+     */
+    @Inject
+    public CacheAddInterceptor(@Name(Coherence.DEFAULT_NAME) Coherence coherence, CoherenceExtension extension)
+        {
+        super(coherence, extension);
+        }
+
+    // ---- interceptor methods ---------------------------------------------
+
+    /**
+     * Always invokes target method and caches invocation result before
+     * returning it.
+     *
+     * @param ctxInvocation  the invocation context
+     *
+     * @return  a result of the invocation of the target method
+     *
+     * @throws Exception  if thrown by target method
+     */
+    @AroundInvoke
+    public Object cacheAdd(InvocationContext ctxInvocation)
+            throws Exception
+        {
+        CoherenceExtension.MethodInterceptorInfo mi = getExtension().interceptorInfo(ctxInvocation.getMethod());
+        if (mi == null)
+            {
+            throw new IllegalStateException("Method interceptor data not ready in CDI extension for method " + ctxInvocation.getMethod());
+            }
+
+        Session                    session = getSession(mi.sessionName());
+        NamedCache cache   = session.getCache(mi.cacheName());
+        Object                     oKey    = mi.cacheKeyFunction().apply(ctxInvocation.getParameters());
+        Object                     oValue  = ctxInvocation.proceed();
+
+        cache.put(oKey, oValue);
+        return oValue;
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGet.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGet.java
new file mode 100644
index 0000000000000..ad0239db39ccd
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGet.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Get the value from cache if present, invoke the method and cache the result
+ * otherwise.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheGet
+    {
+    /**
+     * An annotation literal for the {@link CacheGet} annotation.
+     */
+    class Literal
+            extends AnnotationLiteral
+            implements CacheGet
+        {
+        public static final Literal INSTANCE = new Literal();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGetInterceptor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGetInterceptor.java
new file mode 100644
index 0000000000000..57a5454ac5039
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheGetInterceptor.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import com.tangosol.net.Coherence;
+import com.tangosol.net.NamedCache;
+import com.tangosol.net.Session;
+
+import javax.annotation.Priority;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import javax.inject.Inject;
+
+/**
+ * A CDI interceptor that returns cached value if present; otherwise it
+ * returns and caches result of target method invocation.
+ */
+@Interceptor
+@Priority(Interceptor.Priority.LIBRARY_BEFORE + 5)
+public class CacheGetInterceptor
+    extends AbstractCacheInterceptor
+    {
+    // ----- constructors ---------------------------------------------------
+
+    /**
+     * Create the {@link CacheGetInterceptor}
+     *
+     * @param coherence  the Coherence instance
+     * @param extension  the Coherence CDI extension
+     */
+    @Inject
+    public CacheGetInterceptor(@Name(Coherence.DEFAULT_NAME) Coherence coherence, CoherenceExtension extension)
+        {
+        super(coherence, extension);
+        }
+
+    // ---- interceptor methods ---------------------------------------------
+
+    /**
+     * Returns cached value if available; otherwise invokes target method and
+     * stores invocation result in the cache before returning it.
+     *
+     * @param ctxInvocation  the invocation context
+     *
+     * @return the cached value if available; otherwise returns the result of
+     * the invocation of the target method
+     *
+     * @throws Exception  if thrown by target method
+     */
+    @AroundInvoke
+    public Object cacheGet(InvocationContext ctxInvocation)
+            throws Exception
+        {
+        CoherenceExtension.MethodInterceptorInfo mi = getExtension().interceptorInfo(ctxInvocation.getMethod());
+        if (mi == null)
+            {
+            throw new IllegalStateException("Method interceptor data not ready in CDI extension for method " + ctxInvocation.getMethod());
+            }
+
+        Session                    session = getSession(mi.sessionName());
+        NamedCache cache   = session.getCache(mi.cacheName());
+        Object                     oKey    = mi.cacheKeyFunction().apply(ctxInvocation.getParameters());
+        Object                     oValue  = cache.get(oKey);
+
+        if (oValue == null)
+            {
+            oValue = ctxInvocation.proceed();
+            cache.put(oKey, oValue);
+            }
+        return oValue;
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheKey.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheKey.java
new file mode 100644
index 0000000000000..c53feeea22c7d
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheKey.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Parameters annotated with this annotation are considered part of the key.
+ * If no parameters are annotated, then all parameters that are not annotated with {@link CacheValue}
+ * form the key.
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheKey
+    {
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePut.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePut.java
new file mode 100644
index 0000000000000..e12339114103c
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePut.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Put a value to cache AND call the method.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CachePut
+    {
+    /**
+     * An annotation literal for the {@link CachePut} annotation.
+     */
+    class Literal
+            extends AnnotationLiteral
+            implements CachePut
+        {
+        public static final Literal INSTANCE = new Literal();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePutInterceptor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePutInterceptor.java
new file mode 100644
index 0000000000000..222bfa2c8f54a
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CachePutInterceptor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import com.tangosol.net.Coherence;
+import com.tangosol.net.NamedCache;
+import com.tangosol.net.Session;
+
+import javax.annotation.Priority;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import javax.inject.Inject;
+
+/**
+ * A CDI interceptor that stores parameter value in the Coherence cache.
+ */
+@Interceptor
+@Priority(Interceptor.Priority.LIBRARY_BEFORE + 5)
+public class CachePutInterceptor
+    extends AbstractCacheInterceptor
+    {
+    // ----- constructors ---------------------------------------------------
+
+    /**
+     * Create the {@link CachePutInterceptor}
+     *
+     * @param coherence  the Coherence instance
+     * @param extension  the Coherence CDI extension
+     */
+    @Inject
+    public CachePutInterceptor(@Name(Coherence.DEFAULT_NAME) Coherence coherence, CoherenceExtension extension)
+        {
+        super(coherence, extension);
+        }
+
+    // ---- interceptor methods ---------------------------------------------
+
+    /**
+     * Stores parameter annotated with {@link CacheValue} into cache, invokes
+     * target method and returns result of the invocation.
+     *
+     * @param ctxInvocation  the invocation context
+     *
+     * @return  the result of the invocation of the target method
+     *
+     * @throws Exception  if thrown by target method
+     */
+    @AroundInvoke
+    public Object cachePut(InvocationContext ctxInvocation)
+            throws Exception
+        {
+        CoherenceExtension.MethodInterceptorInfo mi = getExtension().interceptorInfo(ctxInvocation.getMethod());
+        if (mi == null)
+            {
+            throw new IllegalStateException("Method interceptor data not ready in CDI extension for method " + ctxInvocation.getMethod());
+            }
+
+        Session                    session      = getSession(mi.sessionName());
+        NamedCache cache        = session.getCache(mi.cacheName());
+        Object                     oKey         = mi.cacheKeyFunction().apply(ctxInvocation.getParameters());
+        Object[]                   aoParameters = ctxInvocation.getParameters();
+        Object                     oValue       = aoParameters[mi.getValueParameterIndex()];
+
+        cache.put(oKey, oValue);
+        return ctxInvocation.proceed();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemove.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemove.java
new file mode 100644
index 0000000000000..c2d2d65246409
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemove.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Remove the value from the cache and call the method.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheRemove
+    {
+    /**
+     * An annotation literal for the {@link CacheRemove} annotation.
+     */
+    class Literal
+            extends AnnotationLiteral
+            implements CacheRemove
+        {
+        public static final Literal INSTANCE = new Literal();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemoveInterceptor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemoveInterceptor.java
new file mode 100644
index 0000000000000..816cb4fb648df
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheRemoveInterceptor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import com.tangosol.net.Coherence;
+import com.tangosol.net.NamedCache;
+import com.tangosol.net.Session;
+
+import javax.annotation.Priority;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InvocationContext;
+
+import javax.inject.Inject;
+
+/**
+ * A CDI interceptor that removes value from the cache and invokes target method.
+ */
+@Interceptor
+@Priority(Interceptor.Priority.LIBRARY_BEFORE + 5)
+public class CacheRemoveInterceptor
+    extends AbstractCacheInterceptor
+    {
+    // ----- constructors ---------------------------------------------------
+
+    /**
+     * Create the {@link CacheRemoveInterceptor}
+     *
+     * @param coherence  the Coherence instance
+     * @param extension  the Coherence CDI extension
+     */
+    @Inject
+    public CacheRemoveInterceptor(@Name(Coherence.DEFAULT_NAME) Coherence coherence, CoherenceExtension extension)
+        {
+        super(coherence, extension);
+        }
+
+    // ---- interceptor methods ---------------------------------------------
+
+    /**
+     * Removes cached value.
+     *
+     * @param ctxInvocation  the invocation context
+     *
+     * @return  result of the invocation of the target method
+     *
+     * @throws Exception  if thrown by target method
+     */
+    @AroundInvoke
+    public Object cacheRemove(InvocationContext ctxInvocation)
+            throws Exception
+        {
+        CoherenceExtension.MethodInterceptorInfo mi = getExtension().interceptorInfo(ctxInvocation.getMethod());
+        if (mi == null)
+            {
+            throw new IllegalStateException("Method interceptor data not ready in CDI extension for method " + ctxInvocation.getMethod());
+            }
+
+        Session                    session = getSession(mi.sessionName());
+        NamedCache cache   = session.getCache(mi.cacheName());
+        Object                     oKey    = mi.cacheKeyFunction().apply(ctxInvocation.getParameters());
+
+        cache.remove(oKey);
+        return ctxInvocation.proceed();
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheValue.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheValue.java
new file mode 100644
index 0000000000000..ece84634a6199
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CacheValue.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a parameter as the cache value. Only a single parameter may be marked.
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheValue
+    {
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiHelpers.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiHelpers.java
new file mode 100644
index 0000000000000..9a418316b6101
--- /dev/null
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiHelpers.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates.
+ *
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * https://oss.oracle.com/licenses/upl.
+ */
+package com.oracle.coherence.cdi;
+
+import com.oracle.coherence.cdi.events.CacheName;
+
+import com.tangosol.internal.cdi.MethodKey;
+
+import com.tangosol.net.Coherence;
+
+import java.lang.annotation.Annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.AnnotatedMethod;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+import javax.enterprise.inject.spi.DeploymentException;
+
+/**
+ * A helper class providing CDI helper methods.
+ */
+final class CdiHelpers
+    {
+    // ----- constructors ---------------------------------------------------
+
+    /**
+     * Utility class must not have public constructor.
+     */
+    private CdiHelpers()
+        {
+        }
+
+    // ----- helper methods -------------------------------------------------
+
+    /**
+     * Create key producing function based on method parameters.
+     *
+     * @param method  the annotated method
+     *
+     * @return key generator function
+     */
+    static Function cacheKeyFunction(AnnotatedMethod method)
+        {
+        List> parameters = method.getParameters();
+        List keyIndices = new ArrayList<>();
+        List allIndices = new ArrayList<>();
+        for (int i = 0; i < parameters.size(); i++)
+            {
+            AnnotatedParameter annotatedParameter = parameters.get(i);
+            if (annotatedParameter.getAnnotation(CacheKey.class) != null)
+                {
+                keyIndices.add(i);
+                }
+            if (annotatedParameter.getAnnotation(CacheValue.class) == null)
+                {
+                allIndices.add(i);
+                }
+            }
+        if (keyIndices.isEmpty())
+            {
+            // all parameters except for @CacheValue are keys
+            keyIndices = allIndices;
+            }
+        if (keyIndices.size() == 1)
+            {
+            int index = keyIndices.iterator().next();
+            return paramValues -> paramValues[index];
+            }
+
+        Integer[] indices = keyIndices.toArray(new Integer[0]);
+        return paramValues -> new MethodKey(paramValues, indices);
+        }
+
+    /**
+     * Extract cache name from the annotated method or class.
+     *
+     * @param annotatedType  the type potentially annotated with {@link CacheName} annotation
+     * @param callable       the method potentially annotated with {@link CacheName} annotation
+     *
+     * @return a cache name
+     */
+    static String cacheName(Annotated annotatedType, Annotated callable)
+        {
+        CacheName found = annotatedType == null
+                          ? null
+                          : annotatedType.getAnnotation(CacheName.class);
+
+        if (callable != null)
+            {
+            CacheName cacheName = callable.getAnnotation(CacheName.class);
+            if (cacheName != null)
+                {
+                found = cacheName;
+                }
+            }
+
+        if (found == null)
+            {
+            throw new DeploymentException("CacheName must be defined either on type, method/constructor, or field/parameter."
+                                          + " Type " + annotatedType
+                                          + ", callable: " + callable);
+            }
+        return found.value();
+        }
+
+    /**
+     * Returns position of method parameter annotated with specified annotation.
+     *
+     * @param parameters  the method parameters
+     * @param annotation  the annotation to search for
+     * @param          the declared type of annotated class
+     *
+     * @return  annotated parameter position
+     */
+    static  Integer annotatedParameterIndex(List> parameters, Class annotation)
+        {
+        return parameters.stream()
+                .filter(annotatedParameter -> annotatedParameter.isAnnotationPresent(annotation))
+                .findFirst()
+                .map(AnnotatedParameter::getPosition)
+                .orElse(null);
+        }
+
+    /**
+     * Extract session name from the annotated method or class.
+     *
+     * @param annotatedType  the target type
+     * @param callable       the target method
+     *
+     * @return  the session name
+     */
+    static String sessionName(Annotated annotatedType, Annotated callable)
+        {
+        SessionName sessionName = null;
+        if (callable != null)
+            {
+            sessionName = callable.getAnnotation(SessionName.class);
+            }
+        if (sessionName == null && annotatedType != null)
+            {
+            sessionName = annotatedType.getAnnotation(SessionName.class);
+            }
+        if (sessionName != null)
+            {
+            return sessionName.value();
+            }
+        return Coherence.DEFAULT_NAME;
+        }
+    }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiMapListenerManager.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiMapListenerManager.java
index 590e0e266d15e..6b1247ebc16ee 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiMapListenerManager.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CdiMapListenerManager.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2020, 2021 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi;
 
@@ -47,4 +47,16 @@ private void registerCacheListeners(@Observes @Created CacheLifecycleEvent event
                 event.getSessionName(), event.getServiceName()));
 
         }
+    /**
+     * Register the cache listeners.
+     * 
+     * @param sCacheName    the cache name
+     * @param sScopeName    the scope name
+     * @param sSessionName  the session name
+     * @param sServiceName  the service name
+     */
+    public void registerCacheListeners(String sCacheName, String sScopeName, String sSessionName, String sServiceName)
+        {
+        registerListeners(sCacheName, sScopeName, sSessionName, sServiceName);
+        }
     }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceExtension.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceExtension.java
index 307bfd9bac553..c55f8df7ee1bd 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceExtension.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceExtension.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2019, 2022, Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2023, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi;
 
@@ -10,7 +10,6 @@
 import com.oracle.coherence.cdi.events.EventObserverSupport;
 
 import com.tangosol.net.Coherence;
-
 import com.tangosol.net.SessionProvider;
 
 import com.tangosol.net.events.CoherenceLifecycleEvent;
@@ -21,29 +20,39 @@
 
 import com.tangosol.util.MapEvent;
 
+import java.lang.reflect.Method;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
 import javax.annotation.Priority;
 
 import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.Dependent;
 import javax.enterprise.context.Initialized;
 
 import javax.enterprise.event.Observes;
 
 import javax.enterprise.inject.Default;
 import javax.enterprise.inject.Instance;
-
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.AnnotatedMethod;
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
 import javax.enterprise.inject.spi.BeforeShutdown;
 import javax.enterprise.inject.spi.Extension;
 import javax.enterprise.inject.spi.ProcessAnnotatedType;
 import javax.enterprise.inject.spi.ProcessObserverMethod;
 import javax.enterprise.inject.spi.WithAnnotations;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import javax.enterprise.inject.spi.configurator.AnnotatedMethodConfigurator;
+import javax.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
 
 /**
  * A Coherence CDI {@link Extension} that is used on both cluster members and
@@ -170,6 +179,213 @@ private  void processMapEventObservers(@Observes ProcessObserverMethod m_mapMapEventTransformerSupplier.put(a, type.getJavaClass()));
         }
 
+    /**
+     * Process beans annotated with caching annotations.
+     *
+     * @param event  the event to process
+     * @param     the declared type of the injection point
+     */
+     void processCacheAnnotatedTypes(@Observes @WithAnnotations({CacheGet.class, CacheAdd.class, CachePut.class, CacheRemove.class})
+                                        ProcessAnnotatedType event)
+        {
+        AnnotatedTypeConfigurator                annotatedTypeConfigurator = event.configureAnnotatedType();
+        AnnotatedType                            annotatedType             = annotatedTypeConfigurator.getAnnotated();
+        Set> methods                   = annotatedTypeConfigurator.methods();
+
+        for (AnnotatedMethodConfigurator methodConfigurator : methods)
+            {
+            AnnotatedMethod method = methodConfigurator.getAnnotated();
+            if (method.getAnnotation(CacheGet.class) != null)
+                {
+                String interceptorKey = makeInterceptorKey(method);
+                m_mapInterceptorCache.put(interceptorKey, createInterceptorInfo(annotatedType, method));
+                }
+            if (method.getAnnotation(CacheAdd.class) != null)
+                {
+                String interceptorKey = makeInterceptorKey(method);
+                m_mapInterceptorCache.put(interceptorKey, createInterceptorInfo(annotatedType, method));
+                }
+            if (method.getAnnotation(CachePut.class) != null)
+                {
+                String interceptorKey      = makeInterceptorKey(method);
+                MethodInterceptorInfo info = createInterceptorInfo(annotatedType, method);
+                if (info.getValueParameterIndex() == null)
+                    {
+                    throw new IllegalStateException("@CacheValue annotation is missing on a parameter on @CachePut method " + interceptorKey);
+                    }
+                m_mapInterceptorCache.put(interceptorKey, info);
+                }
+            if (method.getAnnotation(CacheRemove.class) != null)
+                {
+                String interceptorKey = makeInterceptorKey(method);
+                m_mapInterceptorCache.put(interceptorKey, createInterceptorInfo(annotatedType, method));
+                }
+            }
+        }
+
+    /**
+     * Create unique key based on the target method.
+     *
+     * @param method  target method
+     *
+     * @return  unique key
+     *
+     * @throws IllegalStateException if the same key already exists in interceptor cache
+     */
+    private String makeInterceptorKey(AnnotatedMethod method)
+        {
+        String interceptorKey = methodCacheKey(method.getJavaMember());
+        if (m_mapInterceptorCache.containsKey(interceptorKey))
+            {
+            throw new IllegalStateException("Multiple cache annotation are not allowed on a method " + interceptorKey);
+            }
+        return interceptorKey;
+        }
+
+    /**
+     * Capture all cache related metadata from the target type and method.
+     *
+     * @param annotatedType  target type
+     * @param method         target method
+     *
+     * @return the captured metadata
+     */
+    private MethodInterceptorInfo createInterceptorInfo(Annotated annotatedType, AnnotatedMethod method)
+        {
+        String                     cacheNameDef        = CdiHelpers.cacheName(annotatedType, method);
+        String                     sessionNameDef      = CdiHelpers.sessionName(annotatedType, method);
+        Function cacheKeyFunction    = CdiHelpers.cacheKeyFunction(method);
+        Integer                    valueParameterIndex = CdiHelpers.annotatedParameterIndex(method.getParameters(), CacheValue.class);
+        return new MethodInterceptorInfo(cacheNameDef, sessionNameDef, cacheKeyFunction, valueParameterIndex);
+        }
+
+    /**
+     * A class that stores necessary method related data for caching interceptors.
+     */
+    static class MethodInterceptorInfo
+        {
+
+        /**
+         * Constructs {@link MethodInterceptorInfo}.
+         *
+         * @param cacheName            the cache name
+         * @param sessionName          the session name
+         * @param cacheKeyFunction     the cache key function
+         * @param valueParameterIndex  the parameter index of the cache value
+         */
+        MethodInterceptorInfo(String cacheName, String sessionName, Function cacheKeyFunction, Integer valueParameterIndex)
+            {
+            m_cacheName           = cacheName;
+            m_cacheKeyFunction    = cacheKeyFunction;
+            m_sessionName         = sessionName;
+            m_valueParameterIndex = valueParameterIndex;
+            }
+
+        /**
+         * Returns the cache name.
+         *
+         * @return  the cache name
+         */
+        String cacheName()
+            {
+            return m_cacheName;
+            }
+
+        /**
+         * Return the session name.
+         *
+         * @return  session name
+         */
+        String sessionName()
+            {
+            return m_sessionName;
+            }
+
+        /**
+         * Return the cache key function.
+         *
+         * @return  cache key function
+         */
+        Function cacheKeyFunction()
+            {
+            return m_cacheKeyFunction;
+            }
+
+        /**
+         * Return the parameter index of the cache value.
+         *
+         * @return  the parameter index of the cache value
+         */
+        public Integer getValueParameterIndex()
+            {
+            return m_valueParameterIndex;
+            }
+
+        /**
+         * The name of the cache.
+         */
+        private final String m_cacheName;
+
+        /**
+         * The name of the session.
+         */
+        private final String m_sessionName;
+
+        /**
+         * The cache key function.
+         */
+        private final Function m_cacheKeyFunction;
+
+        /**
+         * The parameter index of the cache value.
+         */
+        private final Integer m_valueParameterIndex;
+        }
+
+    /**
+     * Return {@link MethodInterceptorInfo} for specified method.
+     *
+     * @param method  the target method
+     *
+     * @return interceptor info
+     */
+    MethodInterceptorInfo interceptorInfo(Method method)
+        {
+        return m_mapInterceptorCache.get(methodCacheKey(method));
+        }
+
+    private String methodCacheKey(Method method)
+        {
+        return method.getDeclaringClass().getName()
+               + "." + method.getName()
+               + "(" + Arrays.toString(method.getParameterTypes()) + ")";
+        }
+
+    /**
+     * Register caching annotations as interceptor binding types.
+     *
+     * @param event  the event to use for annotation registration
+     */
+    void registerInterceptorBindings(@Observes BeforeBeanDiscovery event)
+        {
+        event.addInterceptorBinding(CacheGet.class);
+        event.addAnnotatedType(CacheGetInterceptor.class, CacheGetInterceptor.class.getName())
+                .add(CacheGet.Literal.INSTANCE)
+                .add(Dependent.Literal.INSTANCE);
+        event.addInterceptorBinding(CacheAdd.class);
+        event.addAnnotatedType(CacheAddInterceptor.class, CacheAddInterceptor.class.getName())
+                .add(CacheAdd.Literal.INSTANCE)
+                .add(Dependent.Literal.INSTANCE);
+        event.addInterceptorBinding(CachePut.class);
+        event.addAnnotatedType(CachePutInterceptor.class, CachePutInterceptor.class.getName())
+                .add(CachePut.Literal.INSTANCE)
+                .add(Dependent.Literal.INSTANCE);
+        event.addInterceptorBinding(CacheRemove.class);
+        event.addAnnotatedType(CacheRemoveInterceptor.class, CacheRemoveInterceptor.class.getName())
+                .add(CacheRemove.Literal.INSTANCE)
+                .add(Dependent.Literal.INSTANCE);
+        }
+
     /**
      * Register dynamic beans for this extension.
      *
@@ -338,4 +554,10 @@ public interface InterceptorProvider
      * A list of discovered map listeners.
      */
     private final List> m_listListener = new ArrayList<>();
+
+    /**
+     * A map of {@link MethodInterceptorInfo} defined for each cache annotated
+     * method.
+     */
+    private final Map m_mapInterceptorCache = new HashMap<>();
     }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceProducer.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceProducer.java
index 033a4642c85e7..b4b69bd490204 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceProducer.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/CoherenceProducer.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2020, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2022, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi;
 
@@ -100,12 +100,20 @@ protected CoherenceConfiguration createConfiguration(BeanManager beanManager)
             }
         else
             {
+            List sessionConfiguration = configurations.stream().collect(Collectors.toList());
+            
+            boolean fHasDefault = sessionConfiguration.stream().anyMatch(cfg -> Coherence.DEFAULT_NAME.equals(cfg.getName()));
+
             // else there is no CoherenceConfiguration.Builder bean so we create one
             builder = CoherenceConfiguration.builder()
-                        .withSession(SessionConfiguration.defaultSession())
-                        .withSessions(configurations)
+                        .withSessions(sessionConfiguration)
                         .withEventInterceptors(listInterceptor)
                         .discoverSessions();
+
+            if (!fHasDefault)
+                {
+                builder.withSession(SessionConfiguration.defaultSession());
+                }
             }
 
         // build the configuration - ensuring the correct name is set
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/NamedCacheProducer.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/NamedCacheProducer.java
index 1b8f7592bc81a..7afd2d80d59c0 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/NamedCacheProducer.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/NamedCacheProducer.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2020, 2021 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2025 Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi;
 
@@ -28,12 +28,20 @@
 import javax.enterprise.inject.Produces;
 import javax.enterprise.inject.Typed;
 
+import javax.enterprise.inject.spi.Annotated;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.DefinitionException;
 import javax.enterprise.inject.spi.InjectionPoint;
 
 import javax.inject.Inject;
 
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
 /**
  * A CDI producer for {@link NamedCache}, {@link AsyncNamedCache} and {@link
  * ContinuousQueryCache} instances.
@@ -50,20 +58,23 @@ public class NamedCacheProducer
     /**
      * Construct a {@link NamedCacheProducer} instance.
      *
-     * @param beanManager        the CDI bean manager
-     * @param filterProducer     the producer to use to obtain {@link
-     *                           com.tangosol.util.Filter} instances
-     * @param extractorProducer  the producer to use to obtain {@link
-     *                           com.tangosol.util.ValueExtractor} instances
+     * @param beanManager            the CDI bean manager
+     * @param filterProducer         the producer to use to obtain {@link
+     *                               com.tangosol.util.Filter} instances
+     * @param extractorProducer      the producer to use to obtain {@link
+     *                               com.tangosol.util.ValueExtractor} instances
+     * @param cdiMapListenerManager  the CDI map listener manager
      */
     @Inject
-    NamedCacheProducer(BeanManager       beanManager,
-                       FilterProducer    filterProducer,
-                       ExtractorProducer extractorProducer)
+    NamedCacheProducer(BeanManager           beanManager,
+                       FilterProducer        filterProducer,
+                       ExtractorProducer     extractorProducer,
+                       CdiMapListenerManager cdiMapListenerManager)
         {
-        f_beanManager       = beanManager;
-        f_filterProducer    = filterProducer;
-        f_extractorProducer = extractorProducer;
+        f_beanManager           = beanManager;
+        f_filterProducer        = filterProducer;
+        f_extractorProducer     = extractorProducer;
+        f_cdiMapListenerManager = cdiMapListenerManager;
         }
 
     // ---- producer methods ------------------------------------------------
@@ -330,26 +341,168 @@ else if (View.class.equals(annotation.annotationType()))
         String  sSessionName = sSession;
         Instance instance = f_beanManager.createInstance()
                 .select(Session.class, Name.Literal.of(sSessionName));
-        Session session      = instance
-                                            .get();
-
-        C cache = (C) session.getCache(sName);
+        Session         session    = instance.get();
+        Set qualifiers = extractQualifiers(injectionPoint);
+        CacheId         cacheId    = new CacheId(sName, sSessionName);
+        final C         cache      = (C) f_cacheInstances.compute(cacheId, (id, current) ->
+            {
+            if (current != null && current.isActive())
+                {
+                return current;
+                }
+            C c = (C) session.getCache(id.f_cacheName);
+            f_cdiMapListenerManager.registerCacheListeners(id.f_cacheName, session.getScopeName(), id.f_sessionName, c.getCacheService().getInfo().getServiceName());
+            return c;
+            });
 
         if (fView)
             {
-            Filter         filter    = f_filterProducer.getFilter(injectionPoint);
-            ValueExtractor extractor = f_extractorProducer.getValueExtractor(injectionPoint);
+            final boolean fCacheVals = fCacheValues;
+            CacheId       cqcId      = new CacheId(sName, sSessionName, qualifiers);
+            return (C) f_cqcInstances.compute(cqcId, (id, current) ->
+                {
+                if (current != null && current.isActive())
+                    {
+                    return current;
+                    }
+                Filter         filter    = f_filterProducer.getFilter(injectionPoint);
+                ValueExtractor extractor = f_extractorProducer.getValueExtractor(injectionPoint);
+                return new ContinuousQueryCache<>(cache, filter, fCacheVals, null, extractor);
+                });
+            }
+        return cache;
+        }
+
+    private Set extractQualifiers(InjectionPoint injectionPoint)
+        {
+        Annotated annotated = injectionPoint.getAnnotated();
+        if (annotated == null)
+            {
+            return Collections.emptySet();
+            }
+        return annotated.getAnnotations().stream()
+                .filter(annotation -> annotation.annotationType().isAnnotationPresent(FilterBinding.class) 
+                                      || annotation.annotationType().isAnnotationPresent(ExtractorBinding.class)
+                                      || annotation.annotationType().isAnnotationPresent(View.class))
+                .collect(Collectors.toSet());
+        }
+
+    /**
+     * A cache identifier used as a key for Coherence caches.
+     */
+    static class CacheId 
+        {
+        // ---- constructors ----------------------------------------------------
+
+        /**
+         * Construct a CacheId.
+         * 
+         * @param cacheName    cache name
+         * @param sessionName  session name
+         */
+        CacheId(String cacheName, String sessionName)
+            {
+            this(cacheName, sessionName, null);
+            }
+
+        /**
+         * Construct a CacheId.
+         *
+         * @param cacheName    cache name
+         * @param sessionName  session name
+         * @param qualifiers   qualifiers
+         */
+        CacheId(String cacheName, String sessionName, Set qualifiers)
+            {
+            this.f_cacheName   = cacheName;
+            this.f_sessionName = sessionName;
+            this.f_qualifiers  = qualifiers;
+            }
+
+        /**
+         * Return cache name.
+         *
+         * @return cache name
+         */
+        public String getCacheName()
+            {
+            return f_cacheName;
+            }
 
-            return (C) new ContinuousQueryCache<>(cache, filter, fCacheValues, null, extractor);
+        /**
+         * Return the session name.
+         *
+         * @return the session name
+         */
+        public String getSessionName()
+            {
+            return f_sessionName;
             }
-        else
+
+        /**
+         * Return qualifiers.
+         * 
+         * @return the qualifiers
+         */
+        public Set getQualifiers()
             {
-            return cache;
+            return f_qualifiers;
             }
+
+        // ----- Object methods -------------------------------------------------
+
+        @Override
+        public boolean equals(Object o)
+            {
+            if (!(o instanceof CacheId))
+                {
+                return false;
+                }
+            CacheId cacheId = (CacheId) o;
+            return Objects.equals(f_cacheName, cacheId.f_cacheName) 
+                   && Objects.equals(f_sessionName, cacheId.f_sessionName) 
+                   && Objects.equals(f_qualifiers, cacheId.f_qualifiers);
+            }
+
+        @Override
+        public int hashCode()
+            {
+            int result = Objects.hashCode(f_cacheName);
+            result = 31 * result + Objects.hashCode(f_sessionName);
+            result = 31 * result + Objects.hashCode(f_qualifiers);
+            return result;
+            }
+
+        // ---- data members ----------------------------------------------------
+
+        /**
+         * Cache name.
+         */
+        private final String f_cacheName;
+
+        /**
+         * Session name.
+         */
+        private final String f_sessionName;
+
+        /**
+         * Qualifiers.
+         */
+        private final Set f_qualifiers;
         }
 
     // ---- data members ----------------------------------------------------
 
+    /**
+     * The cached instances of NamedCache.
+     */
+    private final Map f_cacheInstances = new ConcurrentHashMap<>();
+
+    /**
+     * The cached instances of ContinuousQueryCache.
+     */
+    private final Map f_cqcInstances = new ConcurrentHashMap<>();
+
     /**
      * The CDI bean manager.
      */
@@ -364,4 +517,9 @@ else if (View.class.equals(annotation.annotationType()))
      * The producer of {@link com.tangosol.util.ValueExtractor} instances.
      */
     private final ExtractorProducer f_extractorProducer;
+
+    /**
+     * The CDI map listener manager.
+     */
+    private final CdiMapListenerManager f_cdiMapListenerManager;
     }
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/PofExtractor.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/PofExtractor.java
index 3027e4c2b0050..b10764671273c 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/PofExtractor.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/PofExtractor.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates.
+ * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi;
 
@@ -174,6 +174,7 @@ public static Literal of(int... value)
         /**
          * Create a {@link PofExtractor.Literal}.
          *
+         * @param type  the type to extract property from
          * @param value the POF indexes to use to extract the value
          *
          * @return a {@link PofExtractor.Literal} with the specified value
@@ -186,7 +187,8 @@ public static Literal of(Class type, int... value)
        /**
          * Create a {@link PofExtractor.Literal}.
          *
-         * @param sPath  the POF indexes to use to extract the value
+         * @param type   the type to extract property from
+         * @param sPath  the property path to use to extract the value
          *
          * @return a {@link PofExtractor.Literal} with the specified value
          */
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/AnnotatedMapListener.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/AnnotatedMapListener.java
index 477125a1b095b..8c04f73f75939 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/AnnotatedMapListener.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/AnnotatedMapListener.java
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates.
+ * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi.events;
 
@@ -33,6 +33,9 @@
  * {@link MapListener} implementation that dispatches {@code MapEvent}s
  * to a CDI observer.
  *
+ * @param   the type of the map entry keys
+ * @param   the type of the map entry values
+ *
  * @author Aleks Seovic  2020.04.14
  * @since 20.06
  */
@@ -427,6 +430,7 @@ public interface FilterProducer
          * Produce a {@link Filter} instance from a set of annotations.
          *
          * @param annotations  the annotations to use to produce the {@link Filter}
+         * @param           the type of the input argument to the filter
          *
          * @return an instance of a {@link Filter}
          */
@@ -445,6 +449,9 @@ public interface MapEventTransformerProducer
          * Produce a {@link MapEventTransformer} instance from a set of annotations.
          *
          * @param annotations  the annotations to use to produce the {@link MapEventTransformer}
+         * @param           the type of the event's key
+         * @param           the type of event's value
+         * @param           the type of resulting transformed value
          *
          * @return an instance of a {@link MapEventTransformer}
          */
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/EventObserverSupport.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/EventObserverSupport.java
index 4582e7d1749b7..800a410743f0a 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/EventObserverSupport.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/events/EventObserverSupport.java
@@ -2,7 +2,7 @@
  * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 package com.oracle.coherence.cdi.events;
 
@@ -93,7 +93,8 @@ EventHandler createObserver(Class type, EventObserver observer)
 
     /**
      * An observer of a specific event type.
-     * @param 
+     *
+     * @param   event type
      */
     @SuppressWarnings("rawtypes")
     public interface EventObserver
diff --git a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/package-info.java b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/package-info.java
index 7561b41d64705..df34639a29436 100644
--- a/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/package-info.java
+++ b/prj/coherence-cdi/src/main/java/com/oracle/coherence/cdi/package-info.java
@@ -1,19 +1,19 @@
 /*
- * Copyright (c) 2019, 2020, Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2022, Oracle and/or its affiliates.
  *
  * Licensed under the Universal Permissive License v 1.0 as shown at
- * http://oss.oracle.com/licenses/upl.
+ * https://oss.oracle.com/licenses/upl.
  */
 
 /**
  * Coherence CDI provides support for CDI (Contexts and Dependency Injection)
  * within Coherence cluster members.
- * 

+ *

* It allows you both to inject Coherence-managed resources, such as * `NamedCache` and `Session` instances into CDI managed beans, and to inject * CDI beans into Coherence-managed resources, such as event interceptors and * cache stores. - *

+ *

* In addition, Coherence CDI provides support for automatic injection of * transient objects upon deserialization. This allows you to inject CDI managed * beans such as services and repositories (to use DDD nomenclature) into diff --git a/prj/coherence-concurrent/README.adoc b/prj/coherence-concurrent/README.adoc index c5b231ba7bbf6..0c99fdeceb572 100644 --- a/prj/coherence-concurrent/README.adoc +++ b/prj/coherence-concurrent/README.adoc @@ -12,11 +12,11 @@ Coherence Concurrent module provides distributed implementations of the concurrency primitives from the `java.util.concurrent` package that you are already familiar with, such as executors, atomics, locks, semaphores and latches. -This allows you to implement concurrent applications using the constructs you are already familiar with, but to expand the "scope" of concurrency from a single process to potentially hundreds of processes within a Coherence cluster. You can use executors to submit tasks to be executed somewhere in the cluster; you can use locks, latches and semaphores to synchronize execution across many cluster members; you can use atomics to implement global counters across many processes, etc. +This allows you to implement concurrent applications using the constructs you are already familiar with, but to expand the "scope" of concurrency from a single process to potentially hundreds of processes within a Coherence cluster. For example - you can use executors to submit tasks to be executed somewhere in the cluster; you can use locks, latches and semaphores to synchronize execution across many cluster members; you can use atomics to implement global counters across many processes, etc. Please keep in mind that while these features are extremely powerful and allow you to reuse the knowledge you already have, they may have detrimental effect on scalability and/or performance. Whenever you synchronize execution via locks, latches or semaphores, you are introducing a potential bottleneck into the architecture. Whenever you use a distributed atomic to implement a global counter, you are turning very simple operations that take mere nanoseconds locally, such as increment and decrement, into fairly expensive network calls that could take milliseconds (and potentially block even longer under heavy load). -So, use these features sparingly. In many cases there is a better, faster and more scalable way to accomplish the same goal using Coherence primitives such as entry processors, aggregators and events, which were designed to perform and scale well in a distributed environment from the get go. +So, use these features sparingly. In many cases there is a better, faster and more scalable way to accomplish the same goal using Coherence primitives such as entry processors, aggregators and events, which were designed to perform and scale well in a distributed environment from the get-go. === Factory Classes @@ -28,7 +28,7 @@ For example, you will use factory methods within `Atomics` class to get instance In many cases the factory classes will allow you to get both the *local* and the *remote* instances of various constructs. For example, `Locks.localLock` will give you an instance of a standard `java.util.concurrent.locks.ReentrantLock`, while `Locks.remoteLock` will return an instance of a `RemoteLock`. In cases where JDK doesn't provide a standard interface, which is the case with atomics, latches and semaphores, we've extracted the interface from the existing JDK class, and created a thin wrapper around the corresponding JDK implementation. For example, Coherence Concurrent provides a `Semaphore` interface, and `LocalSemaphore` class that wraps `java.util.concurrent.Semaphore`. The same is true for the `CountDownLatch`, and all atomic types. -The main advantage of using factory classes to construct both the local and the remote instances is that it allows you to name local locks the same way you have to name remote locks: calling `Locks.localLock("foo")` will always return the same `Lock` instance, as the `Locks` class internally caches both the local and the remote instances it created. Of course, in the case of remote locks, every locally cached remote lock instance is ultimately backed by a shared lock instance somewhere in the cluster, which is used to synchronize lock state across the processes. +The main advantage of using factory classes to construct both the local and remote instances is that it allows you to name local locks the same way you name remote locks.: calling `Locks.localLock("foo")` will always return the same `Lock` instance, as the `Locks` class internally caches both the local and the remote instances it created. Of course, in the case of remote locks, every locally cached remote lock instance is ultimately backed by a shared lock instance somewhere in the cluster, which is used to synchronize lock state across the processes. === Serialization @@ -58,62 +58,337 @@ In order to use Coherence Concurrent features, you need to declare it as a depen Once the necessary dependency is in place, you can start using the features it provides, as the following sections describe. * <> - ** <> - ** <> - ** <> - *** <> - ** <> - ** <> - ** <> +** <> +** <> +*** <> +**** <> +**** <> +**** <> +**** <> +**** <> +**** <> +*** <> +** <> +*** <> +** <> +** <> +** <> * <> - ** <> - ** <> +** <> +** <> * <> - ** <> - ** <> - ** <> +** <> +** <> +** <> * <> - ** <> - ** <> - ** <> +** <> +** <> +** <> [#executors] === Executors [#executors-overview] -==== Overview -Coherence Concurrent provides a facility to dispatch tasks, either a `Runnable` or `Callable` to +=== Overview +Coherence Concurrent provides a facility to dispatch tasks, either a `Runnable`, `Callable`, or `Task` to a Coherence cluster for execution. Executors that will actually execute the submitted tasks are configured on each cluster member by defining one or more named executors within a cache configuration resource. [#executors-usage] -==== Usage Examples +=== Usage Examples By default, each Coherence cluster with the `coherence-concurrent` module on the classpath, will include a single-threaded executor that may be used to execute dispatched tasks. Given this, the simplest example would be: -```java +[source,java] +---- RemoteExecutor remoteExecutor = RemoteExecutor.getDefault(); Future result = remoteExecutor.submit(() -> System.out.println("Executed")); result.get(); // block until completion -``` +---- If for example, an executor was configured named `Fixed5`, the code would be: -```java +[source,java] +---- RemoteExecutor remoteExecutor = RemoteExecutor.get("Fixed5"); -``` +---- + +If no executor has been configured with the given name, the `RemoteExecutor` +will throw `RejectedExecutionException`. + +Each `RemoteExecutor` instance may hold local resources that should be released +when the `RemoteExecutor` is no longer needed. Like an `ExecutorService`, +a `RemoteExecutor` has similar methods to shut the executor down. +When calling these methods, it will have no impact on the executors registered +within the cluster. + +[#executors-orchestration] +=== Orchestration +While the `RemoteExecutor` does provide functionality similar to the standard `ExecutorService` included in the JDK, this may not be enough in the context of Coherence. A task might need to run across multiple Coherence members, produce intermediate results, and remain durable in case a cluster member executing the task fails. In such cases, task orchestration can be used. Before diving into the details of +orchestration, the following concepts should be understood: + +|=== +|Interface |Description + +|Task +|Tasks are like `Callable` and `Runnable` classes in that +they are designed to be potentially executed by one or more threads. +Unlike `Callable` and `Runnable` classes, the execution may occur in different Java Virtual Machines, fail and/or recover between different Java Virtual Machine processes. + +|Task.Context +|Provides contextual information for a `Task` as it is executed, including +the ability to access and update intermediate results for the `Executor` +executing the said `Task`. + +|Task.Orchestration +|Defines information concerning the orchestration of a `Task` across a +set of executors defined across multiple Coherence members for a given +`RemoteExecutor`. + +|Task.Coordinator +|A publisher of collected `Task` results that additionally permits +* coordination of the submitted `Task`. + +|Task.Subscriber +|A receiver of items produced by a `Task.Coordinator`. + +|Task.Properties +|State sharing mechanism for tasks. + +|Task.Collector +|A mutable reduction operation that accumulates results into a mutable result +container, optionally transforming the accumulated result into a final +representation after all results have been processed. +|=== + +[#executors-orchestration-tasks] +=== Tasks + +`Task` implementations define a single method called `execute(Context)` +that performs the task, possibly yielding execution to some later point. +Once the method has completed execution, by returning a result or throwing +an exception (but not a `Yield` exception), the task is considered completed +for the assigned `Executor`. + +A `Task` may yield execution for a given time by throwing a `Yield` exception. +This exception type signals the execution of a `Task` by an `Executor` is to +be suspended and resumed at some later point in time, typically by the same `Executor`. + +[#executors-orchestration-context] +=== Task Context +When a `Task` is executed a `Context` instance will be passed as an execution +argument. +The `Context` provides access to task properties allowing shared state between tasks running in multiple Java Virtual Machines. +The `Context` provides details on overall execution status: + +|=== +|Execution State |Method| Description + +|Complete +|`Context.isDone()` +|Allows a `Task` to determine if the task is complete. +Completion may be due to normal termination, an exception or cancellation. +In all of these cases, this method will return `true`. + +|Cancelled +|`Context.isCancelled()` +|Allows a `Task` to determine if the task is effectively cancelled. + +|Resuming +|`Context.isResuming()` +|Determines if a `Task` execution by an `Executor` resuming +after being recovered (i.e. fail-over) or due to resumption after a task +had previously thrown a `Yield` exception. + +|=== + +[#executors-orchestration-orchestration] +=== Task Orchestration + +Orchestrations begin by calling `RemoteExecutor.orchestrate(Task)` which +will return a `Task.Orchestration` instance for the given `Task`. +With the `Task.Orchestration`, it's possible to configure the aspects +of where the task will be run. + +|=== +|Method |Description + +|concurrently() +|Tasks will be run, concurrently, across all Java Virtual Machines where +the named executor is defined/configured. This is the default. + +|sequentially() +|Tasks will be run, in sequence, across all Java Virtual Machines where +the named executor is defined/configured. -If no executor has been configured with the given name, the `RemoteExecutor` will throw `RejectedExecutionException`. +|limit(int) +|Limit the task to `n` executors. Use this to limit the number of +executors that will be considered for task execution. If not set, the default behavior is to run the task on all Java Virtual Machine where +the named executor is defined/configured. -Each `RemoteExecutor` instance may hold local resources that should be released when the `RemoteExecutor` is no longer needed. Like an `ExecutorService`, a `RemoteExecutor` has similar methods to shut the executor down. When calling these methods, it will have no impact on the executors registered within the cluster. +|filter(Predicate) +|Filtering provides an additional way to constrain where a task may be run. +The predicates will be applied against metadata associated with each executor on each Java Virtual Machine. Some examples of metadata would be the member in which the executor is running, or the role of a member. +Predicates may be chained to provide boolean logic in determining an appropriate executor. + +|define(String, ) +|Define initial state that will be available to all tasks no matter which Java Virtual Machine that task is running on. + +|retrain(Duration) +|When specified, the task will be retained allowing new subscribers to be notified of the final result of a task computation after it has completed. + +|collect(Collector) +|This is the terminal of the orchestration builder returning a `Task.Collectable` which defines how results are to be collected and ultimately submits the task to the grid. + +|=== + +[#executors-orchestration-collect] +=== Task Collector and Collectable + +The `Task.Collector` passed to the orchestration will collect results from +tasks and optionally transforms the collected results into a final format. +Collectors are best illustrated by using examples of Collectors that are available in the `TaskCollector` class: + +|=== +|Method |Description + +|count() +|The count of non-null results that have been collected from the executing task(s). + +|firstOf() +|Collects and returns the first result provided by the executing task(s). + +|lastOf() +|Collects and returns the last result returned by the executing task(s). + +|setOf() +|Collects and returns all non-null results as a Set. + +|listOf() +|Collects and returns all non-null results as a List. + +|=== + +The `Task.Collectable` instance returned by calling `collect` on the orchestration allows, among other things, setting the condition under which +no more results will be collected or published any registered subscribers. +Calling `submit()` on the `Task.Collectable` will being the orchestration of the task. + +[#executors-orchestration-coordinator] +=== Task Coordinator +Upon calling `submit()` on the orchestration `Collectable`, a `Task.Coordinator` is returned. Like the `Task.Collectable` the `Task.Coordinator` allows for the registration of subscribers. Additionally, +provides the ability to cancel or check the completion status of the orchestration. + +[#executors-orchestration-subscriber] +=== Task Subscriber +The `Task.Subscriber` receives various events pertaining to the execution status of the orchestration: + +|=== +|Method |Description + +|onComplete() +|Signals the completion of the orchestration. + +|onError(Throwable) +|Called when an unrecoverable error (given as the argument) +has occurred. + +|onNext() +|Called when the `Task.Coordinator` has produced a result. + +|onSubscribe(Task.Subscription) +|Called prior to any calls to `onComplete()`, `onError(Throwable)`, or `onNext()` are called. The `Task.Subscription` provided gives access to +cancelling the subscription, or obtaining a reference to the `Task.Coordinator`. + +|=== + +[#executors-orchestration-examples] +=== Advanced Orchestration Examples + +To begin, consider the following code common to the orchestration examples: + +[source,java] +---- +// demonstrate orchestration using the default RemoteExecutor +RemoteExecutor executor = RemoteExecutor.getDefault(); + +// WaitingSubscriber is an implementation of the +// com.oracle.coherence.concurrent.executor.Task.Subscriber interface +// that has a get() method that blocks until Subscriber.onComplete() is +// called and will return the results received by onNext() +WaitingSubscriber subscriber = new WaitingSubscriber(); + +// ValueTask is an implementation of the +// com.oracle.coherence.concurrent.executor.Task interface +// that returns the value provided at construction time +ValueTask task = new ValueTask("Hello World"); +---- + +Given the above, the simplest example of an orchestration: + +[source,java] +---- +// orchestrate the task, subscribe, and submit +executor.orchestrate(task) + .subscribe(subscriber) + .submit(); + +// wait for the task to complete +// if this was run on four cluster members running the default executor service, +// the returned Collection will have four results +Collection results = subscriber.get(); +---- + +Building on the above, assume a cluster with two storage and two proxy members. +The cluster members are configured with the roles of `storage` and `proxy`, +respectively. Let's say the task needs to run on `storage` members only, then +the orchestration could look like: + +[source,java] +---- +// orchestrate the task, filtering by a role, subscribe, and submit +executor.orchestrate(task) + .filter(Predicates.role("storage")) + .subscribe(subscriber) + .submit(); + +// wait for the task to complete +// as there are only two storage members in this hypothetical, only two +// results will be returned +Collection results = subscriber.get(); +---- +There are several predicates available for use in `com.oracle.coherence.concurrent.executor.function.Predicates`, +however, in the case none apply to the target use case, simply implement the +`Remote.Predicate` interface. + +Collection of results and how they are presented to the subscriber +can be customized by using `collect(Collector)` and `until(Predicate)`: + +[source,java] +---- +// orchestrate the task, collecting the first non-null result, +// subscribe, and submit +executor.orchestrate(new MayReturnNullTask()) + .collect(TaskCollectors.firstOf()) + .until(Predicates.nonNullValue()) + .subscribe(subscriber) + .submit(); + +// wait for the task to complete +// the first non-result returned will be the one provided to the +// subscriber +Collection results = subscriber.get(); +---- +Several collectors are provided in `com.oracle.coherence.concurrent.executor.TaskCollectors`, +however, in the case none apply to the target use case, implement the +`Task.Collector` interface. [#executors-configuration] -==== Configuration +=== Configuration Several executor types are available for configuration. @@ -121,19 +396,22 @@ Several executor types are available for configuration. |ExecutorService Type |Description |Single thread -|Creates an ExecutorService with a single thread +|Creates an ExecutorService with a single thread. |Fixed thread -|Creates an ExecutorService with a fixed number of threads +|Creates an ExecutorService with a fixed number of threads. |Cached -|Create an ExecutorService that will create new threads as needed and reuse existing threads when possible +|Create an ExecutorService that will create new threads as needed and reuse existing threads when possible. |Work stealing |Creates a work-stealing thread pool using the number of available processors as its target parallelism level. + +|Custom +|Allows the creation of non-standard executors. |=== -===== Configuration Elements +==== Configuration Elements |=== |Element Name |Required |Expected Type |Description @@ -158,6 +436,11 @@ Several executor types are available for configuration. |N/A |Defines a work-stealing-pool executor +|custom-executor +|no +|java.util.concurrent.ExecutorService +|Defines a custom executor + |name |yes |java.lang.String @@ -180,8 +463,8 @@ Several executor types are available for configuration. |instance |yes -|java.util.concurrent.ThreadFactory -|Defines how the ThreadFactory will be instantiated. See the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/cache-configuration-elements.html#GUID-D81B8574-CC8F-4AF1-BD0F-7068BC6432FD[docs] for details on the `instance` element. This element must be a child of the `thread-factory` element. +|Depending on the context, it will yield either a `java.util.concurrent.ExecutorService` or a `java.util.concurrent.ThreadFactory` +|Defines how the ThreadFactory or the ExecutorService will be instantiated. See the https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/develop-applications/cache-configuration-elements.html#GUID-D81B8574-CC8F-4AF1-BD0F-7068BC6432FD[docs] for details on the `instance` element. This element must be a child of the `thread-factory` element. |=== See the https://github.com/oracle/coherence/blob/master/prj/coherence-concurrent/src/main/resources/concurrent.xsd[schema] for full details. @@ -191,7 +474,8 @@ See the https://github.com/oracle/coherence/blob/master/prj/coherence-concurrent To define executors, the `cache-config` root element needs to include the `coherence-concurrent` NamespaceHandler in order to recognize the configuration elements. -```xml +[source,xml] +---- -``` +---- TIP: Executors defined by configuration must precede any other elements in the document. Failing to do so, will prevent the document from validating. The following examples assume the xml namespace defined for the NamespaceHandler is `c`: -```xml - +[source,xml] +---- + Single -``` +---- -```xml - +[source,xml] +---- + SingleTF - - my.custom.ThreadFactory - + + my.custom.ThreadFactory + -``` +---- -```xml - +[source,xml] +---- + - Single + Fixed5 5 -``` +---- + +[source,xml] +---- + + + custom + + com.acme.CustomExecutorFactory + createExecutor + + +---- [#executors-management] ==== Management @@ -239,9 +538,10 @@ The ExecutorMBean represents the operational state of a registered executor. The object name of the MBean is: -``` +[source] +---- type=Executor,name=,nodeId= -``` +---- ===== ExecutorMBean Attributes @@ -320,12 +620,12 @@ ExecutorMBean instances. |View all Executors with matching name |GET -|/management/coherence/cluster/executors/{name} +|/management/coherence/cluster/executors/+{name}+ | JSON |Reset Executor statistics by name |POST -|/management/coherence/cluster/executors/{name}/resetStatistics +|/management/coherence/cluster/executors/+{name}+/resetStatistics | JSON |=== @@ -335,14 +635,15 @@ ExecutorMBean instances. RemoteExecutors may be injected via CDI. For example: -```java +[source,java] +---- @Inject private RemoteExecutor single; // <1> @Inject @Name("Fixed5") private RemoteExecutor fixedPoolRemoteExecutor; // <2> -``` +---- <1> injects a RemoteExecutor named `single`. <2> injects a `RemoteExecutor` named `Fixed5`. @@ -353,32 +654,35 @@ Coherence Concurrent provides distributed implementations of atomic types, such To create instances of atomic types you need to call the appropriate factory method on the `Atomics` class: -```java +[source,java] +---- AtomicInteger localFoo = Atomics.localAtomicInteger("foo"); // <1> AtomicInteger remoteFoo = Atomics.remoteAtomicInteger("foo"); // <2> AtomicLong remoteBar = Atomics.remoteAtomicLong("bar", 5L); // <3> -``` +---- <1> creates a local, in-process instance of named `AtomicInteger` with an implicit initial value of 0 <2> creates a remote, distributed instance of named `AtomicInteger`, distinct from the local instance `foo`, with an implicit initial value of 0 <3> creates a remote, distributed instance of named `AtomicLong`, with an initial value of 5 -Note that the `AtomicInteger` and `AtomicLong` types used above _are not_ types from the `java.util.concurrent.atomic` package they you are familiar with -- they are actually interfaces defined within `com.oracle.coherence.concurrent.atomic` package, that both `LocalAtomicXyz` and `RemoteAtomicXyz` classes implement, which are the instances that are actually returned by the methods above. +Note that the `AtomicInteger` and `AtomicLong` types used above _are not_ types from the `java.util.concurrent.atomic` package that you are familiar with -- they are actually interfaces defined within `com.oracle.coherence.concurrent.atomic` package, that both `LocalAtomicXyz` and `RemoteAtomicXyz` classes implement, which are the instances that are actually returned by the methods above. That means that the above code could be rewritten as: -```java +[source,java] +---- LocalAtomicInteger localFoo = Atomics.localAtomicInteger("foo"); RemoteAtomicInteger remoteFoo = Atomics.remoteAtomicInteger("foo"); RemoteAtomicLong remoteBar = Atomics.remoteAtomicLong("bar", 5L); -``` +---- However, we strongly suggest that you use interfaces instead of concrete types, as they make it easy to switch between local and distributed implementations when necessary. Once created, these instances can be used the same way you would use any of the corresponding `java.util.concurrent.atomic` types: -```java +[source,java] +---- int counter1 = remoteFoo.incrementAndGet(); long counter5 = remoteBar.addAndGet(5L); -``` +---- [#atomics-async] ==== Asynchronous Implementations @@ -391,20 +695,22 @@ To reduce the impact of remote calls in those situations, Coherence Concurrent a To obtain a non-blocking instance of any supported atomic type, simply call `async` method on the blocking instance of that type: -```java +[source,java] +---- AsyncAtomicInteger asyncFoo = Atomics.remoteAtomicInteger("foo").async(); // <1> AsyncAtomicLong asyncBar = Atomics.remoteAtomicLong("bar", 5L).async(); // <2> -``` +---- <1> creates a remote, distributed instance of named, non-blocking `AsyncAtomicInteger`, with an implicit initial value of 0 <2> creates a remote, distributed instance of named, non-blocking `AsyncAtomicLong`, with an initial value of 5 Once created, these instances can be used the same way you would use any of the corresponding blocking types. The only difference is that they will simply return a `CompletableFuture` for the result, and will not block: -```java +[source,java] +---- CompletableFuture futureCounter1 = asyncFoo.incrementAndGet(); CompletableFuture futureCounter5 = asyncBar.addAndGet(5L); -``` +---- Both the blocking and the non-blocking instance of any distributed atomic type, with the same name, are backed by the same cluster-side atomic instance state, so they can be used interchangeably. @@ -413,7 +719,8 @@ Both the blocking and the non-blocking instance of any distributed atomic type, Atomic types from Coherence Concurrent can also be injected using CDI, which eliminates the need for explicit factory method calls on the `Atomics` class. -```java +[source,java] +---- @Inject @Name("foo") private AtomicInteger localFoo; // <1> @@ -425,9 +732,9 @@ private AtomicInteger remoteFoo; // <2> @Inject @Remote -private AsyncAtomicLong asyncBar // <3> +private AsyncAtomicLong asyncBar; // <3> -``` +---- <1> injects a local, in-process instance of an `AtomicInteger` named `foo`, with an implicit initial value of 0 <2> injects a remote, distributed instance of an `AtomicInteger` named `foo`, distinct from the local instance `foo`, with an implicit initial value of 0 <3> injects a remote, distributed instance of non-blocking `AsyncAtomicLong`, with an implicit name of `asyncBar` @@ -448,19 +755,22 @@ A `RemoteLock` class provides an implementation of a `Lock` interface and allows To obtain an instance of a `RemoteLock`, call `Locks.remoteLock` factory method: -```java +[source,java] +---- Lock foo = Locks.remoteLock("foo"); -``` +---- Just like with `Atomics`, you can also obtain a local `Lock` instance from the `Locks` class, with will simply return an instance of a standard `java.util.concurrent.locks.ReentrantLock`, by calling `localLock` factory method: -```java +[source,java] +---- Lock foo = Locks.localLock("foo"); -``` +---- Once you have a `Lock` instance, you can use it as you normally would: -```java +[source,java] +---- foo.lock(); try { // critical section guarded by the exclusive lock `foo` @@ -468,7 +778,7 @@ try { finally { foo.unlock(); } -``` +---- [#read-write-locks] ==== Read/Write Locks @@ -477,46 +787,51 @@ A `RemoteReadWriteLock` class provides an implementation of a `ReadWriteLock` in To obtain an instance of a `RemoteReadWriteLock`, call `Locks.remoteReadWriteLock` factory method: -```java +[source,java] +---- ReadWriteLock bar = Locks.remoteReadWriteLock("bar"); -``` +---- Just like with `Atomics`, you can also obtain a local `ReadWriteLock` instance from the `Locks` class, with will simply return an instance of a standard `java.util.concurrent.locks.ReentrantReadWriteLock`, by calling `localReadWriteLock` factory method: -```java +[source,java] +---- ReadWriteLock bar = Locks.localReadWriteLock("bar"); -``` +---- Once you have a `ReadWriteLock` instance, you can use it as you normally would: -```java -bar.writeLock().lock() +[source,java] +---- +bar.writeLock().lock(); try { // critical section guarded by the exclusive write lock `bar` } finally { bar.writeLock().unlock(); } -``` +---- Or: -```java -bar.readLock().lock() +[source,java] +---- +bar.readLock().lock(); try { // critical section guarded by the shared read lock `bar` } finally { bar.readLock().unlock(); } -``` +---- [#cdi-locks] ==== CDI Support You can also use CDI to inject both the exclusive and read/write lock instances into objects that need them: -```java +[source,java] +---- @Inject @Remote @Name("foo") @@ -525,7 +840,7 @@ private Lock lock; // <1> @Inject @Remote private ReadWriteLock bar; // <2> -``` +---- <1> injects distributed exclusive lock named `foo` into `lock` field <2> injects distributed read/write lock named `bar` into `bar` field @@ -545,16 +860,18 @@ The `RemoteCoundDownLatch` class provides a distributed implementation of a `Cou To obtain an instance of a `RemoteCountDownLatch`, call `Latches.remoteCountDownLatch` factory method: -```java +[source,java] +---- CoundDownLatch foo = Latches.remoteCountDownLatch("foo", 5); // <1> -``` +---- <1> create an instance of a `RemoteCountDownLatch` with the initial count of 5 Just like with `Atomics` and `Locks`, you can also obtain a local `CountDownLatch` instance from the `Latches` class by calling `remoteCountDownLatch` factory method: -```java +[source,java] +---- CoundDownLatch foo = Latches.localCountDownLatch("foo", 10); // <1> -``` +---- <1> create an instance of a `LocalCountDownLatch` with the initial count of 10 Once you have a `RemoteCountDownLatch` instance, you can use it as you normally would, by calling `countDown` and `await` methods on it. @@ -566,16 +883,18 @@ The `RemoteSemaphore` class provides a distributed implementation of a `Semaphor To obtain an instance of a `RemoteSemaphore`, call `Semaphores.remoteSemaphore` factory method: -```java +[source,java] +---- Semaphore foo = Semaphores.remoteSemaphore("foo", 5); // <1> -``` +---- <1> create an instance of a `RemoteSemaphore` with 5 permits Just like with `Atomics` and `Locks`, you can also obtain a local `Semaphore` instance from the `Semaphores` class by calling `localSemaphore` factory method: -```java +[source,java] +---- Semaphore foo = Semaphores.localSemaphore("foo"); // <1> -``` +---- <1> create an instance of a `LocalSemaphore` with 0 permits Once you have a `Semaphore` instance, you can use it as you normally would, by calling `release` and `acquire` methods on it. @@ -585,7 +904,8 @@ Once you have a `Semaphore` instance, you can use it as you normally would, by c You can also use CDI to inject both the `CountDownLatch` and `Semaphore` instances into objects that need them: -```java +[source,java] +---- @Inject @Name("foo") @Count(5) @@ -607,7 +927,7 @@ private Semaphore localSemaphoreBar; // <3> @Remote @Permits(1) private Semaphore remoteSemaphoreBar; // <4> -``` +---- <1> inject an instance of a `LocalCountDownLatch` with the initial count of five <2> inject an instance of a `RemoteCountDownLatch` with the initial count of ten <3> inject an instance of a `LocalSemaphore` with zero permits available diff --git a/prj/coherence-concurrent/pom.xml b/prj/coherence-concurrent/pom.xml index 4a1e4fdd50a76..7771694dae0b4 100644 --- a/prj/coherence-concurrent/pom.xml +++ b/prj/coherence-concurrent/pom.xml @@ -1,6 +1,6 @@ + + org.apache.felix + maven-bundle-plugin + + + + + maven-jar-plugin + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + org.apache.maven.plugins diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/CountDownLatch.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/CountDownLatch.java index 3787a782ae65a..e6825f4d441b1 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/CountDownLatch.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/CountDownLatch.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent; @@ -10,6 +10,9 @@ import java.util.concurrent.TimeUnit; /** + * A synchronization aid that allows one or more threads to wait until a set + * of operations being performed in other threads completes. + * * @author Aleks Seovic 2021.12.05 * @since 21.12 */ diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Latches.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Latches.java index 71fe9dad2fc85..553c5a19ad96c 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Latches.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Latches.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent; @@ -17,7 +17,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; /** * Factory methods for various distributed countdown latch implementations. @@ -78,7 +77,7 @@ public static RemoteCountDownLatch remoteCountDownLatch(String sName, int count) /** * Return a singleton instance of a {@link LocalCountDownLatch} * with a specified name and initial count. - *

+ *

* The specified latch count is only relevant during the initial latch creation, * and is ignored if the latch already exists in the current process. That means * that the returned latch instance could have a different count from the one diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/PermitAcquirer.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/PermitAcquirer.java index 6034bc7670949..8e86e5bf52244 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/PermitAcquirer.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/PermitAcquirer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -11,8 +11,10 @@ import com.tangosol.io.pof.PofWriter; import com.tangosol.io.pof.PortableObject; +import com.tangosol.net.Member; + import com.tangosol.util.ExternalizableHelper; -import com.tangosol.util.UID; +import com.tangosol.util.UUID; import java.io.DataInput; import java.io.DataOutput; @@ -21,7 +23,7 @@ import java.util.Objects; /** - * The identity of a acquirer, represented by the UID of the member, and the ID + * The identity of an acquirer, represented by the UUID of the member, and the ID * of a thread holding or attempting to acquire permit. * * @author Vaso Putica 2021.11.30 @@ -39,21 +41,22 @@ public PermitAcquirer() /** * Construct {@code PermitAcquirer} instance. * - * @param memberId the member UID + * @param member the member * @param threadId the thread ID */ - public PermitAcquirer(UID memberId, long threadId) + public PermitAcquirer(Member member, long threadId) { - f_memberId = memberId; + f_memberId = member.getUuid(); f_threadId = threadId; + f_client = member.isRemoteClient(); } /** - * Return the member UID. + * Return the member UUID. * - * @return the member UID + * @return the member UUID */ - public UID getMemberId() + public UUID getMemberId() { return f_memberId; } @@ -68,6 +71,16 @@ public long getThreadId() return f_threadId; } + /** + * Return {@code true} if this permit acquirer is a remote client (Extend or gRPC). + * + * @return {@code true} if this permit acquirer is a remote client (Extend or gRPC) + */ + public boolean isClient() + { + return f_client; + } + @Override public boolean equals(Object o) { @@ -80,13 +93,13 @@ public boolean equals(Object o) return false; } PermitAcquirer acquirer = (PermitAcquirer) o; - return f_threadId == acquirer.f_threadId && f_memberId.equals(acquirer.f_memberId); + return f_threadId == acquirer.f_threadId && f_memberId.equals(acquirer.f_memberId) && f_client == acquirer.f_client; } @Override public int hashCode() { - return Objects.hash(f_memberId, f_threadId); + return Objects.hash(f_memberId, f_threadId, f_client); } @Override @@ -95,6 +108,7 @@ public String toString() return "PermitAcquirer{" + "memberId=" + f_memberId + ", threadId=" + f_threadId + + ", client=" + f_client + '}'; } @@ -105,6 +119,7 @@ public void readExternal(DataInput in) throws IOException { f_memberId = ExternalizableHelper.readObject(in); f_threadId = ExternalizableHelper.readLong(in); + f_client = in.readBoolean(); } @Override @@ -112,6 +127,7 @@ public void writeExternal(DataOutput out) throws IOException { ExternalizableHelper.writeObject(out, f_memberId); ExternalizableHelper.writeLong(out, f_threadId); + out.writeBoolean(f_client); } // ----- PortableObject interface ------------------------------- @@ -121,6 +137,7 @@ public void readExternal(PofReader in) throws IOException { f_memberId = in.readObject(0); f_threadId = in.readLong(1); + f_client = in.readBoolean(2); } @Override @@ -128,10 +145,23 @@ public void writeExternal(PofWriter out) throws IOException { out.writeObject(0, f_memberId); out.writeLong(1, f_threadId); + out.writeBoolean(2, f_client); } // ---- data members ---------------------------------------------------- - private UID f_memberId; + /** + * The member {@link UUID}. + */ + private UUID f_memberId; + + /** + * The thread ID. + */ private long f_threadId; + + /** + * Flag indicating the member is a remote client. + */ + private boolean f_client; } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/RemoteSemaphore.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/RemoteSemaphore.java index da22c6cf80de5..f3a48d788f636 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/RemoteSemaphore.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/RemoteSemaphore.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2022 Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent; @@ -13,7 +13,6 @@ import com.tangosol.util.MapEvent; import com.tangosol.util.Processors; -import com.tangosol.util.UID; import com.tangosol.util.function.Remote; import com.tangosol.util.listener.SimpleMapListener; @@ -477,7 +476,7 @@ static class Sync extends AbstractQueuedSynchronizer f_sName = sName; f_semaphores = semaphores; - f_memberId = localMember.getUid(); + f_localMember = localMember; f_initialPermits = permits; setState(permits); @@ -495,7 +494,7 @@ protected int tryAcquireShared(final int acquires) final Thread thread = Thread.currentThread(); final int initialPermits = f_initialPermits; - final PermitAcquirer acquirer = new PermitAcquirer(f_memberId, thread.getId()); + final PermitAcquirer acquirer = new PermitAcquirer(f_localMember, thread.getId()); final Remote.Supplier supplier = () -> new SemaphoreStatus(initialPermits); Integer acquired = f_semaphores.invoke(f_sName, entry -> { @@ -524,7 +523,7 @@ protected final boolean tryReleaseShared(int releases) } final Thread thread = Thread.currentThread(); - final PermitAcquirer acquirer = new PermitAcquirer(f_memberId, thread.getId()); + final PermitAcquirer acquirer = new PermitAcquirer(f_localMember, thread.getId()); Integer state = f_semaphores.invoke(f_sName, entry -> { SemaphoreStatus status = entry.getValue(); @@ -548,7 +547,7 @@ protected final boolean tryReleaseShared(int releases) final int drainPermits() { final Thread thread = Thread.currentThread(); - final PermitAcquirer acquirer = new PermitAcquirer(f_memberId, thread.getId()); + final PermitAcquirer acquirer = new PermitAcquirer(f_localMember, thread.getId()); Integer cur = f_semaphores.invoke(f_sName, entry -> { SemaphoreStatus status = entry.getValue(); @@ -563,7 +562,7 @@ final int drainPermits() final void reducePermits(int reductions) { final Thread thread = Thread.currentThread(); - final PermitAcquirer acquirer = new PermitAcquirer(f_memberId, thread.getId()); + final PermitAcquirer acquirer = new PermitAcquirer(f_localMember, thread.getId()); Integer nextState = f_semaphores.invoke(f_sName, entry -> { SemaphoreStatus status = entry.getValue(); @@ -596,7 +595,7 @@ public int getInitialPermits() final boolean isAcquiredByThread(Thread thread) { - final PermitAcquirer acquirer = new PermitAcquirer(f_memberId, thread.getId()); + final PermitAcquirer acquirer = new PermitAcquirer(f_localMember, thread.getId()); return f_semaphores.invoke(f_sName, entry -> { SemaphoreStatus status = entry.getValue(); @@ -619,15 +618,14 @@ private void onSemaphoreStatusChange(MapEvent 0 && status.getPermits() > oldStatus.getPermits() - && !f_memberId.equals(status.getMember())) + && !f_localMember.getUuid().equals(status.getMember())) { // other node released permits; put local semaphore back in business releaseShared(-1); @@ -639,7 +637,7 @@ else if (status.getPermits() > 0 /** * Local member/current process identifier. */ - private final UID f_memberId; + private final Member f_localMember; /** * The name of the remote semaphore; used as a key in the NamedMap containing the semaphores. diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Semaphore.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Semaphore.java index 20f1463587390..a3df7bec4cfab 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Semaphore.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/Semaphore.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent; @@ -12,6 +12,12 @@ /** + * A counting semaphore. Conceptually, a semaphore maintains a set of permits. + * Each {@link #acquire()} blocks if necessary until a permit is available, + * and then takes it. Each {@link #release()} adds a permit, potentially + * releasing a blocking acquirer. However, no actual permit objects are used; + * the Semaphore just keeps a count of the number available and acts accordingly. + * * @author Aleks Seovic 2021.12.05 * @since 21.12 */ diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/atomic/Atomics.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/atomic/Atomics.java index 7e9547c52b4c2..c24f4e25d4f5f 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/atomic/Atomics.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/atomic/Atomics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -463,8 +463,8 @@ public SerializableAtomicStampedReference() /** * See {@link com.oracle.coherence.concurrent.atomic.AtomicStampedReference} * - * @param initialRef {@inheritDoc} - * @param initialStamp {@inheritDoc} + * @param initialRef the initial reference + * @param initialStamp the initial stamp */ public SerializableAtomicStampedReference(V initialRef, int initialStamp) { @@ -515,8 +515,8 @@ public SerializableAtomicMarkableReference() /** * See {@link com.oracle.coherence.concurrent.atomic.AtomicMarkableReference} * - * @param initialRef {@inheritDoc} - * @param initialMark {@inheritDoc} + * @param initialRef the initial reference + * @param initialMark the initial mark */ public SerializableAtomicMarkableReference(V initialRef, boolean initialMark) { diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentConfiguration.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentConfiguration.java index c235091489a2c..871c27796ca08 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentConfiguration.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentConfiguration.java @@ -1,13 +1,14 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config; import com.oracle.coherence.concurrent.executor.ClusteredExecutorService; +import com.oracle.coherence.concurrent.executor.options.CloseExecutor; import com.oracle.coherence.concurrent.executor.options.Description; import com.oracle.coherence.concurrent.executor.options.Name; @@ -18,7 +19,6 @@ import java.util.Map; import java.util.Objects; - /** * A simple holder for the parsing result of an {@code coherence-concurrent} * configuration artifacts. @@ -83,7 +83,7 @@ public synchronized void setExecutorService(ClusteredExecutorService executorSer for (NamedExecutorService service : f_mapNamedExecutorServices.values()) { executorService.register(service.getExecutorService(), - Name.of(service.getName()), Description.of(service.getDescription())); + Name.of(service.getName()), new CloseExecutor(), Description.of(service.getDescription())); } } @@ -125,7 +125,7 @@ public synchronized void addNamedExecutorService(NamedExecutorService service) // when this is the case, register the executors on-the-fly if (m_executorService != null) { - m_executorService.register(service.getExecutorService(), Name.of(sName)); + m_executorService.register(service.getExecutorService(), Name.of(sName), new CloseExecutor()); } } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentServicesSessionConfiguration.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentServicesSessionConfiguration.java index 6f46332831df2..b15034a550170 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentServicesSessionConfiguration.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/ConcurrentServicesSessionConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -13,6 +13,9 @@ import java.util.Optional; +/** + * Session configuration for the concurrent service. + */ public class ConcurrentServicesSessionConfiguration implements SessionConfiguration { @@ -51,7 +54,8 @@ public Context createSession(SessionConfiguration configuration, Context context { if (SESSION_NAME.equals(configuration.getName())) { - if (context.getMode() == Coherence.Mode.ClusterMember) + Coherence.Mode mode = context.getMode(); + if (mode.isClusterMember()) { return context.createSession(configuration); } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/NamespaceHandler.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/NamespaceHandler.java index e48860e827d74..b779cd1580332 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/NamespaceHandler.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/NamespaceHandler.java @@ -1,12 +1,13 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config; import com.oracle.coherence.concurrent.config.processors.CachedProcessor; +import com.oracle.coherence.concurrent.config.processors.CustomExecutorProcessor; import com.oracle.coherence.concurrent.config.processors.FixedProcessor; import com.oracle.coherence.concurrent.config.processors.SingleProcessor; import com.oracle.coherence.concurrent.config.processors.ThreadFactoryProcessor; @@ -37,5 +38,6 @@ public NamespaceHandler() registerProcessor(SingleProcessor.class); registerProcessor(ThreadFactoryProcessor.class); registerProcessor(WorkStealingProcessor.class); + registerProcessor(CustomExecutorProcessor.class); } } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/AbstractExecutorWithFactoryBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/AbstractExecutorWithFactoryBuilder.java index 2e710e3668574..e386c103a9fb0 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/AbstractExecutorWithFactoryBuilder.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/AbstractExecutorWithFactoryBuilder.java @@ -1,15 +1,19 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.builders; +import com.oracle.coherence.concurrent.executor.util.NamedThreadFactory; +import com.tangosol.coherence.config.ParameterList; import com.tangosol.coherence.config.builder.ParameterizedBuilder; import com.tangosol.config.annotation.Injectable; +import com.tangosol.config.expression.Parameter; +import com.tangosol.config.expression.ParameterResolver; import java.util.concurrent.ThreadFactory; /** @@ -38,6 +42,35 @@ public void setInstanceBuilder(ParameterizedBuilder bldr) m_bldr = bldr; } + // ----- helper methods ------------------------------------------------- + + /** + * Creates and returns a ThreadFactory. + * + * @param sName the name to use if no user-defined + * ThreadFactory is defined + * @param resolver the {@link ParameterResolver} for resolving + * named {@link Parameter}s + * @param loader the {@link ClassLoader} for loading any + * necessary classes and if null the + * {@link ClassLoader} used to load the builder + * will be used instead + * @param listParameters an optional {@link ParameterList} + * (may be null) to be used for + * realizing the instance, eg: used as constructor + * parameters + * + * @return the {@link ThreadFactory} + */ + protected ThreadFactory instantiateThreadFactory(String sName, + ParameterResolver resolver, ClassLoader loader, ParameterList listParameters) + { + return m_bldr == null + ? new NamedThreadFactory(sName) + : m_bldr.realize(resolver, loader, listParameters); + + } + // ----- data members --------------------------------------------------- /** diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CachedBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CachedBuilder.java index f4aac271fc99f..9dd28d9b3680e 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CachedBuilder.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CachedBuilder.java @@ -1,13 +1,14 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.builders; import com.oracle.coherence.concurrent.config.NamedExecutorService; +import com.oracle.coherence.concurrent.executor.util.NamedThreadFactory; import com.tangosol.coherence.config.ParameterList; import com.tangosol.coherence.config.builder.ParameterizedBuilder; @@ -36,9 +37,8 @@ public class CachedBuilder public NamedExecutorService realize(ParameterResolver resolver, ClassLoader loader, ParameterList listParameters) { String sName = m_name.evaluate(resolver); - ThreadFactory factory = m_bldr == null - ? null - : m_bldr.realize(resolver, loader, listParameters); + ThreadFactory factory = instantiateThreadFactory(sName, resolver, + loader, listParameters); Supplier supplier = factory == null ? Executors::newCachedThreadPool : () -> Executors.newCachedThreadPool(factory); @@ -60,7 +60,9 @@ public NamedExecutorService realize(ParameterResolver resolver, ClassLoader load */ protected String description(ThreadFactory factory) { - String sFactory = factory == null ? "default" : factory.getClass().getName(); + String sFactory = factory == null || NamedThreadFactory.class.equals(factory.getClass()) + ? "default" + : factory.getClass().getName(); return String.format("CachedThreadPool(ThreadFactory=%s)", sFactory); } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CustomExecutorBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CustomExecutorBuilder.java new file mode 100644 index 0000000000000..5ffa3f11c87d9 --- /dev/null +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/CustomExecutorBuilder.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.coherence.concurrent.config.builders; + +import com.oracle.coherence.concurrent.config.NamedExecutorService; + +import com.oracle.coherence.concurrent.executor.util.NamedThreadFactory; + +import com.tangosol.coherence.config.ParameterList; + +import com.tangosol.coherence.config.builder.ParameterizedBuilder; + +import com.tangosol.config.annotation.Injectable; + +import com.tangosol.config.expression.ParameterResolver; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; + +import java.util.function.Supplier; + +/** + * A {@link ParameterizedBuilder} for constructing an {@link ExecutorService}. + * + * @author rl 5.16.24 + * @since 14.1.2.0.0 + */ +public class CustomExecutorBuilder + extends AbstractExecutorBuilder + { + // ----- ParameterizedBuilder interface --------------------------------- + + @Override + public NamedExecutorService realize(ParameterResolver resolver, ClassLoader loader, ParameterList listParameters) + { + String sName = m_name.evaluate(resolver); + Supplier supplier = () -> m_bldr.realize(resolver, loader, listParameters); + NamedExecutorService service = new NamedExecutorService(sName, "CustomExecutorService", supplier); + + register(service); + + return service; + } + + // ----- setters -------------------------------------------------------- + + /** + * Set the {@link ParameterizedBuilder} that will be used to construct + * the {@link ExecutorService}. + * + * @param bldr the {@link ParameterizedBuilder} that will be used to construct + * the {@link ExecutorService} + */ + @SuppressWarnings("unused") + @Injectable("instance") + public void setInstanceBuilder(ParameterizedBuilder bldr) + { + m_bldr = bldr; + } + + // ----- helper methods ------------------------------------------------- + + /** + * Creates the description for this executor. + * + * @param factory the {@link ThreadFactory}, if any + * + * @return the description for this executor + */ + protected String description(ThreadFactory factory) + { + String sFactory = factory == null || NamedThreadFactory.class.equals(factory.getClass()) + ? "default" + : factory.getClass().getName(); + + return String.format("CachedThreadPool(ThreadFactory=%s)", sFactory); + } + + // ----- data members --------------------------------------------------- + + /** + * A {@link ParameterizedBuilder} that creates a {@link ExecutorService}. + */ + protected ParameterizedBuilder m_bldr; + } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/FixedBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/FixedBuilder.java index 885befb5bf1e3..8ce1da4dbb001 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/FixedBuilder.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/FixedBuilder.java @@ -1,13 +1,14 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.builders; import com.oracle.coherence.concurrent.config.NamedExecutorService; +import com.oracle.coherence.concurrent.executor.util.NamedThreadFactory; import com.tangosol.coherence.config.ParameterList; import com.tangosol.coherence.config.builder.ParameterizedBuilder; @@ -40,9 +41,8 @@ public NamedExecutorService realize(ParameterResolver resolver, ClassLoader load { String sName = m_name.evaluate(resolver); int cThreadCount = m_threadCount.evaluate(resolver); - ThreadFactory factory = m_bldr == null - ? null - : m_bldr.realize(resolver, loader, listParameters); + ThreadFactory factory = instantiateThreadFactory(sName, resolver, + loader, listParameters); Supplier supplier = factory == null ? () -> Executors.newFixedThreadPool(cThreadCount) : () -> Executors.newFixedThreadPool(cThreadCount, factory); @@ -72,13 +72,16 @@ public void setThreadCount(Expression threadCount) /** * Creates the description for this executor. * - * @param factory the {@link ThreadFactory}, if any + * @param cThreadCount the configured thread count + * @param factory the {@link ThreadFactory}, if any * * @return the description for this executor */ protected String description(int cThreadCount, ThreadFactory factory) { - String sFactory = factory == null ? "default" : factory.getClass().getName(); + String sFactory = factory == null || NamedThreadFactory.class.equals(factory.getClass()) + ? "default" + : factory.getClass().getName(); return String.format("FixedThreadPool(ThreadCount=%s, ThreadFactory=%s)", cThreadCount, sFactory); } @@ -86,7 +89,7 @@ protected String description(int cThreadCount, ThreadFactory factory) // ----- data members --------------------------------------------------- /** - * The number of threads; + * The number of threads. */ protected Expression m_threadCount; } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/SingleBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/SingleBuilder.java index e7036b9e3e3b3..a8767755130df 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/SingleBuilder.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/SingleBuilder.java @@ -1,13 +1,14 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.builders; import com.oracle.coherence.concurrent.config.NamedExecutorService; +import com.oracle.coherence.concurrent.executor.util.NamedThreadFactory; import com.tangosol.coherence.config.ParameterList; import com.tangosol.coherence.config.builder.ParameterizedBuilder; @@ -36,9 +37,8 @@ public class SingleBuilder public NamedExecutorService realize(ParameterResolver resolver, ClassLoader loader, ParameterList listParameters) { String sName = m_name.evaluate(resolver); - ThreadFactory factory = m_bldr == null - ? null - : m_bldr.realize(resolver, loader, listParameters); + ThreadFactory factory = instantiateThreadFactory(sName, resolver, + loader, listParameters); Supplier supplier = factory == null ? Executors::newSingleThreadExecutor : () -> Executors.newSingleThreadExecutor(factory); @@ -60,7 +60,9 @@ public NamedExecutorService realize(ParameterResolver resolver, ClassLoader load */ protected String description(ThreadFactory factory) { - String sFactory = factory == null ? "default" : factory.getClass().getName(); + String sFactory = factory == null || NamedThreadFactory.class.equals(factory.getClass()) + ? "default" + : factory.getClass().getName(); return String.format("SingleThreaded(ThreadFactory=%s)", sFactory); } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/ThreadFactoryBuilder.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/ThreadFactoryBuilder.java index a4f00d64867ed..5f483a8cc8011 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/ThreadFactoryBuilder.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/builders/ThreadFactoryBuilder.java @@ -1,13 +1,11 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.builders; -import com.oracle.coherence.persistence.PersistenceEnvironment; - import com.tangosol.coherence.config.ParameterList; import com.tangosol.coherence.config.builder.ParameterizedBuilder; @@ -21,7 +19,7 @@ /** * A {@link ParameterizedBuilder} for constructing a {@link ThreadFactory}. * - * @author rl 11.20.26 + * @author rl 11.20.21 * @since 21.12 */ public class ThreadFactoryBuilder @@ -54,7 +52,7 @@ public void setInstanceBuilder(ParameterizedBuilder bldr) // ----- data members --------------------------------------------------- /** - * A {@link ParameterizedBuilder} that creates a {@link PersistenceEnvironment}. + * A {@link ParameterizedBuilder} that creates a {@link ThreadFactory}. */ protected ParameterizedBuilder m_bldr; } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/AbstractExecutorProcessor.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/AbstractExecutorProcessor.java index 36f74fc1c4e38..a75f5a58b79ce 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/AbstractExecutorProcessor.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/AbstractExecutorProcessor.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.processors; @@ -21,6 +21,8 @@ * Base class for {@link ElementProcessor}s producing {@link NamedExecutorService} * instances. * + * @param the {@link ParameterizedBuilder} type this processor supports + * * @author rl 11.26.21 * @since 21.12 */ diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/CustomExecutorProcessor.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/CustomExecutorProcessor.java new file mode 100644 index 0000000000000..4e21174a2b53b --- /dev/null +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/CustomExecutorProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at + * https://oss.oracle.com/licenses/upl. + */ +package com.oracle.coherence.concurrent.config.processors; + +import com.oracle.coherence.concurrent.config.NamedExecutorService; +import com.oracle.coherence.concurrent.config.builders.AbstractExecutorBuilder; +import com.oracle.coherence.concurrent.config.builders.CachedBuilder; +import com.oracle.coherence.concurrent.config.builders.CustomExecutorBuilder; + +import com.tangosol.config.ConfigurationException; + +import com.tangosol.config.xml.ElementProcessor; +import com.tangosol.config.xml.ProcessingContext; +import com.tangosol.config.xml.XmlSimpleName; + +import com.tangosol.run.xml.XmlElement; + +/** + * An {@link ElementProcessor} for {@code custom-executor} elements. + * + * @author rl 5.16.24 + * @since 14.1.2.0.0 + */ +@XmlSimpleName("custom-executor") +public class CustomExecutorProcessor + extends AbstractExecutorProcessor + { + // ----- AbstractExecutorProcessor methods ------------------------------ + + protected CustomExecutorBuilder builder() + { + return new CustomExecutorBuilder(); + } + } diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/ThreadFactoryProcessor.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/ThreadFactoryProcessor.java index 95dafe9f7049f..91220c8bb35a5 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/ThreadFactoryProcessor.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/config/processors/ThreadFactoryProcessor.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2021 Oracle and/or its affiliates. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.config.processors; @@ -26,6 +26,8 @@ public class ThreadFactoryProcessor implements ElementProcessor { + // ----- ElementProcessor interface ------------------------------------- + public ThreadFactoryBuilder process(ProcessingContext context, XmlElement xmlElement) throws ConfigurationException { diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/AbstractTaskCoordinator.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/AbstractTaskCoordinator.java index 2cf2984b30ab1..285057b9c3573 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/AbstractTaskCoordinator.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/AbstractTaskCoordinator.java @@ -1,8 +1,8 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. + * Copyright (c) 2016, 2023, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at - * http://oss.oracle.com/licenses/upl. + * https://oss.oracle.com/licenses/upl. */ package com.oracle.coherence.concurrent.executor; @@ -188,7 +188,7 @@ public Task.Coordinator getCoordinator() protected void closeSubscriber(Task.Subscriber subscriber, boolean fRemove) { - ExecutorTrace.entering(AbstractTaskCoordinator.class, "closeSubscriber", subscriber, getTaskId()); + ExecutorTrace.entering(AbstractTaskCoordinator.class, "closeSubscriber", subscriber, getTaskId(), m_lastValue); if (fRemove) { @@ -204,6 +204,7 @@ protected void closeSubscriber(Task.Subscriber subscriber, { if (f_cancelled.get()) { + ExecutorTrace.log(() -> String.format("Notifying Subscriber %s of cancellation", subscriber)); subscriber.onError(new InterruptedException("Task " + getTaskId() + " has been cancelled.")); } else if (m_lastValue != null && m_lastValue.isValue()) @@ -211,6 +212,10 @@ else if (m_lastValue != null && m_lastValue.isValue()) // only call onComplete() if the task returned a value subscriber.onComplete(); } + else + { + ExecutorTrace.log(() -> String.format("Subscriber %s of closed, but no results received.", subscriber)); + } } catch (Throwable throwable) { diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredAssignment.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredAssignment.java index f5b7fc2d133c4..f4108757668bc 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredAssignment.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredAssignment.java @@ -338,7 +338,7 @@ public static void removeAssignments(String sTaskId, CacheService service) * An {@link InvocableMap.EntryProcessor} which updates an assignment due to an assignment {@link Action}. */ public static class AssignmentProcessor - extends PortableAbstractProcessor + extends PortableAbstractProcessor { // ----- constructors ----------------------------------------------- @@ -363,11 +363,10 @@ public AssignmentProcessor(ClusteredAssignment assignment, Action action) m_assignment = assignment; } - // ----- PortableAbstractProcessor methods -------------------------- + // ----- EntryProcessor interface ----------------------------------- - @SuppressWarnings("rawtypes") @Override - public Object process(InvocableMap.Entry entry) + public Void process(InvocableMap.Entry entry) { ExecutorTrace.log(() -> String.format("ClusteredAssignment State for Executor [%s] being configured because of [%s]", entry.getKey(), m_action)); @@ -376,7 +375,7 @@ public Object process(InvocableMap.Entry entry) case ASSIGN: case RECOVER: - ClusteredAssignment current = (ClusteredAssignment) entry.getValue(); + ClusteredAssignment current = entry.getValue(); // only update when it doesn't exist or is not equal if (!entry.isPresent() || entry.isPresent() && !current.equals(m_assignment)) @@ -384,7 +383,6 @@ public Object process(InvocableMap.Entry entry) m_assignment.setState(State.ASSIGNED); m_assignment.setRecovered(m_action == Action.RECOVER); - //noinspection unchecked entry.setValue(m_assignment); } @@ -440,7 +438,7 @@ public void writeExternal(PofWriter out) throws IOException * the previous state. */ public static class SetStateProcessor - extends PortableAbstractProcessor + extends PortableAbstractProcessor { // ----- constructors ----------------------------------------------- @@ -477,20 +475,20 @@ public SetStateProcessor(State previous, m_desired = desired; } - @SuppressWarnings("rawtypes") + // ----- EntryProcessor interface ----------------------------------- + @Override - public Object process(InvocableMap.Entry entry) + public State process(InvocableMap.Entry entry) { if (entry.isPresent()) { - ClusteredAssignment assignment = (ClusteredAssignment) entry.getValue(); + ClusteredAssignment assignment = entry.getValue(); State existing = assignment.getState(); if (existing != null && existing.equals(m_previous) || m_previous == null) { assignment.setState(m_desired); - //noinspection unchecked entry.setValue(assignment); ExecutorTrace.log(() -> String.format("ClusteredAssignment State for Executor [%s] changed from [%s] to [%s]", entry.getKey(), m_previous, m_desired)); diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorInfo.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorInfo.java index 370805280473f..c7602ed15859d 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorInfo.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -24,14 +24,17 @@ import com.oracle.coherence.concurrent.executor.util.Caches; import com.oracle.coherence.concurrent.executor.util.OptionsByType; +import com.tangosol.io.AbstractEvolvable; + +import com.tangosol.io.pof.EvolvablePortableObject; import com.tangosol.io.pof.PofReader; import com.tangosol.io.pof.PofWriter; -import com.tangosol.io.pof.PortableObject; import com.tangosol.net.CacheFactory; import com.tangosol.net.CacheService; import com.tangosol.net.NamedCache; +import com.tangosol.util.Base; import com.tangosol.util.ExternalizableHelper; import com.tangosol.util.InvocableMap; import com.tangosol.util.InvocableMap.Entry; @@ -61,8 +64,9 @@ * @since 21.12 */ public class ClusteredExecutorInfo + extends AbstractEvolvable implements TaskExecutorService.ExecutorInfo, LiveObject, - Leased, ClusterMemberAware, PortableObject + Leased, ClusterMemberAware, EvolvablePortableObject { // ----- constructors --------------------------------------------------- @@ -94,9 +98,7 @@ public ClusteredExecutorInfo(String sIdentity, long ldtUpdate, long cMaxMemory, m_cMaxMemory = cMaxMemory; m_cTotalMemory = totalMemory; m_cFreeMemory = freeMemory; - m_cCompleted = 0; - m_cRejected = 0; - m_cInProgress = 0; + m_ldtJoined = System.nanoTime(); } // ----- public methods ------------------------------------------------- @@ -134,6 +136,12 @@ public long getLastUpdateTime() return m_ldtUpdate; } + @Override + public long getJoinTime() + { + return m_ldtJoined; + } + @Override public long getMaxMemory() { @@ -158,6 +166,14 @@ public T getOption(Class return m_optionsByType.get(clzOfOption, defaultIfNotFound); } + // ----- Evolvable interface -------------------------------------------- + + @Override + public int getImplVersion() + { + return VERSION; + } + // ----- accessors ------------------------------------------------------ /** @@ -295,7 +311,8 @@ public String toString() { return "ClusteredExecutorInfo{"+ "name=" + getExecutorName() + ", description=" + getDescription() + ", identity='" + m_sIdentity + '\'' + ", state=" + m_state + ", lastUpdateTime=" - + m_ldtUpdate + ", maxMemory=" + m_cMaxMemory + ", totalMemory=" + m_cTotalMemory + ", freeMemory=" + + m_ldtUpdate + ", joinTime=" + m_ldtJoined + ", maxMemory=" + + m_cMaxMemory + ", totalMemory=" + m_cTotalMemory + ", freeMemory=" + m_cFreeMemory + ", optionsByType=" + m_optionsByType + ", completed=" + m_cCompleted + ", in-progress=" + m_cInProgress + ", rejected=" + m_cRejected + '}'; } @@ -484,6 +501,25 @@ public void readExternal(DataInput in) throws IOException m_cCompleted = ExternalizableHelper.readLong(in); m_cRejected = ExternalizableHelper.readLong(in); m_cInProgress = ExternalizableHelper.readLong(in); + + try + { + m_ldtJoined = ExternalizableHelper.readLong(in); + } + catch (Exception e) + { + // we've encountered an older version of the ClusteredExecutorInfo + // so, we've lost the original join time. Since it's only used + // to provide a stable sort when distributing tasks, use + // the current time with the impact being a small change in + // the dispatch order for tasks + State state = m_state; + + if (state == State.JOINING || state == State.RUNNING) + { + m_ldtJoined = Base.getSafeTimeMillis(); + } + } } @Override @@ -499,6 +535,7 @@ public void writeExternal(DataOutput out) throws IOException ExternalizableHelper.writeLong(out, m_cCompleted); ExternalizableHelper.writeLong(out, m_cRejected); ExternalizableHelper.writeLong(out, m_cInProgress); + ExternalizableHelper.writeLong(out, m_ldtJoined); } // ----- PortableObject interface --------------------------------------- @@ -516,6 +553,27 @@ public void readExternal(PofReader in) throws IOException m_cCompleted = in.readLong(7); m_cRejected = in.readLong(8); m_cInProgress = in.readLong(9); + + int nVersion = in.getVersionId(); + if (nVersion < VERSION) + { + // we've encountered an older version of the ClusteredExecutorInfo + // so, we've lost the original join time. Since it's only used + // to provide a stable sort when distributing tasks, use + // the current time with the impact being a small change in + // the dispatch order for tasks + State state = m_state; + + if (state == State.JOINING || state == State.RUNNING) + { + m_ldtJoined = System.nanoTime(); + } + } + + if (nVersion >= VERSION) + { + m_ldtJoined = in.readLong(10); + } } @Override @@ -531,15 +589,25 @@ public void writeExternal(PofWriter out) throws IOException out.writeLong(7, m_cCompleted); out.writeLong(8, m_cRejected); out.writeLong(9, m_cInProgress); + out.writeLong(10, m_ldtJoined); } // ----- inner class: CacheAwareContinuation ---------------------------- + /** + * A base {@link ComposableContinuation} that allows subclasses access + * to the underlying caches that drive the executor service. + */ protected static abstract class CacheAwareContinuation implements ComposableContinuation { // ----- constructors ----------------------------------------------- + /** + * Constructs a new {@code CacheAwareContinuation}. + * + * @param cacheService the executor cache service + */ public CacheAwareContinuation(CacheService cacheService) { f_cacheService = cacheService; @@ -625,25 +693,36 @@ public void proceed(Object o) // remove task assignments for the Executor ExecutorTrace.log(() -> String.format("Determining Tasks Assigned to Executor [%s]", sExecutorId)); - // determine all tasks assigned to the executor - Map assignmentMap = assignments() - .invokeAll(new EqualsFilter("getExecutorId", sExecutorId), - new ExtractorProcessor<>("getTaskId")); + try + { + // determine all tasks assigned to the executor + Map assignmentMap = assignments() + .invokeAll(new EqualsFilter("getExecutorId", sExecutorId), + new ExtractorProcessor<>("getTaskId")); - Logger.finer(() -> String.format("Found %d Tasks Assigned to Executor [%s]. Notifying them of the Closing Executor", assignmentMap.size(), sExecutorId)); + Logger.finer(() -> String.format("Found %d Tasks Assigned to Executor [%s]. Notifying them of the Closing Executor", + assignmentMap.size(), sExecutorId)); - // notify the Tasks assigned to the Executor to re-assign - tasks().invokeAll(assignmentMap.values(), new ClusteredTaskManager.NotifyExecutionStrategyProcessor()); + // notify the Tasks assigned to the Executor to re-assign + tasks().invokeAll(assignmentMap.values(), new ClusteredTaskManager.NotifyExecutionStrategyProcessor()); - Logger.finer(() -> String.format("Removing Assignments for Executor [%s]", sExecutorId)); + Logger.finer(() -> String.format("Removing Assignments for Executor [%s]", sExecutorId)); - // now remove the assignments for the tasks - tasks().invokeAll(assignmentMap.keySet(), new ConditionalRemove<>(AlwaysFilter.INSTANCE)); + // now remove the assignments for the tasks + tasks().invokeAll(assignmentMap.keySet(), new ConditionalRemove<>(AlwaysFilter.INSTANCE)); - Logger.finer(() -> String.format("Notifying Executor [%s] that it is now Closed", sExecutorId)); + Logger.finer(() -> String.format("Notifying Executor [%s] that it is now Closed", sExecutorId)); - // change the state of the Executor to be CLOSED - executors().invoke(sExecutorId, new SetStateProcessor(State.CLOSED)); + // change the state of the Executor to be CLOSED + executors().invoke(sExecutorId, new SetStateProcessor(State.CLOSED)); + } + catch (Exception e) + { + if (ExecutorTrace.isEnabled()) + { + Logger.warn("Exception cleaning up executor resources", e); + } + } } @Override @@ -766,7 +845,9 @@ public void proceed(Object o) String sExecutorName = (String) results[1]; - tasks().invokeAll(AlwaysFilter.INSTANCE, new ClusteredTaskManager.NotifyExecutionStrategyProcessor()); + NamedCache cacheTasks = tasks(); + + cacheTasks.invokeAll(cacheTasks.keySet(), new ClusteredTaskManager.NotifyExecutionStrategyProcessor()); Logger.fine(() -> String.format("Executor [name=%s, id=%s] joined.", sExecutorName, m_sExecutorId)); } @@ -837,7 +918,7 @@ public ComposableContinuation compose(ComposableContinuation continuation) * the previous state. */ public static class SetStateProcessor - extends PortableAbstractProcessor + extends PortableAbstractProcessor { // ----- constructors ----------------------------------------------- @@ -872,14 +953,14 @@ public SetStateProcessor(State previous, State desired) m_desired = desired; } - // ----- PortableAbstractProcessor methods -------------------------- + // ----- EntryProcessor interface ----------------------------------- @Override - public Object process(@SuppressWarnings("rawtypes") InvocableMap.Entry entry) + public State process(InvocableMap.Entry entry) { if (entry.isPresent()) { - ClusteredExecutorInfo info = (ClusteredExecutorInfo) entry.getValue(); + ClusteredExecutorInfo info = entry.getValue(); State existing = info.getState(); if ((existing.equals(m_previous) || m_previous == null) && !existing.equals(m_desired)) @@ -947,7 +1028,6 @@ public Object process(@SuppressWarnings("rawtypes") InvocableMap.Entry entry) info.setState(m_desired); - //noinspection unchecked entry.setValue(info); } @@ -995,7 +1075,7 @@ public void writeExternal(PofWriter out) throws IOException * of a {@link ClusteredExecutorInfo}. */ public static class TouchProcessor - extends PortableAbstractProcessor + extends PortableAbstractProcessor { // ----- constructors ----------------------------------------------- @@ -1009,15 +1089,14 @@ public TouchProcessor() // ----- PortableAbstractProcessor methods -------------------------- @Override - public Object process(@SuppressWarnings("rawtypes") Entry entry) + public Void process(InvocableMap.Entry entry) { if (entry.isPresent()) { - ClusteredExecutorInfo info = (ClusteredExecutorInfo) entry.getValue(); + ClusteredExecutorInfo info = entry.getValue(); info.touch(); - //noinspection unchecked entry.setValue(info); } @@ -1036,6 +1115,8 @@ public static class TouchRunnable // ----- constructors ----------------------------------------------- /** + * Constructs a new {@code TouchRunnable}. + * * @param sExecutorId the {@link Executor} identity to update * @param cacheService the {@link CacheService} */ @@ -1073,7 +1154,7 @@ public void run() * An {@link InvocableMap.EntryProcessor} to update the state of an {@link TaskExecutorService.ExecutorInfo}. */ public static class UpdateInfoProcessor - extends PortableAbstractProcessor + extends PortableAbstractProcessor { // ----- constructors ----------------------------------------------- @@ -1109,15 +1190,15 @@ public UpdateInfoProcessor(long cMaxMemory, long cTotalMemory, long cFreeMemory, m_cTasksInProgress = cTasksInProgress; } - // ----- PortableAbstractProcessor methods -------------------------- + // ----- EntryProcessor interface ----------------------------------- @Override - public Object process(@SuppressWarnings("rawtypes") InvocableMap.Entry entry) + public Void process(InvocableMap.Entry entry) { // only update when there's an entry if (entry.isPresent()) { - ClusteredExecutorInfo info = (ClusteredExecutorInfo) entry.getValue(); + ClusteredExecutorInfo info = entry.getValue(); // update the info info.setMaxMemory(m_cMaxMemory); @@ -1139,7 +1220,6 @@ public Object process(@SuppressWarnings("rawtypes") InvocableMap.Entry entry) // touch the info (to mark is as updated) info.touch(); - //noinspection unchecked entry.setValue(info); } @@ -1158,7 +1238,6 @@ public void readExternal(PofReader in) throws IOException m_cTasksCompleted = in.readLong(4); m_cTasksFailed = in.readLong(5); m_cTasksInProgress = in.readLong(6); - } @Override @@ -1171,7 +1250,6 @@ public void writeExternal(PofWriter out) throws IOException out.writeLong(4, m_cTasksCompleted); out.writeLong(5, m_cTasksFailed); out.writeLong(6, m_cTasksInProgress); - } // ----- data members ----------------------------------------------- @@ -1309,6 +1387,11 @@ public void run() */ public static long LEASE_DURATION_MS = 30 * 1000; // 30 seconds + /** + * PortableObject version. + */ + protected static int VERSION = 2; + // ----- data members --------------------------------------------------- /** @@ -1331,6 +1414,11 @@ public void run() */ protected long m_ldtUpdate; + /** + * The cluster time when the {@link TaskExecutorService.ExecutorInfo} joined the service. + */ + protected long m_ldtJoined = -1; + /** * The maximum memory as reported by {@link Runtime#maxMemory()}. */ diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorService.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorService.java index ee7767237b19a..f5cbe87b24959 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorService.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredExecutorService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -220,13 +220,14 @@ public Registration deregister(ExecutorService executor) @Override public Task.Orchestration orchestrate(Task task) { - return new ClusteredOrchestration<>(this, task); + return new ClusteredOrchestration<>(this, Objects.requireNonNull(task)); } @SuppressWarnings("rawtypes") @Override public Task.Coordinator acquire(String sTaskId) { + Objects.requireNonNull(sTaskId); CacheService service = getCacheService(); if (Caches.tasks(service).containsKey(sTaskId)) @@ -350,6 +351,9 @@ public void execute(Remote.Runnable command) @Override public ScheduledFuture schedule(Remote.Callable callable, long lcDelay, TimeUnit unit) { + Objects.requireNonNull(callable); + Objects.requireNonNull(unit); + ScheduledCallableTask callableTask = new ScheduledCallableTask(callable, lcDelay == 0 @@ -375,13 +379,23 @@ public ScheduledFuture schedule(Remote.Runnable command, long lcDelay, TimeUn @Override public ScheduledFuture scheduleAtFixedRate(Remote.Runnable command, long lcInitialDelay, long lcPeriod, TimeUnit unit) { + if (lcPeriod <= 0) + { + throw new IllegalArgumentException("Period must be greater than zero"); + } + return scheduleRunnable(command, lcInitialDelay, lcPeriod, 0, unit); } @Override - public ScheduledFuture scheduleWithFixedDelay(Remote.Runnable command, long cInitialDelay, long cDelay, TimeUnit unit) + public ScheduledFuture scheduleWithFixedDelay(Remote.Runnable command, long lcInitialDelay, long lcDelay, TimeUnit unit) { - return scheduleRunnable(command, cInitialDelay, 0, cDelay, unit); + if (lcDelay <= 0) + { + throw new IllegalArgumentException("Delay must be greater than zero"); + } + + return scheduleRunnable(command, lcInitialDelay, 0, lcDelay, unit); } @Override @@ -411,16 +425,18 @@ public T invokeAny(Collection> colTasks, long l } @Override - public List> invokeAll(Collection> tasks) + public List> invokeAll(Collection> colTasks) throws InterruptedException { - Objects.requireNonNull(tasks); + Objects.requireNonNull(colTasks); - ArrayList> listFutures = new ArrayList<>(tasks.size()); + ArrayList> listFutures = new ArrayList<>(colTasks.size()); try { - for (Callable t : tasks) + for (Callable t : colTasks) { + Objects.requireNonNull(t); + CESRunnableFuture f = new CESRunnableFuture<>(t); listFutures.add(f); execute(f); @@ -453,6 +469,7 @@ public List> invokeAll(Collection> ta throws InterruptedException { Objects.requireNonNull(tasks); + Objects.requireNonNull(unit); final long lcNanos = unit.toNanos(lcTimeout); final long lcDeadline = System.nanoTime() + lcNanos; @@ -464,6 +481,8 @@ public List> invokeAll(Collection> ta { for (Callable t : tasks) { + Objects.requireNonNull(t); + listFutures.add(new CESRunnableFuture<>(t)); } @@ -806,6 +825,8 @@ protected void init(CacheService cacheService) protected ScheduledFuture scheduleRunnable(Runnable command, long cInitialDelay, long cPeriod, long cDelay, TimeUnit unit) { + Objects.requireNonNull(command); + Duration initialDelayDur = cInitialDelay == 0 ? null : Duration.ofNanos(unit.toNanos(cInitialDelay)); diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredProperties.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredProperties.java index e51ab6c511753..c5e283c952254 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredProperties.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -8,6 +8,7 @@ import com.oracle.coherence.common.base.Logger; +import com.oracle.coherence.concurrent.executor.internal.ExecutorTrace; import com.oracle.coherence.concurrent.executor.util.Caches; import com.tangosol.io.ExternalizableLite; @@ -21,7 +22,9 @@ import com.tangosol.net.cache.KeyAssociation; +import com.tangosol.util.BinaryEntry; import com.tangosol.util.ExternalizableHelper; +import com.tangosol.util.InvocableMap; import java.io.DataInput; import java.io.DataOutput; @@ -75,11 +78,12 @@ public ClusteredProperties(String sTaskId, CacheService service, TaskProperties m_sTaskId = sTaskId; m_service = service; - NamedCache propertyCache = Caches.properties(service); + NamedCache, PropertyValue> propertyCache = Caches.properties(service); for (Object o : properties.getProperties().entrySet()) { Map.Entry entry = (Map.Entry) o; - propertyCache.put(new PropertyKey(sTaskId, entry.getKey()), new PropertyValue(sTaskId, entry.getValue())); + propertyCache.put(new PropertyKey(sTaskId, (Serializable) entry.getKey()), + new PropertyValue(sTaskId, (Serializable) entry.getValue())); } } @@ -106,10 +110,11 @@ public V put(String sKey, V value) { NamedCache propertyCache = Caches.properties(getCacheService()); - PropertyValue oldValue = propertyCache.put(new PropertyKey(m_sTaskId, sKey), - new PropertyValue(m_sTaskId, value)); + PropertyValue oldValue = (PropertyValue) propertyCache + .invoke(new PropertyKey(m_sTaskId, sKey), + new SetPropertyValueProcessor(new PropertyValue(m_sTaskId, value))); - return oldValue == null ? null : (V) oldValue.getValue(); + return oldValue == null ? null : oldValue.getValue(); } else { @@ -168,10 +173,12 @@ protected CacheService getCacheService() /** * Property key. + * + * @param the key type */ @SuppressWarnings("rawtypes") - public static class PropertyKey - implements ExternalizableLite, KeyAssociation, PortableObject + public static class PropertyKey + implements ExternalizableLite, KeyAssociation, PortableObject { // ----- constructors ----------------------------------------------- @@ -189,7 +196,7 @@ public PropertyKey() * @param sTaskId task ID * @param oKey property key */ - public PropertyKey(String sTaskId, Object oKey) + public PropertyKey(String sTaskId, T oKey) { m_sTaskId = sTaskId; m_oKey = oKey; @@ -200,7 +207,7 @@ public PropertyKey(String sTaskId, Object oKey) /** * {@inheritDoc} */ - public Object getAssociatedKey() + public String getAssociatedKey() { return m_sTaskId; } @@ -263,7 +270,7 @@ public String getTaskId() * * @return property key */ - public Object getKey() + public T getKey() { return m_oKey; } @@ -310,15 +317,17 @@ public void writeExternal(PofWriter out) throws IOException /** * Property key. */ - protected Object m_oKey; + protected T m_oKey; } // ----- inner class: PropertyValue ------------------------------------- /** * Property value. + * + * @param the property value type */ - public static class PropertyValue + public static class PropertyValue implements ExternalizableLite, PortableObject { // ----- constructors ----------------------------------------------- @@ -337,10 +346,10 @@ public PropertyValue() * @param sTaskId task ID * @param oValue property value */ - public PropertyValue(String sTaskId, Object oValue) + public PropertyValue(String sTaskId, T oValue) { m_sTaskId = sTaskId; - m_oValue = oValue; + m_oValue = oValue; } // ----- Object methods --------------------------------------------- @@ -401,7 +410,7 @@ public String getTaskId() * * @return property value */ - public Object getValue() + public T getValue() { return m_oValue; } @@ -448,7 +457,121 @@ public void writeExternal(PofWriter out) throws IOException /** * Property value. */ - protected Object m_oValue; + protected T m_oValue; + } + + // ----- inner class: SetPropertyValueProcessor ------------------------- + + /** + * An {@link InvocableMap.EntryProcessor} for inserting/updating + * {@link PropertyKey}/{@link PropertyValue} mappings. + * + * @param the property key type + * @param the property value type + * + * @since 22.06.1 + */ + public static class SetPropertyValueProcessor + extends PortableAbstractProcessor, PropertyValue, PropertyValue> + implements ExternalizableLite + { + // ----- constructors ----------------------------------------------- + + /** + * Constructs a new {@code SetPropertyValueProcessor} + * (required for serialization) + */ + @SuppressWarnings("unused") + public SetPropertyValueProcessor() + { + } + + /** + * Constructs a new {@code SetPropertyValueProcessor} + * + * @param oValue the property value + */ + public SetPropertyValueProcessor(PropertyValue oValue) + { + m_oValue = oValue; + } + + // ----- EntryProcessor interface ----------------------------------- + + @Override + public PropertyValue process(InvocableMap.Entry, PropertyValue> entry) + { + String sTaskId = entry.getKey().getTaskId(); + ClusteredTaskManager manager = null; + + ExecutorTrace.entering(SetPropertyValueProcessor.class, "process", sTaskId); + + BinaryEntry managerEntry = ((BinaryEntry) entry) + .getAssociatedEntry(Caches.TASKS_CACHE_NAME, sTaskId); + + PropertyValue result = null; + + if (managerEntry != null) + { + manager = (ClusteredTaskManager) managerEntry.getValue(); + } + + if (manager != null && !manager.isCompleted()) + { + if (entry.isPresent()) + { + result = entry.getValue(); + } + + entry.setValue(m_oValue); + } + else + { + // task is no longer preset or has been completed; this is a no-op + ExecutorTrace.log(() -> String.format("Ignoring attempt to set property [%s]" + + " for task [%s] as it has been completed or no longer exists", + m_oValue, sTaskId)); + } + + ExecutorTrace.exiting(SetPropertyValueProcessor.class, "process", sTaskId); + + return result; + } + + // ----- PortableObject interface ----------------------------------- + + @Override + public void readExternal(PofReader in) throws IOException + { + m_oValue = in.readObject(0); + } + + @Override + public void writeExternal(PofWriter out) throws IOException + { + out.writeObject(0, m_oValue); + } + + // ----- ExternalizableHelper interface ----------------------------- + + @Override + public void readExternal(DataInput in) throws IOException + { + m_oValue = ExternalizableHelper.readObject(in); + } + + @Override + public void writeExternal(DataOutput out) throws IOException + { + ExternalizableHelper.writeObject(out, m_oValue); + } + + // ----- data members ----------------------------------------------- + + /** + * The property value. + */ + protected PropertyValue m_oValue; } // ----- data members --------------------------------------------------- diff --git a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredRegistration.java b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredRegistration.java index e3c05954dfeef..8601371e88020 100644 --- a/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredRegistration.java +++ b/prj/coherence-concurrent/src/main/java/com/oracle/coherence/concurrent/executor/ClusteredRegistration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Oracle and/or its affiliates. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl. @@ -10,9 +10,9 @@ import com.oracle.coherence.concurrent.executor.management.ExecutorMBean; +import com.oracle.coherence.concurrent.executor.options.CloseExecutor; import com.oracle.coherence.concurrent.executor.options.Description; import com.oracle.coherence.concurrent.executor.options.Member; - import com.oracle.coherence.concurrent.executor.options.Name; import com.oracle.coherence.concurrent.executor.tasks.CronTask; @@ -22,6 +22,8 @@ import com.oracle.coherence.concurrent.executor.util.Caches; import com.oracle.coherence.concurrent.executor.util.OptionsByType; +import com.tangosol.coherence.component.util.SafeNamedCache; + import com.tangosol.coherence.config.Config; import com.tangosol.internal.tracing.Scope; @@ -87,8 +89,9 @@ public class ClusteredRegistration * @param executor the registered {@link Executor} * @param optionsByType the {@link Option}s for the {@link Executor} */ + @SuppressWarnings("unchecked") public ClusteredRegistration(ClusteredExecutorService clusteredExecutorService, String sExecutorId, - ExecutorService executor, OptionsByType

- *

+ * A RemoteExecutor allows submitting and/or scheduling + * {@link Remote.Runnable runnables}, {@link Remote.Callable callables}, + * and {@link Task tasks} for execution within a Coherence cluster. + * *

Using a RemoteExecutor

* A RemoteExecutor may be obtained by a known name: * {@link #get(String) RemoteExecutor.get(“executorName”} - * + *

* Once a reference to a {@code RemoteExecutor} has been obtained, * similar to an {@link ExecutorService}, tasks may be submitted: - * {@code + * *

+ * {@code
  *     // obtain the named RemoteExecutor (defined in xml configuration; see below)
  *     RemoteExecutor executor = RemoteExecutor.get("MyExecutor");
  *
@@ -51,9 +53,9 @@
  *
  *     // block until completed
  *     future.get();
- * 
* } - *

+ *

+ * * A {@code RemoteExecutor} allows scheduling of tasks independent of the * underlying thread pool (more about that below); See: *