From 3f9739766611999c6fbd905753b6cfcc43dafff9 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 May 2022 13:57:15 +0800 Subject: [PATCH 01/27] Update to pull depdendencies from Maven. Min 2.1 VisualVM required --- .github/workflows/test-against-released.yml | 16 ++----- .github/workflows/test-against-snapshot.yml | 16 ++----- coherence-visualvm-plugin/pom.xml | 14 +++--- .../coherence-visualvm-tests-ce/pom.xml | 2 +- .../pom.xml | 2 +- .../coherence-visualvm-tests-core/pom.xml | 2 +- coherence-visualvm-tests/pom.xml | 2 +- pom.xml | 45 ++++++++++--------- 8 files changed, 40 insertions(+), 59 deletions(-) diff --git a/.github/workflows/test-against-released.yml b/.github/workflows/test-against-released.yml index 61c72f0..4cb16ae 100644 --- a/.github/workflows/test-against-released.yml +++ b/.github/workflows/test-against-released.yml @@ -55,14 +55,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up JDK 1.8 for NetBeans Dependencies build - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - - name: Print Versions - run: mvn -version && ant -version - - name: Cache Maven packages uses: actions/cache@v1 with: @@ -70,16 +62,14 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Generate NetBeans Dependencies - shell: bash - run: | - bin/install-artifacts.sh /tmp/install/${{ matrix.version }} - - name: Set up JDK 11 for Build uses: actions/setup-java@v1 with: java-version: '11' + - name: Print Versions + run: mvn -version && ant -version + - name: Build shell: bash run: | diff --git a/.github/workflows/test-against-snapshot.yml b/.github/workflows/test-against-snapshot.yml index 3d621e4..d2fe1ce 100644 --- a/.github/workflows/test-against-snapshot.yml +++ b/.github/workflows/test-against-snapshot.yml @@ -52,14 +52,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Set up JDK 1.8 for NetBeans Dependencies build - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - - name: Print Versions - run: mvn -version && ant -version - - name: Cache Maven packages uses: actions/cache@v1 with: @@ -67,16 +59,14 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2-snapshots - - name: Generate NetBeans Dependencies - shell: bash - run: | - bin/install-artifacts.sh /tmp/install/${{ matrix.branch }} - - name: Set up JDK 11 for Build uses: actions/setup-java@v1 with: java-version: '11' + - name: Print Versions + run: mvn -version && ant -version + - name: Build Coherence Snapshot shell: bash env: diff --git a/coherence-visualvm-plugin/pom.xml b/coherence-visualvm-plugin/pom.xml index 04d4e51..9b9de69 100644 --- a/coherence-visualvm-plugin/pom.xml +++ b/coherence-visualvm-plugin/pom.xml @@ -33,7 +33,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-main - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT ../pom.xml @@ -55,7 +55,7 @@ org-graalvm-visualvm-uisupport - org.graalvm.visualvm.api + org.netbeans.api org-openide-awt @@ -79,23 +79,23 @@ org-graalvm-visualvm-tools - org.graalvm.visualvm.api + org.netbeans.api org-openide-modules - org.graalvm.visualvm.api + org.netbeans.api org-openide-util - org.graalvm.visualvm.api + org.netbeans.api org-openide-dialogs - org.graalvm.visualvm.api + org.netbeans.api org-openide-windows - org.graalvm.visualvm.api + org.netbeans.api org-netbeans-modules-options-api diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml b/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml index b23e537..f68aec1 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml +++ b/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml @@ -33,7 +33,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-tests - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT ../pom.xml diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-commercial/pom.xml b/coherence-visualvm-tests/coherence-visualvm-tests-commercial/pom.xml index 7838d26..29f1254 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-commercial/pom.xml +++ b/coherence-visualvm-tests/coherence-visualvm-tests-commercial/pom.xml @@ -33,7 +33,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-tests - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT ../pom.xml diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/pom.xml b/coherence-visualvm-tests/coherence-visualvm-tests-core/pom.xml index 6964c1a..5ed40c3 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/pom.xml +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/pom.xml @@ -34,7 +34,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-tests - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT ../pom.xml diff --git a/coherence-visualvm-tests/pom.xml b/coherence-visualvm-tests/pom.xml index f4fb57b..dfc11e6 100644 --- a/coherence-visualvm-tests/pom.xml +++ b/coherence-visualvm-tests/pom.xml @@ -33,7 +33,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-main - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 7082c35..5849b97 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ com.oracle.coherence.plugin.visualvm coherence-visualvm-main pom - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT Coherence VisualVM Plugin Parent @@ -53,7 +53,7 @@ 2.3.0 - 21.12.3 + 21.12.4 14.1.2-0-0-SNAPSHOT @@ -68,7 +68,8 @@ 8 1.2.0 4.13.1 - RELEASE204 + 2.1 + RELEASE124 2.2 3.6.0 @@ -97,67 +98,67 @@ org.graalvm.visualvm.modules org-graalvm-visualvm-uisupport - ${jvisualvm.version} + ${visualvm.version} org.graalvm.visualvm.api org-graalvm-visualvm-application - ${jvisualvm.version} + ${visualvm.version} org.graalvm.visualvm.api org-graalvm-visualvm-application-views - ${jvisualvm.version} + ${visualvm.version} org.graalvm.visualvm.api org-graalvm-visualvm-charts - ${jvisualvm.version} + ${visualvm.version} org.graalvm.visualvm.api org-graalvm-visualvm-core - ${jvisualvm.version} + ${visualvm.version} org.graalvm.visualvm.api org-graalvm-visualvm-tools - ${jvisualvm.version} + ${visualvm.version} - org.graalvm.visualvm.api + org.netbeans.api org-openide-modules - ${jvisualvm.version} + ${netbeans.version} - org.graalvm.visualvm.api + org.netbeans.api org-openide-awt - ${jvisualvm.version} + ${netbeans.version} - org.graalvm.visualvm.api + org.netbeans.api org-openide-util - ${jvisualvm.version} + ${netbeans.version} - org.graalvm.visualvm.api + org.netbeans.api org-openide-dialogs - ${jvisualvm.version} + ${netbeans.version} - org.graalvm.visualvm.api + org.netbeans.api org-openide-windows - ${jvisualvm.version} + ${netbeans.version} - org.graalvm.visualvm.api + org.netbeans.api org-netbeans-modules-options-api - ${jvisualvm.version} + ${netbeans.version} org.graalvm.visualvm.modules org-graalvm-visualvm-lib-ui - ${jvisualvm.version} + ${visualvm.version} From 4410e043229e9bd967ba08a2cf86bac89ee9849f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 May 2022 14:20:41 +0800 Subject: [PATCH 02/27] Update readme to remove building Netbeans --- README.adoc | 93 ++++------------------------------------------------- 1 file changed, 6 insertions(+), 87 deletions(-) diff --git a/README.adoc b/README.adoc index c685d7a..ce5d9ea 100644 --- a/README.adoc +++ b/README.adoc @@ -37,7 +37,7 @@ You can connect to clusters via JMX or via management over REST with Coherence v The Plugin is an ideal tool for monitoring and managing Coherence clusters during the development and testing lifecycle and supports connecting to both Community Edition and Commercial versions of Coherence. -NOTE: This version of the Plugin requires VisualVM release 2.0.6 or later which is available from https://visualvm.github.io/. +NOTE: This version of the Plugin requires VisualVM release 2.1 or later which is available from https://visualvm.github.io/. image::assets/coherence-visualvm.png[Coherence VisualVM Plugin,800,479] @@ -218,21 +218,15 @@ Depending upon the edition and functionality you are using, the following option [#build] == Building the Plugin -If you wish to build the Plugin from scratch you need to build the VisualVM dependencies first. -To build the plugin is a two-step process: - -1. Generate the VisualVM dependencies -2. Build the Coherence VisualVM Plugin +If you wish to build the Plugin from scratch please follow the instructions below. === Pre-requisites You must have the following: -1. Java JDK 1.8 - To build VisualVM dependencies -2. Java JDK 11+ - To build and test the plugin -3. Ant version >= 1.9.9 -4. Maven 3.6.3+ -5. Git +1. Java JDK 11+ - To build and test the plugin +2. Maven 3.6.3+ +3. Git === Clone the Repository @@ -243,81 +237,6 @@ You must have the following: $ git clone https://github.com/oracle/coherence-visualvm.git ---- -=== Generate the VisualVM dependencies - -NOTE: These instructions have been summarized from https://github.com/oracle/visualvm/blob/release204/README.md. - -NOTE: A Script `install-artifacts.sh` is available in the `bin` directory to run this for a Linux/Mac environment. - -1. Ensure you have JDK8 in you PATH. - -2. Checkout the VisualVM repository -+ -[source,shell] ----- -$ git clone https://github.com/oracle/visualvm.git - -Cloning into 'visualvm'... ----- - -3. Checkout the `release204` branch -+ -[source,shell] ----- -$ cd visualvm - -$ git checkout release204 - -Switched to a new branch 'release204' ----- - -4. Unzip the NetBeans Platform 11.3 -+ -[source,shell] ----- -$ cd visualvm - -$ unzip nb113_platform_19062020.zip ----- - -5. Build the Plugins -+ -[source,shell] ----- -$ ant build-zip ----- - -6. Unzip the artefacts -+ -[source,shell] ----- -$ cd dist - -$ unzip visualvm.zip - -$ cd .. ----- - -7. Generate the NBM's -+ -[source,shell] ----- -$ ant nbms ----- - -8. Install into the local repository -+ -[source,shell] ----- -$ mvn -DnetbeansInstallDirectory=dist/visualvm \ - -DnetbeansNbmDirectory=build/updates \ - -DgroupIdPrefix=org.graalvm.visualvm \ - -DforcedVersion=RELEASE204 org.apache.netbeans.utilities:nb-repository-plugin:populate ----- - -NOTE: See https://github.com/oracle/visualvm/blob/release204/README.md[here] for instructions on how to -push the artefacts to a remote Maven repository. - ==== Build the VisualVM Plugin 1. Ensure you have JDK11 or above in your PATH. @@ -335,7 +254,7 @@ If you wish to run the Community Edition tests then leave out the `-DskipTests`. 3. Install the Plugin + -The plugin will be available in the location `coherence-visualvm-plugin/target/coherence-visualvm-plugin-{version}` +The plugin will be available in the location `coherence-visualvm-plugin/target/coherence-visualvm-plugin-{version}.nbm` Follow the instructions https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/using-jmx-manage-oracle-coherence.html[here] to install the plugin manually. From 133e9a2816d50328a5c857006f606e55eb2da3ff Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 May 2022 14:22:14 +0800 Subject: [PATCH 03/27] Update readme to remove building Netbeans --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index ce5d9ea..34ac894 100644 --- a/README.adoc +++ b/README.adoc @@ -32,7 +32,7 @@ The Coherence-VisualVM Plugin (the Plugin) provides management and monitoring of The Plugin aggregates Coherence MBean data and shows a concise operational view of a single Coherence cluster. Some management information is presented over time, which allows real-time analysis and troubleshooting. -You can connect to clusters via JMX or via management over REST with Coherence versions 14.1.1 or above. +You can connect to clusters via JMX or via management over REST with Coherence versions 12.2.1.4 or above. The Plugin is an ideal tool for monitoring and managing Coherence clusters during the development and testing lifecycle and supports connecting to both Community Edition and Commercial versions of Coherence. From b6771e8e60af55c704ababffbe4585c199c13a6c Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Thu, 12 May 2022 16:21:37 +0800 Subject: [PATCH 04/27] minor --- coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml | 3 +-- pom.xml | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml b/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml index f68aec1..700ae07 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml +++ b/coherence-visualvm-tests/coherence-visualvm-tests-ce/pom.xml @@ -1,7 +1,7 @@ org.apache.maven.plugins @@ -265,7 +264,6 @@ ${maven.nbm.plugin.version} - org.apache.maven.plugins @@ -283,5 +281,4 @@ - From 0463aadc639cdd9904ea0c25e961cf66782fdd48 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 23 May 2022 14:06:52 +0800 Subject: [PATCH 05/27] Minor changes --- .../visualvm/helper/HttpRequestSender.java | 114 +++++++++--------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index 9dc7581..a75b85f 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -163,7 +163,7 @@ public String getAttribute(ObjectName objectName, String attribute) String restName = getRestName(attribute); URLBuilder urlBuilder = getBasePath(); - modifyTarget(objectName, urlBuilder).addQueryParameter("fields", restName); + modifyTarget(objectName, urlBuilder).addQueryParameter(FIELDS, restName); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -197,7 +197,7 @@ public AttributeList getAttributes(ObjectName objectName, String[] asAttribute) URLBuilder urlBuilder = getBasePath(); - modifyTarget(objectName, urlBuilder).addQueryParameter("fields", attributes); + modifyTarget(objectName, urlBuilder).addQueryParameter(FIELDS, attributes); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -235,7 +235,7 @@ public Set getAllCacheMembers() { URLBuilder urlBuilder = getBasePath(); urlBuilder.addPathSegment("caches").addPathSegment("members") - .addQueryParameter("fields", "name,service,domainPartition"); + .addQueryParameter(FIELDS, "name,service,domainPartition"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeCacheItems = rootNode.get("items"); @@ -274,7 +274,7 @@ public Set getAllJournalMembers(String sJournalType) URLBuilder urlBuilder = getBasePath(); urlBuilder.addPathSegment("journal") .addPathSegment(sJournalUrlType).addPathSegment("members") - .addQueryParameter("fields", "nodeId,type,name"); + .addQueryParameter(FIELDS, "nodeId,type,name"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeJournalMemberItems = rootNode.get("items"); @@ -310,8 +310,8 @@ public Set getCacheMembers(String sServiceName, String sCacheName, S urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder.addQueryParameter("fields", "service,name,type,tier,nodeId") - .addQueryParameter("links", ""); + urlBuilder.addQueryParameter(FIELDS, "service,name,type,tier,nodeId") + .addQueryParameter(LINKS, ""); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -344,7 +344,7 @@ public Set getAllClusters() } // build the list of clusters - URLBuilder urlBuilder = getBasePath().addQueryParameter("links", ""); + URLBuilder urlBuilder = getBasePath().addQueryParameter(LINKS, ""); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode clusterItems = rootNode.get("items"); Set setObjectNames = new HashSet<>(); @@ -368,7 +368,7 @@ public Set getHotCacheMembers() { URLBuilder urlBuilder = getBasePath().addPathSegment("hotcache") .addPathSegment("members"); - urlBuilder.addQueryParameter("fields", "name,type,nodeId"); + urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -380,8 +380,8 @@ public Set getHotCachePerCacheAdapters(String sMember) URLBuilder urlBuilder = getBasePath().addPathSegment("hotcache") .addPathSegment("members").addPathSegment(sMember); - urlBuilder.addQueryParameter("fields", "name,type,nodeId") - .addQueryParameter("links", ""); + urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId") + .addQueryParameter(LINKS, ""); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -391,7 +391,7 @@ public Set getAllCoherenceWebMembers(String sSessionManager) throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("proxy").addPathSegment("members").addQueryParameter("fields", "name,type,nodeId"); + .addPathSegment("proxy").addPathSegment("members").addQueryParameter(FIELDS, "name,type,nodeId"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeWebAppItems = (JsonNode) rootNode.get("items"); @@ -421,8 +421,8 @@ public Set getCoherenceWebMembersForApplication(String sSessionManag URLBuilder urlBuilder = getBasePath().addPathSegment("webApplications") .addPathSegment(sAppId).addPathSegment("members"); - urlBuilder.addQueryParameter("fields", "name,type,nodeId") - .addQueryParameter("links", ""); + urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId") + .addQueryParameter(LINKS, ""); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -441,8 +441,8 @@ public Set getAllClusterMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("members"); - urlBuilder.addQueryParameter("fields", "type,nodeId") - .addQueryParameter("links", ""); + urlBuilder.addQueryParameter(FIELDS, "type,nodeId") + .addQueryParameter(LINKS, ""); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -451,7 +451,7 @@ public Set getAllClusterMembers() public Set getAllExecutorMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment("members"); - urlBuilder.addQueryParameter("links", "").addQueryParameter("fields", "name"); + urlBuilder.addQueryParameter(LINKS, "").addQueryParameter(FIELDS, "name"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -470,9 +470,9 @@ public Set getAllServiceMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members").addQueryParameter("fields", "name,type,domainPartition,nodeId," + + .addPathSegment("members").addQueryParameter(FIELDS, "name,type,domainPartition,nodeId," + "storageEnabled,persistenceActiveSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeServiceMembersItems = (JsonNode) rootNode.get("items"); @@ -503,7 +503,7 @@ public Set getAllProxyServerMembers() { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment("proxy").addPathSegment("members") - .addQueryParameter("fields", "name,type,domainPartition,nodeId"); + .addQueryParameter(FIELDS, "name,type,domainPartition,nodeId"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -546,7 +546,7 @@ public Set getPartitionAssignmentAttributes(String sService, String sD { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment(encodeServiceName(sService)).addPathSegment("partition") - .addQueryParameter("fields", "averagePartitionSizeKB,maxPartitionSizeKB,averageStorageSizeKB," + + .addQueryParameter(FIELDS, "averagePartitionSizeKB,maxPartitionSizeKB,averageStorageSizeKB," + "maxStorageSizeKB,maxLoadNodeId"); if (sDomainPartition != null) { @@ -736,8 +736,8 @@ public Set getMembersOfService(String sServiceName, String sDomainPa urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder.addQueryParameter("fields", "name,type,nodeId,domainPartition") - .addQueryParameter("links", ""); + urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId,domainPartition") + .addQueryParameter(LINKS, ""); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeServiceMembers = (JsonNode) rootNode.get("items"); @@ -774,8 +774,8 @@ public JsonNode getListOfServiceCaches(String sServiceName, String sDomainPartit { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches") - .addQueryParameter("links", "") - .addQueryParameter("fields", "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + .addQueryParameter(LINKS, "") + .addQueryParameter(FIELDS, "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + "averageMissMillis"); if (sDomainPartition != null) @@ -798,8 +798,8 @@ public JsonNode getListOfCaches() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("caches") - .addQueryParameter("links", "") - .addQueryParameter("fields", "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + .addQueryParameter(LINKS, "") + .addQueryParameter(FIELDS, "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + "averageMissMillis,service"); return getResponseJson(sendGetRequest(urlBuilder)); @@ -816,8 +816,8 @@ public JsonNode getNodeStorage() { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment("members") - .addQueryParameter("links", "") - .addQueryParameter("fields", "nodeId,ownedPartitionsPrimary"); + .addQueryParameter(LINKS, "") + .addQueryParameter(FIELDS, "nodeId,ownedPartitionsPrimary"); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -835,8 +835,8 @@ public JsonNode getListOfStorageMembers(String sServiceName, String sDomainParti { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("members") - .addQueryParameter("links", "") - .addQueryParameter("fields", "ownedPartitionsPrimary,nodeId"); + .addQueryParameter(LINKS, "") + .addQueryParameter(FIELDS, "ownedPartitionsPrimary,nodeId"); if (sDomainPartition != null) { @@ -856,8 +856,8 @@ public JsonNode getAllStorageMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment("members") - .addQueryParameter("links", "") - .addQueryParameter("fields", "type,name,domainPartition,nodeId,persistenceMode," + + .addQueryParameter(LINKS, "") + .addQueryParameter(FIELDS, "type,name,domainPartition,nodeId,persistenceMode," + "storageEnabled,persistenceActiveSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage"); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -877,10 +877,10 @@ public JsonNode getDataForStorageManagerMembers(String sServiceName, String sDom { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches").addPathSegment(sCacheName) - .addPathSegment("members").addQueryParameter("fields", + .addPathSegment("members").addQueryParameter(FIELDS, "nodeId,locksGranted,locksPending,listenerRegistrations,maxQueryDurationMillis,maxQueryDescription," + "nonOptimizedQueryAverageMillis,optimizedQueryAverageMillis,indexTotalUnits,indexingTotalMillis") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); if (sDomainPartition != null) { @@ -912,9 +912,9 @@ public JsonNode getDataForCacheMembers(String sServiceName, String sCacheName, S urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder.addQueryParameter("fields", "name,type,size,service,nodeId," + + urlBuilder.addQueryParameter(FIELDS, "name,type,size,service,nodeId," + "domainPartition,tier,units,unitFactor,totalGets,totalPuts,cacheHits,cacheMisses,hitProbability") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -929,11 +929,11 @@ public JsonNode getListOfClusterMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("members"); - urlBuilder = urlBuilder.addQueryParameter("fields", "nodeId," + + urlBuilder = urlBuilder.addQueryParameter(FIELDS, "nodeId," + "publisherSuccessRate,receiverSuccessRate," + "sendQueueSize,memoryMaxMB,memoryAvailableMB,unicastAddress,roleName,unicastPort," + "machineName,rackName,siteName,productEdition") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -948,11 +948,11 @@ public JsonNode getListOfServices() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") .addPathSegment("members") - .addQueryParameter("fields", "name,type,domainPartition,nodeId," + + .addQueryParameter(FIELDS, "name,type,domainPartition,nodeId," + "statusHA,memberCount,partitionsAll,partitionsEndangered," + "partitionsVulnerable,partitionsUnbalanced,requestPendingCount," + "storageEnabledCount,type") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -965,7 +965,7 @@ public JsonNode getListOfServices() throws Exception public JsonNode getExecutors() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment("members") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -978,7 +978,7 @@ public JsonNode getExecutors() throws Exception public JsonNode getDataForProxyMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("proxy").addPathSegment("members").addQueryParameter("fields", "hostIP,name,nodeId," + + .addPathSegment("proxy").addPathSegment("members").addQueryParameter(FIELDS, "hostIP,name,nodeId," + "connectionCount,outgoingMessageBacklog,totalBytesReceived,totalBytesSent," + "totalMessagesReceived,totalMessagesSent,protocol," + "domainPartition,httpServerType,totalRequestCount," + @@ -997,7 +997,7 @@ public JsonNode getDataForProxyMembers() throws Exception public JsonNode getDataForServiceMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members").addQueryParameter("fields", "name,type,domainPartition,nodeId,taskBacklog," + + .addPathSegment("members").addQueryParameter(FIELDS, "name,type,domainPartition,nodeId,taskBacklog," + "threadCount,threadIdleCount,requestAverageDuration,taskAverageDuration"); return getResponseJson(sendGetRequest(urlBuilder)); @@ -1053,7 +1053,7 @@ public JsonNode getDataForElasticDataMembers(String sElasticDataType) { URLBuilder urlBuilder = getBasePath().addPathSegment("journal") .addPathSegment(sElasticDataType).addPathSegment("members") - .addQueryParameter("fields", "nodeId,fileCount,maxJournalFilesNumber,maxFileSize,totalDataSize," + + .addQueryParameter(FIELDS, "nodeId,fileCount,maxJournalFilesNumber,maxFileSize,totalDataSize," + "compactionCount,exhaustiveCompactionCount,currentCollectorLoadFactor"); return getResponseJson(sendGetRequest(urlBuilder)); @@ -1078,9 +1078,9 @@ public JsonNode getAggregatedProxyData(String sServiceName, String sDomainPartit urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder = urlBuilder.addQueryParameter("fields", "name,type,httpServerType,totalRequestCount," + + urlBuilder = urlBuilder.addQueryParameter(FIELDS, "name,type,httpServerType,totalRequestCount," + "totalErrorCount,requestsPerSecond,averageRequestTime,protocol") - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1104,9 +1104,9 @@ public JsonNode getAggregatedServiceData(String sServiceName, String sDomainPart urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder = urlBuilder.addQueryParameter("fields", "name,domainPartition,statusHA,partitionsAll," + + urlBuilder = urlBuilder.addQueryParameter(FIELDS, "name,domainPartition,statusHA,partitionsAll," + "partitionsEndangered,partitionsVulnerable,partitionsUnbalanced,requestPendingCount,storageEnabled," + - "memberCount").addQueryParameter("links", ""); + "memberCount").addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1133,8 +1133,8 @@ public JsonNode getAggregatedIncomingData(String sServiceName, String sDomainPar urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder = urlBuilder.addQueryParameter("fields", "status,bytesReceivedSecs,msgsReceivedSecs") - .addQueryParameter("links", ""); + urlBuilder = urlBuilder.addQueryParameter(FIELDS, "status,bytesReceivedSecs,msgsReceivedSecs") + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1163,8 +1163,8 @@ public JsonNode getAggregatedOutgoingData(String sServiceName, String sDomainPar urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } - urlBuilder = urlBuilder.addQueryParameter("fields", "status,bytesSentSecs,msgsSentSecs") - .addQueryParameter("links", ""); + urlBuilder = urlBuilder.addQueryParameter(FIELDS, "status,bytesSentSecs,msgsSentSecs") + .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1385,7 +1385,7 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) case "Node": return urlBuilder.addPathSegment("members") .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); case "Journal": String sJournalUrlType = objectName.getKeyProperty("name").equals("FlashJournalRM") ? "flash" : "ram"; @@ -1413,7 +1413,7 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) return urlBuilder.addPathSegment("services") .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "name"))) .addPathSegment("members").addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) - .addQueryParameter("links", ""); + .addQueryParameter(LINKS, ""); case "ConnectionManager": return urlBuilder.addPathSegment("services") .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "name"))) @@ -1737,6 +1737,12 @@ private void initSSL() */ public static final String CLUSTER_PREFIX = "Coherence:type=Cluster,cluster="; + /** + * Various constants. + */ + private static final String FIELDS = "fields"; + private static final String LINKS = "links"; + /** * A trust manager that will trust all certificates. Only used when the preference to ignore SSL certs is chosen. * Should be used with care. From 87ea7c8fb24a911825b0087c652670fca49fda49 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 23 May 2022 15:51:15 +0800 Subject: [PATCH 06/27] More code updates --- .../visualvm/helper/HttpRequestSender.java | 153 +++++++++--------- 1 file changed, 78 insertions(+), 75 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index a75b85f..1d8010b 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -234,7 +234,7 @@ public Set getAllCacheMembers() throws Exception { URLBuilder urlBuilder = getBasePath(); - urlBuilder.addPathSegment("caches").addPathSegment("members") + urlBuilder.addPathSegment(CACHES).addPathSegment(MEMBERS) .addQueryParameter(FIELDS, "name,service,domainPartition"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -273,7 +273,7 @@ public Set getAllJournalMembers(String sJournalType) URLBuilder urlBuilder = getBasePath(); urlBuilder.addPathSegment("journal") - .addPathSegment(sJournalUrlType).addPathSegment("members") + .addPathSegment(sJournalUrlType).addPathSegment(MEMBERS) .addQueryParameter(FIELDS, "nodeId,type,name"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -302,9 +302,9 @@ public Set getAllJournalMembers(String sJournalType) public Set getCacheMembers(String sServiceName, String sCacheName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches") - .addPathSegment(sCacheName).addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(CACHES) + .addPathSegment(sCacheName).addPathSegment(MEMBERS); if (sDomainPartition != null) { urlBuilder.addQueryParameter("domainPartition", sDomainPartition); @@ -367,7 +367,7 @@ public Set getHotCacheMembers() throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("hotcache") - .addPathSegment("members"); + .addPathSegment(MEMBERS); urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); @@ -378,7 +378,7 @@ public Set getHotCachePerCacheAdapters(String sMember) throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("hotcache") - .addPathSegment("members").addPathSegment(sMember); + .addPathSegment(MEMBERS).addPathSegment(sMember); urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId") .addQueryParameter(LINKS, ""); @@ -390,8 +390,8 @@ public Set getHotCachePerCacheAdapters(String sMember) public Set getAllCoherenceWebMembers(String sSessionManager) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("proxy").addPathSegment("members").addQueryParameter(FIELDS, "name,type,nodeId"); + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment("proxy").addPathSegment(MEMBERS).addQueryParameter(FIELDS, "name,type,nodeId"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); JsonNode nodeWebAppItems = (JsonNode) rootNode.get("items"); @@ -419,7 +419,7 @@ public Set getCoherenceWebMembersForApplication(String sSessionManag throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("webApplications") - .addPathSegment(sAppId).addPathSegment("members"); + .addPathSegment(sAppId).addPathSegment(MEMBERS); urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId") .addQueryParameter(LINKS, ""); @@ -440,7 +440,7 @@ public Set getClusterMemberOS(int nodeId) public Set getAllClusterMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment(MEMBERS); urlBuilder.addQueryParameter(FIELDS, "type,nodeId") .addQueryParameter(LINKS, ""); @@ -450,7 +450,7 @@ public Set getAllClusterMembers() @Override public Set getAllExecutorMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment(MEMBERS); urlBuilder.addQueryParameter(LINKS, "").addQueryParameter(FIELDS, "name"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); @@ -469,8 +469,8 @@ public Set getAllGrpcProxyMembers() public Set getAllServiceMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members").addQueryParameter(FIELDS, "name,type,domainPartition,nodeId," + + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(MEMBERS).addQueryParameter(FIELDS, "name,type,domainPartition,nodeId," + "storageEnabled,persistenceActiveSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage") .addQueryParameter(LINKS, ""); @@ -501,8 +501,8 @@ public Set getAllServiceMembers() public Set getAllProxyServerMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("proxy").addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment("proxy").addPathSegment(MEMBERS) .addQueryParameter(FIELDS, "name,type,domainPartition,nodeId"); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); @@ -529,7 +529,7 @@ public Set getPartitionAssignmentObjectName(String sService, String public String getScheduledDistributions(String sService, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("partition").addPathSegment("scheduledDistributions"); if (sDomainPartition != null) { @@ -544,7 +544,7 @@ public String getScheduledDistributions(String sService, String sDomainPartition public Set getPartitionAssignmentAttributes(String sService, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("partition") .addQueryParameter(FIELDS, "averagePartitionSizeKB,maxPartitionSizeKB,averageStorageSizeKB," + "maxStorageSizeKB,maxLoadNodeId"); @@ -569,7 +569,7 @@ public Set getPartitionAssignmentAttributes(String sService, String sD public void invokeFederationOperation(String sService, String sOperation, String sParticipant) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("federation").addPathSegment("participants") .addPathSegment(sParticipant).addPathSegment(sOperation); sendPostRequest(urlBuilder); @@ -579,7 +579,7 @@ public void invokeFederationOperation(String sService, String sOperation, String public Integer retrievePendingIncomingMessages(String sService) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("federation").addPathSegment("pendingIncomingMessages"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -590,7 +590,7 @@ public Integer retrievePendingIncomingMessages(String sService) public Integer retrievePendingOutgoingMessages(String sService) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("federation").addPathSegment("pendingOutgoingMessages"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -601,7 +601,7 @@ public Integer retrievePendingOutgoingMessages(String sService) public String getNodeState(Integer nNodeId) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(MEMBERS) .addPathSegment(nNodeId + "").addPathSegment("state"); JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); @@ -626,7 +626,7 @@ public void dumpClusterHeap(String sRole) throws Exception public String[] getSnapshots(String sService, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence").addPathSegment("snapshots"); if (sDomainPartition != null) { @@ -652,7 +652,7 @@ public String[] getSnapshots(String sService, String sDomainPartition) public String[] getArchivedSnapshots(String sService, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence").addPathSegment("archives"); if (sDomainPartition != null) { @@ -680,7 +680,7 @@ public void executePersistenceOperation(String sService, String sSnapshotName) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence"); switch (sOperationName) @@ -729,8 +729,8 @@ public void executePersistenceOperation(String sService, public Set getMembersOfService(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(MEMBERS); if (sDomainPartition != null) { urlBuilder.addQueryParameter("domainPartition", sDomainPartition); @@ -772,8 +772,8 @@ public Set getMembersOfService(String sServiceName, String sDomainPa public JsonNode getListOfServiceCaches(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(CACHES) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + "averageMissMillis"); @@ -797,7 +797,7 @@ public JsonNode getListOfServiceCaches(String sServiceName, String sDomainPartit public JsonNode getListOfCaches() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("caches") + URLBuilder urlBuilder = getBasePath().addPathSegment(CACHES) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "nodeId,name,unitFactor,size,unitsBytes,units,memoryUnits," + "averageMissMillis,service"); @@ -814,8 +814,8 @@ public JsonNode getListOfCaches() public JsonNode getNodeStorage() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(MEMBERS) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "nodeId,ownedPartitionsPrimary"); return getResponseJson(sendGetRequest(urlBuilder)); @@ -833,8 +833,8 @@ public JsonNode getNodeStorage() public JsonNode getListOfStorageMembers(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(MEMBERS) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "ownedPartitionsPrimary,nodeId"); @@ -854,8 +854,8 @@ public JsonNode getListOfStorageMembers(String sServiceName, String sDomainParti */ public JsonNode getAllStorageMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(MEMBERS) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "type,name,domainPartition,nodeId,persistenceMode," + "storageEnabled,persistenceActiveSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage"); @@ -875,9 +875,9 @@ public JsonNode getAllStorageMembers() throws Exception public JsonNode getDataForStorageManagerMembers(String sServiceName, String sDomainPartition, String sCacheName) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches").addPathSegment(sCacheName) - .addPathSegment("members").addQueryParameter(FIELDS, + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(CACHES).addPathSegment(sCacheName) + .addPathSegment(MEMBERS).addQueryParameter(FIELDS, "nodeId,locksGranted,locksPending,listenerRegistrations,maxQueryDurationMillis,maxQueryDescription," + "nonOptimizedQueryAverageMillis,optimizedQueryAverageMillis,indexTotalUnits,indexingTotalMillis") .addQueryParameter(LINKS, ""); @@ -904,9 +904,9 @@ public JsonNode getDataForStorageManagerMembers(String sServiceName, String sDom public JsonNode getDataForCacheMembers(String sServiceName, String sCacheName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("caches").addPathSegment(sCacheName) - .addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(encodeServiceName(sServiceName)).addPathSegment(CACHES).addPathSegment(sCacheName) + .addPathSegment(MEMBERS); if (sDomainPartition != null) { urlBuilder.addQueryParameter("domainPartition", sDomainPartition); @@ -927,7 +927,7 @@ public JsonNode getDataForCacheMembers(String sServiceName, String sCacheName, S */ public JsonNode getListOfClusterMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("members"); + URLBuilder urlBuilder = getBasePath().addPathSegment(MEMBERS); urlBuilder = urlBuilder.addQueryParameter(FIELDS, "nodeId," + "publisherSuccessRate,receiverSuccessRate," + @@ -946,8 +946,8 @@ public JsonNode getListOfClusterMembers() throws Exception */ public JsonNode getListOfServices() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(MEMBERS) .addQueryParameter(FIELDS, "name,type,domainPartition,nodeId," + "statusHA,memberCount,partitionsAll,partitionsEndangered," + "partitionsVulnerable,partitionsUnbalanced,requestPendingCount," + @@ -964,7 +964,7 @@ public JsonNode getListOfServices() throws Exception */ public JsonNode getExecutors() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment("members") + URLBuilder urlBuilder = getBasePath().addPathSegment("executors").addPathSegment(MEMBERS) .addQueryParameter(LINKS, ""); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -977,8 +977,8 @@ public JsonNode getExecutors() throws Exception */ public JsonNode getDataForProxyMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("proxy").addPathSegment("members").addQueryParameter(FIELDS, "hostIP,name,nodeId," + + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment("proxy").addPathSegment(MEMBERS).addQueryParameter(FIELDS, "hostIP,name,nodeId," + "connectionCount,outgoingMessageBacklog,totalBytesReceived,totalBytesSent," + "totalMessagesReceived,totalMessagesSent,protocol," + "domainPartition,httpServerType,totalRequestCount," + @@ -996,8 +996,8 @@ public JsonNode getDataForProxyMembers() throws Exception */ public JsonNode getDataForServiceMembers() throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") - .addPathSegment("members").addQueryParameter(FIELDS, "name,type,domainPartition,nodeId,taskBacklog," + + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) + .addPathSegment(MEMBERS).addQueryParameter(FIELDS, "name,type,domainPartition,nodeId,taskBacklog," + "threadCount,threadIdleCount,requestAverageDuration,taskAverageDuration"); return getResponseJson(sendGetRequest(urlBuilder)); @@ -1014,10 +1014,10 @@ public JsonNode getDataForServiceMembers() throws Exception public JsonNode getIncomingDataForParticipant(String sServiceName, String sParticipantName) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("federation").addPathSegment("statistics") .addPathSegment("incoming").addPathSegment("participants") - .addPathSegment(sParticipantName).addPathSegment("members"); + .addPathSegment(sParticipantName).addPathSegment(MEMBERS); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1033,10 +1033,10 @@ public JsonNode getIncomingDataForParticipant(String sServiceName, String sParti public JsonNode getOutgoingDataForParticipant(String sServiceName, String sParticipantName) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("federation").addPathSegment("statistics") .addPathSegment("outgoing").addPathSegment("participants").addPathSegment(sParticipantName) - .addPathSegment("members"); + .addPathSegment(MEMBERS); return getResponseJson(sendGetRequest(urlBuilder)); } @@ -1052,7 +1052,7 @@ public JsonNode getDataForElasticDataMembers(String sElasticDataType) throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment("journal") - .addPathSegment(sElasticDataType).addPathSegment("members") + .addPathSegment(sElasticDataType).addPathSegment(MEMBERS) .addQueryParameter(FIELDS, "nodeId,fileCount,maxJournalFilesNumber,maxFileSize,totalDataSize," + "compactionCount,exhaustiveCompactionCount,currentCollectorLoadFactor"); @@ -1071,7 +1071,7 @@ public JsonNode getDataForElasticDataMembers(String sElasticDataType) public JsonNode getAggregatedProxyData(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("proxy"); if (sDomainPartition != null) { @@ -1097,7 +1097,7 @@ public JsonNode getAggregatedProxyData(String sServiceName, String sDomainPartit public JsonNode getAggregatedServiceData(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)); if (sDomainPartition != null) { @@ -1124,7 +1124,7 @@ public JsonNode getAggregatedServiceData(String sServiceName, String sDomainPart public JsonNode getAggregatedIncomingData(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)).addPathSegment("federation").addPathSegment("statistics") .addPathSegment("incoming").addPathSegment("participants"); @@ -1152,7 +1152,7 @@ public JsonNode getAggregatedIncomingData(String sServiceName, String sDomainPar public JsonNode getAggregatedOutgoingData(String sServiceName, String sDomainPartition) throws Exception { - URLBuilder urlBuilder = getBasePath().addPathSegment("services") + URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) .addPathSegment(encodeServiceName(sServiceName)) .addPathSegment("federation").addPathSegment("statistics").addPathSegment("outgoing") .addPathSegment("participants"); @@ -1383,19 +1383,19 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) switch (objectName.getKeyProperty("type")) { case "Node": - return urlBuilder.addPathSegment("members") + return urlBuilder.addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) .addQueryParameter(LINKS, ""); case "Journal": String sJournalUrlType = objectName.getKeyProperty("name").equals("FlashJournalRM") ? "flash" : "ram"; - return urlBuilder.addPathSegment("journal").addPathSegment(sJournalUrlType).addPathSegment("members") + return urlBuilder.addPathSegment("journal").addPathSegment(sJournalUrlType).addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")); case "Cache": - urlBuilder = urlBuilder.addPathSegment("services") + urlBuilder = urlBuilder.addPathSegment(SERVICES) .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) - .addPathSegment("caches").addPathSegment(getKeyPropertyFromObjName(objectName, "name")) - .addPathSegment("members") + .addPathSegment(CACHES).addPathSegment(getKeyPropertyFromObjName(objectName, "name")) + .addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) .addQueryParameter("tier", getKeyPropertyFromObjName(objectName, "tier")); String loader = objectName.getKeyProperty("loader"); @@ -1405,28 +1405,28 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) } return urlBuilder; case "StorageManager": - return urlBuilder.addPathSegment("services") + return urlBuilder.addPathSegment(SERVICES) .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) - .addPathSegment("caches").addPathSegment(objectName.getKeyProperty("cache")) - .addPathSegment("members").addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")); + .addPathSegment(CACHES).addPathSegment(objectName.getKeyProperty("cache")) + .addPathSegment(MEMBERS).addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")); case "Service": - return urlBuilder.addPathSegment("services") + return urlBuilder.addPathSegment(SERVICES) .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "name"))) - .addPathSegment("members").addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) + .addPathSegment(MEMBERS).addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) .addQueryParameter(LINKS, ""); case "ConnectionManager": - return urlBuilder.addPathSegment("services") + return urlBuilder.addPathSegment(SERVICES) .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "name"))) - .addPathSegment("members").addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) + .addPathSegment(MEMBERS).addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) .addPathSegment("proxy"); case "Cluster": return urlBuilder; case "Persistence": - return urlBuilder.addPathSegment("services") + return urlBuilder.addPathSegment(SERVICES) .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) .addPathSegment("persistence"); case "Platform": - urlBuilder = urlBuilder.addPathSegment("members") + urlBuilder = urlBuilder.addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) .addPathSegment("platform"); String subType = objectName.getKeyProperty("subType"); @@ -1740,8 +1740,11 @@ private void initSSL() /** * Various constants. */ - private static final String FIELDS = "fields"; - private static final String LINKS = "links"; + private static final String FIELDS = "fields"; + private static final String LINKS = "links"; + private static final String SERVICES = "services"; + private static final String MEMBERS = "members"; + private static final String CACHES = "caches"; /** * A trust manager that will trust all certificates. Only used when the preference to ignore SSL certs is chosen. From 881526eec92f8e9df87558ec5dbbf4b302857d18 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 24 May 2022 11:04:18 +0800 Subject: [PATCH 07/27] code cleanup --- .../visualvm/helper/HttpRequestSender.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index 1d8010b..2844d5d 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -249,7 +249,7 @@ public Set getAllCacheMembers() Hashtable mapKeysProps = new Hashtable<>(); mapKeysProps.put("name", cacheMember.get("name").asText()); - mapKeysProps.put("service", cacheMember.get("service").asText()); + mapKeysProps.put(SERVICE, cacheMember.get(SERVICE).asText()); JsonNode domainPartition = cacheMember.get("domainPartition"); if (domainPartition != null) @@ -627,14 +627,14 @@ public String[] getSnapshots(String sService, String sDomainPartition) throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence").addPathSegment("snapshots"); + .addPathSegment(encodeServiceName(sService)).addPathSegment(PERSISTENCE).addPathSegment(SNAPSHOTS); if (sDomainPartition != null) { urlBuilder.addQueryParameter("domainPartition", sDomainPartition); } JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); - JsonNode nodeSnapshots = rootNode.get("snapshots"); + JsonNode nodeSnapshots = rootNode.get(SNAPSHOTS); List listSnapshots = new ArrayList<>(); if (nodeSnapshots != null && nodeSnapshots.isArray()) @@ -653,7 +653,7 @@ public String[] getArchivedSnapshots(String sService, String sDomainPartition) throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence").addPathSegment("archives"); + .addPathSegment(encodeServiceName(sService)).addPathSegment(PERSISTENCE).addPathSegment("archives"); if (sDomainPartition != null) { urlBuilder.addQueryParameter("domainPartition", sDomainPartition); @@ -681,7 +681,7 @@ public void executePersistenceOperation(String sService, throws Exception { URLBuilder urlBuilder = getBasePath().addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(sService)).addPathSegment("persistence"); + .addPathSegment(encodeServiceName(sService)).addPathSegment(PERSISTENCE); switch (sOperationName) { @@ -698,15 +698,15 @@ public void executePersistenceOperation(String sService, sendPostRequest(urlBuilder); break; case CoherencePersistencePanel.CREATE_SNAPSHOT: - urlBuilder.addPathSegment("snapshots").addPathSegment(sSnapshotName); + urlBuilder.addPathSegment(SNAPSHOTS).addPathSegment(sSnapshotName); sendPostRequest(urlBuilder); break; case CoherencePersistencePanel.REMOVE_SNAPSHOT: - urlBuilder.addPathSegment("snapshots").addPathSegment(sSnapshotName); + urlBuilder.addPathSegment(SNAPSHOTS).addPathSegment(sSnapshotName); sendDeleteRequest(urlBuilder); break; case CoherencePersistencePanel.RECOVER_SNAPSHOT: - urlBuilder.addPathSegment("snapshots").addPathSegment(sSnapshotName).addPathSegment("recover"); + urlBuilder.addPathSegment(SNAPSHOTS).addPathSegment(sSnapshotName).addPathSegment("recover"); sendPostRequest(urlBuilder); break; case CoherencePersistencePanel.FORCE_RECOVERY: @@ -1393,7 +1393,7 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")); case "Cache": urlBuilder = urlBuilder.addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) + .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, SERVICE))) .addPathSegment(CACHES).addPathSegment(getKeyPropertyFromObjName(objectName, "name")) .addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) @@ -1406,7 +1406,7 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) return urlBuilder; case "StorageManager": return urlBuilder.addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) + .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, SERVICE))) .addPathSegment(CACHES).addPathSegment(objectName.getKeyProperty("cache")) .addPathSegment(MEMBERS).addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")); case "Service": @@ -1423,8 +1423,8 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) return urlBuilder; case "Persistence": return urlBuilder.addPathSegment(SERVICES) - .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, "service"))) - .addPathSegment("persistence"); + .addPathSegment(encodeServiceName(getKeyPropertyFromObjName(objectName, SERVICE))) + .addPathSegment(PERSISTENCE); case "Platform": urlBuilder = urlBuilder.addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) @@ -1740,11 +1740,14 @@ private void initSSL() /** * Various constants. */ - private static final String FIELDS = "fields"; - private static final String LINKS = "links"; - private static final String SERVICES = "services"; - private static final String MEMBERS = "members"; - private static final String CACHES = "caches"; + private static final String FIELDS = "fields"; + private static final String LINKS = "links"; + private static final String SERVICES = "services"; + private static final String SERVICE = "service"; + private static final String MEMBERS = "members"; + private static final String CACHES = "caches"; + private static final String PERSISTENCE = "persistence"; + private static final String SNAPSHOTS = "snapshots"; /** * A trust manager that will trust all certificates. Only used when the preference to ignore SSL certs is chosen. From 2429878fdba43788ec4b12650cb78ce19cb36085 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 30 May 2022 08:28:25 +0800 Subject: [PATCH 08/27] Add code to recognize all active persistence modes --- .../panel/AbstractCoherencePanel.java | 4 +++- .../tablemodel/model/PersistenceData.java | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java index 1cb2aab..30bec16 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java @@ -698,7 +698,9 @@ protected Object[] getPersistenceData(List> persistenceD for (Map.Entry entry : persistenceData) { // only include services with active persistence - if (entry.getValue().getColumn(PersistenceData.PERSISTENCE_MODE).equals("active")) + String sPersistenceMode = (String) entry.getValue().getColumn(PersistenceData.PERSISTENCE_MODE); + + if (PersistenceData.isActivePersistence(sPersistenceMode)) { long cTotalMem = (Long) entry.getValue().getColumn(PersistenceData.TOTAL_ACTIVE_SPACE_USED); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java index ac6bfdb..f682108 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java @@ -162,8 +162,8 @@ public List> getJMXData(RequestSender requestSender, Vis { data = (PersistenceData) mapData.get(sServiceNameKey); - // PersistenceActiveSpaceUsed is only valid for active persistence mode - if ("active".equals(data.getColumn(PERSISTENCE_MODE))) + // PersistenceActiveSpaceUsed is only valid for active or active-backup persistence mode + if (isActivePersistence((String) data.getColumn(PERSISTENCE_MODE))) { data.setColumn(TOTAL_ACTIVE_SPACE_USED, (Long) data.getColumn(TOTAL_ACTIVE_SPACE_USED) @@ -275,8 +275,8 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel m } // update the details for each node - // PersistenceActiveSpaceUsed is only valid for active persistence mode - if ("active".equals(sPersistenceMode)) + // PersistenceActiveSpaceUsed is only valid for active or active-backup persistence mode + if (isActivePersistence(sPersistenceMode)) { long nPersistenceActiveSpaceUsed = details.get("persistenceActiveSpaceUsed").asLong(); data.setColumn(TOTAL_ACTIVE_SPACE_USED, @@ -409,6 +409,17 @@ public static String getMBeanName(String sServiceName) + "," + PERSISTENCE_COORDINATOR +",*"; } + /** + * Returns true if we are using active persistence mode. + * + * @param sPersistenceMode the persistence mode + * @return true if we are using active persistence mode. + */ + public static boolean isActivePersistence(String sPersistenceMode) + { + return sPersistenceMode != null && sPersistenceMode.startsWith("active-"); + } + // ----- constants ------------------------------------------------------ private static final long serialVersionUID = 7769559573242105947L; From 10dc45aec925dd6e257bd60f4472ba1b8a54de7a Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 15 Jun 2022 10:39:06 +0800 Subject: [PATCH 09/27] Add new Topic Service --- .../panel/AbstractCoherencePanel.java | 2 +- .../tablemodel/model/ServiceData.java | 26 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java index 30bec16..66bdce9 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java @@ -731,7 +731,7 @@ protected Object[] getPersistenceData(List> persistenceD */ protected int getNullEntry(Object oValue) { - return (oValue == null ? Integer.valueOf(0) : (Integer) oValue); + return (oValue == null ? 0 : (Integer) oValue); } /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/ServiceData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/ServiceData.java index d1e88dd..57ec084 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/ServiceData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/ServiceData.java @@ -103,8 +103,8 @@ public List> getJMXData(RequestSender sender, VisualVMMo // if its dist cache or federated then save so we don't, double count size for repl // caches - String sCacheType = (String) sender.getAttribute(cacheNameObjName, "Type"); - if (DISTRIBUTED_CACHE_TYPE.equals(sCacheType) || FEDERATED_CACHE_TYPE.equals(sCacheType) ) + String sServiceType = (String) sender.getAttribute(cacheNameObjName, "Type"); + if (isDistributed(sServiceType)) { setDistributedCaches.add(sServiceName); } @@ -205,7 +205,7 @@ public Data processReporterData(Object[] aoColumns, VisualVMModel model) data.setColumn(ServiceData.PARTITIONS_PENDING, Integer.valueOf(getNumberValue(aoColumns[nStart++].toString()))); // record the list of distributed & federated caches - if (DISTRIBUTED_CACHE_TYPE.equals(aoColumns[nStart]) || FEDERATED_CACHE_TYPE.equals(aoColumns[nStart])) + if (isDistributed((String) aoColumns[nStart])) { setDistributedCaches.add(data.getColumn(ServiceData.SERVICE_NAME).toString()); } @@ -269,8 +269,7 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel data.setColumn(ServiceData.MEMBERS, nTotalMemberCount); // record the list of distributed, federated or replicated caches - if (DISTRIBUTED_CACHE_TYPE.equals(sServiceType) || FEDERATED_CACHE_TYPE.equals(sServiceType) || - REPLICATED_CACHE_TYPE.equals(sServiceType)) + if (isDistributed(sServiceType) || REPLICATED_CACHE_TYPE.equals(sServiceType)) { data.setColumn(ServiceData.PARTITION_COUNT, serviceDetails.get("partitionsAll").asInt()); @@ -311,6 +310,18 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel return mapData; } + /** + * Returns true if the server type is a distributed cache. + * @param sServiceType cache type + * + * @return true if the cache type is a distributed cache + */ + public static boolean isDistributed(String sServiceType) + { + return DISTRIBUTED_CACHE_TYPE.equals(sServiceType) || FEDERATED_CACHE_TYPE.equals(sServiceType) || + PAGED_TOPIC_TYPE.equals(sServiceType); + } + // ----- constants ------------------------------------------------------ private static final long serialVersionUID = -9192162461585760564L; @@ -376,6 +387,11 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel */ private static final String DISTRIBUTED_CACHE_TYPE = "DistributedCache"; + /** + * Service of type PagedTopic. + */ + private static final String PAGED_TOPIC_TYPE = "PagedTopic"; + /** * Service of type FederatedCache. */ From bea5e0a6d9a9bd454db997df4c6161e32c60203f Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 21 Jun 2022 12:55:30 +0800 Subject: [PATCH 10/27] Add persistence backup space --- .../plugin/visualvm/VisualVMModel.java | 5 +- .../plugin/visualvm/helper/GraphHelper.java | 10 ++-- .../visualvm/helper/HttpRequestSender.java | 2 +- .../panel/AbstractCoherencePanel.java | 24 +++++---- .../panel/CoherenceClusterSnapshotPanel.java | 9 ++-- .../panel/CoherencePersistencePanel.java | 15 +++--- .../tablemodel/model/PersistenceData.java | 49 ++++++++++++++++--- .../plugin/visualvm/Bundle.properties | 7 ++- 8 files changed, 86 insertions(+), 35 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java index 08103b8..1b7e582 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java @@ -1441,8 +1441,9 @@ public String[] getMetadata() { Localization.getLocalText("LBL_service_name"), Localization.getLocalText("LBL_persistence_mode"), Localization.getLocalText("LBL_active_space_bytes"), Localization.getLocalText("LBL_active_space_mb"), - Localization.getLocalText("LBL_avge_persistence"), Localization.getLocalText("LBL_max_persistence"), - Localization.getLocalText("LBL_snapshot_count"), Localization.getLocalText("LBL_status") + Localization.getLocalText("LBL_backup_space_mb"), Localization.getLocalText("LBL_avge_persistence"), + Localization.getLocalText("LBL_max_persistence"), Localization.getLocalText("LBL_snapshot_count"), + Localization.getLocalText("LBL_status") }; /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/GraphHelper.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/GraphHelper.java index b417fd0..265b013 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/GraphHelper.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/GraphHelper.java @@ -589,7 +589,8 @@ public static SimpleXYChartSupport createPersistenceActiveTotalGraph() VALUES_LIMIT); sxycd.setChartTitle(getLocalText("GRPH_total_active_space")); - sxycd.addLineFillItems(getLocalText("GRPH_total_space")); + sxycd.addLineItems(getLocalText("GRPH_total_space")); + sxycd.addLineItems(getLocalText("GRPH_backup_space")); return createChart(sxycd); } @@ -598,11 +599,12 @@ public static SimpleXYChartSupport createPersistenceActiveTotalGraph() * Add values to the total active persistence space used. * * @param graph {@link SimpleXYChartSupport} to add values to - * @param cTotalPersistenceSpace total persistence size of all caches + * @param cTotalPersistenceSpace total persistence active size of all caches + * @param cTotalBackupSpace total persistence backup size of all caches */ - public static void addValuesToPersistenceActiveTotalGraph(SimpleXYChartSupport graph, long cTotalPersistenceSpace) + public static void addValuesToPersistenceActiveTotalGraph(SimpleXYChartSupport graph, long cTotalPersistenceSpace, long cTotalBackupSpace) { - graph.addValues(System.currentTimeMillis(), new long[] {cTotalPersistenceSpace}); + graph.addValues(System.currentTimeMillis(), new long[] {cTotalPersistenceSpace, cTotalBackupSpace}); } /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index 2844d5d..b6c2b16 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -858,7 +858,7 @@ public JsonNode getAllStorageMembers() throws Exception .addPathSegment(MEMBERS) .addQueryParameter(LINKS, "") .addQueryParameter(FIELDS, "type,name,domainPartition,nodeId,persistenceMode," + - "storageEnabled,persistenceActiveSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage"); + "storageEnabled,persistenceActiveSpaceUsed,persistenceBackupSpaceUsed,persistenceLatencyMax,persistenceLatencyAverage"); return getResponseJson(sendGetRequest(urlBuilder)); } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java index 66bdce9..af320f1 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java @@ -28,6 +28,7 @@ import com.oracle.coherence.plugin.visualvm.Localization; import com.oracle.coherence.plugin.visualvm.VisualVMView; +import com.oracle.coherence.plugin.visualvm.helper.GraphHelper; import com.oracle.coherence.plugin.visualvm.helper.RenderHelper; import com.oracle.coherence.plugin.visualvm.helper.RequestSender; import com.oracle.coherence.plugin.visualvm.tablemodel.model.Data; @@ -685,12 +686,14 @@ protected Object[] getStorageDetails(List> memberData) protected Object[] getPersistenceData(List> persistenceData) { Object[] aoResults = new Object[] {}; - // [0] = cTotalMemory (long) - // [1] = cLatencyMax (long) - // [2] = cLatencyTotal(float) - // [3] = count - - long cTotalMemory = 0; + // [0] = cTotalActiveSpace (long) + // [1] = cTotalBackupSpace (long) + // [2] = cLatencyMax (long) + // [3] = cLatencyTotal(float) + // [4] = count + + long cTotalActiveSpace = 0; + long cTotalBackupSpace = 0; long cLatencyMax = 0L; float cLatencyTotal = 0.0f; int count = 0; @@ -702,9 +705,12 @@ protected Object[] getPersistenceData(List> persistenceD if (PersistenceData.isActivePersistence(sPersistenceMode)) { - long cTotalMem = (Long) entry.getValue().getColumn(PersistenceData.TOTAL_ACTIVE_SPACE_USED); + long cTotalActive = (Long) entry.getValue().getColumn(PersistenceData.TOTAL_ACTIVE_SPACE_USED); + long cTotalBackup = (Long) entry.getValue().getColumn(PersistenceData.TOTAL_BACKUP_SPACE_USED_MB); + + cTotalActiveSpace += cTotalActive == -1 ? 0 : cTotalActive; + cTotalBackupSpace += cTotalBackup == -1 ? 0 : cTotalBackup * GraphHelper.MB; - cTotalMemory += cTotalMem == -1 ? 0 : cTotalMem; cLatencyTotal += (Float) entry.getValue().getColumn(PersistenceData.AVERAGE_LATENCY); cLatencyMax = (Long) entry.getValue().getColumn(PersistenceData.MAX_LATENCY); @@ -717,7 +723,7 @@ protected Object[] getPersistenceData(List> persistenceD } } - return new Object[] {cTotalMemory, cMaxLatencyMax, cLatencyTotal, count}; + return new Object[] {cTotalActiveSpace, cTotalBackupSpace, cMaxLatencyMax, cLatencyTotal, count}; } /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceClusterSnapshotPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceClusterSnapshotPanel.java index 0b6e142..7a70815 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceClusterSnapshotPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceClusterSnapshotPanel.java @@ -417,11 +417,13 @@ private String persistenceOverview() StringBuilder sb = new StringBuilder(title(getLabel("LBL_persistence"))); Object[] persistenceData = getPersistenceData(m_persistenceData); - long cTotalMemory = (Long) persistenceData[0]; - long cLatencyMax = (Long) persistenceData[1]; + long cTotalActive = (Long) persistenceData[0]; + long cTotalBackup = (Long) persistenceData[1]; + float cLatencyMax = (Float) persistenceData[3]; sb.append(tableStart()) - .append(tableRow(getLabel("LBL_total_active_space"), getMemoryFormat(cTotalMemory))) + .append(tableRow(getLabel("LBL_total_active_space"), getMemoryFormat(cTotalActive))) + .append(tableRow(getLabel("LBL_total_backup_space"), getMemoryFormat(cTotalBackup))) .append(tableRow(getLabel("LBL_max_latency_across_services"), getLatencyValue(Float.toString(cLatencyMax)))) .append(tableEnd()); @@ -437,6 +439,7 @@ private String persistenceOverview() .append(td(entry.getValue().getColumn(PersistenceData.PERSISTENCE_MODE).toString())) .append(td(getMemoryFormat(entry.getValue().getColumn(PersistenceData.TOTAL_ACTIVE_SPACE_USED).toString()))) .append(td(getMemoryFormat(entry.getValue().getColumn(PersistenceData.TOTAL_ACTIVE_SPACE_USED_MB).toString()))) + .append(td(getMemoryFormat(entry.getValue().getColumn(PersistenceData.TOTAL_BACKUP_SPACE_USED_MB).toString()))) .append(td(getLatencyValue(entry.getValue().getColumn(PersistenceData.AVERAGE_LATENCY).toString()))) .append(td(getLatencyValue(entry.getValue().getColumn(PersistenceData.MAX_LATENCY).toString()))) .append(td(getMemoryFormat(entry.getValue().getColumn(PersistenceData.SNAPSHOT_COUNT).toString()))) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java index 7b99ef2..b0d54ff 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java @@ -200,6 +200,8 @@ public CoherencePersistencePanel(VisualVMModel model) new RenderHelper.DecimalRenderer(RenderHelper.MILLIS_FORMAT)); RenderHelper.setColumnRenderer(f_table, PersistenceData.TOTAL_ACTIVE_SPACE_USED_MB, new RenderHelper.IntegerRenderer()); + RenderHelper.setColumnRenderer(f_table, PersistenceData.TOTAL_BACKUP_SPACE_USED_MB, + new RenderHelper.IntegerRenderer()); RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); // Add some space @@ -272,17 +274,18 @@ public void updateGUI() final String MEM_FORMAT = "%,10.2f"; Object[] persistenceData = getPersistenceData(m_persistenceData); - long cTotalMemory = (Long) persistenceData[0]; - long cLatencyMax = (Long) persistenceData[1]; - float cLatencyTotal = (Float) persistenceData[2]; - int count = (Integer) persistenceData[3]; + long cTotalActive = (Long) persistenceData[0]; + long cTotalBackup = (Long) persistenceData[1]; + long cLatencyMax = (Long) persistenceData[2]; + float cLatencyTotal = (Float) persistenceData[3]; + int count = (Integer) persistenceData[4]; float cLatencyAverage = 0.0f; if (m_persistenceData != null) { m_cMaxMaxLatency = cLatencyMax; - f_txtTotalActiveSpaceUsed.setText(String.format(MEM_FORMAT, (cTotalMemory * 1.0f) / GraphHelper.MB)); + f_txtTotalActiveSpaceUsed.setText(String.format(MEM_FORMAT, (cTotalActive * 1.0f) / GraphHelper.MB)); cLatencyAverage = cLatencyTotal / count; if (cLatencyMax > m_cMaxMaxLatency) { @@ -294,7 +297,7 @@ public void updateGUI() f_txtTotalActiveSpaceUsed.setText(String.format(MEM_FORMAT, 0)); } - GraphHelper.addValuesToPersistenceActiveTotalGraph(f_persistenceTotalSpaceGraph, cTotalMemory); + GraphHelper.addValuesToPersistenceActiveTotalGraph(f_persistenceTotalSpaceGraph, cTotalActive, cTotalBackup); GraphHelper.addValuesToPersistenceLatencyGraph(f_persistenceLatencyGraph, cLatencyAverage * 1000.0f); f_txtMaxLatency.setText(Long.toString(m_cMaxMaxLatency)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java index f682108..c99aeb5 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java @@ -145,7 +145,7 @@ public List> getJMXData(RequestSender requestSender, Vis String sDomainPartition = asParts.length == 2 ? asParts[0] : null; sServiceName = sDomainPartition == null ? sServiceNameKey : asParts[1]; - // select only the current service so we can determine the number of storage-enabled + // select only the current service, so we can determine the number of storage-enabled // members. Set resultSet = requestSender.getMembersOfService(sServiceName, sDomainPartition); @@ -156,7 +156,7 @@ public List> getJMXData(RequestSender requestSender, Vis // only include storage-enabled members AttributeList listAttr = requestSender.getAttributes(objectName, - new String[] { "StorageEnabled", "PersistenceActiveSpaceUsed", "PersistenceLatencyMax", "PersistenceLatencyAverage" }); + new String[] { "StorageEnabled", MBEAN_ACTIVE_SPACE, MBEAN_BACKUP_SPACE, "PersistenceLatencyMax", "PersistenceLatencyAverage" }); if (Boolean.parseBoolean(getAttributeValueAsString(listAttr, "StorageEnabled"))) { @@ -166,8 +166,16 @@ public List> getJMXData(RequestSender requestSender, Vis if (isActivePersistence((String) data.getColumn(PERSISTENCE_MODE))) { data.setColumn(TOTAL_ACTIVE_SPACE_USED, - (Long) data.getColumn(TOTAL_ACTIVE_SPACE_USED) - + Long.parseLong(getAttributeValueAsString(listAttr, "PersistenceActiveSpaceUsed"))); + ((Long) data.getColumn(TOTAL_ACTIVE_SPACE_USED)) + + + Long.parseLong(getAttributeValueAsString(listAttr, MBEAN_ACTIVE_SPACE))); + + String sData = getAttributeValueAsString(listAttr, MBEAN_BACKUP_SPACE); + + long nBackupSpaceUsed = sData == null ? 0L : Long.parseLong(sData); + + data.setColumn(TOTAL_BACKUP_SPACE_USED_MB, + (Long) data.getColumn(TOTAL_BACKUP_SPACE_USED_MB) + + Math.max(nBackupSpaceUsed, 0)); } // update the max (of the max latencies) @@ -199,6 +207,7 @@ public List> getJMXData(RequestSender requestSender, Vis } data.setColumn(TOTAL_ACTIVE_SPACE_USED_MB, (Long) data.getColumn(TOTAL_ACTIVE_SPACE_USED) / GraphHelper.MB); + data.setColumn(TOTAL_BACKUP_SPACE_USED_MB, (Long) data.getColumn(TOTAL_BACKUP_SPACE_USED_MB) / GraphHelper.MB); mapData.put(sServiceNameKey, data); } @@ -279,8 +288,14 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel m if (isActivePersistence(sPersistenceMode)) { long nPersistenceActiveSpaceUsed = details.get("persistenceActiveSpaceUsed").asLong(); + JsonNode jsonBackup = details.get("persistenceBackupSpaceUsed"); + long nPersistenceBackupSpaceUsed = jsonBackup == null ? 0L : jsonBackup.asLong(); + data.setColumn(TOTAL_ACTIVE_SPACE_USED, (Long) data.getColumn(TOTAL_ACTIVE_SPACE_USED) + nPersistenceActiveSpaceUsed); + + data.setColumn(TOTAL_BACKUP_SPACE_USED_MB, + (Long) data.getColumn(TOTAL_BACKUP_SPACE_USED_MB) + nPersistenceBackupSpaceUsed); } // update the max (of the max latencies) @@ -312,6 +327,8 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel m v.setColumn(TOTAL_ACTIVE_SPACE_USED_MB, (Long) v.getColumn(TOTAL_ACTIVE_SPACE_USED) / GraphHelper.MB); + v.setColumn(TOTAL_BACKUP_SPACE_USED_MB, + (Long) v.getColumn(TOTAL_BACKUP_SPACE_USED_MB) / GraphHelper.MB); int cNodes = mapNodeCount.get(k); // update the average of the averages only if > 1 service node @@ -342,6 +359,7 @@ private Data createData(String sServiceName, String sPersistenceMode, String sDo data.setColumn(SERVICE_NAME, sServiceName); data.setColumn(PERSISTENCE_MODE, sPersistenceMode); data.setColumn(TOTAL_ACTIVE_SPACE_USED, 0L); + data.setColumn(TOTAL_BACKUP_SPACE_USED_MB, 0L); data.setColumn(MAX_LATENCY, 0L); data.setColumn(AVERAGE_LATENCY, 0.0f); @@ -454,28 +472,43 @@ public static boolean isActivePersistence(String sPersistenceMode) */ public static final int TOTAL_ACTIVE_SPACE_USED_MB = 3; + /** + * Array index for total backup space used MB. + */ + public static final int TOTAL_BACKUP_SPACE_USED_MB = 4; + /** * Array index for average latency */ - public static final int AVERAGE_LATENCY = 4; + public static final int AVERAGE_LATENCY = 5; /** * Array index for max latency. */ - public static final int MAX_LATENCY = 5; + public static final int MAX_LATENCY = 6; /** * Array index for number of snapshots. */ - public static final int SNAPSHOT_COUNT = 6; + public static final int SNAPSHOT_COUNT = 7; /** * Array index for status. */ - public static final int STATUS = 7; + public static final int STATUS = 8; /** * The logger object to use. */ private static final Logger LOGGER = Logger.getLogger(PersistenceData.class.getName()); + + /** + * Attribute for active space. + */ + private static final String MBEAN_ACTIVE_SPACE = "PersistenceActiveSpaceUsed"; + + /** + * Attribute for backup space. + */ + private static final String MBEAN_BACKUP_SPACE = "PersistenceBackupSpaceUsed"; } diff --git a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties index 390bce3..ba2c965 100644 --- a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties +++ b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties @@ -127,6 +127,7 @@ LBL_data_saved=Thread Dump has been saved to {0}. # CoherencePersistencePanel LBL_total_active_space=Total Active Persistence Space Used (MB) +LBL_total_backup_space=Total Backyup Persistence Space Used (MB) LBL_max_latency_across_services=Max Latency Across Services (ms) LBL_must_select_row=You must select a row LBL_result=Result @@ -388,6 +389,7 @@ LBL_active_space_bytes=Active Space Used on disk (bytes) LBL_avge_persistence=Average Additional Latency (ms) LBL_max_persistence=Maximum Additional Latency (ms) LBL_active_space_mb=Active Space Used on disk (MB) +LBL_backup_space_mb=Backup Space Used on disk (MB) LBL_snapshot_count=Snapshot Count LBL_application_id=Application Id LBL_platform=Platform @@ -494,8 +496,9 @@ GRPH_task_backlog=Task Backlog for {0} GRPH_request_average=Request Average Duration for {0} (ms) GRPH_persistence_latency=Latency Average Across all 'active' Services (\u00b5s) GRPH_current_average_persistence=Current Average (\u00b5s) -GRPH_total_active_space=Total Active Space Used -GRPH_total_space=Total Space +GRPH_total_active_space=Total Active/Backup Space Used +GRPH_total_space=Total Active Space +GRPH_backup_space=Total Backup Space GRPH_session_sizes=HTTP Session Cache Count GRPH_total_session_count=Total Session Count GRPH_total_overflow_count=Total Overflow Count From 60ee91bc89225e3e546284b1f4985e066a53a1ba Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 21 Jun 2022 14:52:25 +0800 Subject: [PATCH 11/27] Add report environment --- .../visualvm/helper/HttpRequestSender.java | 11 ++++ .../visualvm/helper/JMXRequestSender.java | 19 ++++++ .../plugin/visualvm/helper/RequestSender.java | 12 ++++ .../visualvm/panel/CoherenceMemberPanel.java | 58 ++++++++++++++++++- .../plugin/visualvm/Bundle.properties | 2 + 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index b6c2b16..07a78b7 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -608,6 +608,17 @@ public String getNodeState(Integer nNodeId) return rootNode.get("state").asText(); } + @Override + public String reportEnvironment(Integer nNodeId) + throws Exception + { + URLBuilder urlBuilder = getBasePath().addPathSegment(MEMBERS) + .addPathSegment(nNodeId + "").addPathSegment("environment"); + + JsonNode rootNode = getResponseJson(sendGetRequest(urlBuilder)); + return rootNode.get("environment").asText(); + } + /** * Issue a dump cluster heap request. * diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java index 476ce4b..d713627 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java @@ -329,6 +329,25 @@ public String getNodeState(Integer nNodeId) return (String) invoke(new ObjectName(sFQN), "reportNodeState", new Object[0], new String[0]); } + @Override + public String reportEnvironment(Integer nNodeId) + throws Exception + { + // look up the full name of the MBean in case we are in container + Set setResult = getCompleteObjectName( + new ObjectName("Coherence:type=Node,nodeId=" + nNodeId + ",*")); + + String sFQN = null; + + for (Object oResult : setResult) + { + sFQN = oResult.toString(); + break; + } + + return (String) invoke(new ObjectName(sFQN), "reportEnvironment", new Object[0], new String[0]); + } + /** * Issue a dump cluster heap request. * diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RequestSender.java index 89bf68a..869229e 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RequestSender.java @@ -358,6 +358,18 @@ Integer retrievePendingOutgoingMessages(String sService) String getNodeState(Integer nNodeId) throws Exception; + /** + * Get the environment of a Cluster member. + * + * @param nNodeId the ID of the cluster member + * + * @return the environment of a cluster member + * + * @throws Exception in case of errors + */ + String reportEnvironment(Integer nNodeId) + throws Exception; + /** * Issue a dump cluster heap request. * diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java index 221d57d..ff40020 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java @@ -26,7 +26,6 @@ package com.oracle.coherence.plugin.visualvm.panel; -import com.oracle.coherence.plugin.visualvm.Localization; import com.oracle.coherence.plugin.visualvm.helper.GraphHelper; import com.oracle.coherence.plugin.visualvm.helper.RenderHelper; import com.oracle.coherence.plugin.visualvm.helper.RequestSender; @@ -183,6 +182,7 @@ public CoherenceMemberPanel(VisualVMModel model) f_table.setMenuOptions(new MenuOption[] { menuDetail, new ReportNodeStateMenuOption(model, m_requestSender, f_table), + new ReportEnvironmentMenuOption(model, m_requestSender, f_table), new ReportNodeStateMultiMenuOption(model, m_requestSender, f_table)}); } else @@ -296,6 +296,62 @@ public void updateData() } + + // ----- inner classes ReportEnvironmentMenuOption ---------------------- + + /** + * A class to call the environment operation on the selected ClusterNode + * MBean and display the details. + */ + private class ReportEnvironmentMenuOption + extends AbstractMenuOption + { + + /** + * {@inheritDoc} + */ + public ReportEnvironmentMenuOption(VisualVMModel model, RequestSender requestSender, + ExportableJTable jtable) + { + super(model, requestSender, jtable); + } + + // ----- MenuOptions methods ---------------------------------------- + + @Override + public String getMenuItem() + { + return getLocalizedText("LBL_report_node_environment"); + } + + @Override + public void actionPerformed(ActionEvent e) + { + int nRow = getSelectedRow(); + Integer nNodeId = null; + + if (nRow == -1) + { + JOptionPane.showMessageDialog(null, getLocalizedText("LBL_must_select_row")); + } + else + { + try + { + nNodeId = (Integer) getJTable().getModel().getValueAt(nRow, 0); + String sResult = generateHeader(nNodeId) + m_requestSender.reportEnvironment(nNodeId); + + showMessageDialog(getLocalizedText("LBL_environment_for_node") + " " + nNodeId, + sResult, JOptionPane.INFORMATION_MESSAGE, 500, 400, true); + } + catch (Exception ee) + { + showMessageDialog("Error running reportEnvironment for Node " + nNodeId, ee.getMessage(), JOptionPane.ERROR_MESSAGE); + } + } + } + } + // ----- inner classes ReportNodeDetailsMenuOption ---------------------- /** diff --git a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties index ba2c965..73816d1 100644 --- a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties +++ b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties @@ -112,8 +112,10 @@ LBL_member_departure_count=Member Departure Count LBL_total_cluster_memory_avail=Total Storage Heap Avail (MB) LBL_overview=Overview LBL_report_node_state=Generate Thread Dump +LBL_report_node_environment=Report Environment LBL_report_node_state_multi=Generate Multiple Thread Dumps LBL_state_for_node=Thread Dump(s) for Node +LBL_environment_for_node=Environment for Node LBL_number_thread_dumps=Thread Dumps LBL_time_between=Time Between Each (seconds) LBL_multi=Please enter the number of thread dumps and seconds between each From a3f18b7d185d16f6aa65ef5f0e44e1f5cfa9ff0d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 22 Jun 2022 12:52:21 +0800 Subject: [PATCH 12/27] Minor change of menu option order --- .../coherence/plugin/visualvm/panel/CoherenceMemberPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java index ff40020..2544e77 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java @@ -181,8 +181,8 @@ public CoherenceMemberPanel(VisualVMModel model) { f_table.setMenuOptions(new MenuOption[] { menuDetail, - new ReportNodeStateMenuOption(model, m_requestSender, f_table), new ReportEnvironmentMenuOption(model, m_requestSender, f_table), + new ReportNodeStateMenuOption(model, m_requestSender, f_table), new ReportNodeStateMultiMenuOption(model, m_requestSender, f_table)}); } else From ae1bf86c2b988ff4b2a1cf37761b302ba5cb385b Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 27 Jun 2022 11:52:07 +0800 Subject: [PATCH 13/27] Initial health check api support --- .../plugin/visualvm/VisualVMModel.java | 54 +++- .../plugin/visualvm/VisualVMView.java | 10 + .../visualvm/helper/HttpRequestSender.java | 16 +- .../plugin/visualvm/helper/RenderHelper.java | 54 +++- .../visualvm/panel/CoherenceHealthPanel.java | 266 ++++++++++++++++++ .../tablemodel/HealthSummaryTableModel.java | 53 ++++ .../visualvm/tablemodel/model/HealthData.java | 260 +++++++++++++++++ .../tablemodel/model/HealthSummaryData.java | 129 +++++++++ .../tablemodel/model/MachineData.java | 1 - .../plugin/visualvm/Bundle.properties | 11 + .../reports/visualvm/health-stats.xml | 91 ++++++ .../tests/AbstractDataRetrieverTest.java | 19 ++ pom.xml | 9 +- 13 files changed, 963 insertions(+), 10 deletions(-) create mode 100755 coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java create mode 100755 coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/HealthSummaryTableModel.java create mode 100644 coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java create mode 100644 coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java create mode 100644 coherence-visualvm-plugin/src/main/resources/reports/visualvm/health-stats.xml diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java index 1b7e582..d7e69f0 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java @@ -40,6 +40,7 @@ import com.oracle.coherence.plugin.visualvm.tablemodel.model.FederationOriginDetailsData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.FlashJournalData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.GrpcProxyData; +import com.oracle.coherence.plugin.visualvm.tablemodel.model.HealthData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.HotCacheData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.HotCachePerCacheData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.HttpProxyData; @@ -163,6 +164,7 @@ private void init() f_mapDataRetrievers.put(NodeStorageData.class, new NodeStorageData()); f_mapDataRetrievers.put(ExecutorData.class, new ExecutorData()); f_mapDataRetrievers.put(GrpcProxyData.class, new GrpcProxyData()); + f_mapDataRetrievers.put(HealthData.class, new HealthData()); // Loop through each data retriever and initialize the map of // report XML. Doing it this way we load it only once @@ -351,6 +353,22 @@ private boolean shouldRetrieveData(DataType type) return false; } + if (!isHealthConfigured() && + ( + clazz.equals(DataType.HEALTH.getClassName()) + )) + { + return false; + } + + if (!isExecutorConfigured() && + ( + clazz.equals(DataType.EXECUTOR.getClassName()) + )) + { + return false; + } + return true; } @@ -1018,6 +1036,17 @@ public boolean isGrpcProxyConfigured() && m_mapCollectedData.get(DataType.GRPC_PROXY).size() != 0); } + /** + * Returns if Health is configured. + * + * @return true if Health is configured. + */ + public boolean isHealthConfigured() + { + return (m_mapCollectedData.get(DataType.HEALTH) != null + && m_mapCollectedData.get(DataType.HEALTH).size() != 0); + } + /** * Returns if JCache is configured. * @@ -1237,7 +1266,8 @@ public enum DataType HOTCACHE(HotCacheData.class, HOTCACHE_LABELS), HOTCACHE_PERCACHE(HotCachePerCacheData.class, HOTCACHE_PERCACHE_LABELS), EXECUTOR(ExecutorData.class, EXECUTOR_LABELS), - GRPC_PROXY(GrpcProxyData.class, GRPC_PROXY_LABELS); + GRPC_PROXY(GrpcProxyData.class, GRPC_PROXY_LABELS), + HEALTH(HealthData.class, HEALTH_LABELS); private DataType(Class clz, String[] asMeta) { @@ -1434,6 +1464,28 @@ public String[] getMetadata() Localization.getLocalText("LBL_message_duration_mean") }; + /** + * Labels for Health table. + */ + private static final String[] HEALTH_LABELS = new String[] + { + Localization.getLocalText("LBL_health_name"), Localization.getLocalText("LBL_health_subtype"), + Localization.getLocalText("LBL_members"), Localization.getLocalText("LBL_started"), + Localization.getLocalText("LBL_live"), Localization.getLocalText("LBL_ready"), + Localization.getLocalText("LBL_safe"), Localization.getLocalText("LBL_health_class") + }; + + /** + * Labels for Health table. + */ + public static final String[] HEALTH_SUMMARY_LABELS = new String[] + { + Localization.getLocalText("LBL_health_name"), + Localization.getLocalText("LBL_members"), Localization.getLocalText("LBL_started"), + Localization.getLocalText("LBL_live"), Localization.getLocalText("LBL_ready"), + Localization.getLocalText("LBL_safe"), Localization.getLocalText("LBL_health_class") + }; + /** * Labels for persistence table. */ diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java index 498d993..f294a90 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java @@ -35,6 +35,7 @@ import com.oracle.coherence.plugin.visualvm.panel.CoherenceClusterSnapshotPanel; import com.oracle.coherence.plugin.visualvm.panel.CoherenceExecutorPanel; import com.oracle.coherence.plugin.visualvm.panel.CoherenceGrpcProxyPanel; +import com.oracle.coherence.plugin.visualvm.panel.CoherenceHealthPanel; import com.oracle.coherence.plugin.visualvm.panel.CoherenceTopicPanel; import com.oracle.coherence.plugin.visualvm.tablemodel.model.Data; import com.oracle.coherence.plugin.visualvm.panel.CoherenceHttpProxyPanel; @@ -190,6 +191,7 @@ protected DataViewComponent createComponent() final CoherenceHttpProxyPanel pnlHttpProxy = new CoherenceHttpProxyPanel(model); final CoherenceExecutorPanel pnlExecutor = new CoherenceExecutorPanel(model); final CoherenceGrpcProxyPanel pnlGrpcProxy = new CoherenceGrpcProxyPanel(model); + final CoherenceHealthPanel pnlHealth = new CoherenceHealthPanel(model); String sClusterVersion = model.getClusterVersion(); String sClusterName = null; @@ -325,6 +327,13 @@ protected DataViewComponent createComponent() f_setPanels.add(pnlGrpcProxy); } + if (model.isHealthConfigured()) + { + m_dvc.addDetailsView(new DataViewComponent.DetailsView(Localization.getLocalText("LBL_health"), + null, 10, pnlHealth, null), DataViewComponent.TOP_RIGHT); + f_setPanels.add(pnlHealth); + } + // update the request sender pnlClusterOverview.setRequestSender(requestSender); pnlMachine.setRequestSender(requestSender); @@ -341,6 +350,7 @@ protected DataViewComponent createComponent() pnlJCache.setRequestSender(requestSender); pnlExecutor.setRequestSender(requestSender); pnlGrpcProxy.setRequestSender(requestSender); + pnlHealth.setRequestSender(requestSender); // display a warning if we are connected to a WLS domain and we can // see more that 1 domainPartition key. This code relies on us diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index 07a78b7..26c52e6 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -858,7 +858,21 @@ public JsonNode getListOfStorageMembers(String sServiceName, String sDomainParti } /** - * Get a list of all storage members. + * Get a list of all health members. + * + * @return a list of all storage members + * @throws Exception in case of errors + */ + public JsonNode getAllHealthMembers() throws Exception + { + URLBuilder urlBuilder = getBasePath().addPathSegment("health") + .addPathSegment(MEMBERS) + .addQueryParameter(LINKS, ""); + return getResponseJson(sendGetRequest(urlBuilder)); + } + + /** + * Get a list of all health checks. * * @return a list of all storage members * @throws Exception in case of errors diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java index 5e638bf..43b8177 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java @@ -150,7 +150,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - float fValue = Float.valueOf(getText()); + float fValue = Float.parseFloat(getText()); if (fValue <= 0.500) { @@ -184,6 +184,56 @@ else if (fValue <= 0.75) } } + /** + * Renderer for health. + */ + @SuppressWarnings("serial") + public static class HealthRenderer + extends DefaultTableCellRenderer + { + /** + * {@inheritDoc} + */ + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int column) + { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + + String sValue = getText(); + + if (sValue.contains("0/")) + { + // means zero out of n are started so make this red + setBackground(Color.red); + setForeground(Color.black); + } + else if (sValue.contains("/")) + { + // means at least 1 is not ready so make orange + setBackground(Color.orange); + setForeground(Color.black); + } + else + { + // must be ok so make it green + setBackground(Color.green); + setForeground(Color.black); + } + + if (c instanceof JLabel && value instanceof Number) + { + JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + + renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setText(sValue); + } + + return c; + } + } + /** * Render for a milliseconds column of type Float. * @@ -262,7 +312,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole if (!"".equals(getText()) && getText() != null) { - float fValue = Float.valueOf(getText()); + float fValue = Float.parseFloat(getText()); if (fValue < .15) { diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java new file mode 100755 index 0000000..c4d79fe --- /dev/null +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.coherence.plugin.visualvm.panel; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; +import com.oracle.coherence.plugin.visualvm.VisualVMModel; +import com.oracle.coherence.plugin.visualvm.helper.RenderHelper; +import com.oracle.coherence.plugin.visualvm.panel.util.ExportableJTable; +import com.oracle.coherence.plugin.visualvm.tablemodel.HealthSummaryTableModel; +import com.oracle.coherence.plugin.visualvm.tablemodel.model.Data; +import com.oracle.coherence.plugin.visualvm.tablemodel.model.HealthData; +import com.oracle.coherence.plugin.visualvm.tablemodel.model.HealthSummaryData; +import com.oracle.coherence.plugin.visualvm.tablemodel.model.Pair; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextField; + +/** + * An implementation of an {@link AbstractCoherencePanel} to view + * summarized health data. + * + * @author tam 2022.06.22 + * @since 1.4.0 + */ +public class CoherenceHealthPanel + extends AbstractCoherencePanel + { + + // ----- constructors --------------------------------------------------- + + /** + * Create the layout for the {@link CoherenceHealthPanel}. + * + * @param model {@link VisualVMModel} to use for this panel + */ + public CoherenceHealthPanel(VisualVMModel model) + { + super(new BorderLayout(), model); + + // create a split pane for resizing + JSplitPane pneSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + pneSplit.setOpaque(false); + + // Create the header panel + JPanel pnlHeader = new JPanel(); + pnlHeader.setLayout(new FlowLayout()); + pnlHeader.setOpaque(false); + + f_txtTotalHealthChecks = getTextField(5, JTextField.RIGHT); + pnlHeader.add(getLocalizedLabel("LBL_total_health_checks", f_txtTotalHealthChecks)); + pnlHeader.add(f_txtTotalHealthChecks); + + // create the table + f_tmodel = new HealthSummaryTableModel(VisualVMModel.HEALTH_SUMMARY_LABELS); + + f_table = new ExportableJTable(f_tmodel); + + f_table.setPreferredScrollableViewportSize(new Dimension(500, 150)); + + // define renderers for the columns + RenderHelper.setColumnRenderer(f_table, HealthSummaryData.MEMBERS, new RenderHelper.IntegerRenderer()); + RenderHelper.setColumnRenderer(f_table, HealthSummaryData.STARTED, new RenderHelper.HealthRenderer()); + RenderHelper.setColumnRenderer(f_table, HealthSummaryData.LIVE, new RenderHelper.HealthRenderer()); + RenderHelper.setColumnRenderer(f_table, HealthSummaryData.READY, new RenderHelper.HealthRenderer()); + RenderHelper.setColumnRenderer(f_table, HealthSummaryData.SAFE, new RenderHelper.HealthRenderer()); + + RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + + // Add some space + f_table.setIntercellSpacing(new Dimension(6, 3)); + f_table.setRowHeight(f_table.getRowHeight() + 4); + + // Create the scroll pane and add the table to it. + JScrollPane pneScroll = new JScrollPane(f_table); + configureScrollPane(pneScroll, f_table); + pneScroll.setOpaque(false); + + JPanel pnlTop = new JPanel(new BorderLayout()); + pnlTop.setOpaque(false); + + pnlTop.add(pnlHeader, BorderLayout.PAGE_START); + pnlTop.add(pneScroll, BorderLayout.CENTER); + + pneSplit.add(pnlTop); + + add(pneSplit); + } + + // ----- AbstractCoherencePanel methods --------------------------------- + + @Override + public void updateGUI() + { + + } + + @Override + public void updateData() + { + m_healthData = f_model.getData(VisualVMModel.DataType.HEALTH); + + SortedMap mapData = new TreeMap<>(); + + // summarise the data by name and sub-type + m_healthData.forEach(v -> + { + HealthData.HealthKey key = (HealthData.HealthKey) v.getKey(); + HealthData value = (HealthData) v.getValue(); + Pair newKey = new Pair<>(key.getName(), key.getSubType()); + + HealthSummaryData data = (HealthSummaryData) mapData.get(newKey); + if (data == null) + { + data = new HealthSummaryData(); + data.setColumn(HealthSummaryData.HEALTH_NAME, newKey); + data.setColumn(HealthSummaryData.CLASS_NAME, value.getColumn(HealthData.CLASS_NAME)); + data.setColumn(HealthSummaryData.MEMBERS, 0); + data.setColumn(HealthSummaryData.STARTED, 0); + data.setColumn(HealthSummaryData.LIVE, 0); + data.setColumn(HealthSummaryData.READY, 0); + data.setColumn(HealthSummaryData.SAFE, 0); + } + + // increment the values + data.setColumn(HealthSummaryData.MEMBERS, (Integer)data.getColumn(HealthSummaryData.MEMBERS) + 1); + if ((Boolean) value.getColumn(HealthData.STARTED)) + { + data.setColumn(HealthSummaryData.STARTED, (Integer)data.getColumn(HealthSummaryData.STARTED) + 1); + } + if ((Boolean) value.getColumn(HealthData.LIVE)) + { + data.setColumn(HealthSummaryData.LIVE, (Integer)data.getColumn(HealthSummaryData.LIVE) + 1); + } + if ((Boolean) value.getColumn(HealthData.READY)) + { + data.setColumn(HealthSummaryData.READY, (Integer)data.getColumn(HealthSummaryData.READY) + 1); + } + if ((Boolean) value.getColumn(HealthData.SAFE)) + { + data.setColumn(HealthSummaryData.SAFE, (Integer)data.getColumn(HealthSummaryData.SAFE) + 1); + } + + // update the map + mapData.put(newKey, data); + }); + + + AtomicInteger totalChecks = new AtomicInteger(0); + AtomicInteger totalOk = new AtomicInteger(0); + + // process the data and change any values to fractions if they do not equal the member count + mapData.forEach((k,v) -> + { + int cMembers = (Integer) v.getColumn(HealthSummaryData.MEMBERS); + int cStarted = (Integer) v.getColumn(HealthSummaryData.STARTED); + int cLive = (Integer) v.getColumn(HealthSummaryData.LIVE); + int cReady = (Integer) v.getColumn(HealthSummaryData.READY); + int cSafe = (Integer) v.getColumn(HealthSummaryData.SAFE); + + if (cStarted != cMembers) + { + v.setColumn(HealthSummaryData.STARTED, String.format("%d/%d", cStarted, cMembers)); + } + if (cLive != cMembers) + { + v.setColumn(HealthSummaryData.LIVE, String.format("%d/%d", cLive, cMembers)); + } + if (cReady != cMembers) + { + v.setColumn(HealthSummaryData.READY, String.format("%d/%d", cReady, cMembers)); + } + if (cSafe != cMembers) + { + v.setColumn(HealthSummaryData.SAFE, String.format("%d/%d", cSafe, cMembers)); + } + + totalChecks.addAndGet(cMembers * 4); + totalOk.addAndGet(cStarted + cLive + cReady + cSafe); + }); + + // update total health + f_txtTotalHealthChecks.setForeground(Color.black); + String sValue = String.format("%d/%d", totalOk.get(), totalChecks.get()); + + f_txtTotalHealthChecks.setText(sValue); + if (totalOk.get() != totalChecks.get()) + { + // means at least 1 is not ready so make orange + f_txtTotalHealthChecks.setBackground(Color.orange); + } + else + { + // must be ok so make it green + f_txtTotalHealthChecks.setBackground(Color.green); + } + + f_tmodel.setDataList(new ArrayList<>(mapData.entrySet())); + f_tmodel.fireTableDataChanged(); + } + + // ----- constants ------------------------------------------------------ + + private static final long serialVersionUID = -7612569043492412546L; + + // ----- data members ---------------------------------------------------- + + /** + * The total number of health checks. + */ + private final JTextField f_txtTotalHealthChecks; + + /** + * A check-box to indicate if the NameService should be included in the list of proxy servers. + */ + private JCheckBox m_cbxIncludeNameService = null; + + /** + * The health data retrieved from the {@link VisualVMModel}. + */ + private List> m_healthData; + + /** + * The {@link HealthSummaryTableModel} to display proxy data. + */ + protected final HealthSummaryTableModel f_tmodel; + + /** + * the {@link ExportableJTable} to use to display data. + */ + protected final ExportableJTable f_table; + } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/HealthSummaryTableModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/HealthSummaryTableModel.java new file mode 100755 index 0000000..518e3e8 --- /dev/null +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/HealthSummaryTableModel.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.coherence.plugin.visualvm.tablemodel; + +import com.oracle.coherence.plugin.visualvm.tablemodel.model.Data; + +/** + * A model for holding cache Health data. + * + * @author tam 2022.06.22 + * @since 1.4.0 + */ +public class HealthSummaryTableModel + extends AbstractCoherenceTableModel + { + // ----- constructors --------------------------------------------------- + + /** + * Creates a table model with the given columns. + * + * @param asColumns the columns for this table model + */ + public HealthSummaryTableModel(String[] asColumns) + { + super(asColumns); + } + + // ----- constants ------------------------------------------------------ + + private static final long serialVersionUID = 8892857232931509048L; + } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java new file mode 100644 index 0000000..d387fa2 --- /dev/null +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.coherence.plugin.visualvm.tablemodel.model; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; + +import java.util.logging.Logger; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.oracle.coherence.plugin.visualvm.VisualVMModel; +import com.oracle.coherence.plugin.visualvm.helper.HttpRequestSender; +import com.oracle.coherence.plugin.visualvm.helper.RequestSender; + +/** + * A class to hold health data. + * + * @author tam 2022.06.22 + * @since 1.4.0 + */ +public class HealthData + extends AbstractData + { + // ----- constructors --------------------------------------------------- + + /** + * Create PersistenceData passing in the number of columns. + */ + public HealthData() + { + super(CLASS_NAME + 1); + } + + // ----- DataRetriever methods ------------------------------------------ + + @Override + public List> getJMXData(RequestSender requestSender, VisualVMModel model) + { + return null; + } + + @Override + public String getReporterReport() + { + return REPORT_HEALTH; // see comment below + } + + @Override + public Data processReporterData(Object[] aoColumns, VisualVMModel model) + { + Data data = new HealthData(); + + HealthKey key = new HealthKey((String) aoColumns[3], (String) aoColumns[4], Integer.parseInt(getNumberValue(aoColumns[2].toString()))); + + data.setColumn(HealthData.HEALTH_NAME, key); + data.setColumn(HealthData.NODE_ID, Integer.valueOf(getNumberValue(aoColumns[2].toString()))); + data.setColumn(HealthData.SUB_TYPE, aoColumns[4]); + data.setColumn(HealthData.STARTED, Boolean.valueOf(aoColumns[5].toString())); + data.setColumn(HealthData.LIVE, Boolean.valueOf(aoColumns[6].toString())); + data.setColumn(HealthData.READY, Boolean.valueOf(aoColumns[7].toString())); + data.setColumn(HealthData.SAFE, Boolean.valueOf(aoColumns[8].toString())); + data.setColumn(HealthData.CLASS_NAME, aoColumns[9].toString()); + + return data; + } + + @Override + public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel model, HttpRequestSender requestSender) + throws Exception + { + SortedMap mapData = new TreeMap<>(); + JsonNode allStorageMembers = requestSender.getAllHealthMembers(); + JsonNode healthItemsNode = allStorageMembers.get("items"); + + Map mapNodeCount = new HashMap<>(); + Data data; + + if (healthItemsNode != null && healthItemsNode.isArray()) + { + for (int i = 0; i < ((ArrayNode) healthItemsNode).size(); i++) + { + JsonNode details = healthItemsNode.get(i); + + data = new HealthData(); + String sName = details.get("name").asText(); + String sSubType = details.get("subType").asText(); + int nNodeId = details.get("nodeId").asInt(); + + HealthKey key = new HealthKey(sName, sSubType, nNodeId); + + data.setColumn(HealthData.HEALTH_NAME, key); + data.setColumn(HealthData.NODE_ID, nNodeId); + data.setColumn(HealthData.SUB_TYPE, sSubType); + data.setColumn(HealthData.STARTED, details.get("started").asBoolean()); + data.setColumn(HealthData.LIVE, details.get("live").asBoolean()); + data.setColumn(HealthData.READY, details.get("ready").asBoolean()); + data.setColumn(HealthData.SAFE, details.get("safe").asBoolean()); + data.setColumn(HealthData.CLASS_NAME, details.get("className").asText()); + + mapData.put(key, data); + } + } + return mapData; + } + + /** + * Key to be used for the Health Data. + */ + public static class HealthKey implements Comparable + { + + public HealthKey(String sName, String sSubType, int nNodeId) + { + f_sName = sName; + f_sSubType = sSubType; + f_nNodeId = nNodeId; + } + + public int getNodeId() { + return f_nNodeId; + } + + public String getName() { + return f_sName; + } + + public String getSubType() { + return f_sSubType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + HealthKey healthKey = (HealthKey) o; + + if (f_nNodeId != healthKey.f_nNodeId) return false; + if (!Objects.equals(f_sName, healthKey.f_sName)) return false; + return Objects.equals(f_sSubType, healthKey.f_sSubType); + } + + @Override + public int hashCode() { + int result = f_sName != null ? f_sName.hashCode() : 0; + result = 31 * result + (f_sSubType != null ? f_sSubType.hashCode() : 0); + result = 31 * result + f_nNodeId; + return result; + } + + @Override + public String toString() + { + return f_sName + "/" + f_sSubType + "/" + f_nNodeId; + } + + @Override + public int compareTo(HealthKey o) { + int i = Integer.compare(o.f_nNodeId, this.f_nNodeId); + if (i != 0 ) + { + return i; + } + i = o.f_sName.compareTo(this.f_sName); + + if (i != 0 ) + { + return i; + } + + return o.f_sSubType.compareTo(this.f_sSubType); + } + + private final String f_sName; + private final String f_sSubType; + private final int f_nNodeId; + } + + // ----- constants ------------------------------------------------------ + + private static final long serialVersionUID = 7769559573242105947L; + + /** + * Array index for health name. + */ + public static final int HEALTH_NAME = 0; + + /** + * Array index for node id. + */ + public static final int NODE_ID = 1; + + /** + * Array index for sub-type. + */ + public static final int SUB_TYPE = 2; + + /** + * Array index for started. + */ + public static final int STARTED = 3; + + /** + * Array index for live. + */ + public static final int LIVE = 4; + + /** + * Array index for ready. + */ + public static final int READY = 5; + + /** + * Array index for safe + */ + public static final int SAFE = 6; + + /** + * Array index for description. + */ + public static final int CLASS_NAME = 7; + + /** + * The logger object to use. + */ + private static final Logger LOGGER = Logger.getLogger(HealthData.class.getName()); + + /** + * Report for cluster data. + */ + public static final String REPORT_HEALTH = "reports/visualvm/health-stats.xml"; + } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java new file mode 100644 index 0000000..400aa7d --- /dev/null +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.coherence.plugin.visualvm.tablemodel.model; + +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.logging.Logger; +import com.oracle.coherence.plugin.visualvm.VisualVMModel; +import com.oracle.coherence.plugin.visualvm.helper.HttpRequestSender; +import com.oracle.coherence.plugin.visualvm.helper.RequestSender; + +/** + * A class to hold summary health data. + * + * @author tam 2022.06.22 + * @since 1.4.0 + */ +public class HealthSummaryData + extends AbstractData + { + // ----- constructors --------------------------------------------------- + + /** + * Create PersistenceData passing in the number of columns. + */ + public HealthSummaryData() + { + super(CLASS_NAME + 1); + } + + // ----- DataRetriever methods ------------------------------------------ + + @Override + public List> getJMXData(RequestSender requestSender, VisualVMModel model) + { + return null; + } + + @Override + public String getReporterReport() + { + return REPORT_HEALTH; // see comment below + } + + @Override + public Data processReporterData(Object[] aoColumns, VisualVMModel model) + { + return null; + } + + @Override + public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel model, HttpRequestSender requestSender) + throws Exception + { + return new TreeMap<>(); + } + + private static final long serialVersionUID = 7769559573242105947L; + + /** + * Array index for health name. + */ + public static final int HEALTH_NAME = 0; + + /** + * Array index for members. + */ + public static final int MEMBERS = 1; + + /** + * Array index for started. + */ + public static final int STARTED = 2; + + /** + * Array index for live. + */ + public static final int LIVE = 3; + + /** + * Array index for ready. + */ + public static final int READY = 4; + + /** + * Array index for safe + */ + public static final int SAFE = 5; + + /** + * Array index for description. + */ + public static final int CLASS_NAME = 6; + + /** + * The logger object to use. + */ + private static final Logger LOGGER = Logger.getLogger(HealthSummaryData.class.getName()); + + /** + * Report for cluster data. + */ + public static final String REPORT_HEALTH = "reports/visualvm/health-stats.xml"; + } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java index 608c69a..c4386cd 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java @@ -130,7 +130,6 @@ public List> getJMXData(RequestSender requestSender, Vis // put it into the mapData with just a machine as the key mapData.put(machineName, data); - } } diff --git a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties index 73816d1..b7ad66f 100644 --- a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties +++ b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties @@ -287,6 +287,16 @@ LBL_messages_received=Messages Received LBL_request_duration_mean=Mean Req Duration (ms) LBL_message_duration_mean=Mean Msg Duration (ms) +# Health +LBL_health_name=Name +LBL_health_subtype=Sub-Type +LBL_started=Started +LBL_live=Live +LBL_ready=Ready +LBL_safe=Safe +LBL_health_class=Class Name +LBL_total_health_checks=Total Health Checks - All Members + # ExportableJTable LBL_save_data_as=Save data as... LBL_unable_to_save=Unable to save data to file {0}. Error: {1} @@ -312,6 +322,7 @@ LBL_JCache=JCache LBL_executors=Executors LBL_http_proxy_servers=HTTP Servers LBL_grpc=gRPC Proxies +LBL_health=Health LBL_mt_warning=You have connected to a multi-tenant environment as a WebLogic Administrator.\n\ As a result you can see all Domain Partitions. When carrying out any operations, please\n\ ensure that you choose the correct partition for your operation. diff --git a/coherence-visualvm-plugin/src/main/resources/reports/visualvm/health-stats.xml b/coherence-visualvm-plugin/src/main/resources/reports/visualvm/health-stats.xml new file mode 100644 index 0000000..ce34d76 --- /dev/null +++ b/coherence-visualvm-plugin/src/main/resources/reports/visualvm/health-stats.xml @@ -0,0 +1,91 @@ + + + + + + + + {date}-health-stats.txt + {tab} + + + Coherence:type=Health,* + equals + + + + + global + {batch-counter} +
Batch Counter
+
+ + + key + nodeId +
NodeId
+
+ + + key + name +
Service Name
+
+ + + key + subType +
Sub Type
+
+ + + Started + + + + Live + + + + Ready + + + + Safe + + + + ClassName + +
+
+
\ No newline at end of file diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java index 802b8bb..63af423 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java @@ -354,6 +354,8 @@ private void testCacheData() throws Exception { testElasticData(); } + + testHealthData(); } /** @@ -672,7 +674,24 @@ public void testPersistenceData() int cSnapshots = (Integer) entryPersistence1.getValue().getColumn(PersistenceData.SNAPSHOT_COUNT); assertThat(cSnapshots, is(0)); + } + + public void testHealthData() + { + List> healthData; + + VisualVMModel model = getModel(); + assertClusterReady(); + waitForRefresh(); + + // refresh the statistics + model.refreshStatistics(getRequestSender()); + healthData = model.getData(VisualVMModel.DataType.HEALTH); + if (healthData != null && !healthData.isEmpty()) + { + + } } // ----- helpers -------------------------------------------------------- diff --git a/pom.xml b/pom.xml index d3fe2df..a65f1aa 100644 --- a/pom.xml +++ b/pom.xml @@ -53,15 +53,14 @@ 2.3.0 - 21.12.4 - 14.1.2-0-0-SNAPSHOT - + 22.06-SNAPSHOT + 14.1.2-0-0-SNAPSHOT -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${project.build.directory} -XX:+ExitOnOutOfMemoryError -Dfile.encoding=UTF-8 --illegal-access=permit - 2.11.3 + 2.13.3 ${jackson.version} 1.8 1.8 @@ -74,7 +73,7 @@ 2.2 3.6.0 - 5.0.20 + 6.0.0 3.8.0 4.2.1 From 279753b2583316755033614fd7c5e458454c8160 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 27 Jun 2022 12:13:45 +0800 Subject: [PATCH 14/27] Fix incorrect verison --- .../visualvm/helper/JMXRequestSender.java | 88 ++++++------------- pom.xml | 2 +- 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java index d713627..63601a5 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXRequestSender.java @@ -250,13 +250,7 @@ public String getScheduledDistributions(String sService, String sDomainPartition // look up the full name of the MBean in case we are in container Set setResult = getPartitionAssignmentObjectName(sService, sDomainPartition); - String sFQN = null; - - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } + String sFQN = getFirstResult(setResult); return (String) invoke(new ObjectName(sFQN), "reportScheduledDistributions", new Object[]{true}, new String[]{boolean.class.getName()}); @@ -269,15 +263,8 @@ public Set getPartitionAssignmentAttributes(String sService, String sD // look up the full name of the MBean in case we are in container Set setResult = getPartitionAssignmentObjectName(sService, sDomainPartition); - String sFQN = null; + String sFQN = getFirstResult(setResult); - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - - - } return JMXUtils.runJMXQuery(f_connection, sFQN, new JMXUtils.JMXField[]{ new JMXUtils.Attribute("AveragePartitionSizeKB"), new JMXUtils.Attribute("MaxPartitionSizeKB"), @@ -318,13 +305,7 @@ public String getNodeState(Integer nNodeId) Set setResult = getCompleteObjectName( new ObjectName("Coherence:type=Node,nodeId=" + nNodeId + ",*")); - String sFQN = null; - - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } + String sFQN = getFirstResult(setResult); return (String) invoke(new ObjectName(sFQN), "reportNodeState", new Object[0], new String[0]); } @@ -337,13 +318,7 @@ public String reportEnvironment(Integer nNodeId) Set setResult = getCompleteObjectName( new ObjectName("Coherence:type=Node,nodeId=" + nNodeId + ",*")); - String sFQN = null; - - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } + String sFQN = getFirstResult(setResult); return (String) invoke(new ObjectName(sFQN), "reportEnvironment", new Object[0], new String[0]); } @@ -360,13 +335,8 @@ public void dumpClusterHeap(String sRole) throws Exception Set setResult = getCompleteObjectName( new ObjectName("Coherence:type=Cluster,*")); - String sFQN = null; + String sFQN = getFirstResult(setResult); - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } invoke(new ObjectName(sFQN), "dumpClusterHeap", new Object[]{sRole}, new String[] {String.class.getName()}); } @@ -380,13 +350,7 @@ public String[] getSnapshots(String sService, String sDomainPartition) : PersistenceData.getFullServiceName(sDomainPartition, sService); Set setResult = getCompleteObjectName(new ObjectName(PersistenceData.getMBeanName(sServiceName))); - String sFQN = null; - - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } + String sFQN = getFirstResult(setResult); return (String[]) f_connection.getAttribute(new ObjectName(sFQN), "Snapshots"); } @@ -398,13 +362,7 @@ public String[] getArchivedSnapshots(String sService, String sDomainPartition) Set setResult = getCompleteObjectName(new ObjectName(PersistenceData.getMBeanName( PersistenceData.getFullServiceName(sDomainPartition, sService)))); - String sFQN = null; - - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } + String sFQN = getFirstResult(setResult); return (String[]) invoke(new ObjectName(sFQN), "listArchivedSnapshots", null, null); } @@ -419,13 +377,8 @@ public void executePersistenceOperation(String sService, ObjectName objectName = new ObjectName(PersistenceData.getMBeanName(PersistenceData.getFullServiceName(sDomainPartition, sService))); Set setResult = getCompleteObjectName(objectName); - String sFQN = null; + String sFQN = getFirstResult(setResult); - for (Object oResult : setResult) - { - sFQN = oResult.toString(); - break; - } invoke(new ObjectName(sFQN), sOperationName, new Object[]{sSnapshotName}, new String[]{String.class.getName()}); } @@ -447,17 +400,14 @@ public String getReporterObjectName(MBeanServerConnection server, int nLocalMemb try { Set setResult = server.queryNames(new ObjectName(sQuery), null); - for (Object oResult : setResult) - { - return oResult.toString(); - } + + return getFirstResult(setResult); } catch (Exception e) { throw new RuntimeException("Unable to obtain reporter for nodeId=" + nLocalMemberId + ": " + e.getMessage()); } - return null; } /** @@ -573,6 +523,24 @@ protected String getFederationManagerObjectName(String sService) return null; } + /** + * Returns the first result from a {@link Set} of {@link ObjectName}s. + * + * @param setResult {@link Set} of {@link ObjectName}s + * + * @return result + */ + private String getFirstResult(Set setResult) + { + String sFQN = null; + + if (!setResult.isEmpty()) + { + sFQN = setResult.iterator().next().toString(); + } + return sFQN; + } + // ------ constants ----------------------------------------------------- /** diff --git a/pom.xml b/pom.xml index a65f1aa..234c600 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ 2.3.0 - 22.06-SNAPSHOT + 21.12.4 14.1.2-0-0-SNAPSHOT -Xmx1g -XX:+HeapDumpOnOutOfMemoryError From bfd8809b90e685d2aa28c2298fd4dbbec1113812 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 29 Jun 2022 10:36:17 +0800 Subject: [PATCH 15/27] More health updates --- .../plugin/visualvm/VisualVMModel.java | 7 ++-- .../visualvm/panel/CoherenceHealthPanel.java | 4 +- .../tablemodel/model/HealthSummaryData.java | 12 +----- .../tests/AbstractDataRetrieverTest.java | 10 ++--- .../visualvm/tests/AbstractVisualVMTest.java | 40 ++++++++++++++++++- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java index d7e69f0..22bf25f 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java @@ -1480,10 +1480,9 @@ public String[] getMetadata() */ public static final String[] HEALTH_SUMMARY_LABELS = new String[] { - Localization.getLocalText("LBL_health_name"), - Localization.getLocalText("LBL_members"), Localization.getLocalText("LBL_started"), - Localization.getLocalText("LBL_live"), Localization.getLocalText("LBL_ready"), - Localization.getLocalText("LBL_safe"), Localization.getLocalText("LBL_health_class") + Localization.getLocalText("LBL_health_name"), Localization.getLocalText("LBL_members"), + Localization.getLocalText("LBL_started"), Localization.getLocalText("LBL_live"), + Localization.getLocalText("LBL_ready"), Localization.getLocalText("LBL_safe") }; /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java index c4d79fe..bd2d315 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -126,7 +126,7 @@ public CoherenceHealthPanel(VisualVMModel model) @Override public void updateGUI() { - + // no-op, done in update data } @Override @@ -148,7 +148,6 @@ public void updateData() { data = new HealthSummaryData(); data.setColumn(HealthSummaryData.HEALTH_NAME, newKey); - data.setColumn(HealthSummaryData.CLASS_NAME, value.getColumn(HealthData.CLASS_NAME)); data.setColumn(HealthSummaryData.MEMBERS, 0); data.setColumn(HealthSummaryData.STARTED, 0); data.setColumn(HealthSummaryData.LIVE, 0); @@ -179,7 +178,6 @@ public void updateData() mapData.put(newKey, data); }); - AtomicInteger totalChecks = new AtomicInteger(0); AtomicInteger totalOk = new AtomicInteger(0); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java index 400aa7d..d7f420d 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java @@ -50,7 +50,7 @@ public class HealthSummaryData */ public HealthSummaryData() { - super(CLASS_NAME + 1); + super(SAFE + 1); } // ----- DataRetriever methods ------------------------------------------ @@ -112,16 +112,6 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel m */ public static final int SAFE = 5; - /** - * Array index for description. - */ - public static final int CLASS_NAME = 6; - - /** - * The logger object to use. - */ - private static final Logger LOGGER = Logger.getLogger(HealthSummaryData.class.getName()); - /** * Report for cluster data. */ diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java index 63af423..d700ac1 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java @@ -355,7 +355,10 @@ private void testCacheData() throws Exception testElasticData(); } - testHealthData(); + if (isHealthCheckAvailable()) + { + testHealthData(); + } } /** @@ -688,10 +691,7 @@ public void testHealthData() model.refreshStatistics(getRequestSender()); healthData = model.getData(VisualVMModel.DataType.HEALTH); - if (healthData != null && !healthData.isEmpty()) - { - - } + assertTrue("Health Data is Missing", healthData.size() > 0); } // ----- helpers -------------------------------------------------------- diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java index ccaa4a1..66b1603 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java @@ -51,6 +51,7 @@ import com.oracle.bedrock.runtime.coherence.options.RoleName; import com.oracle.bedrock.runtime.coherence.options.WellKnownAddress; import com.oracle.bedrock.runtime.java.features.JmxFeature; +import com.oracle.bedrock.runtime.java.options.ClassName; import com.oracle.bedrock.runtime.java.options.SystemProperty; import com.oracle.bedrock.runtime.java.profiles.JmxProfile; import com.oracle.bedrock.runtime.network.AvailablePortIterator; @@ -283,9 +284,41 @@ protected static OptionsByType createCacheServerOptions(String sClusterName, int RoleName.of(ROLE_NAME), s_logs.builder()); + // check if HealthCheck class is available, then use Coherence to start + if (isHealthCheckAvailable()) + { + try + { + optionsByType.add(ClassName.of( Class.forName(HEALTH_CLASS))); + } + catch (ClassNotFoundException e) + { + // ignore as it should not fail as we have already done the check + } + } + return optionsByType; } + /** + * Returns true if health check is available. + * + * @return true if health check is available + */ + protected static boolean isHealthCheckAvailable() + { + try + { + Class.forName(HEALTH_CLASS); + // health check available + return true; + } + catch (ClassNotFoundException e) + { + return false; + } + } + /** * Validate the generated data for basic completeness including the number of rows * returned and column count for each row. @@ -299,7 +332,7 @@ protected void validateData(VisualVMModel.DataType type, List Date: Wed, 29 Jun 2022 11:20:28 +0800 Subject: [PATCH 16/27] Fixup failing test --- .../plugin/visualvm/VisualVMModel.java | 16 +++++++-------- .../plugin/visualvm/VisualVMView.java | 3 ++- .../visualvm/panel/CoherenceHealthPanel.java | 20 ++++++++++--------- .../panel/CoherencePersistencePanel.java | 4 ++-- .../visualvm/tablemodel/model/HealthData.java | 2 -- .../tablemodel/model/HealthSummaryData.java | 2 +- .../visualvm/tests/AbstractVisualVMTest.java | 2 +- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java index 22bf25f..e14dfc0 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java @@ -1009,9 +1009,9 @@ public boolean isFederationCongfigured() public boolean isElasticDataConfigured() { return (m_mapCollectedData.get(DataType.RAMJOURNAL) != null - && m_mapCollectedData.get(DataType.RAMJOURNAL).size() != 0) || + && !m_mapCollectedData.get(DataType.RAMJOURNAL).isEmpty()) || (m_mapCollectedData.get(DataType.FLASHJOURNAL) != null - && m_mapCollectedData.get(DataType.FLASHJOURNAL).size() != 0); + && !m_mapCollectedData.get(DataType.FLASHJOURNAL).isEmpty()); } /** @@ -1022,7 +1022,7 @@ public boolean isElasticDataConfigured() public boolean isExecutorConfigured() { return (m_mapCollectedData.get(DataType.EXECUTOR) != null - && m_mapCollectedData.get(DataType.EXECUTOR).size() != 0); + && !m_mapCollectedData.get(DataType.EXECUTOR).isEmpty()); } /** @@ -1033,7 +1033,7 @@ public boolean isExecutorConfigured() public boolean isGrpcProxyConfigured() { return (m_mapCollectedData.get(DataType.GRPC_PROXY) != null - && m_mapCollectedData.get(DataType.GRPC_PROXY).size() != 0); + && !m_mapCollectedData.get(DataType.GRPC_PROXY).isEmpty()); } /** @@ -1044,7 +1044,7 @@ public boolean isGrpcProxyConfigured() public boolean isHealthConfigured() { return (m_mapCollectedData.get(DataType.HEALTH) != null - && m_mapCollectedData.get(DataType.HEALTH).size() != 0); + && !m_mapCollectedData.get(DataType.HEALTH).isEmpty()); } /** @@ -1055,9 +1055,9 @@ public boolean isHealthConfigured() public boolean isJCacheConfigured() { return (m_mapCollectedData.get(DataType.JCACHE_CONFIG) != null - && m_mapCollectedData.get(DataType.JCACHE_CONFIG).size() != 0) || + && !m_mapCollectedData.get(DataType.JCACHE_CONFIG).isEmpty()) || (m_mapCollectedData.get(DataType.JCACHE_STATS) != null - && m_mapCollectedData.get(DataType.JCACHE_STATS).size() != 0); + && !m_mapCollectedData.get(DataType.JCACHE_STATS).isEmpty()); } /** @@ -1068,7 +1068,7 @@ public boolean isJCacheConfigured() public boolean isHttpProxyConfigured() { return (m_mapCollectedData.get(DataType.HTTP_PROXY) != null - && m_mapCollectedData.get(DataType.HTTP_PROXY).size() != 0); + && !m_mapCollectedData.get(DataType.HTTP_PROXY).isEmpty()); } /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java index f294a90..608721d 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMView.java @@ -70,6 +70,7 @@ import javax.swing.Timer; import org.graalvm.visualvm.application.Application; +import org.graalvm.visualvm.core.datasupport.Stateful; import org.graalvm.visualvm.core.ui.DataSourceView; import org.graalvm.visualvm.core.ui.components.DataViewComponent; import org.graalvm.visualvm.tools.jmx.JmxModel; @@ -382,7 +383,7 @@ public void run() try { // application may be null inside the constructor - if (m_application == null || m_application.getState() == Application.STATE_AVAILABLE) + if (m_application == null || m_application.getState() == Stateful.STATE_AVAILABLE) { // Schedule the SwingWorker to update the GUI model.refreshStatistics(requestSender); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java index bd2d315..a534e94 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -157,19 +157,19 @@ public void updateData() // increment the values data.setColumn(HealthSummaryData.MEMBERS, (Integer)data.getColumn(HealthSummaryData.MEMBERS) + 1); - if ((Boolean) value.getColumn(HealthData.STARTED)) + if (Boolean.TRUE.equals(value.getColumn(HealthData.STARTED))) { data.setColumn(HealthSummaryData.STARTED, (Integer)data.getColumn(HealthSummaryData.STARTED) + 1); } - if ((Boolean) value.getColumn(HealthData.LIVE)) + if (Boolean.TRUE.equals(value.getColumn(HealthData.LIVE))) { data.setColumn(HealthSummaryData.LIVE, (Integer)data.getColumn(HealthSummaryData.LIVE) + 1); } - if ((Boolean) value.getColumn(HealthData.READY)) + if (Boolean.TRUE.equals(value.getColumn(HealthData.READY))) { data.setColumn(HealthSummaryData.READY, (Integer)data.getColumn(HealthSummaryData.READY) + 1); } - if ((Boolean) value.getColumn(HealthData.SAFE)) + if (Boolean.TRUE.equals(value.getColumn(HealthData.SAFE))) { data.setColumn(HealthSummaryData.SAFE, (Integer)data.getColumn(HealthSummaryData.SAFE) + 1); } @@ -192,19 +192,19 @@ public void updateData() if (cStarted != cMembers) { - v.setColumn(HealthSummaryData.STARTED, String.format("%d/%d", cStarted, cMembers)); + v.setColumn(HealthSummaryData.STARTED, String.format(FORMAT, cStarted, cMembers)); } if (cLive != cMembers) { - v.setColumn(HealthSummaryData.LIVE, String.format("%d/%d", cLive, cMembers)); + v.setColumn(HealthSummaryData.LIVE, String.format(FORMAT, cLive, cMembers)); } if (cReady != cMembers) { - v.setColumn(HealthSummaryData.READY, String.format("%d/%d", cReady, cMembers)); + v.setColumn(HealthSummaryData.READY, String.format(FORMAT, cReady, cMembers)); } if (cSafe != cMembers) { - v.setColumn(HealthSummaryData.SAFE, String.format("%d/%d", cSafe, cMembers)); + v.setColumn(HealthSummaryData.SAFE, String.format(FORMAT, cSafe, cMembers)); } totalChecks.addAndGet(cMembers * 4); @@ -213,7 +213,7 @@ public void updateData() // update total health f_txtTotalHealthChecks.setForeground(Color.black); - String sValue = String.format("%d/%d", totalOk.get(), totalChecks.get()); + String sValue = String.format(FORMAT, totalOk.get(), totalChecks.get()); f_txtTotalHealthChecks.setText(sValue); if (totalOk.get() != totalChecks.get()) @@ -235,6 +235,8 @@ public void updateData() private static final long serialVersionUID = -7612569043492412546L; + private static final String FORMAT = "%d/%d"; + // ----- data members ---------------------------------------------------- /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java index b0d54ff..330f588 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java @@ -903,12 +903,12 @@ else if (sType.indexOf(END) > 0) /** * The graph of persistence latency averages. */ - private final SimpleXYChartSupport f_persistenceLatencyGraph; + private final transient SimpleXYChartSupport f_persistenceLatencyGraph; /** * The graph of persistence latency averages. */ - private final SimpleXYChartSupport f_persistenceTotalSpaceGraph; + private final transient SimpleXYChartSupport f_persistenceTotalSpaceGraph; /** * the {@link ExportableJTable} to use to display data. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java index d387fa2..22ccb6b 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthData.java @@ -25,7 +25,6 @@ package com.oracle.coherence.plugin.visualvm.tablemodel.model; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -100,7 +99,6 @@ public SortedMap getAggregatedDataFromHttpQuerying(VisualVMModel m JsonNode allStorageMembers = requestSender.getAllHealthMembers(); JsonNode healthItemsNode = allStorageMembers.get("items"); - Map mapNodeCount = new HashMap<>(); Data data; if (healthItemsNode != null && healthItemsNode.isArray()) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java index d7f420d..4820862 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/HealthSummaryData.java @@ -29,7 +29,7 @@ import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import java.util.logging.Logger; + import com.oracle.coherence.plugin.visualvm.VisualVMModel; import com.oracle.coherence.plugin.visualvm.helper.HttpRequestSender; import com.oracle.coherence.plugin.visualvm.helper.RequestSender; diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java index 66b1603..b50a0d2 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java @@ -289,7 +289,7 @@ protected static OptionsByType createCacheServerOptions(String sClusterName, int { try { - optionsByType.add(ClassName.of( Class.forName(HEALTH_CLASS))); + optionsByType.add(ClassName.of(Class.forName("com.tangosol.net.Coherence"))); } catch (ClassNotFoundException e) { From f4237e6903e56f093d3d7cc45a655e7847331dd6 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Wed, 29 Jun 2022 14:49:37 +0800 Subject: [PATCH 17/27] test updates --- .../tests/AbstractDataRetrieverTest.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java index d700ac1..135b2df 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java @@ -28,6 +28,7 @@ import com.oracle.bedrock.testsupport.deferred.Eventually; import com.oracle.coherence.plugin.visualvm.VisualVMModel; +import com.oracle.coherence.plugin.visualvm.helper.HttpRequestSender; import com.oracle.coherence.plugin.visualvm.helper.RequestSender; import com.oracle.coherence.plugin.visualvm.tablemodel.model.CacheData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.CacheDetailData; @@ -355,7 +356,8 @@ private void testCacheData() throws Exception testElasticData(); } - if (isHealthCheckAvailable()) + // only include health check if the reporter is available and HealthCheck is available + if (isHealthCheckAvailable() && (getRequestSender() instanceof HttpRequestSender || model.isReporterAvailable())) { testHealthData(); } @@ -662,9 +664,12 @@ public void testPersistenceData() validateData(VisualVMModel.DataType.PERSISTENCE, persistenceData, cCount); + int nIndex1 = isHealthCheckAvailable() ? 1 : 0; + int nIndex2 = isHealthCheckAvailable() ? 2 : 1; + // the services will be ordered as above, alphabetically - Map.Entry entryPersistence1 = persistenceData.get(0); - Map.Entry entryPersistence2 = persistenceData.get(1); + Map.Entry entryPersistence1 = persistenceData.get(nIndex1); + Map.Entry entryPersistence2 = persistenceData.get(nIndex2); assertThat(entryPersistence1, is(notNullValue())); assertThat(entryPersistence2, is(notNullValue())); @@ -683,6 +688,11 @@ public void testHealthData() { List> healthData; + if (!getModel().isReporterAvailable()) + { + return; + } + VisualVMModel model = getModel(); assertClusterReady(); waitForRefresh(); @@ -691,7 +701,7 @@ public void testHealthData() model.refreshStatistics(getRequestSender()); healthData = model.getData(VisualVMModel.DataType.HEALTH); - assertTrue("Health Data is Missing", healthData.size() > 0); + assertTrue("Health Data is Missing", healthData != null && healthData.size() > 0); } // ----- helpers -------------------------------------------------------- From 3bb9f87a266e4acc4378f52675edba726763e393 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 1 Jul 2022 15:45:13 +0800 Subject: [PATCH 18/27] Update to use 22.06 --- .github/workflows/test-against-released.yml | 4 +--- .../plugin/visualvm/panel/CoherenceHealthPanel.java | 2 +- .../visualvm/tablemodel/model/MachineData.java | 2 +- .../visualvm/tests/AbstractDataRetrieverTest.java | 6 ++++++ .../plugin/visualvm/tests/AbstractVisualVMTest.java | 13 +++++++++++++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-against-released.yml b/.github/workflows/test-against-released.yml index 4cb16ae..8997537 100644 --- a/.github/workflows/test-against-released.yml +++ b/.github/workflows/test-against-released.yml @@ -44,12 +44,10 @@ jobs: version: - 14.1.1-0-9 - 14.1.1-0-8 - - 20.06.1 - 20.12.2 - 21.06.1 - - 21.12.3 - 21.12.4 - - 21.12.2 + - 22.06 steps: - name: Checkout diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java index a534e94..d4449f5 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -81,7 +81,7 @@ public CoherenceHealthPanel(VisualVMModel model) pnlHeader.setLayout(new FlowLayout()); pnlHeader.setOpaque(false); - f_txtTotalHealthChecks = getTextField(5, JTextField.RIGHT); + f_txtTotalHealthChecks = getTextField(10, JTextField.RIGHT); pnlHeader.add(getLocalizedLabel("LBL_total_health_checks", f_txtTotalHealthChecks)); pnlHeader.add(f_txtTotalHealthChecks); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java index c4386cd..6ef3476 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/MachineData.java @@ -105,7 +105,7 @@ public List> getJMXData(RequestSender requestSender, Vis new String[] { "Name", ATTR_FREE_MEM, ATTR_LOAD_AVG, ATTR_AVAIL_PROC, ATTR_TOTAL_MEM_AIX, ATTR_TOTAL_MEM }); String sOSType = getAttributeValueAsString(listAttr, "Name"); - String sMemoryAttr = sOSType.toLowerCase().contains("aix") ? ATTR_TOTAL_MEM_AIX : ATTR_TOTAL_MEM; + String sMemoryAttr = sOSType != null && sOSType.toLowerCase().contains("aix") ? ATTR_TOTAL_MEM_AIX : ATTR_TOTAL_MEM; data.setColumn(MACHINE_NAME, machineName); data.setColumn(FREE_PHYSICAL_MEMORY, Long.parseLong(getAttributeValueAsString(listAttr, ATTR_FREE_MEM))); diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java index 135b2df..6843951 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractDataRetrieverTest.java @@ -55,6 +55,7 @@ import java.security.CodeSource; import java.security.ProtectionDomain; +import com.tangosol.util.Base; import javax.management.Attribute; import javax.management.ObjectName; @@ -513,6 +514,11 @@ public void testFederationData() assertThat(federationOriginDetailsData, is(notNullValue())); assertThat("Total origin entries sent should be positive.", lTotalOrigEntriesSent, is(greaterThan(0L))); + + if ("true".equals(System.getProperty("pause.federation"))) + { + Base.sleep(Long.MAX_VALUE); + } } /** diff --git a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java index b50a0d2..6079049 100644 --- a/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java +++ b/coherence-visualvm-tests/coherence-visualvm-tests-core/src/test/java/com/oracle/coherence/plugin/visualvm/tests/AbstractVisualVMTest.java @@ -166,6 +166,14 @@ public static void startupCacheServers(boolean fRestRequestSender) OptionsByType optionsByTypeMemberB1 = OptionsByType.of(optionsByTypeB1).add(DisplayName.of("memberB1")); OptionsByType optionsByTypeMemberB2 = OptionsByType.of(optionsByTypeB2).add(DisplayName.of("memberB2")); + if (fRestRequestSender) + { + optionsByTypeMemberB1.add(SystemProperty.of("coherence.management", "dynamic")); + optionsByTypeMemberB1.add(SystemProperty.of("coherence.management.http", "inherit")); + optionsByTypeMemberB1.add(SystemProperty.of("coherence.management.http.host", REST_MGMT_HOST)); + optionsByTypeMemberB1.add(SystemProperty.of("coherence.management.http.port", REST_MGMT_PORT2)); + } + s_memberB1 = platform.launch(CoherenceCacheServer.class, optionsByTypeMemberB1.asArray()); s_memberB2 = platform.launch(CoherenceCacheServer.class, optionsByTypeMemberB2.asArray()); @@ -585,6 +593,11 @@ protected static RequestSender getRequestSender() */ protected static String REST_MGMT_PORT = "8080"; + /** + * Rest Management Port2 + */ + protected static String REST_MGMT_PORT2 = "8081"; + /** * port iterator for ephemeral ports. */ From 753cbf08ab4c27979697bd0a0c87a3acf8943a26 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 5 Jul 2022 08:34:16 +0800 Subject: [PATCH 19/27] Minor Signed-off-by: Tim Middleton --- .../plugin/visualvm/helper/HttpRequestSender.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java index 26c52e6..2d6160f 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/HttpRequestSender.java @@ -368,7 +368,8 @@ public Set getHotCacheMembers() { URLBuilder urlBuilder = getBasePath().addPathSegment("hotcache") .addPathSegment(MEMBERS); - urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId"); + urlBuilder.addQueryParameter(FIELDS, "name,type,nodeId") + .addQueryParameter(LINKS, ""); return getSetObjectNamesFromResponse(sendGetRequest(urlBuilder)); } @@ -1407,6 +1408,10 @@ private URLBuilder modifyTarget(ObjectName objectName, URLBuilder urlBuilder) { switch (objectName.getKeyProperty("type")) { + case "CoherenceAdapter": + return urlBuilder.addPathSegment("hotcache").addPathSegment(MEMBERS) + .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) + .addQueryParameter(LINKS, ""); case "Node": return urlBuilder.addPathSegment(MEMBERS) .addPathSegment(getKeyPropertyFromObjName(objectName, "nodeId")) From 213d95baf2f8f493a9aa0018e59e2b3a9bc7e76e Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 5 Jul 2022 09:06:11 +0800 Subject: [PATCH 20/27] Minor updates Signed-off-by: Tim Middleton --- README.adoc | 11 ++++---- .../plugin/visualvm/VisualVMModel.java | 10 ------- .../plugin/visualvm/helper/RenderHelper.java | 23 +++++++-------- .../panel/AbstractCoherencePanel.java | 3 +- .../visualvm/panel/CoherenceCachePanel.java | 9 +++--- .../panel/CoherenceElasticDataPanel.java | 11 ++++---- .../panel/CoherenceExecutorPanel.java | 3 +- .../panel/CoherenceFederationPanel.java | 7 +++-- .../panel/CoherenceGrpcProxyPanel.java | 3 +- .../visualvm/panel/CoherenceHealthPanel.java | 28 ++++++++++--------- .../panel/CoherenceHotCachePanel.java | 5 ++-- .../panel/CoherenceHttpProxyPanel.java | 5 ++-- .../panel/CoherenceHttpSessionPanel.java | 3 +- .../visualvm/panel/CoherenceMachinePanel.java | 3 +- .../visualvm/panel/CoherenceMemberPanel.java | 2 +- .../panel/CoherencePersistencePanel.java | 5 ++-- .../visualvm/panel/CoherenceProxyPanel.java | 3 +- .../visualvm/panel/CoherenceServicePanel.java | 5 ++-- .../visualvm/panel/CoherenceTopicPanel.java | 3 +- .../plugin/visualvm/Bundle.properties | 5 ++-- 20 files changed, 78 insertions(+), 69 deletions(-) diff --git a/README.adoc b/README.adoc index 34ac894..93c70f3 100644 --- a/README.adoc +++ b/README.adoc @@ -56,9 +56,9 @@ image::assets/coherence-visualvm.png[Coherence VisualVM Plugin,800,479] The Plugin will connect to and display data for the following Coherence versions: -**Community Editions**: 14.1.1.0.x, 20.06.x, 20.12.x, 21.06.x and 21.12.x +**Community Editions**: 14.1.1.0.x, 20.06.x, 20.12.x, 21.06.x, 21.12.x and 22.06.x -**Commercial Editions**: 14.1.1.0.x, 12.2.1.5.x, 12.2.1.4.x, 12.1.3.x, 12.1.2.x and 3.7.1.x +**Commercial Editions**: 14.1.1.0.x, 12.2.1.5.x, 12.2.1.4.x, 12.1.3.x, 12.1.2.x and 3.7.1.x (Support will be dropped for versions 12.1.3.x and older in an upcoming release) NOTE: If you wish to connect to Coherence version 12.2.1.4.x via REST you should have Coherence version 12.2.1.4.7 or greater. @@ -85,7 +85,7 @@ Other useful resources: * https://coherence.community/[The Coherence Community - All things Coherence] * https://visualvm.github.io/[VisualVM Home Page] * https://github.com/oracle/coherence[Coherence Community Edition on GitHub] -* https://github.com/oracle/coherence/tree/master/examples[Various Coherence Examples] +* https://coherence.community/latest/22.06/docs/#/examples/README[Various Coherence Examples] * https://github.com/oracle/coherence-operator[The Coherence Operator - Run your clusters in Kubernetes] [#connect] @@ -186,7 +186,7 @@ There are tool tips for each of the preferences, but a summary is shown below. | Enable Zoom on Graphs | false | Enables additional zoom function for all graphs. | Enable Cluster Snapshot tab | false | Enables experimental Cluster Snapshot tab. This tab is useful for seeing all the relevant cluster information on one pae in a text format. | Enable Cluster Heap Dump | false | Enables the cluster heap dump button on the Cluster Overview tab. -| Analyze Unavailable Time in LogFile| | Provides the ability to analyze log files where Partition Events Logging has been enabled for logs generated from Coherence versions 21.06 and above. See https://coherence.community/21.06/docs/#/docs/core/07_partition_events_logging[here] for more details. Note: You select a Coherence log file to analyze and don't need to be connected to a running cluster. +| Analyze Unavailable Time in LogFile| | Provides the ability to analyze log files where Partition Events Logging has been enabled for logs generated from Coherence versions 21.06 and above. See https://docs.oracle.com/pls/topic/lookup?ctx=en/middleware/standalone/coherence/14.1.1.2206/release-notes&id=COHDG-GUID-41F5341C-0318-41B2-AEBF-B9DB7FBF25E7[here] for more details. Note: You select a Coherence log file to analyze and don't need to be connected to a running cluster. !=== [#capabilities] @@ -213,7 +213,8 @@ Depending upon the edition and functionality you are using, the following option * **Elastic Data** - If your cluster is configured with Elastic Data, this tab displays graphs and information about RAM Journal and Flash Journal usage. You can click on each of the usage bars to show detailed node information * **JCache** - If your cluster is being used to store JCache caches, this tab displays JCache "Management" and "Statistics" MBean information regarding the configured caches. * **HotCache** - If your cluster contains HotCache node(s), then this tab lists the running HotCache instances. If you select an instance, on the next data refresh the console will display statistics and graphs for the operations performed. You may click on tabs and cache-ops to see further fine-grained information. -* **gRPC Proxies** – If you cluster is configured with gRPC Proxies, this tab displays information about the requests sent and received as well as successful and failed requests. A Graph of message rates and durations is also displayed. This tab will only show when connected via JMX and is not supported for REST connections. +* **gRPC Proxies** – If your cluster is configured with gRPC Proxies, this tab displays information about the requests sent and received as well as successful and failed requests. A Graph of message rates and durations is also displayed. This tab will only show when connected via JMX and is not supported for REST connections. +* **Health** – If your cluster supports the Health Check API, this tab displays information regarding the status of all health endpoints. [#build] == Building the Plugin diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java index e14dfc0..5c5697a 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/VisualVMModel.java @@ -1475,16 +1475,6 @@ public String[] getMetadata() Localization.getLocalText("LBL_safe"), Localization.getLocalText("LBL_health_class") }; - /** - * Labels for Health table. - */ - public static final String[] HEALTH_SUMMARY_LABELS = new String[] - { - Localization.getLocalText("LBL_health_name"), Localization.getLocalText("LBL_members"), - Localization.getLocalText("LBL_started"), Localization.getLocalText("LBL_live"), - Localization.getLocalText("LBL_ready"), Localization.getLocalText("LBL_safe") - }; - /** * Labels for persistence table. */ diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java index 43b8177..a5e532d 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/RenderHelper.java @@ -36,6 +36,7 @@ import javax.swing.JLabel; import javax.swing.JTable; +import javax.swing.SwingConstants; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; @@ -173,7 +174,7 @@ else if (fValue <= 0.75) JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); String text = MILLIS_FORMAT.format((Number) value); @@ -226,7 +227,7 @@ else if (sValue.contains("/")) JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); renderedLabel.setText(sValue); } @@ -249,7 +250,7 @@ public static class DecimalRenderer public DecimalRenderer() { super(); - setHorizontalAlignment(JLabel.RIGHT); + setHorizontalAlignment(SwingConstants.RIGHT); } /** @@ -262,7 +263,7 @@ public DecimalRenderer(NumberFormat sFormat) { super(); this.numberFormat = sFormat; - setHorizontalAlignment(JLabel.RIGHT); + setHorizontalAlignment(SwingConstants.RIGHT); } /** @@ -279,7 +280,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); String text = numberFormat.format((Number) value); @@ -338,7 +339,7 @@ else if (fValue < 0.25) JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); String text = PERCENT_FORMAT.format((Number) value); @@ -369,7 +370,7 @@ public static class IntegerRenderer public IntegerRenderer() { super(); - setHorizontalAlignment(JLabel.RIGHT); + setHorizontalAlignment(SwingConstants.RIGHT); } /** @@ -385,7 +386,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole { JLabel label = (JLabel) c; - label.setHorizontalAlignment(JLabel.RIGHT); + label.setHorizontalAlignment(SwingConstants.RIGHT); String text = INTEGER_FORMAT.format((Number) value); @@ -419,7 +420,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole { JLabel label = (JLabel) c; - label.setHorizontalAlignment(JLabel.RIGHT); + label.setHorizontalAlignment(SwingConstants.RIGHT); long nLongValue = value instanceof Integer ? ((Integer)value) * 1L : ((Long)value).longValue(); label.setText(getRenderedBytes(nLongValue)); @@ -572,7 +573,7 @@ else if (fValue <= 0.950) JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); String text = MILLIS_FORMAT.format((Number) value); @@ -623,7 +624,7 @@ else if (fValue >= 0.60) JLabel renderedLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - renderedLabel.setHorizontalAlignment(JLabel.RIGHT); + renderedLabel.setHorizontalAlignment(SwingConstants.RIGHT); String text = PERCENT_FORMAT.format((Number) value); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java index af320f1..490b8ee 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/AbstractCoherencePanel.java @@ -73,6 +73,7 @@ import javax.swing.JTable; import javax.swing.JTextField; +import javax.swing.SwingConstants; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; @@ -317,7 +318,7 @@ public boolean isCellEditable(int row, int column) } }; m_table = new ExportableJTable(m_tmodel); - RenderHelper.setHeaderAlignment(m_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(m_table, SwingConstants.CENTER); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceCachePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceCachePanel.java index ea8232f..4198441 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceCachePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceCachePanel.java @@ -83,6 +83,7 @@ import javax.swing.JTextField; import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; import javax.swing.ToolTipManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -156,10 +157,10 @@ public CoherenceCachePanel(VisualVMModel model) RenderHelper.setColumnRenderer(table, CacheData.MEMORY_USAGE_BYTES, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(table, CacheData.UNIT_CALCULATOR, new RenderHelper.UnitCalculatorRenderer()); - RenderHelper.setHeaderAlignment(table, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_tableDetail, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_tableFrontDetail, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_tableStorage, JLabel.CENTER); + RenderHelper.setHeaderAlignment(table, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_tableDetail, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_tableFrontDetail, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_tableStorage, SwingConstants.CENTER); RenderHelper.setColumnRenderer(f_tableDetail, CacheDetailData.CACHE_HITS, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_tableDetail, CacheDetailData.CACHE_MISSES, new RenderHelper.IntegerRenderer()); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceElasticDataPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceElasticDataPanel.java index edc7468..38c295f 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceElasticDataPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceElasticDataPanel.java @@ -50,6 +50,7 @@ import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; +import javax.swing.SwingConstants; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; @@ -211,10 +212,10 @@ public void updateGUI() int cFlashExhaustiveDelta = cFlashExhaustive - m_cLastFlashExhaustive; GraphHelper.addValuesToRamJournalCompactionGraph(f_ramJournalCompactionGraph, - cRamCompactionDelta < 0 ? 0 : cRamCompactionDelta, cRamExhaustiveDelta < 0 ? 0 : cRamExhaustiveDelta); + Math.max(cRamCompactionDelta, 0), Math.max(cRamExhaustiveDelta, 0)); GraphHelper.addValuesToFlashJournalCompactionGraph(f_flashJournalCompactionGraph, - cFlashCompactionDelta < 0 ? 0 : cFlashCompactionDelta, - cFlashExhaustiveDelta < 0 ? 0 : cFlashExhaustiveDelta); + Math.max(cFlashCompactionDelta, 0), + Math.max(cFlashExhaustiveDelta, 0)); // set the last values to calculate deltas m_cLastFlashCompaction = cFlashCompaction; @@ -297,7 +298,7 @@ public boolean isCellEditable(int row, int column) setColumnRenderer(f_table, 5, null); // load factor RenderHelper.setColumnRenderer(f_table, 6, new RenderHelper.BytesRenderer()); // max file size - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); @@ -328,7 +329,7 @@ private void setColumnRenderer(ExportableJTable exportableJTable, int nColumn, S rndRightAlign.setToolTipText(sToolTip); } - rndRightAlign.setHorizontalAlignment(JLabel.RIGHT); + rndRightAlign.setHorizontalAlignment(SwingConstants.RIGHT); exportableJTable.getColumnModel().getColumn(nColumn).setCellRenderer(rndRightAlign); } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceExecutorPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceExecutorPanel.java index 6800918..ae5545f 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceExecutorPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceExecutorPanel.java @@ -45,6 +45,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -102,7 +103,7 @@ public CoherenceExecutorPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, ExecutorData.TASKS_COMPLETED, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, ExecutorData.TASKS_REJECTED, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, ExecutorData.TASKS_IN_PROGRESS, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceFederationPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceFederationPanel.java index 8e1e472..de13005 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceFederationPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceFederationPanel.java @@ -65,6 +65,7 @@ import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -281,9 +282,9 @@ public CoherenceFederationPanel(VisualVMModel model) RenderHelper.setColumnRenderer(tableInbound, FederationOriginDetailsData.Column.TOTAL_MSG_UNACKED.ordinal(), new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(tableFed, JLabel.CENTER); - RenderHelper.setHeaderAlignment(tableInbound, JLabel.CENTER); - RenderHelper.setHeaderAlignment(tableOutbound, JLabel.CENTER); + RenderHelper.setHeaderAlignment(tableFed, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(tableInbound, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(tableOutbound, SwingConstants.CENTER); // set sizes tableFed.setPreferredScrollableViewportSize(new Dimension(500, tableFed.getRowHeight() * 4)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceGrpcProxyPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceGrpcProxyPanel.java index 954065f..18c1213 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceGrpcProxyPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceGrpcProxyPanel.java @@ -42,6 +42,7 @@ import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -102,7 +103,7 @@ public CoherenceGrpcProxyPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, GrpcProxyData.MESSAGES_RECEIVED_COUNT, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, GrpcProxyData.REQUEST_DURATION_MEAN, new RenderHelper.DecimalRenderer()); RenderHelper.setColumnRenderer(f_table, GrpcProxyData.MESSAGE_DURATION_MEAN, new RenderHelper.DecimalRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java index d4449f5..2d73ed2 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -35,6 +35,7 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; +import com.oracle.coherence.plugin.visualvm.Localization; import com.oracle.coherence.plugin.visualvm.VisualVMModel; import com.oracle.coherence.plugin.visualvm.helper.RenderHelper; import com.oracle.coherence.plugin.visualvm.panel.util.ExportableJTable; @@ -49,6 +50,7 @@ import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; /** * An implementation of an {@link AbstractCoherencePanel} to view @@ -86,7 +88,7 @@ public CoherenceHealthPanel(VisualVMModel model) pnlHeader.add(f_txtTotalHealthChecks); // create the table - f_tmodel = new HealthSummaryTableModel(VisualVMModel.HEALTH_SUMMARY_LABELS); + f_tmodel = new HealthSummaryTableModel(HEALTH_SUMMARY_LABELS); f_table = new ExportableJTable(f_tmodel); @@ -99,7 +101,7 @@ public CoherenceHealthPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, HealthSummaryData.READY, new RenderHelper.HealthRenderer()); RenderHelper.setColumnRenderer(f_table, HealthSummaryData.SAFE, new RenderHelper.HealthRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); @@ -132,7 +134,7 @@ public void updateGUI() @Override public void updateData() { - m_healthData = f_model.getData(VisualVMModel.DataType.HEALTH); + List> m_healthData = f_model.getData(VisualVMModel.DataType.HEALTH); SortedMap mapData = new TreeMap<>(); @@ -244,16 +246,6 @@ public void updateData() */ private final JTextField f_txtTotalHealthChecks; - /** - * A check-box to indicate if the NameService should be included in the list of proxy servers. - */ - private JCheckBox m_cbxIncludeNameService = null; - - /** - * The health data retrieved from the {@link VisualVMModel}. - */ - private List> m_healthData; - /** * The {@link HealthSummaryTableModel} to display proxy data. */ @@ -263,4 +255,14 @@ public void updateData() * the {@link ExportableJTable} to use to display data. */ protected final ExportableJTable f_table; + + /** + * Labels for Health table. + */ + private static final String[] HEALTH_SUMMARY_LABELS = new String[] + { + Localization.getLocalText("LBL_health_name"), Localization.getLocalText("LBL_members"), + Localization.getLocalText("LBL_started"), Localization.getLocalText("LBL_live"), + Localization.getLocalText("LBL_ready"), Localization.getLocalText("LBL_safe") + }; } diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java index 89103d8..3a59674 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java @@ -42,6 +42,7 @@ import javax.swing.ListSelectionModel; import javax.swing.JLabel; +import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.BorderLayout; @@ -107,8 +108,8 @@ public CoherenceHotCachePanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_percachetable, HotCachePerCacheData.Max, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_percachetable, HotCachePerCacheData.Min, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(table, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_percachetable, JLabel.CENTER); + RenderHelper.setHeaderAlignment(table, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_percachetable, SwingConstants.CENTER); f_percachetable.setIntercellSpacing(new Dimension(6,3)); f_percachetable.setRowHeight(f_percachetable.getRowHeight() + 4); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java index 22fc295..6d02e02 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java @@ -43,6 +43,7 @@ import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.BorderLayout; @@ -98,8 +99,8 @@ public CoherenceHttpProxyPanel(VisualVMModel model) RenderHelper.setMillisRenderer(f_tableDetail, HttpProxyMemberData.AVG_REQ_TIME); RenderHelper.setMillisRenderer(f_tableDetail, HttpProxyMemberData.REQ_PER_SECOND); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_tableDetail, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_tableDetail, SwingConstants.CENTER); f_table.setPreferredScrollableViewportSize(new Dimension(500, f_table.getRowHeight() * 5)); f_tableDetail.setPreferredScrollableViewportSize(new Dimension(500, 125)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java index e9e9332..baed760 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java @@ -48,6 +48,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -105,7 +106,7 @@ public CoherenceHttpSessionPanel(VisualVMModel model) new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, HttpSessionData.SESSION_UPDATES, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java index e3a15f3..9bd7e8d 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java @@ -47,6 +47,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -100,7 +101,7 @@ public CoherenceMachinePanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, MachineData.SYSTEM_LOAD_AVERAGE, new RenderHelper.DecimalRenderer(RenderHelper.LOAD_AVERAGE_FORMAT)); RenderHelper.setColumnRenderer(f_table, MachineData.PERCENT_FREE_MEMORY, new RenderHelper.FreeMemoryRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java index 2544e77..4f7c2f3 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java @@ -168,7 +168,7 @@ public CoherenceMemberPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, MemberData.RECEIVER_SUCCESS, new RenderHelper.SuccessRateRenderer()); RenderHelper.setColumnRenderer(f_table, MemberData.SENDQ_SIZE, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java index 330f588..ac3278f 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherencePersistencePanel.java @@ -83,6 +83,7 @@ import javax.swing.JTextField; import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; @@ -202,7 +203,7 @@ public CoherencePersistencePanel(VisualVMModel model) new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, PersistenceData.TOTAL_BACKUP_SPACE_USED_MB, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); @@ -236,7 +237,7 @@ public CoherencePersistencePanel(VisualVMModel model) new RenderHelper.ToolTipRenderer()); RenderHelper.setColumnRenderer(f_tableNotifications, PersistenceNotificationsData.DURATION, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_tableNotifications, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_tableNotifications, SwingConstants.CENTER); // Add some space f_tableNotifications.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java index 98d9812..464be07 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java @@ -49,6 +49,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -121,7 +122,7 @@ public CoherenceProxyPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, ProxyData.TOTAL_BYTES_SENT, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, ProxyData.TOTAL_MSG_RECEIVED, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, ProxyData.TOTAL_MSG_SENT, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); // Add some space f_table.setIntercellSpacing(new Dimension(6, 3)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java index 7b9ee40..63ae46d 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java @@ -57,6 +57,7 @@ import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -119,8 +120,8 @@ else if (model.getClusterVersionAsInt() >= 121200) RenderHelper.setColumnRenderer(f_tableDetail, ServiceMemberData.TASK_AVERAGE_DURATION, new RenderHelper.DecimalRenderer()); - RenderHelper.setHeaderAlignment(table, JLabel.CENTER); - RenderHelper.setHeaderAlignment(f_tableDetail, JLabel.CENTER); + RenderHelper.setHeaderAlignment(table, SwingConstants.CENTER); + RenderHelper.setHeaderAlignment(f_tableDetail, SwingConstants.CENTER); table.setPreferredScrollableViewportSize(new Dimension(500, table.getRowHeight() * 5)); f_tableDetail.setPreferredScrollableViewportSize(new Dimension(700, 125)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java index 50a116d..b65b1dd 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java @@ -46,6 +46,7 @@ import javax.swing.JSplitPane; import javax.swing.JTextField; +import javax.swing.SwingConstants; import org.graalvm.visualvm.charts.SimpleXYChartSupport; /** @@ -104,7 +105,7 @@ public CoherenceTopicPanel(VisualVMModel model) RenderHelper.setColumnRenderer(f_table, TopicData.MEMORY_USAGE_BYTES, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, TopicData.PUBLISHER_SENDS, new RenderHelper.IntegerRenderer()); RenderHelper.setColumnRenderer(f_table, TopicData.SUBSCRIBER_RECEIVES, new RenderHelper.IntegerRenderer()); - RenderHelper.setHeaderAlignment(f_table, JLabel.CENTER); + RenderHelper.setHeaderAlignment(f_table, SwingConstants.CENTER); f_table.setIntercellSpacing(new Dimension(6, 3)); f_table.setRowHeight(f_table.getRowHeight() + 4); diff --git a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties index b7ad66f..d7df400 100644 --- a/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties +++ b/coherence-visualvm-plugin/src/main/resources/com/oracle/coherence/plugin/visualvm/Bundle.properties @@ -42,6 +42,7 @@ OpenIDE-Module-Long-Description=\
  • HTTP Servers - If your cluster is running proxy servers with HTTP acceptors, this tab displays information about the HTTP servers, the number of connections across each server, total connections and graphs of response codes, errors and requests over time for a selected service.
  • \
  • Executors - If your cluster is configured to run the Executor Service, this tab displays information about the number of tasks completed, in-progress and rejected.
  • \
  • gRPC Proxies - If your cluster is configured to run gRPC Proxies, this tab displays information regarding the gRPC requests and responses. Note: This currently only works when connected via JMX.
  • \ +
  • Health - If your cluster supports the Health Check API, this tab displays information regarding the status of all health endpoints.
  • \
  • Coherence*Web - If your cluster is configured for Coherence*Web, this tab displays information about the number applications deployed, the number of HTTP sessions being stored as well as other information regarding session reaping. (**)
  • \
  • Federation - If your cluster is configured with Federated Caching, this tab displays information about each federated service. If you select a service, on the next data refresh you will see detailed outbound/inbound federation traffic information for each node of the service as well as graphs of that information. (**)
  • \
  • Persistence - If your cluster is configured with Persistence, this tab displays information about each service configured with Persistence. Graphs showing active space used and any additional latencies incurred are also showed.
  • \ @@ -49,11 +50,11 @@ OpenIDE-Module-Long-Description=\
  • JCache - If your cluster is being used to store JCache caches, this tab displays JCache "Management" and "Statistics" MBean information regarding the configured caches.
  • \
  • HotCache - If your cluster contains HotCache node(s), then this tab lists the running HotCache instances. If you select an instance, on the next data refresh the console will display statistics and graphs for the operations performed. You may click on tabs and cache-ops to see further fine-grained information. (**)
  • \
    \ - Please see https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.0/manage/using-jmx-manage-oracle-coherence.html for more information on enabling JMX on your \ + Please see https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.2206/manage/using-jmx-manage-oracle-coherence.html for more information on enabling JMX on your \ Coherence cluster. \

    Note: This is a developer tool for viewing information about a single cluster at a time. For more time based enterprise monitoring you can enable Coherence metrics and use Grafana Dashboards or use a tool such as Oracle's Enterprise Manager \ for enterprise level monitoring, management and alerting. \ -

    Note: Items marked with ** will only show when connected to a commercial edition of Coherence. +

    Note: Items marked with ** will only show when connected to a commercial edition of Coherence where the feature is utilized. OpenIDE-Module-Short-Description=A Developer plug-in for providing high level information and statistics about a running Coherence Cluster. # Messages and labels that need to be internationalized From c57da47c0a78412bd978105c4f6ae6dc8efb00f8 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 5 Jul 2022 09:18:16 +0800 Subject: [PATCH 21/27] Minor updates Signed-off-by: Tim Middleton --- .../visualvm/panel/CoherenceHotCachePanel.java | 17 ++++++++--------- .../visualvm/panel/CoherenceHttpProxyPanel.java | 9 ++++----- .../panel/CoherenceHttpSessionPanel.java | 5 ++--- .../visualvm/panel/CoherenceMachinePanel.java | 3 +-- .../visualvm/panel/CoherenceProxyPanel.java | 5 ++--- .../visualvm/panel/CoherenceServicePanel.java | 11 +++++------ .../visualvm/panel/CoherenceTopicPanel.java | 5 ++--- 7 files changed, 24 insertions(+), 31 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java index 3a59674..9633fe1 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHotCachePanel.java @@ -40,7 +40,6 @@ import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.ListSelectionModel; -import javax.swing.JLabel; import javax.swing.SwingConstants; import javax.swing.event.ListSelectionEvent; @@ -408,42 +407,42 @@ public void updateRowSelection() /** * The graph of Execution Time Per Operation statistics. */ - private SimpleXYChartSupport m_hotcacheGraph1; + private transient SimpleXYChartSupport m_hotcacheGraph1; /** * The graph of Execution Time Per Transaction Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph2; + private transient SimpleXYChartSupport m_hotcacheGraph2; /** * The graph of Number Of Invocations Per Operation Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph3; + private transient SimpleXYChartSupport m_hotcacheGraph3; /** * The graph of Last ExecutionTime Per Operation Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph4; + private transient SimpleXYChartSupport m_hotcacheGraph4; /** * The graph of Last Operation ReplicationLag Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph5; + private transient SimpleXYChartSupport m_hotcacheGraph5; /** * The graph of Operation ReplicationLag Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph6; + private transient SimpleXYChartSupport m_hotcacheGraph6; /** * The graph of Number Of Operations Per Transaction Statistics. */ - private SimpleXYChartSupport m_hotcacheGraph7; + private transient SimpleXYChartSupport m_hotcacheGraph7; /** * The graph of PerCacheOperation Statistics */ - private SimpleXYChartSupport m_hotcacheGraph8; + private transient SimpleXYChartSupport m_hotcacheGraph8; /** * The tabbed panel. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java index 6d02e02..5610399 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpProxyPanel.java @@ -36,7 +36,6 @@ import com.oracle.coherence.plugin.visualvm.VisualVMModel; import com.oracle.coherence.plugin.visualvm.panel.util.ExportableJTable; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -490,22 +489,22 @@ public void updateData() /** * The graph of request time. */ - private SimpleXYChartSupport m_requestTimeGraph; + private transient SimpleXYChartSupport m_requestTimeGraph; /** * The graph of average requests per second time. */ - private SimpleXYChartSupport m_requestsPerSecondGraph; + private transient SimpleXYChartSupport m_requestsPerSecondGraph; /** * The graph of requests and errors */ - private SimpleXYChartSupport m_requestsGraph; + private transient SimpleXYChartSupport m_requestsGraph; /** * The graphs of response codes */ - private SimpleXYChartSupport m_responseGraph; + private transient SimpleXYChartSupport m_responseGraph; /** * Last error count. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java index baed760..59574a0 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHttpSessionPanel.java @@ -42,7 +42,6 @@ import java.util.Map; import java.util.Set; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -251,12 +250,12 @@ public void updateData() /** * The graph of session counts. */ - private final SimpleXYChartSupport f_sessionCountGraph; + private transient final SimpleXYChartSupport f_sessionCountGraph; /** * The graph of overflow session counts. */ - private final SimpleXYChartSupport f_reapDurationGraph; + private transient final SimpleXYChartSupport f_reapDurationGraph; /** * the {@link ExportableJTable} to use to display data. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java index 9bd7e8d..25855cc 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMachinePanel.java @@ -41,7 +41,6 @@ import java.util.List; import java.util.Map.Entry; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -208,7 +207,7 @@ public void updateData() /** * The graph of machine load averages. */ - private final SimpleXYChartSupport f_machineGraph; + private transient final SimpleXYChartSupport f_machineGraph; /** * The machine statistics data retrieved from the {@link VisualVMModel}. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java index 464be07..55f39f2 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceProxyPanel.java @@ -43,7 +43,6 @@ import java.util.Map.Entry; import javax.swing.JCheckBox; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -248,12 +247,12 @@ public void updateData() /** * The graph of proxy server connections. */ - private final SimpleXYChartSupport f_proxyGraph; + private final transient SimpleXYChartSupport f_proxyGraph; /** * The graph of proxy server stats. */ - private final SimpleXYChartSupport f_proxyStatsGraph; + private final transient SimpleXYChartSupport f_proxyStatsGraph; /** * The proxy statistics data retrieved from the {@link VisualVMModel}. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java index 63ae46d..0fc410b 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java @@ -49,7 +49,6 @@ import java.util.Set; import java.util.logging.Logger; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JOptionPane; import javax.swing.JScrollPane; @@ -677,27 +676,27 @@ public void updateRowSelection() /** * The graph of thread utilization percent. */ - private SimpleXYChartSupport m_threadUtilGraph = null; + private transient SimpleXYChartSupport m_threadUtilGraph = null; /** * The graph of task average duration. */ - private SimpleXYChartSupport m_taskAverageGraph = null; + private transient SimpleXYChartSupport m_taskAverageGraph = null; /** * The graph of task backlog. */ - private SimpleXYChartSupport m_taskBacklogGraph = null; + private transient SimpleXYChartSupport m_taskBacklogGraph = null; /** * The graph of request average. */ - private SimpleXYChartSupport m_requestAverageGraph = null; + private transient SimpleXYChartSupport m_requestAverageGraph = null; /** * The graph of service partitions. */ - private SimpleXYChartSupport m_servicePartitionsGraph = null; + private transient SimpleXYChartSupport m_servicePartitionsGraph = null; /** * The row selection listener. diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java index b65b1dd..e1ae668 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceTopicPanel.java @@ -40,7 +40,6 @@ import com.oracle.coherence.plugin.visualvm.tablemodel.model.Data; import com.oracle.coherence.plugin.visualvm.tablemodel.model.TopicData; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -223,12 +222,12 @@ public void updateGUI() /** * The graph of unconsumed messages. */ - private final SimpleXYChartSupport f_unconsumedGraph; + private final transient SimpleXYChartSupport f_unconsumedGraph; /** * The graph of topics rates. */ - private final SimpleXYChartSupport f_topicsRatesGraph; + private final transient SimpleXYChartSupport f_topicsRatesGraph; /** * the {@link ExportableJTable} to use to display data. From 7194b90c45879866c4c8d50bb39706053d6cca28 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 5 Jul 2022 09:25:34 +0800 Subject: [PATCH 22/27] Minor updates Signed-off-by: Tim Middleton --- .../plugin/visualvm/panel/CoherenceHealthPanel.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java index 2d73ed2..e077153 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceHealthPanel.java @@ -29,12 +29,14 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; + import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; + import com.oracle.coherence.plugin.visualvm.Localization; import com.oracle.coherence.plugin.visualvm.VisualVMModel; import com.oracle.coherence.plugin.visualvm.helper.RenderHelper; @@ -44,8 +46,7 @@ import com.oracle.coherence.plugin.visualvm.tablemodel.model.HealthData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.HealthSummaryData; import com.oracle.coherence.plugin.visualvm.tablemodel.model.Pair; -import javax.swing.JCheckBox; -import javax.swing.JLabel; + import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; @@ -83,7 +84,7 @@ public CoherenceHealthPanel(VisualVMModel model) pnlHeader.setLayout(new FlowLayout()); pnlHeader.setOpaque(false); - f_txtTotalHealthChecks = getTextField(10, JTextField.RIGHT); + f_txtTotalHealthChecks = getTextField(10, SwingConstants.RIGHT); pnlHeader.add(getLocalizedLabel("LBL_total_health_checks", f_txtTotalHealthChecks)); pnlHeader.add(f_txtTotalHealthChecks); @@ -134,12 +135,12 @@ public void updateGUI() @Override public void updateData() { - List> m_healthData = f_model.getData(VisualVMModel.DataType.HEALTH); + List> healthData = f_model.getData(VisualVMModel.DataType.HEALTH); SortedMap mapData = new TreeMap<>(); // summarise the data by name and sub-type - m_healthData.forEach(v -> + healthData.forEach(v -> { HealthData.HealthKey key = (HealthData.HealthKey) v.getKey(); HealthData value = (HealthData) v.getValue(); From f6799792afa0204103d0b70595a3d4cd5ec45748 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Jul 2022 12:33:11 +0800 Subject: [PATCH 23/27] Fix issue with active persistence mode Signed-off-by: Tim Middleton --- .../plugin/visualvm/tablemodel/model/AbstractData.java | 8 ++++---- .../plugin/visualvm/tablemodel/model/CacheData.java | 3 ++- .../plugin/visualvm/tablemodel/model/PersistenceData.java | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/AbstractData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/AbstractData.java index cc0d0f1..66c69d0 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/AbstractData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/AbstractData.java @@ -320,10 +320,10 @@ public static String[] getDomainAndService(String sRawServiceName) */ protected String getFirstMemberOfArray(JsonNode nodeJson, String sAttributeName) { - JsonNode partitionsVulnerableNode = nodeJson.get(sAttributeName); - return partitionsVulnerableNode == null && partitionsVulnerableNode.isArray() - ? 0 + "" - : partitionsVulnerableNode.get(0).asText(); + JsonNode jsonNode = nodeJson.get(sAttributeName); + return jsonNode == null || !jsonNode.isArray() + ? (0 + "") + : jsonNode.get(0).asText(); } /** diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/CacheData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/CacheData.java index 251c0a6..d0ee290 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/CacheData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/CacheData.java @@ -399,8 +399,9 @@ private Data getData(VisualVMModel model, String sServiceName, JsonNode cacheDet : nCacheSize / nMembers); data.setColumn(UNIT_CALCULATOR, sUnitCalculator); + JsonNode units = cacheDetails.get("units"); long cMemoryUsageBytes = Long.parseLong(getFirstMemberOfArray(cacheDetails, "unitFactor")) - * cacheDetails.get("units").asLong(); + * (units == null ? 0 : units.asLong()); data.setColumn(MEMORY_USAGE_BYTES, cMemoryUsageBytes); data.setColumn(MEMORY_USAGE_MB, (int) (cMemoryUsageBytes / 1024 / 1024)); diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java index c99aeb5..dce4bb8 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/tablemodel/model/PersistenceData.java @@ -435,7 +435,7 @@ public static String getMBeanName(String sServiceName) */ public static boolean isActivePersistence(String sPersistenceMode) { - return sPersistenceMode != null && sPersistenceMode.startsWith("active-"); + return sPersistenceMode != null && sPersistenceMode.startsWith("active"); } // ----- constants ------------------------------------------------------ From 13ec89e171b920828307e6a550df06fb53234dc8 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 12 Jul 2022 11:40:32 +0800 Subject: [PATCH 24/27] Fixup display of error when node is gone --- .../visualvm/panel/CoherenceMemberPanel.java | 15 +++++++++++++-- .../visualvm/panel/CoherenceServicePanel.java | 18 ++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java index 4f7c2f3..25e1ad6 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceMemberPanel.java @@ -346,7 +346,7 @@ public void actionPerformed(ActionEvent e) } catch (Exception ee) { - showMessageDialog("Error running reportEnvironment for Node " + nNodeId, ee.getMessage(), JOptionPane.ERROR_MESSAGE); + showMessageDialog("Error running reportEnvironment for Node " + nNodeId, getSanitizedMessage(ee), JOptionPane.ERROR_MESSAGE); } } } @@ -501,7 +501,7 @@ public void actionPerformed(ActionEvent e) } catch (Exception ee) { - showMessageDialog("Error running reportNodeState for Node " + nNodeId, ee.getMessage(), JOptionPane.ERROR_MESSAGE); + showMessageDialog("Error running reportNodeState for Node " + nNodeId, getSanitizedMessage(ee), JOptionPane.ERROR_MESSAGE); } } } @@ -630,6 +630,17 @@ public String getMenuItem() } } + /** + * Return a sanitized message to make common errors more meaningful. + * @param e {@link Exception} to get message from + * @return final message + */ + private String getSanitizedMessage(Exception e) + { + String sError = e.getMessage(); + return sError.contains("name cannot be null") ? "Node no longer available." : sError; + } + // ----- constants ------------------------------------------------------ private static final long serialVersionUID = -7612569043492412546L; diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java index 0fc410b..748407b 100755 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/panel/CoherenceServicePanel.java @@ -437,7 +437,6 @@ public String getMenuItem() public void actionPerformed(ActionEvent e) { int nRow = getSelectedRow(); - int nCol = getSelectedColumn(); String sService = null; String sRawService = null; @@ -496,17 +495,28 @@ else if (f_nOption == SHOW_PARTITION_STATS) throw new RuntimeException("Invalid option " + f_nOption); } - showMessageDialog(Localization.getLocalText("LBL_details_service", new String[] { sRawService}), + showMessageDialog(Localization.getLocalText("LBL_details_service", sRawService), sResult, JOptionPane.INFORMATION_MESSAGE); } catch (Exception ee) { - showMessageDialog(Localization.getLocalText("ERR_cannot_run", new String[] { sRawService }), - ee.getMessage(), JOptionPane.ERROR_MESSAGE); + showMessageDialog(Localization.getLocalText("ERR_cannot_run", sRawService), + getSanitizedMessage(ee), JOptionPane.ERROR_MESSAGE); } } } + /** + * Return a sanitized message to make common errors more meaningful. + * @param e {@link Exception} to get message from + * @return final message + */ + private String getSanitizedMessage(Exception e) + { + String sError = e.getMessage(); + return sError.contains("name cannot be null") ? "Node no longer available or operation not valid for service type." : sError; + } + /** * Format a long value * From 933060d6b4f5506ea45d6a94035af1a9411d51b2 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Mon, 18 Jul 2022 08:20:45 +0800 Subject: [PATCH 25/27] Add health image Signed-off-by: Tim Middleton --- assets/coherence-visualvm-health.png | Bin 0 -> 154987 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/coherence-visualvm-health.png diff --git a/assets/coherence-visualvm-health.png b/assets/coherence-visualvm-health.png new file mode 100644 index 0000000000000000000000000000000000000000..4f742cddf1889969f0037e56e26ade230c4ae3f4 GIT binary patch literal 154987 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�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 literal 0 HcmV?d00001 From 90ec8c704268dbad3d9511ae81dc23600b35819d Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 19 Jul 2022 12:58:29 +0800 Subject: [PATCH 26/27] minor NPE protection Signed-off-by: Tim Middleton --- .../com/oracle/coherence/plugin/visualvm/helper/JMXUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXUtils.java b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXUtils.java index 25d86b1..0767f2a 100644 --- a/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXUtils.java +++ b/coherence-visualvm-plugin/src/main/java/com/oracle/coherence/plugin/visualvm/helper/JMXUtils.java @@ -137,7 +137,7 @@ public static String getAttributeValueAsString(AttributeList listAttr, String sN for (int i = 0; i < listAttr.size() ; i++) { javax.management.Attribute attr = (javax.management.Attribute) listAttr.get(i); - if (attr.getName().equals(sName)) + if (sName.equals(attr.getName())) { return (attr.getValue() == null ? "" : attr.getValue().toString()); } @@ -159,7 +159,7 @@ public static Object getAttributeValue(AttributeList listAttr, String sName) for (int i = 0; i < listAttr.size() ; i++) { javax.management.Attribute attr = (javax.management.Attribute) listAttr.get(i); - if (attr.getName().equals(sName)) + if (sName.equals(attr.getName())) { return attr.getValue(); } From 184c0619f341a28dbd972f75cfc0e9aae6a8af6b Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Tue, 19 Jul 2022 14:25:27 +0800 Subject: [PATCH 27/27] Update sheilds Signed-off-by: Tim Middleton --- README.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.adoc b/README.adoc index 93c70f3..0cdd6ca 100644 --- a/README.adoc +++ b/README.adoc @@ -27,6 +27,8 @@ questions. image::https://oracle.github.io/coherence/assets/images/logo-red.png[Coherence CE] image:https://github.com/oracle/coherence-visualvm/workflows/Java%20CI%20-%20Released%20versions/badge.svg[CI Build] +image:https://img.shields.io/github/v/release/oracle/coherence-visualvm[Release] +image:https://sonarcloud.io/api/project_badges/measure?project=oracle_coherence-visualvm&metric=alert_status[Sonarcloud] The Coherence-VisualVM Plugin (the Plugin) provides management and monitoring of a single Coherence cluster using the VisualVM management utility.