diff --git a/.gitignore b/.gitignore index e009c51037..82f0757511 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,6 @@ core/tests/org.openjdk.jmc.flightrecorder.rules.jdk.test/baseline/Generated_JfrR # Ignore configuration directories generated by spotbugs **/configuration/spotbugs + +# Ignore vendor specific agent instructions +CLAUDE.md diff --git a/application/org.openjdk.jmc.docs/html/GUID-0352E76D-96F5-4EDA-A8DE-88B9E18635B9.htm b/application/org.openjdk.jmc.docs/html/GUID-0352E76D-96F5-4EDA-A8DE-88B9E18635B9.htm index 25cd6720df..c6032f2b78 100644 --- a/application/org.openjdk.jmc.docs/html/GUID-0352E76D-96F5-4EDA-A8DE-88B9E18635B9.htm +++ b/application/org.openjdk.jmc.docs/html/GUID-0352E76D-96F5-4EDA-A8DE-88B9E18635B9.htm @@ -1,7 +1,7 @@ @@ -24,7 +24,7 @@ - + @@ -39,7 +39,7 @@
- + Next
Next
@@ -115,7 +115,7 @@

Using the

- + Next
Next
diff --git a/application/org.openjdk.jmc.docs/html/GUID-63742D06-CF58-47F2-9CF2-08C0DB9F09F1.htm b/application/org.openjdk.jmc.docs/html/GUID-63742D06-CF58-47F2-9CF2-08C0DB9F09F1.htm index 674e9889c3..02f3f74086 100644 --- a/application/org.openjdk.jmc.docs/html/GUID-63742D06-CF58-47F2-9CF2-08C0DB9F09F1.htm +++ b/application/org.openjdk.jmc.docs/html/GUID-63742D06-CF58-47F2-9CF2-08C0DB9F09F1.htm @@ -1,7 +1,7 @@ @@ -23,7 +23,7 @@ - + @@ -33,7 +33,7 @@
- + Previous
Previous
@@ -72,7 +72,7 @@

Using the

- + Previous
Previous
diff --git a/application/org.openjdk.jmc.docs/html/SecurityPage.htm b/application/org.openjdk.jmc.docs/html/SecurityPage.htm new file mode 100644 index 0000000000..239738446d --- /dev/null +++ b/application/org.openjdk.jmc.docs/html/SecurityPage.htm @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + +Using the Security Page + + + + + + + + + + + + + + + + + + + + + +
+ +Previous
+Previous +
+
+ +Next
+Next +
+
+ +
+

Using the Security Page

+

The security page is designed to display information related to certificates used. It mainly displays the information derived from X509Certificate events.

+

There is one table which shows information related to the cryptographic algorithm. For example: Signature Algorithm, Key type, Key length, Certificate Valid from, Certificate expiring on, Certificate Id and Issuer details.

+

We have also added a new column Crypto Remark which will help customer to identify the action/attention item. Action and attention items are decided based on the JDK guidelines for Crypto usages.

+
Examples of Action and attention items:
  • Action Required. The Certificate has expired before 1,822 days. It should be replaced.
  • +
  • Attention Needed. SHA-1 signature. It should be updated to use SHA-256 or SHA-512.
  • +
+
+

Certificates with no issues are marked with an OK status in the Crypto Remark column.

+

There is one chart where we are displaying the occurrences of X509Certificate events with respect to the timeline.

+
+ +
+ + + + + + + + + + + +

+
+ +Previous
+Previous +
+
+ +Next
+Next +
+
+ + + + diff --git a/application/org.openjdk.jmc.docs/html/toc.htm b/application/org.openjdk.jmc.docs/html/toc.htm index afb7e67a60..e39810f23d 100644 --- a/application/org.openjdk.jmc.docs/html/toc.htm +++ b/application/org.openjdk.jmc.docs/html/toc.htm @@ -1,7 +1,7 @@ @@ -134,6 +134,7 @@

Using the Socket I/O Page
  • Using the Method Profiling Page
  • Using the Exceptions and Errors Page
  • +
  • Using the Security Page
  • Using the Thread Dumps Page
  • diff --git a/application/org.openjdk.jmc.docs/toc.xml b/application/org.openjdk.jmc.docs/toc.xml index b545f57ff6..a71239f5e1 100644 --- a/application/org.openjdk.jmc.docs/toc.xml +++ b/application/org.openjdk.jmc.docs/toc.xml @@ -1,6 +1,6 @@ Couldnt send discovery message from /127.0.0.1: java.net.BindException: Can't assign requested address - // D> --> Exception during lookup: java.util.concurrent.ExecutionException: - // org.jolokia.service.discovery.MulticastUtil$CouldntSendDiscoveryPacketException: - // Can't send discovery UDP packet from /127.0.0.1: Can't assign requested address' + // Multicast discovery does not work on macOS due to network stack limitations + // 'D> --> Couldnt send discovery message from /127.0.0.1: + // java.net.BindException: Can't assign requested address + // D> --> Exception during lookup: java.util.concurrent.ExecutionException: + // org.jolokia.service.discovery.MulticastUtil$CouldntSendDiscoveryPacketException: + // Can't send discovery UDP packet from /127.0.0.1: Can't assign requested + // address' // We get test coverage on both Linux and Windows return; @@ -229,11 +226,7 @@ public boolean shouldRunDiscovery() { @Override public JolokiaContext getJolokiaContext() { - StaticConfiguration configuration = new StaticConfiguration(ConfigKey.AGENT_ID, "jolokiatest"); - JolokiaServiceManager serviceManager = JolokiaServiceManagerFactory.createJolokiaServiceManager(configuration, - new StdoutLogHandler(true), new AllowAllRestrictor(), - logHandler -> new TreeSet(Arrays.asList(ServerDetector.FALLBACK))); - return serviceManager.start(); + return JmcJolokiaContext.proxyJolokiaContext(); } @Override diff --git a/application/uitests/org.openjdk.jmc.console.uitest/src/test/java/org/openjdk/jmc/console/uitest/DiagnosticCommandsTabTest.java b/application/uitests/org.openjdk.jmc.console.uitest/src/test/java/org/openjdk/jmc/console/uitest/DiagnosticCommandsTabTest.java index e004ff9bc3..8e0ba03ff4 100644 --- a/application/uitests/org.openjdk.jmc.console.uitest/src/test/java/org/openjdk/jmc/console/uitest/DiagnosticCommandsTabTest.java +++ b/application/uitests/org.openjdk.jmc.console.uitest/src/test/java/org/openjdk/jmc/console/uitest/DiagnosticCommandsTabTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -186,10 +186,14 @@ public void testOutputUpdated() { @Test public void testStartFlightRecording() { commandTable.select("JFR.start"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, "name"); params.enterText(""); executeButton.click(); - sleep(500); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); String result = resultTabFolder.getText(); resultTabFolder.closeAll(); @@ -203,14 +207,18 @@ public void testStartFlightRecording() { String recordingIdentifier = matcher.group(1); commandTable.select("JFR.check"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, recording_parameter_name); params.enterText(recordingIdentifier); executeButton.click(); - sleep(500); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); result = resultTabFolder.getText(); resultTabFolder.closeAll(); - String expectedOutput = "Recording \\d+:.*name=" + recordingIdentifier + ".* \\(running\\).*"; + String expectedOutput = "Recording " + recordingIdentifier + ":.*\\(running\\).*"; Assert.assertTrue( "Output from JFR.check diagnostic command" + " is not matching expected pattern. Actual output was: '" @@ -218,12 +226,16 @@ public void testStartFlightRecording() { patternMatcher(result, expectedOutput)); commandTable.select("JFR.stop"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, recording_parameter_name); params.enterText(recordingIdentifier); executeButton.click(); - sleep(500); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); result = resultTabFolder.getText(); - expectedOutput = "Stopped [recording ]*[\"Recording-]*" + recordingIdentifier + ".*"; + expectedOutput = "Stopped [recording ]*.*"; Assert.assertTrue( "Output from JFR.stop diagnostic command" + " is not matching expected pattern. Actual output was: '" + result + "'. Expected output was: '" + expectedOutput + '\'', @@ -234,19 +246,39 @@ public void testStartFlightRecording() { public void testStartNamedFlightRecording() { String recordingName = getRandomRecordingName(); commandTable.select("JFR.start"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); + } params.select(true, "name"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.enterText(recordingName); executeButton.click(); - sleep(500); - String result = resultTabFolder.getText(); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); + String startResult = resultTabFolder.getText(); resultTabFolder.closeAll(); + Assert.assertTrue( + "JFR.start output doesn't indicate recording started with name '" + recordingName + + "'. Actual output: '" + startResult + "'", + patternMatcher(startResult, "Started recording") && startResult.contains(recordingName)); commandTable.select("JFR.check"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, "name"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.enterText(recordingName); executeButton.click(); - sleep(500); - result = resultTabFolder.getText(); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); + String result = resultTabFolder.getText(); resultTabFolder.closeAll(); String expectedOutput = "Recording [\\d]+:.*" + recordingName + ".*"; Assert.assertTrue("Output from JFR.check (with name) diagnostic command" @@ -269,10 +301,18 @@ public void testStartNamedFlightRecording() { commandTable.select("JFR.dump"); commandTable.select("JFR.check"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, recording_parameter_name); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.enterText(recordingId); executeButton.click(); - sleep(500); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); result = resultTabFolder.getText(); resultTabFolder.closeAll(); Assert.assertTrue("Output from JFR.check (with recording id) diagnostic command" @@ -281,10 +321,18 @@ public void testStartNamedFlightRecording() { // stop recording commandTable.select("JFR.stop"); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.select(true, recording_parameter_name); + MCJemmyBase.waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(500); + } params.enterText(recordingId); executeButton.click(); - sleep(500); + sleep(MCJemmyBase.isOSX() ? 2000 : 500); result = resultTabFolder.getText(); expectedOutput = "Stopped [recording ]*\"?" + ((ConnectionHelper.is9u0EAorLater(TEST_CONNECTION)) ? recordingName : recordingId) + ".*"; diff --git a/application/uitests/org.openjdk.jmc.flightrecorder.uitest/src/test/java/org/openjdk/jmc/flightrecorder/uitest/JfrThreadsPageLegacyTest.java b/application/uitests/org.openjdk.jmc.flightrecorder.uitest/src/test/java/org/openjdk/jmc/flightrecorder/uitest/JfrThreadsPageLegacyTest.java index d7650b24ce..a32acbc25d 100644 --- a/application/uitests/org.openjdk.jmc.flightrecorder.uitest/src/test/java/org/openjdk/jmc/flightrecorder/uitest/JfrThreadsPageLegacyTest.java +++ b/application/uitests/org.openjdk.jmc.flightrecorder.uitest/src/test/java/org/openjdk/jmc/flightrecorder/uitest/JfrThreadsPageLegacyTest.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019, 2025, Red Hat Inc. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Red Hat Inc. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -93,6 +93,7 @@ public void testMenuItemEnablement() { public void testHideAllThreads() { final int numSelection = 7; final int numThreads = threadsTable.getItemCount(); + System.out.println("[testHideAllThreads] numThreads=" + numThreads + ", numSelection=" + numSelection); Assert.assertTrue(numThreads > 0 && numThreads >= numSelection); Assert.assertTrue(chartCanvas.isContextMenuItemEnabled(HIDE_THREAD)); Assert.assertFalse(chartCanvas.isContextMenuItemEnabled(RESET_CHART)); @@ -102,12 +103,17 @@ public void testHideAllThreads() { // Hide all the threads from the chart for (int i = 0; i < numSelection; i++) { + System.out.println("[testHideAllThreads] Hiding thread " + (i + 1) + " of " + numSelection); chartCanvas.clickContextMenuItem(HIDE_THREAD); } // Once all threads are hidden from the chart, the hide thread menu item will be disabled - Assert.assertFalse(chartCanvas.isContextMenuItemEnabled(HIDE_THREAD)); - Assert.assertTrue(chartCanvas.isContextMenuItemEnabled(RESET_CHART)); + boolean hideEnabled = chartCanvas.isContextMenuItemEnabled(HIDE_THREAD); + boolean resetEnabled = chartCanvas.isContextMenuItemEnabled(RESET_CHART); + System.out.println("[testHideAllThreads] After hiding: HIDE_THREAD enabled=" + hideEnabled + + ", RESET_CHART enabled=" + resetEnabled); + Assert.assertFalse("HIDE_THREAD should be disabled after hiding all selected threads", hideEnabled); + Assert.assertTrue("RESET_CHART should be enabled after hiding threads", resetEnabled); chartCanvas.clickContextMenuItem(RESET_CHART); diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/base/wrappers/MCJemmyBase.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/base/wrappers/MCJemmyBase.java index b7134f03af..b6a396a035 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/base/wrappers/MCJemmyBase.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/base/wrappers/MCJemmyBase.java @@ -584,7 +584,16 @@ public void run() { } }; Display.getDefault().syncExec(fetcher); - return (fetcher.getOutput() == null) ? false : fetcher.getOutput(); + boolean result = (fetcher.getOutput() == null) ? false : fetcher.getOutput(); + // Dismiss the context menu by pressing Escape to prevent interference + // with subsequent context menu operations, especially on macOS + control.keyboard().pushKey(KeyboardButtons.ESCAPE); + waitForIdle(); + if (isOSX()) { + sleep(500); + } + System.out.println("[MCJemmyBase] isContextMenuItemEnabled(\"" + menuItemText + "\") = " + result); + return result; } /** diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JfrWizard.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JfrWizard.java index 65126c9118..fda61d0235 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JfrWizard.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JfrWizard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -84,7 +84,7 @@ public JfrWizard() { /** * Get a wrapper for the flight recording wizard - * + * * @param wizardTitle * the title of the wizard shell */ @@ -92,6 +92,16 @@ public JfrWizard(String wizardTitle) { wizardDialog = new MCDialog(wizardTitle); } + /** + * Get a wrapper for the flight recording wizard using an already found dialog + * + * @param dialog + * the already found dialog + */ + public JfrWizard(MCDialog dialog) { + wizardDialog = dialog; + } + /** * @return the eventSettingsTree */ diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JvmBrowser.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JvmBrowser.java index d400f3d2d3..f66f641f16 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JvmBrowser.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/JvmBrowser.java @@ -172,14 +172,12 @@ public void createConnection( String connectionName = null; String[] finalPath = null; if (itemExists(path)) { // if the path specified already exists then it's a folder - getTree().select(path); - getTree().contextChoose(ACTION_NEW_CONNECTION_TEXT); + selectAndContextChoose(path, ACTION_NEW_CONNECTION_TEXT); finalPath = Arrays.copyOf(path, path.length + 1); // we need to save the name of the folder path finalPath[finalPath.length - 1] = getDefaultConnectionName(host, port); // with auto generated name } else if (path.length > 1) { // since the path doesn't exist, we have been specified a specific name String[] subPath = Arrays.copyOf(path, path.length - 1); - getTree().select(subPath); - getTree().contextChoose(ACTION_NEW_CONNECTION_TEXT); + selectAndContextChoose(subPath, ACTION_NEW_CONNECTION_TEXT); finalPath = path; connectionName = path[path.length - 1]; } else { @@ -268,8 +266,7 @@ private String getDefaultConnectionName(String host, String port) { public void createFolder(String ... path) { if (path.length > 1) { String[] subPath = Arrays.copyOf(path, path.length - 1); - getTree().select(subPath); - getTree().contextChoose(ACTION_NEW_FOLDER_TEXT); + selectAndContextChoose(subPath, ACTION_NEW_FOLDER_TEXT); } else { getToolBar().clickToolItem(ACTION_NEW_FOLDER_TOOLTIP); } @@ -321,8 +318,10 @@ public void deleteItem(String ... path) { delete.clickButton(MCButton.Labels.YES); waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); + } Assert.assertFalse("Failed deleting", itemExists(path)); - // Mac needs time to recover UI state after deletion before next operation if (MCJemmyBase.isOSX()) { sleep(500); } @@ -467,8 +466,22 @@ public MCDialog doubleClickRecording(String name) { * @return a {@link MCDialog} */ public MCDialog doubleClickRecording(String name, String ... path) { - getTree().selectAndClick(2, createRecordingPath(name, path)); - return MCDialog.getByAnyDialogTitle(false, DUMP_RECORDING_WIZARD_PAGE_TITLE); + int maxAttempts = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < maxAttempts; attempt++) { + getTree().selectAndClick(2, createRecordingPath(name, path)); + waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); + } + MCDialog dialog = MCDialog.getByAnyDialogTitle(true, DUMP_RECORDING_WIZARD_PAGE_TITLE); + if (dialog != null) { + System.out.println("[JvmBrowser] doubleClickRecording: dialog found on attempt " + (attempt + 1)); + return dialog; + } + System.out.println("[JvmBrowser] doubleClickRecording: dialog not found on attempt " + (attempt + 1)); + sleep(1000); + } + return MCDialog.getByAnyDialogTitle(true, DUMP_RECORDING_WIZARD_PAGE_TITLE); } /** @@ -496,12 +509,20 @@ public MCDialog dumpRecording(String name, String ... path) { } private MCDialog doDumpRecording(String actionName, String ... path) { - selectContextOption(actionName, path); - waitForIdle(); - if (MCJemmyBase.isOSX()) { + int maxAttempts = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < maxAttempts; attempt++) { + selectContextOption(actionName, path); + waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); + } + MCDialog dialog = MCDialog.getByAnyDialogTitle(true, DUMP_RECORDING_WIZARD_PAGE_TITLE); + if (dialog != null) { + return dialog; + } sleep(1000); } - return MCDialog.getByAnyDialogTitle(false, DUMP_RECORDING_WIZARD_PAGE_TITLE); + return MCDialog.getByAnyDialogTitle(true, DUMP_RECORDING_WIZARD_PAGE_TITLE); } /** @@ -525,7 +546,23 @@ public JfrWizard editRecording(String name) { * @return a {@link JfrWizard} */ public JfrWizard editRecording(String name, String ... path) { - selectContextOption(ACTION_EDIT_RECORDING_LABEL, createRecordingPath(name, path)); + int maxAttempts = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < maxAttempts; attempt++) { + selectContextOption(ACTION_EDIT_RECORDING_LABEL, createRecordingPath(name, path)); + waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); + } + MCDialog dialog = MCDialog.getByAnyDialogTitle(true, JfrWizard.EDIT_RECORDING_WIZARD_PAGE_TITLE); + if (dialog != null) { + System.out.println("[JvmBrowser] editRecording: dialog found on attempt " + (attempt + 1)); + return new JfrWizard(dialog); + } + System.out.println( + "[JvmBrowser] editRecording: dialog not found on attempt " + (attempt + 1) + ", retrying..."); + sleep(1000); + } + System.out.println("[JvmBrowser] editRecording: falling back to direct constructor"); return new JfrWizard(JfrWizard.EDIT_RECORDING_WIZARD_PAGE_TITLE); } @@ -634,8 +671,22 @@ public void editConnection( String name, String host, String port, String user, String serverPasswd, String mcPasswd, Boolean save, String ... path) { MCMenu.ensureJvmBrowserVisible(); - getTree().select(path); - getTree().contextChoose(ACTION_EDIT_TEXT); + int retries = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + getTree().select(path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(ACTION_EDIT_TEXT); + break; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } MCDialog properties = new MCDialog(DIALOG_CONNECTION_PROPERTIES_TITLE); if (host != null) { properties.enterText(ConnectionWizardPage.HOSTNAME_FIELD_NAME, host); @@ -691,8 +742,22 @@ public void openPersistedJMXData() { */ public void openPersistedJMXData(String ... path) { MCMenu.ensureJvmBrowserVisible(); - selectAction(TREE_ITEM_CONSOLE, path); - getTree().contextChoose(ACTION_OPEN_PERSISTED_JMX_DATA); + int retries = MCJemmyBase.isOSX() ? 5 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + selectAction(TREE_ITEM_CONSOLE, path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(ACTION_OPEN_PERSISTED_JMX_DATA); + break; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } Assert.assertTrue("Unable to find console editor \"Persisted JMX Data\"", MCJemmyBase.waitForSubstringMatchedEditor("Persisted JMX Data")); } @@ -708,8 +773,22 @@ public void openPersistedJMXData(String ... path) { public void renameFolder(String newName, String ... path) { String[] finalPath = Arrays.copyOf(path, path.length); finalPath[path.length - 1] = newName; - getTree().select(path); - getTree().contextChoose(ACTION_EDIT_TEXT); + int retries = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + getTree().select(path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(ACTION_EDIT_TEXT); + break; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } MCDialog rename = new MCDialog(DIALOG_FOLDER_PROPERTIES_TITLE); rename.replaceText(path[path.length - 1], newName); rename.closeWithButton(MCButton.Labels.OK); @@ -728,12 +807,22 @@ public void renameFolder(String newName, String ... path) { */ public void selectContextOption(String option, String ... path) { MCMenu.ensureJvmBrowserVisible(); - getTree().select(path); - waitForIdle(); - // Mac needs significantly more time for UI to stabilize after operations - int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; - sleep(delay); - getTree().contextChoose(option); + int retries = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + getTree().select(path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(option); + return; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } } /** @@ -779,8 +868,22 @@ public JfrWizard startFlightRecordingWizard(String ... path) { */ public JfrWizard startFlightRecordingWizard(boolean enableCommercialFeatures, String ... path) { MCMenu.ensureJvmBrowserVisible(); - selectAction(TREE_ITEM_FLIGHTRECORDER, path); - getTree().contextChoose(ACTION_START_FLIGHTRECORDER_LABEL); + int retries = MCJemmyBase.isOSX() ? 5 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + selectAction(TREE_ITEM_FLIGHTRECORDER, path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(ACTION_START_FLIGHTRECORDER_LABEL); + break; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } if (enableCommercialFeatures) { MCDialog dialog = new MCDialog(COMMERCIAL_FEATURES_QUESTION_TITLE); dialog.closeWithButton(Labels.YES); @@ -908,34 +1011,50 @@ private static void setTextByName(org.eclipse.swt.widgets.Composite parent, Stri public void connect(boolean valid, String ... path) { MCMenu.ensureJvmBrowserVisible(); String connectionName = path[path.length - 1]; - int retries = MCJemmyBase.isOSX() ? 5 : 1; - for (int attempt = 0; attempt < retries; attempt++) { - selectAction(TREE_ITEM_CONSOLE, path); - waitForIdle(); - if (MCJemmyBase.isOSX()) { - sleep(1000); - } - try { - getTree().contextChoose(ACTION_START_CONSOLE_LABEL); - break; - } catch (RuntimeException e) { - if (attempt == retries - 1) { - throw e; + int connectAttempts = MCJemmyBase.isOSX() ? 3 : 1; + for (int connectAttempt = 0; connectAttempt < connectAttempts; connectAttempt++) { + int retries = MCJemmyBase.isOSX() ? 5 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + selectAction(TREE_ITEM_CONSOLE, path); + waitForIdle(); + if (MCJemmyBase.isOSX()) { + sleep(1000); } - sleep(1000); - } - } - if (valid) { - if (!ConnectionHelper.is7u40orLater(connectionName)) { try { - MCDialog dialog = new MCDialog(TOO_OLD_JVM_TITLE); - dialog.closeWithButton(MCButton.Labels.OK); - } catch (TimeoutExpiredException tee) { - Assert.fail("JVM Too Old warning did not show."); + getTree().contextChoose(ACTION_START_CONSOLE_LABEL); + break; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); } } - Assert.assertTrue("Could not find JMX Console for connection \"" + connectionName + "\"", - waitForSubstringMatchedEditor(connectionName)); + if (valid) { + MCDialog errorDialog = MCDialog.getByAnyDialogTitle(false, false, "Problem", "Error", "Connection"); + if (errorDialog != null) { + errorDialog.clickButton(Labels.OK); + waitForIdle(); + } + if (!ConnectionHelper.is7u40orLater(connectionName)) { + try { + MCDialog dialog = new MCDialog(TOO_OLD_JVM_TITLE); + dialog.closeWithButton(MCButton.Labels.OK); + } catch (TimeoutExpiredException tee) { + Assert.fail("JVM Too Old warning did not show."); + } + } + long waitTime = (connectAttempt < connectAttempts - 1) ? 10000 : 30000; + boolean found = waitForSubstringMatchedEditor(waitTime, connectionName); + if (found) { + return; + } + if (connectAttempt >= connectAttempts - 1) { + Assert.fail("Could not find JMX Console for connection \"" + connectionName + "\""); + } + } else { + return; + } } } @@ -1021,6 +1140,25 @@ private String[] createRecordingPath(String recordingName, String ... path) { return completePath.toArray(new String[completePath.size()]); } + private void selectAndContextChoose(String[] path, String menuItem) { + int retries = MCJemmyBase.isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + getTree().select(path); + waitForIdle(); + int delay = MCJemmyBase.isOSX() ? 500 : MCJemmyBase.BETWEEN_KEYSTROKES_SLEEP; + sleep(delay); + try { + getTree().contextChoose(menuItem); + return; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } + } + private void selectAction(String action, String ... path) { String[] actionPath = Arrays.copyOf(path, path.length + 1); actionPath[path.length] = action; diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCChartCanvas.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCChartCanvas.java index c6a4ab4038..472ebd4851 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCChartCanvas.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCChartCanvas.java @@ -105,13 +105,33 @@ public static MCChartCanvas getChartCanvas() { */ @SuppressWarnings("unchecked") public void clickContextMenuItem(String menuItemText) { - focusMc(); - StringPopupOwner contextMenu = control.as(StringPopupOwner.class); - contextMenu.setPolicy(StringComparePolicy.SUBSTRING); - contextMenu.push(getRelativeClickPoint(), new String[] {menuItemText}); - waitForIdle(); - if (isOSX()) { - sleep(500); + int maxAttempts = isOSX() ? 3 : 1; + for (int attempt = 0; attempt < maxAttempts; attempt++) { + try { + focusMc(); + if (isOSX()) { + sleep(300); + } + StringPopupOwner contextMenu = control.as(StringPopupOwner.class); + contextMenu.setPolicy(StringComparePolicy.SUBSTRING); + contextMenu.push(getRelativeClickPoint(), new String[] {menuItemText}); + waitForIdle(); + if (isOSX()) { + sleep(500); + } + System.out.println("[MCChartCanvas] clickContextMenuItem(\"" + menuItemText + + "\") succeeded on attempt " + (attempt + 1)); + return; + } catch (RuntimeException e) { + System.out.println("[MCChartCanvas] clickContextMenuItem(\"" + menuItemText + "\") failed on attempt " + + (attempt + 1) + ": " + e.getMessage()); + if (attempt == maxAttempts - 1) { + throw e; + } + // Press Escape to clear any stale menu state + control.keyboard().pushKey(KeyboardButtons.ESCAPE); + sleep(500); + } } } diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCDialog.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCDialog.java index 0584dd51b3..8ab844204c 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCDialog.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -82,7 +82,7 @@ public MCDialog(String dialogTitle) { * @return a matching {@link MCDialog} or {@code null} if not found */ public static MCDialog getByAnyDialogTitle(boolean exactMatching, boolean waitForIdleUi, String ... dialogTitles) { - int maxRetries = 10; + int maxRetries = waitForIdleUi ? 20 : 10; MCDialog result = null; while (result == null && maxRetries > 0) { for (Wrap thisShell : getVisible(Shells.SHELLS.lookup(Shell.class), waitForIdleUi, @@ -96,6 +96,9 @@ public static MCDialog getByAnyDialogTitle(boolean exactMatching, boolean waitFo } } maxRetries--; + if (result == null && waitForIdleUi) { + sleep(200); + } } return result; } diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTabFolder.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTabFolder.java index cf9e413e41..465710cef9 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTabFolder.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTabFolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -220,12 +220,35 @@ public void run() { */ @SuppressWarnings("unchecked") public void closeAll() { - ensureAlive(); - click(); - StringPopupOwner popupMenu = control.as(StringPopupOwner.class); - Wrap item = control.as(Parent.class, CTabItem.class).lookup(CTabItem.class).wrap(); - popupMenu.setPolicy(policy); - popupMenu.push(item.getClickPoint(), "Close All"); + int retries = isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + try { + ensureAlive(); + } catch (RuntimeException | AssertionError e) { + if (attempt == retries - 1) { + return; + } + sleep(500); + continue; + } + click(); + waitForIdle(); + if (isOSX()) { + sleep(500); + } + try { + StringPopupOwner popupMenu = control.as(StringPopupOwner.class); + Wrap item = control.as(Parent.class, CTabItem.class).lookup(CTabItem.class).wrap(); + popupMenu.setPolicy(policy); + popupMenu.push(item.getClickPoint(), "Close All"); + return; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + sleep(1000); + } + } } /** diff --git a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTree.java b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTree.java index 5487089bd0..b098519fd2 100644 --- a/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTree.java +++ b/application/uitests/org.openjdk.jmc.test.jemmy/src/main/java/org/openjdk/jmc/test/jemmy/misc/wrappers/MCTree.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. - * + * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The contents of this file are subject to the terms of either the Universal Permissive License @@ -10,17 +10,17 @@ * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions * and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other materials provided with * the distribution. - * + * * 3. Neither the name of the copyright holder nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR @@ -102,7 +102,7 @@ public static List getAll(Wrap shell) { /** * Returns all currently visible {@link MCTree} (in the main Mission Control Window) - * + * * @return a {@link List} of {@link MCTree} that were found */ public static List getAll() { @@ -133,7 +133,7 @@ public static MCTree getFirstVisibleByName(String name) { /** * Finds the first visible Tree (in the main Mission Control Window) - * + * * @return a {@link MCTree} */ public static MCTree getFirst() { @@ -142,7 +142,7 @@ public static MCTree getFirst() { /** * Finds the tree that contains a matching item - * + * * @param item * the desired item text * @return a {@link MCTree} @@ -212,7 +212,7 @@ static MCTree getFirst(Wrap shell) { /** * Finds the first visible tree in the SWT hierarchy for the given shell - * + * * @param shell * the shell from where to start searching for the widget * @param waitForIdleUi @@ -380,7 +380,7 @@ public void selectAndClick(int times, String ... path) { /** * Returns a list of the currently selected tree item's text values - * + * * @return a {@link List} of {@link String} */ public List getSelectedItemTexts() { @@ -432,7 +432,7 @@ public void run() { /** * Get the currently selected item's direct child item texts - * + * * @return a {@link List} of {@link String} */ public List getSelectedItemChildrenTexts() { @@ -779,10 +779,22 @@ private boolean makeVisibleInTreeByScrolling(ItemWrap itemWrap, Intege */ @SuppressWarnings("unchecked") public void contextChoose(boolean desiredState, String ... choice) { - scrollbarSafeSelection(); - StringPopupSelectableOwner spo = control.as(StringPopupSelectableOwner.class); - spo.setPolicy(policy); - spo.push(desiredState, getRelativeClickPoint(getSelectedItem()), choice); + int retries = isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + scrollbarSafeSelection(); + try { + StringPopupSelectableOwner spo = control.as(StringPopupSelectableOwner.class); + spo.setPolicy(policy); + spo.push(desiredState, getRelativeClickPoint(getSelectedItem()), choice); + return; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + control.keyboard().pushKey(KeyboardButtons.ESCAPE); + sleep(500); + } + } } /** @@ -809,15 +821,28 @@ public boolean getContextOptionState(String ... choice) { */ @SuppressWarnings("unchecked") public void contextChoose(String ... choice) { - scrollbarSafeSelection(); - Wrap selectedWrap = getSelectedItem(); - // workaround (needed on Mac OS X) to make sure that a yellow popup won't disturb during context clicking - if (isOSX()) { - selectedWrap.mouse().click(); + int retries = isOSX() ? 3 : 1; + for (int attempt = 0; attempt < retries; attempt++) { + scrollbarSafeSelection(); + Wrap selectedWrap = getSelectedItem(); + if (isOSX()) { + selectedWrap.mouse().click(); + waitForIdle(); + sleep(200); + } + try { + StringPopupOwner spo = control.as(StringPopupOwner.class); + spo.setPolicy(policy); + spo.push(getRelativeClickPoint(selectedWrap), choice); + return; + } catch (RuntimeException e) { + if (attempt == retries - 1) { + throw e; + } + control.keyboard().pushKey(KeyboardButtons.ESCAPE); + sleep(500); + } } - StringPopupOwner spo = control.as(StringPopupOwner.class); - spo.setPolicy(policy); - spo.push(getRelativeClickPoint(selectedWrap), choice); } /** @@ -891,33 +916,128 @@ public void run() { /** * Sets the text of the currently selected tree item - * + * * @param text * the text to set */ public void enterText(String text) { - int retries = isOSX() ? 3 : 1; - for (int attempt = 0; attempt < retries; attempt++) { - waitForIdle(); - if (isOSX()) { - sleep(500); - } - try { - contextChoose("Change Value"); - break; - } catch (RuntimeException e) { - if (attempt == retries - 1) { - throw e; + if (isOSX()) { + enterTextMacOS(text); + } else { + enterTextDefault(text); + } + } + + private void enterTextDefault(String text) { + waitForIdle(); + contextChoose("Change Value"); + waitForIdle(); + typeText(text); + control.keyboard().pushKey(KeyboardButtons.ENTER); + } + + private void enterTextMacOS(String text) { + // On macOS, cell editor activation via context menu is unreliable due to + // focus-stealing events. Set the attribute value directly on the model instead. + if (setAttributeValueDirectly(text)) { + return; + } + // Fallback: try context menu + typeText + waitForIdle(); + sleep(500); + try { + contextChoose("Change Value"); + } catch (RuntimeException e) { + sleep(1000); + contextChoose("Change Value"); + } + waitForIdle(); + sleep(500); + typeText(text); + control.keyboard().pushKey(KeyboardButtons.ENTER); + } + + private boolean setAttributeValueDirectly(String text) { + Fetcher fetcher = new Fetcher() { + @Override + public void run() { + Tree tree = getWrap().getControl(); + TreeItem[] selection = tree.getSelection(); + if (selection == null || selection.length == 0) { + setOutput(false); + return; } - sleep(1000); + Object data = selection[0].getData(); + if (data instanceof org.openjdk.jmc.rjmx.services.IAttribute) { + org.openjdk.jmc.rjmx.services.IAttribute attr = (org.openjdk.jmc.rjmx.services.IAttribute) data; + String typeName = attr.getInfo().getType(); + // Skip array types - ArrayLengthCellEditor creates child model objects + // that direct setValue() cannot replicate + if (typeName != null && typeName.startsWith("[")) { + setOutput(false); + return; + } + Object typedValue = convertToType(text, typeName); + attr.setValue(typedValue); + // Refresh the viewer to reflect the change + Object viewerObj = tree.getData("org.eclipse.jface.viewer"); + if (viewerObj == null) { + viewerObj = tree.getData("viewer"); + } + if (viewerObj instanceof org.eclipse.jface.viewers.ColumnViewer) { + org.eclipse.jface.viewers.ColumnViewer cv = (org.eclipse.jface.viewers.ColumnViewer) viewerObj; + cv.refresh(); + } + setOutput(true); + return; + } + setOutput(false); } + }; + Display.getDefault().syncExec(fetcher); + return Boolean.TRUE.equals(fetcher.getOutput()); + } + + private static Object convertToType(String text, String typeName) { + if (typeName == null) { + return text; + } + try { + switch (typeName) { + case "long": + case "java.lang.Long": + return Long.parseLong(text); + case "int": + case "java.lang.Integer": + return Integer.parseInt(text); + case "short": + case "java.lang.Short": + return Short.parseShort(text); + case "byte": + case "java.lang.Byte": + return Byte.parseByte(text); + case "double": + case "java.lang.Double": + return Double.parseDouble(text); + case "float": + case "java.lang.Float": + return Float.parseFloat(text); + case "boolean": + case "java.lang.Boolean": + return Boolean.parseBoolean(text); + default: + return text; + } + } catch (NumberFormatException e) { + return text; } + } + + private void typeText(String text) { for (int i = 0; i < text.length(); i++) { control.keyboard().typeChar(text.charAt(i)); sleep(BETWEEN_KEYSTROKES_SLEEP); } - // make sure that the text entered is "submitted" before moving focus elsewhere (necessary for Mac) - control.keyboard().pushKey(KeyboardButtons.ENTER); } /** diff --git a/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/item/Aggregators.java b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/item/Aggregators.java index 9237b79f65..2b0ab18927 100644 --- a/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/item/Aggregators.java +++ b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/item/Aggregators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -1044,6 +1044,10 @@ public SetConsumer newItemConsumer(IType itemType) { } }; + public static IAggregator distinctAttribute(String typeId, IAttribute attribute) { + return filter(distinctAsString(attribute, "~ "), ItemFilters.type(typeId)); //$NON-NLS-1$ + } + public static IAggregator distinctAsString(String typeId, IAttribute attribute) { return filter(distinctAsString(attribute, ", "), ItemFilters.type(typeId)); //$NON-NLS-1$ } diff --git a/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/messages/internal/Messages.java b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/messages/internal/Messages.java index e4d329a696..c967fc3308 100644 --- a/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/messages/internal/Messages.java +++ b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/messages/internal/Messages.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2022, 2025, Datadog, Inc. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Datadog, Inc. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -85,6 +85,21 @@ public class Messages { public static final String TimestampKind_SINCE_1970_MSG = "TimestampKind_SINCE_1970_MSG"; //$NON-NLS-1$ public static final String TypeHandling_MESSAGE_SIZE = "TypeHandling_MESSAGE_SIZE"; //$NON-NLS-1$ public static final String UnitLookup_TIMESTAMP_OUT_OF_RANGE = "UnitLookup_TIMESTAMP_OUT_OF_RANGE"; //$NON-NLS-1$ + public static final String Crypto_ACTION = "Crypto_ACTION"; //$NON-NLS-1$ + public static final String Crypto_ATTENTION = "Crypto_ATTENTION"; //$NON-NLS-1$ + public static final String Crypto_OK = "Crypto_OK"; //$NON-NLS-1$ + public static final String Crypto_OK_DESC = "Crypto_OK_DESC"; //$NON-NLS-1$ + public static final String Crypto_SHA1 = "Crypto_SHA1"; //$NON-NLS-1$ + public static final String Crypto_MD2 = "Crypto_MD2"; //$NON-NLS-1$ + public static final String Crypto_MD5 = "Crypto_MD5"; //$NON-NLS-1$ + public static final String Crypto_RSA_INSUFFICIENT_KEY_SIZE = "Crypto_RSA_INSUFFICIENT_KEY_SIZE"; //$NON-NLS-1$ + public static final String Crypto_RSA_KEY_SIZE_1024 = "Crypto_RSA_KEY_SIZE_1024"; //$NON-NLS-1$ + public static final String Crypto_RSA_KEY_SIZE_LESS_2048 = "Crypto_RSA_KEY_SIZE_LESS_2048"; //$NON-NLS-1$ + public static final String Crypto_DSA_INSUFFICIENT_KEY_SIZE = "Crypto_DSA_INSUFFICIENT_KEY_SIZE"; //$NON-NLS-1$ + public static final String Crypto_DSA_KEY_SIZE_LESS_2048 = "Crypto_DSA_KEY_SIZE_LESS_2048"; //$NON-NLS-1$ + public static final String Crypto_EC_INSUFFICIENT_KEY_SIZE = "Crypto_EC_INSUFFICIENT_KEY_SIZE"; //$NON-NLS-1$ + public static final String Crypto_Certificate_Expiring = "Crypto_Certificate_Expiring"; //$NON-NLS-1$ + public static final String Crypto_Certificate_Expired = "Crypto_Certificate_Expired"; //$NON-NLS-1$ private Messages() { } diff --git a/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/security/CryptoUtil.java b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/security/CryptoUtil.java new file mode 100644 index 0000000000..ee4d0a3e39 --- /dev/null +++ b/core/org.openjdk.jmc.common/src/main/java/org/openjdk/jmc/common/security/CryptoUtil.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at https://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.common.security; + +import java.text.MessageFormat; +import org.openjdk.jmc.common.messages.internal.Messages; +import org.openjdk.jmc.common.unit.IQuantity; +import org.openjdk.jmc.common.unit.UnitLookup; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * This utility class will be utilized to decide whether Action/Attention is required related to + * cryptography algorithm used based on different conditions. + */ +public class CryptoUtil { + + private static final String ACTION = Messages.getString(Messages.Crypto_ACTION); + private static final String ATTENTION = Messages.getString(Messages.Crypto_ATTENTION); + private static final String OK = Messages.getString(Messages.Crypto_OK); + private static final int RSA_MINIMUM_KEY_SIZE = 1024; + private static final int RSA_RECOMMENDED_KEY_SIZE = 2048; + private static final int DSA_MINIMUM_KEY_SIZE = 1024; + private static final int DSA_RECOMMENDED_KEY_SIZE = 2048; + private static final int EC_MINIMUM_KEY_SIZE = 224; + public static long CERTIFICATE_EXPIRY_THRESHOLD = 90; + + public static String getCryptoRemark( + String signatureAlgorithm, String keyType, Long keyLength, IQuantity expiryDate) { + + String remark = ""; + + if (signatureAlgorithm.contains("SHA1")) { + remark = ATTENTION.concat(Messages.getString(Messages.Crypto_SHA1)); + } else if (signatureAlgorithm.contains("MD2")) { + remark = ACTION.concat(Messages.getString(Messages.Crypto_MD2)); + } else if (signatureAlgorithm.contains("MD5")) { + remark = ACTION.concat(Messages.getString(Messages.Crypto_MD5)); + } + + if (keyType.contains("RSA")) { + if (keyLength < RSA_MINIMUM_KEY_SIZE) { + remark = ACTION + .concat(MessageFormat.format(Messages.getString(Messages.Crypto_RSA_INSUFFICIENT_KEY_SIZE), + keyLength, RSA_MINIMUM_KEY_SIZE)) + .concat(remark); + } else if (keyLength == RSA_MINIMUM_KEY_SIZE) { + remark = ACTION.concat(Messages.getString(Messages.Crypto_RSA_KEY_SIZE_1024)).concat(remark); + } else if (keyLength < RSA_RECOMMENDED_KEY_SIZE) { + remark = remark.concat(ATTENTION).concat(MessageFormat + .format(Messages.getString(Messages.Crypto_RSA_KEY_SIZE_LESS_2048), RSA_RECOMMENDED_KEY_SIZE)); + } + } + + if (keyType.contains("DSA")) { + if (keyLength < DSA_MINIMUM_KEY_SIZE) { + remark = ACTION + .concat(MessageFormat.format(Messages.getString(Messages.Crypto_DSA_INSUFFICIENT_KEY_SIZE), + keyLength, DSA_MINIMUM_KEY_SIZE)) + .concat(remark); + } else if (keyLength < DSA_RECOMMENDED_KEY_SIZE) { + remark = remark.concat(ATTENTION).concat(MessageFormat + .format(Messages.getString(Messages.Crypto_DSA_KEY_SIZE_LESS_2048), DSA_RECOMMENDED_KEY_SIZE)); + } + } + + if (keyType.contains("EC")) { + if (keyLength < EC_MINIMUM_KEY_SIZE) { + remark = ACTION + .concat(MessageFormat.format(Messages.getString(Messages.Crypto_EC_INSUFFICIENT_KEY_SIZE), + keyLength, EC_MINIMUM_KEY_SIZE)) + .concat(remark); + } + } + + if (expiryDate != null) { + + IQuantity duration = expiryDate.subtract(UnitLookup.EPOCH_MS.quantity(System.currentTimeMillis())); + long expiringInDays = TimeUnit.MILLISECONDS.toDays(duration.longValue()); + if ((expiringInDays > 0) && (expiringInDays < CERTIFICATE_EXPIRY_THRESHOLD)) { + remark = remark.concat(ATTENTION).concat( + MessageFormat.format(Messages.getString(Messages.Crypto_Certificate_Expiring), expiringInDays)); + } else if (expiringInDays < 0) { + remark = ACTION.concat(MessageFormat.format(Messages.getString(Messages.Crypto_Certificate_Expired), + (expiringInDays * -1))).concat(remark); + } + + } + + if (remark.equals("")) { + remark = OK; + } + + return remark; + } + + public static String getCryptoIcon( + String signatureAlgorithm, String keyType, Long keyLength, IQuantity expiryDate) { + + String icon = ""; + + if (signatureAlgorithm.contains("SHA1")) { + icon = "ATTENTION"; + } else if (signatureAlgorithm.contains("MD2")) { + icon = "ACTION"; + } else if (signatureAlgorithm.contains("MD5")) { + icon = "ACTION"; + } + + if (keyType.contains("RSA")) { + if (keyLength < RSA_MINIMUM_KEY_SIZE) { + icon = "ACTION".concat(icon); + } else if (keyLength == RSA_MINIMUM_KEY_SIZE) { + icon = "ACTION".concat(icon); + } else if (keyLength < RSA_RECOMMENDED_KEY_SIZE) { + icon = icon.concat("ATTENTION"); + } + } + + if (keyType.contains("DSA")) { + if (keyLength < DSA_MINIMUM_KEY_SIZE) { + icon = "ACTION".concat(icon); + } else if (keyLength < DSA_RECOMMENDED_KEY_SIZE) { + icon = icon.concat("ATTENTION"); + } + } + + if (keyType.contains("EC")) { + if (keyLength < EC_MINIMUM_KEY_SIZE) { + icon = "ACTION".concat(icon); + } + } + + if (expiryDate != null) { + + IQuantity duration = expiryDate.subtract(UnitLookup.EPOCH_MS.quantity(System.currentTimeMillis())); + long expiringInDays = TimeUnit.MILLISECONDS.toDays(duration.longValue()); + if ((expiringInDays > 0) && (expiringInDays < CERTIFICATE_EXPIRY_THRESHOLD)) { + icon = icon.concat("ATTENTION"); + } else if (expiringInDays < 0) { + icon = "ACTION".concat(icon); + } + + } + + if (icon.equals("")) { + icon = "OK"; + } + + return icon; + } + + public static String getCryptoRuleResult( + String signatureAlgorithm, String keyType, Long keyLength, IQuantity expiryDate, Number certificateId) { + + List> remarks = new ArrayList<>(); + String strCertificateId = ""; + if (!certificateId.equals(0)) { + strCertificateId = "Certificate Id : ".concat(certificateId.toString()).concat(" - "); + } + + if (signatureAlgorithm.contains("SHA1")) { + remarks.add(Map.entry(ATTENTION, strCertificateId.concat(Messages.getString(Messages.Crypto_SHA1)))); + } else if (signatureAlgorithm.contains("MD2")) { + remarks.add(Map.entry(ACTION, strCertificateId.concat(Messages.getString(Messages.Crypto_MD2)))); + } else if (signatureAlgorithm.contains("MD5")) { + remarks.add(Map.entry(ACTION, strCertificateId.concat(Messages.getString(Messages.Crypto_MD5)))); + } + + if (keyType.contains("RSA")) { + if (keyLength < RSA_MINIMUM_KEY_SIZE) { + remarks.add(Map.entry(ACTION, + strCertificateId.concat( + MessageFormat.format(Messages.getString(Messages.Crypto_RSA_INSUFFICIENT_KEY_SIZE), + keyLength, RSA_MINIMUM_KEY_SIZE)))); + } else if (keyLength == RSA_MINIMUM_KEY_SIZE) { + remarks.add(Map.entry(ACTION, + strCertificateId.concat(Messages.getString(Messages.Crypto_RSA_KEY_SIZE_1024)))); + } else if (keyLength < RSA_RECOMMENDED_KEY_SIZE) { + remarks.add(Map.entry(ATTENTION, strCertificateId.concat(MessageFormat.format( + Messages.getString(Messages.Crypto_RSA_KEY_SIZE_LESS_2048), RSA_RECOMMENDED_KEY_SIZE)))); + } + } + + if (keyType.contains("DSA")) { + if (keyLength < DSA_MINIMUM_KEY_SIZE) { + remarks.add(Map.entry(ACTION, + strCertificateId.concat( + MessageFormat.format(Messages.getString(Messages.Crypto_DSA_INSUFFICIENT_KEY_SIZE), + keyLength, DSA_MINIMUM_KEY_SIZE)))); + } else if (keyLength < DSA_RECOMMENDED_KEY_SIZE) { + remarks.add(Map.entry(ATTENTION, strCertificateId.concat(MessageFormat.format( + Messages.getString(Messages.Crypto_DSA_KEY_SIZE_LESS_2048), DSA_RECOMMENDED_KEY_SIZE)))); + } + } + + if (keyType.contains("EC")) { + if (keyLength < EC_MINIMUM_KEY_SIZE) { + remarks.add(Map.entry(ACTION, + strCertificateId.concat( + MessageFormat.format(Messages.getString(Messages.Crypto_EC_INSUFFICIENT_KEY_SIZE), + keyLength, EC_MINIMUM_KEY_SIZE)))); + } + } + + if (expiryDate != null) { + + IQuantity duration = expiryDate.subtract(UnitLookup.EPOCH_MS.quantity(System.currentTimeMillis())); + long expiringInDays = TimeUnit.MILLISECONDS.toDays(duration.longValue()); + if ((expiringInDays > 0) && (expiringInDays < CERTIFICATE_EXPIRY_THRESHOLD)) { + remarks.add(Map.entry(ATTENTION, strCertificateId.concat(MessageFormat + .format(Messages.getString(Messages.Crypto_Certificate_Expiring), expiringInDays)))); + } else if (expiringInDays < 0) { + remarks.add(Map.entry(ACTION, strCertificateId.concat(MessageFormat + .format(Messages.getString(Messages.Crypto_Certificate_Expired), (expiringInDays * -1))))); + } + + } + + if (remarks.isEmpty()) { + remarks.add(Map.entry(OK, Messages.getString(Messages.Crypto_OK_DESC))); + } + + return convertRemarksToFormattedString(remarks); + } + + public static String convertRemarksToFormattedString(List> entries) { + Map> groupedMap = new LinkedHashMap<>(); + for (Map.Entry entry : entries) { + groupedMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()) + .add(entry.getKey().concat(entry.getValue())); + } + + StringBuilder result = new StringBuilder(); + + for (Map.Entry> entry : groupedMap.entrySet()) { + for (String value : entry.getValue()) { + if (value.contains(Messages.getString(Messages.Crypto_OK_DESC))) + continue; + result.append(" • ").append(value).append("\n"); + } + } + return result.toString().trim(); + } + +} diff --git a/core/org.openjdk.jmc.common/src/main/resources/org/openjdk/jmc/common/messages/internal/messages.properties b/core/org.openjdk.jmc.common/src/main/resources/org/openjdk/jmc/common/messages/internal/messages.properties index 90cc56a2cf..43c3ff85ac 100644 --- a/core/org.openjdk.jmc.common/src/main/resources/org/openjdk/jmc/common/messages/internal/messages.properties +++ b/core/org.openjdk.jmc.common/src/main/resources/org/openjdk/jmc/common/messages/internal/messages.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -208,3 +208,20 @@ NameConverter_JVM_ARCH_32BIT=32-bit NameConverter_JVM_ARCH_64BIT=64-bit NameConverter_DEBUG=Debug NameConverter_UNKNOWN_LOCAL_JVM=Local + +Crypto_ACTION=Action Required. +Crypto_ATTENTION=Attention Needed. +Crypto_OK=Ok +Crypto_OK_DESC=Everything is fine. +Crypto_SHA1= SHA-1 signature. It should be updated to use SHA-256 or SHA-512. +Crypto_MD2= MD2 signature. +Crypto_MD5= MD5 signature. +Crypto_RSA_INSUFFICIENT_KEY_SIZE= RSA signature with insufficient key size. RSA key size {0} bits is below minimum {1} bits. +Crypto_RSA_KEY_SIZE_1024= Removed root certificates with 1024-bit keys. +Crypto_RSA_KEY_SIZE_LESS_2048= Use RSA recommended {0} key size. +Crypto_DSA_INSUFFICIENT_KEY_SIZE= DSA signature with insufficient key size. DSA key size {0} bits is below minimum {1} bits. +Crypto_DSA_KEY_SIZE_LESS_2048= Use DSA recommended {0} key size. +Crypto_EC_INSUFFICIENT_KEY_SIZE= EC signature with insufficient key size. EC key size {0} bits is below minimum {1} bits. + +Crypto_Certificate_Expiring= The Certificate is expiring in {0} days. It should be replaced. +Crypto_Certificate_Expired= The Certificate has expired before {0} days. It should be replaced. diff --git a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/general/CryptoSecurityRule.java b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/general/CryptoSecurityRule.java new file mode 100644 index 0000000000..ab360da243 --- /dev/null +++ b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/general/CryptoSecurityRule.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at https://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.flightrecorder.rules.jdk.general; + +import static org.openjdk.jmc.common.unit.UnitLookup.NUMBER; +import static org.openjdk.jmc.common.unit.UnitLookup.NUMBER_UNITY; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.openjdk.jmc.common.item.IItemCollection; +import org.openjdk.jmc.common.security.CryptoUtil; +import org.openjdk.jmc.common.unit.IQuantity; +import org.openjdk.jmc.common.util.IPreferenceValueProvider; +import org.openjdk.jmc.common.util.TypedPreference; +import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes; +import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs; +import org.openjdk.jmc.flightrecorder.rules.IResult; +import org.openjdk.jmc.flightrecorder.rules.IResultValueProvider; +import org.openjdk.jmc.flightrecorder.rules.IRule; +import org.openjdk.jmc.flightrecorder.rules.ResultBuilder; +import org.openjdk.jmc.flightrecorder.rules.Severity; +import org.openjdk.jmc.flightrecorder.rules.TypedResult; +import org.openjdk.jmc.flightrecorder.rules.jdk.messages.internal.Messages; +import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics; +import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit; +import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability; +import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.RequiredEventsBuilder; + +public class CryptoSecurityRule implements IRule { + + private static final String CRYPTO_SECURITY_RESULT_ID = "CryptoSecurityRule"; //$NON-NLS-1$ + + private static final Map REQUIRED_EVENTS = RequiredEventsBuilder.create() + .addEventType(JdkTypeIDs.X509_CERTIFICATE, EventAvailability.AVAILABLE).build(); + + private static final String ACTION = Messages.getString(Messages.Crypto_ACTION); + + private static final String ATTENTION = Messages.getString(Messages.Crypto_ATTENTION); + + public static final TypedPreference CERTIFICATE_EXPIRY_WARNING_THRESHOLD = new TypedPreference<>( + "certificate.expiry.warning.threshold", //$NON-NLS-1$ + Messages.getString(Messages.Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD), + Messages.getString(Messages.Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD_LONG), NUMBER, + NUMBER_UNITY.quantity(90)); + + private static final List> CONFIG_ATTRIBUTES = Arrays + .> asList(CERTIFICATE_EXPIRY_WARNING_THRESHOLD); + + private static final Collection> RESULT_ATTRIBUTES = Arrays + .> asList(TypedResult.SCORE); + + @Override + public String getId() { + return CRYPTO_SECURITY_RESULT_ID; + } + + @Override + public String getTopic() { + return JfrRuleTopics.SECURITY; + } + + @Override + public String getName() { + return Messages.getString(Messages.CryptoSecurity_RULE_NAME); + } + + @Override + public Map getRequiredEvents() { + return REQUIRED_EVENTS; + } + + private IResult getResult( + IItemCollection items, IPreferenceValueProvider valueProvider, IResultValueProvider resultProvider) { + + long certificateExpiryThreshold = valueProvider.getPreferenceValue(CERTIFICATE_EXPIRY_WARNING_THRESHOLD) + .longValue(); + CryptoUtil.CERTIFICATE_EXPIRY_THRESHOLD = certificateExpiryThreshold; + + boolean actionNeeded = (RulesToolkit.findMatches(JdkTypeIDs.X509_CERTIFICATE, items, + JdkAttributes.CRYPTO_REMARK, Messages.getString(Messages.Crypto_ACTION), false) != null); //$NON-NLS-1$ + boolean attentionNeeded = (RulesToolkit.findMatches(JdkTypeIDs.X509_CERTIFICATE, items, + JdkAttributes.CRYPTO_REMARK, Messages.getString(Messages.Crypto_ATTENTION), false) != null); //$NON-NLS-1$ + + String ruleResult = RulesToolkit.findAttribute(JdkTypeIDs.X509_CERTIFICATE, items, + JdkAttributes.CRYPTO_RULE_RESULT); + Result processedResult = processResult(ruleResult); + + ruleResult = processedResult.cleanedAllActions + "\n" + processedResult.cleanedAllAttentions; + + if (actionNeeded) { + if (attentionNeeded) { + return ResultBuilder.createFor(CryptoSecurityRule.this, valueProvider).setSeverity(Severity.WARNING) + .setSummary(Messages.getString(Messages.Crypto_ACTION_ATTENTION_INFO) + + MessageFormat.format(Messages.getString(Messages.Crypto_REFERENCE_INFO), ruleResult)) + .build(); + } else { + return ResultBuilder.createFor(CryptoSecurityRule.this, valueProvider).setSeverity(Severity.WARNING) + .setSummary(Messages.getString(Messages.Crypto_ACTION_INFO) + + MessageFormat.format(Messages.getString(Messages.Crypto_REFERENCE_INFO), ruleResult)) + .build(); + } + } else if (attentionNeeded) { + return ResultBuilder.createFor(CryptoSecurityRule.this, valueProvider).setSeverity(Severity.WARNING) + .setSummary(Messages.getString(Messages.Crypto_ATTENTION_INFO) + + MessageFormat.format(Messages.getString(Messages.Crypto_REFERENCE_INFO), ruleResult)) + .build(); + } else { + return ResultBuilder.createFor(CryptoSecurityRule.this, valueProvider).setSeverity(Severity.OK) + .setSummary("OK").build(); + } + + } + + public static class Result { + public final String allActions; // joined by '~' + public final String allAttentions; // joined by '~' + public final int allActionCount; + public final int allAttentionCount; + public final String cleanedAllActions; // allActions with "Action Required." removed + public final String cleanedAllAttentions;// allAttentions with "Attention Needed." removed + + public Result(String allActions, String allAttentions, int allActionCount, int allAttentionCount, + String cleanedAllActions, String cleanedAllAttentions) { + this.allActions = allActions; + this.allAttentions = allAttentions; + this.allActionCount = allActionCount; + this.allAttentionCount = allAttentionCount; + this.cleanedAllActions = cleanedAllActions; + this.cleanedAllAttentions = cleanedAllAttentions; + } + } + + private Result processResult(String input) { + + if (input == null) + input = ""; + + final String ACTION_BULLET = "🔴 "; + final String ATTENTION_BULLET = "🟠 "; + + String[] tokens = input.split("~", -1); + + List actionFragments = new ArrayList<>(); + List attentionFragments = new ArrayList<>(); + List originalFragments = new ArrayList<>(); + + String bulletChars = "[\\u2022\\u2023\\u2027•\\*]"; + String patternString = "(?s)\\b(?:Action Required\\.|Attention Needed\\.).*?(?=(?:" + bulletChars + ")|$)"; + Pattern fragmentPattern = Pattern.compile(patternString); + + for (String token : tokens) { + if (token == null || token.trim().isEmpty()) + continue; + + Matcher m = fragmentPattern.matcher(token); + while (m.find()) { + String frag = m.group().trim(); + frag = frag.replaceAll("^[\\s\\u2022\\u2023\\u2027•\\*]+", "").trim(); + + if (frag.isEmpty()) + continue; + + boolean isAction = frag.contains(ACTION); + boolean isAttention = frag.contains(ATTENTION); + + if (isAction) { + actionFragments.add(ACTION_BULLET + frag); + } + if (isAttention) { + attentionFragments.add(ATTENTION_BULLET + frag); + } + + String prefix; + if (isAction && isAttention) + prefix = ACTION_BULLET + ATTENTION_BULLET; + else if (isAction) + prefix = ACTION_BULLET; + else if (isAttention) + prefix = ATTENTION_BULLET; + else + prefix = ""; + + String fragClean = frag.replace(ACTION, "").replace(ATTENTION, "").trim(); + + if (!fragClean.isEmpty()) { + originalFragments.add(prefix + fragClean); + } + } + } + + int allActionCount = actionFragments.size(); + int allAttentionCount = attentionFragments.size(); + + // Build titled sections + String allActions = "\nAction Required (" + allActionCount + ")\n\n" + + (actionFragments.isEmpty() ? "" : String.join("\n", actionFragments)) + "\n"; + + String allAttentions = "\nAttention Needed (" + allAttentionCount + ")\n\n" + + (attentionFragments.isEmpty() ? "" : String.join("\n", attentionFragments)) + "\n"; + + // Clean versions + String cleanedAllActions = "\n\nAction Required (" + allActionCount + ")\n\n" + actionFragments.stream() + .map(s -> s.replace(ACTION, "").trim()).filter(s -> !s.isEmpty()).collect(Collectors.joining("\n")) + + "\n"; + + String cleanedAllAttentions = "\nAttention Needed (" + allAttentionCount + ")\n\n" + attentionFragments.stream() + .map(s -> s.replace(ATTENTION, "").trim()).filter(s -> !s.isEmpty()).collect(Collectors.joining("\n")) + + "\n"; + + return new Result(allActions, allAttentions, allActionCount, allAttentionCount, cleanedAllActions, + cleanedAllAttentions); + } + + @Override + public RunnableFuture createEvaluation( + final IItemCollection items, final IPreferenceValueProvider preferenceValueProvider, + final IResultValueProvider dependencyResults) { + FutureTask evaluationTask = new FutureTask<>(new Callable() { + @Override + public IResult call() throws Exception { + return getResult(items, preferenceValueProvider, dependencyResults); + } + }); + return evaluationTask; + } + + @Override + public Collection> getConfigurationAttributes() { + return CONFIG_ATTRIBUTES; + } + + @Override + public Collection> getResults() { + return RESULT_ATTRIBUTES; + } + +} diff --git a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java index 79a61cb894..8a9a82b517 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java +++ b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -325,6 +325,15 @@ public class Messages { public static final String GcFreedRatioRule_RESULT_SHORT_DESCRIPTION = "GcFreedRatioRule_RESULT_SHORT_DESCRIPTION"; //$NON-NLS-1$ public static final String GarbageCollectionInfo_RULE_NAME = "GarbageCollectionInfo_RULE_NAME"; //$NON-NLS-1$ public static final String ZGCAllocationStall_RULE_NAME = "ZGCAllocationStall_RULE_NAME"; //$NON-NLS-1$ + public static final String CryptoSecurity_RULE_NAME = "CryptoSecurity_RULE_NAME"; //$NON-NLS-1$ + public static final String Crypto_ACTION = "Crypto_ACTION"; //$NON-NLS-1$ + public static final String Crypto_ATTENTION = "Crypto_ATTENTION"; //$NON-NLS-1$ + public static final String Crypto_ACTION_INFO = "Crypto_ACTION_INFO"; //$NON-NLS-1$ + public static final String Crypto_ATTENTION_INFO = "Crypto_ATTENTION_INFO"; //$NON-NLS-1$ + public static final String Crypto_ACTION_ATTENTION_INFO = "Crypto_ACTION_ATTENTION_INFO"; //$NON-NLS-1$ + public static final String Crypto_REFERENCE_INFO = "Crypto_REFERENCE_INFO"; //$NON-NLS-1$ + public static final String Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD = "Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD"; //$NON-NLS-1$ + public static final String Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD_LONG = "Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD_LONG"; //$NON-NLS-1$ public static final String ZGCAllocationStallRule_CONFIG_INFO_LIMIT = "ZGCAllocationStallRule_CONFIG_INFO_LIMIT"; //$NON-NLS-1$ public static final String ZGCAllocationStallRule_CONFIG_INFO_LIMIT_LONG = "ZGCAllocationStallRule_CONFIG_INFO_LIMIT_LONG"; //$NON-NLS-1$ public static final String ZGCAllocationStallRule_RATE = "ZGCAllocationStallRule_RATE"; //$NON-NLS-1$ diff --git a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/META-INF/services/org.openjdk.jmc.flightrecorder.rules.IRule b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/META-INF/services/org.openjdk.jmc.flightrecorder.rules.IRule index 68cc1beff5..57e41dea5a 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/META-INF/services/org.openjdk.jmc.flightrecorder.rules.IRule +++ b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/META-INF/services/org.openjdk.jmc.flightrecorder.rules.IRule @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -49,6 +49,7 @@ org.openjdk.jmc.flightrecorder.rules.jdk.exceptions.ErrorRule org.openjdk.jmc.flightrecorder.rules.jdk.exceptions.ExceptionRule org.openjdk.jmc.flightrecorder.rules.jdk.exceptions.FatalErrorRule org.openjdk.jmc.flightrecorder.rules.jdk.general.ClassLeakingRule +org.openjdk.jmc.flightrecorder.rules.jdk.general.CryptoSecurityRule org.openjdk.jmc.flightrecorder.rules.jdk.general.DMSIncidentRule org.openjdk.jmc.flightrecorder.rules.jdk.general.DebugNonSafepointsRule org.openjdk.jmc.flightrecorder.rules.jdk.general.DiscouragedGcOptionsRule diff --git a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties index 2463ae248c..a6678053af 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties +++ b/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -172,6 +172,15 @@ ContextSwitchRuleFactory_RULE_NAME=Context Switches ContextSwitchRuleFactory_TEXT_INFO=The program causes many context switches during the recording. ContextSwitchRuleFactory_TEXT_INFO_LONG=The program context switched a lot and many threads waited on the same monitor. Consider using fewer threads, or decreasing lock contention by other means. ContextSwitchRuleFactory_TEXT_OK=The program did not context switch excessively during the recording. +CryptoSecurity_RULE_NAME=Crypto Security Rule +Crypto_ACTION=Action Required. +Crypto_ATTENTION=Attention Needed. +Crypto_ACTION_INFO=Action Required in specific domains. +Crypto_ATTENTION_INFO=Attention Needed in specific domains. +Crypto_ACTION_ATTENTION_INFO=Observations indicate action and attention are needed in specific domains. +Crypto_REFERENCE_INFO={0} \n Please refer Security screen for more details. +Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD=Certificate expiring in (days) warning +Crypto_CERTIFICATE_EXPIRY_WARNING_THRESHOLD_LONG=Certificate expiring in (days) threshold to trigger warning DMSIncidentRule_AGGR_INCIDENTS_COUNT=DMS Incidents DMSIncidentRule_AGGR_INCIDENTS_COUNT_DESC=The number of DMS incidents DMSIncidentRule_CONFIG_WARNING_LIMIT=DMS incident warning limit diff --git a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/IRule.java b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/IRule.java index 08f101b630..4c0f75bb61 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/IRule.java +++ b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/IRule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * diff --git a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/JfrRuleTopics.java b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/JfrRuleTopics.java index a23b98422f..1c38a7867d 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/JfrRuleTopics.java +++ b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/JfrRuleTopics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -64,5 +64,6 @@ public final class JfrRuleTopics { public static final String THREAD_DUMPS = "thread_dumps"; //$NON-NLS-1$ public static final String THREADS = "threads"; //$NON-NLS-1$ public static final String TLAB = "tlab"; //$NON-NLS-1$ + public static final String SECURITY = "security"; //$NON-NLS-1$ public static final String VM_OPERATIONS = "vm_operations"; //$NON-NLS-1$ } diff --git a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/RulesToolkit.java b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/RulesToolkit.java index 2b5ddc6823..0185bdf2d9 100644 --- a/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/RulesToolkit.java +++ b/core/org.openjdk.jmc.flightrecorder.rules/src/main/java/org/openjdk/jmc/flightrecorder/rules/util/RulesToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -287,6 +287,12 @@ public static String findMatches( ItemFilters.and(ItemFilters.type(typeId), ItemFilters.matches(attribute, regexp)))); } + public static String findAttribute(String typeId, IItemCollection items, IAttribute attribute) { + + return items.getAggregate((IAggregator) Aggregators + .filter(Aggregators.distinctAttribute(typeId, attribute), ItemFilters.and(ItemFilters.type(typeId)))); + } + /** * Gets the value of a certain attribute for a given item * diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java index e19b8ff55b..5e090abf3c 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAggregators.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -524,6 +524,12 @@ public final class JdkAggregators { public static final IAggregator TOP_ADDRESS = Aggregators.min(JdkTypeIDs.NATIVE_LIBRARY, JdkAttributes.TOP_ADDRESS); + public static final IAggregator X509_CERTIFICATE_COUNT = Aggregators + .count(Messages.getString(Messages.AGGR_X509_CERTIFICATES_COUNT), JdkTypeIDs.X509_CERTIFICATE); + + public static final IAggregator IS_CERTIFICATE_ID_QTY = or(JdkTypeIDs.X509_CERTIFICATE, + JdkAttributes.IS_CERTIFICATE_ID_QTY); + public static final IAggregator TENURING_AGE_SIZE = Aggregators.sum( Messages.getString(Messages.AGGR_TENURING_AGE_SIZE), Messages.getString(Messages.AGGR_TENURING_AGE_SIZE_DESC), JdkTypeIDs.TENURING_DISTRIBUTION, diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java index 3e461d2f01..af7b5754fc 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -70,6 +70,7 @@ import org.openjdk.jmc.common.item.IAttribute; import org.openjdk.jmc.common.item.IMemberAccessor; import org.openjdk.jmc.common.item.IType; +import org.openjdk.jmc.common.security.CryptoUtil; import org.openjdk.jmc.common.unit.ContentType; import org.openjdk.jmc.common.unit.IQuantity; import org.openjdk.jmc.common.unit.UnitLookup; @@ -1443,4 +1444,181 @@ public String getMember(U i) { public static final IAttribute RSS_PEAK = new Attribute("peak", //$NON-NLS-1$ Messages.getString(Messages.ATTR_RSS_PEAK), Messages.getString(Messages.ATTR_RSS_PEAK_DESC), MEMORY) { }; + + //Security related attributes + public static final IAttribute SIGNATURE_ALGORITHM = new Attribute("algorithm", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_SIGNATURE_ALGORITHM), + Messages.getString(Messages.ATTR_SIGNATURE_ALGORITHM), PLAIN_TEXT) { + }; + + public static final IAttribute CERTIFICATE_ID = new Attribute("certificateId", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CERTIFICATE_ID), Messages.getString(Messages.ATTR_CERTIFICATE_ID), + RAW_NUMBER) { + }; + + public static final IAttribute CERTIFICATE_ID_QTY = new Attribute("certificateId", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CERTIFICATE_ID), Messages.getString(Messages.ATTR_CERTIFICATE_ID), + NUMBER) { + }; + + public static final IAttribute IS_CERTIFICATE_ID_QTY = Attribute.canonicalize(new Attribute( + "isCertificateIdQty", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CRYPTO_ICON), Messages.getString(Messages.ATTR_CRYPTO_ICON_DESC), FLAG) { + @Override + public IMemberAccessor customAccessor(IType type) { + final IMemberAccessor certificateIdAccessor = type.getAccessor(CERTIFICATE_ID.getKey()); + final IMemberAccessor certificateIdPre9Accessor = type + .getAccessor(CERTIFICATE_ID_QTY.getKey()); + return new IMemberAccessor() { + @Override + public Boolean getMember(U i) { + if (certificateIdAccessor != null) { + return false; + } else if (certificateIdPre9Accessor != null) { + return true; + } + + return false; + } + }; + } + }); + + public static final IAttribute CERTIFICATE_ISSUER = new Attribute("issuer", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CERTIFICATE_ISSUER), Messages.getString(Messages.ATTR_CERTIFICATE_ISSUER), + PLAIN_TEXT) { + }; + + public static final IAttribute KEY_LENGTH = new Attribute("keyLength", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_KEY_LENGTH), Messages.getString(Messages.ATTR_KEY_LENGTH), NUMBER) { + }; + + public static final IAttribute KEY_TYPE = new Attribute("keyType", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_KEY_TYPE), Messages.getString(Messages.ATTR_KEY_TYPE), PLAIN_TEXT) { + }; + + public static final IAttribute SERIAL_NUMBER = new Attribute("serialNumber", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_SERIAL_NUMBER), Messages.getString(Messages.ATTR_SERIAL_NUMBER), + PLAIN_TEXT) { + }; + + public static final IAttribute SUBJECT = new Attribute("subject", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_SUBJECT), Messages.getString(Messages.ATTR_SUBJECT), PLAIN_TEXT) { + }; + + public static final IAttribute VALID_FROM = new Attribute("validFrom", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_VALID_FROM), Messages.getString(Messages.ATTR_VALID_FROM), TIMESTAMP) { + }; + + public static final IAttribute VALID_UNTIL = new Attribute("validUntil", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_VALID_UNTIL), Messages.getString(Messages.ATTR_VALID_UNTIL), TIMESTAMP) { + }; + + public static final IAttribute CRYPTO_REMARK = Attribute.canonicalize(new Attribute("cryptoRemark", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CRYPTO_REMARK), Messages.getString(Messages.ATTR_CRYPTO_REMARK_DESC), + PLAIN_TEXT) { + @Override + public IMemberAccessor customAccessor(IType type) { + final IMemberAccessor signatureAlgorithmAccessor = type + .getAccessor(SIGNATURE_ALGORITHM.getKey()); + final IMemberAccessor keyTypeAccessor = type.getAccessor(KEY_TYPE.getKey()); + final IMemberAccessor keyLengthAccessor = type.getAccessor(KEY_LENGTH.getKey()); + final IMemberAccessor expiryDateAccessor = type.getAccessor(VALID_UNTIL.getKey()); + if ((signatureAlgorithmAccessor == null) || (keyTypeAccessor == null) || (keyLengthAccessor == null)) { + return null; + } + return new IMemberAccessor() { + @Override + public String getMember(U i) { + String signatureAlgorithm = signatureAlgorithmAccessor.getMember(i); + String keyType = keyTypeAccessor.getMember(i); + IQuantity keyLength = keyLengthAccessor.getMember(i); + IQuantity expiryDate = null; + if (expiryDateAccessor != null) { + expiryDate = expiryDateAccessor.getMember(i); + } + return signatureAlgorithm != null && keyType != null && keyLength != null + ? CryptoUtil.getCryptoRemark(signatureAlgorithm, keyType, keyLength.longValue(), expiryDate) + : null; + } + }; + } + }); + + public static final IAttribute CRYPTO_ICON = Attribute.canonicalize(new Attribute("cryptoIcon", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CRYPTO_ICON), Messages.getString(Messages.ATTR_CRYPTO_ICON_DESC), + PLAIN_TEXT) { + @Override + public IMemberAccessor customAccessor(IType type) { + final IMemberAccessor signatureAlgorithmAccessor = type + .getAccessor(SIGNATURE_ALGORITHM.getKey()); + final IMemberAccessor keyTypeAccessor = type.getAccessor(KEY_TYPE.getKey()); + final IMemberAccessor keyLengthAccessor = type.getAccessor(KEY_LENGTH.getKey()); + final IMemberAccessor expiryDateAccessor = type.getAccessor(VALID_UNTIL.getKey()); + if ((signatureAlgorithmAccessor == null) || (keyTypeAccessor == null) || (keyLengthAccessor == null)) { + return null; + } + return new IMemberAccessor() { + @Override + public String getMember(U i) { + String signatureAlgorithm = signatureAlgorithmAccessor.getMember(i); + String keyType = keyTypeAccessor.getMember(i); + IQuantity keyLength = keyLengthAccessor.getMember(i); + IQuantity expiryDate = null; + if (expiryDateAccessor != null) { + expiryDate = expiryDateAccessor.getMember(i); + } + return signatureAlgorithm != null && keyType != null && keyLength != null + ? CryptoUtil.getCryptoIcon(signatureAlgorithm, keyType, keyLength.longValue(), expiryDate) + : null; + } + }; + } + }); + + public static final IAttribute CRYPTO_RULE_RESULT = Attribute + .canonicalize(new Attribute("cryptoRuleResult", //$NON-NLS-1$ + Messages.getString(Messages.ATTR_CRYPTO_ICON), Messages.getString(Messages.ATTR_CRYPTO_ICON_DESC), + PLAIN_TEXT) { + @Override + public IMemberAccessor customAccessor(IType type) { + final IMemberAccessor signatureAlgorithmAccessor = type + .getAccessor(SIGNATURE_ALGORITHM.getKey()); + final IMemberAccessor keyTypeAccessor = type.getAccessor(KEY_TYPE.getKey()); + final IMemberAccessor keyLengthAccessor = type.getAccessor(KEY_LENGTH.getKey()); + final IMemberAccessor expiryDateAccessor = type.getAccessor(VALID_UNTIL.getKey()); + final IMemberAccessor certificateIdAccessor = type.getAccessor(CERTIFICATE_ID.getKey()); + final IMemberAccessor certificateIdPre9Accessor = type + .getAccessor(CERTIFICATE_ID_QTY.getKey()); + if ((signatureAlgorithmAccessor == null) || (keyTypeAccessor == null) + || (keyLengthAccessor == null)) { + return null; + } + return new IMemberAccessor() { + @Override + public String getMember(U i) { + String signatureAlgorithm = signatureAlgorithmAccessor.getMember(i); + String keyType = keyTypeAccessor.getMember(i); + IQuantity keyLength = keyLengthAccessor.getMember(i); + IQuantity expiryDate = null; + if (expiryDateAccessor != null) { + expiryDate = expiryDateAccessor.getMember(i); + } + Number certificateId = 0; + IQuantity qtyCertificate = null; + if (certificateIdAccessor != null) { + certificateId = certificateIdAccessor.getMember(i); + } else if (certificateIdPre9Accessor != null) { + qtyCertificate = certificateIdPre9Accessor.getMember(i); + certificateId = qtyCertificate.numberValue(); + } + return signatureAlgorithm != null && keyType != null && keyLength != null + ? CryptoUtil.getCryptoRuleResult(signatureAlgorithm, keyType, keyLength.longValue(), + expiryDate, certificateId) + : null; + } + }; + } + }); + } diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkFilters.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkFilters.java index cb734b040d..d4b7b48901 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkFilters.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkFilters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -81,6 +81,7 @@ public final class JdkFilters { JdkTypeIDs.ALLOC_OUTSIDE_TLAB); public static final IItemFilter OBJ_ALLOC = ItemFilters.type(JdkTypeIDs.OBJ_ALLOC_SAMPLE); public static final IItemFilter ZGC_ALLOCATION_STALL = ItemFilters.type(JdkTypeIDs.ZGC_ALLOCATION_STALL); + public static final IItemFilter SECURITY = ItemFilters.type(JdkTypeIDs.X509_CERTIFICATE); public static final IItemFilter REFERENCE_STATISTICS = ItemFilters.type(JdkTypeIDs.GC_REFERENCE_STATISTICS); public static final IItemFilter GARBAGE_COLLECTION = ItemFilters.type(JdkTypeIDs.GARBAGE_COLLECTION); public static final IItemFilter OLD_GARBAGE_COLLECTION = ItemFilters diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkTypeIDs.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkTypeIDs.java index 479c138333..6a285935da 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkTypeIDs.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/JdkTypeIDs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -111,6 +111,8 @@ public final class JdkTypeIDs { public static final String ZGC_ALLOCATION_STALL = PREFIX + "ZAllocationStall"; + public static final String X509_CERTIFICATE = PREFIX + "X509Certificate"; + public static final String THROWABLES_STATISTICS = PREFIX + "ExceptionStatistics"; public static final String ERRORS_THROWN = PREFIX + "JavaErrorThrow"; /* diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/messages/internal/Messages.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/messages/internal/Messages.java index 8462a9f245..58f5ccab6f 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/messages/internal/Messages.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/jdk/messages/internal/Messages.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -209,6 +209,7 @@ public class Messages { public static final String AGGR_VM_OPERATION_COUNT_DESC = "AGGR_VM_OPERATION_COUNT_DESC"; //$NON-NLS-1$ public static final String AGGR_VM_OPERATION_DURATION = "AGGR_VM_OPERATION_DURATION"; //$NON-NLS-1$ public static final String AGGR_VM_OPERATION_DURATION_DESC = "AGGR_VM_OPERATION_DURATION_DESC"; //$NON-NLS-1$ + public static final String AGGR_X509_CERTIFICATES_COUNT = "AGGR_X509_CERTIFICATES_COUNT"; //$NON-NLS-1$ public static final String ATTR_ADAPTORS = "ATTR_ADAPTORS"; //$NON-NLS-1$ public static final String ATTR_AGENT_DYNAMIC = "ATTR_AGENT_DYNAMIC"; //$NON-NLS-1$ public static final String ATTR_AGENT_DYNAMIC_DESC = "ATTR_AGENT_DYNAMIC_DESC"; //$NON-NLS-1$; @@ -572,6 +573,19 @@ public class Messages { public static final String ATTR_THREADS_DAEMON_COUNT_DESC = "ATTR_THREADS_DAEMON_COUNT_DESC"; public static final String ATTR_THREADS_PEAK_COUNT = "ATTR_THREADS_PEAK_COUNT"; public static final String ATTR_THREADS_PEAK_COUNT_DESC = "ATTR_THREADS_PEAK_COUNT_DESC"; + public static final String ATTR_SIGNATURE_ALGORITHM = "ATTR_SIGNATURE_ALGORITHM"; //$NON-NLS-1$ + public static final String ATTR_CERTIFICATE_ID = "ATTR_CERTIFICATE_ID"; //$NON-NLS-1$ + public static final String ATTR_CERTIFICATE_ISSUER = "ATTR_CERTIFICATE_ISSUER"; //$NON-NLS-1$ + public static final String ATTR_KEY_LENGTH = "ATTR_KEY_LENGTH"; //$NON-NLS-1$ + public static final String ATTR_KEY_TYPE = "ATTR_KEY_TYPE"; //$NON-NLS-1$ + public static final String ATTR_SERIAL_NUMBER = "ATTR_SERIAL_NUMBER"; //$NON-NLS-1$ + public static final String ATTR_SUBJECT = "ATTR_SUBJECT"; //$NON-NLS-1$ + public static final String ATTR_VALID_FROM = "ATTR_VALID_FROM"; //$NON-NLS-1$ + public static final String ATTR_VALID_UNTIL = "ATTR_VALID_UNTIL"; //$NON-NLS-1$ + public static final String ATTR_CRYPTO_REMARK = "ATTR_CRYPTO_REMARK"; //$NON-NLS-1$ + public static final String ATTR_CRYPTO_REMARK_DESC = "ATTR_CRYPTO_REMARK_DESC"; //$NON-NLS-1$ + public static final String ATTR_CRYPTO_ICON = "ATTR_CRYPTO_ICON"; //$NON-NLS-1$ + public static final String ATTR_CRYPTO_ICON_DESC = "ATTR_CRYPTO_ICON_DESC"; //$NON-NLS-1$ private Messages() { } diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/parser/synthetic/OracleJdkTypeIDsPre11.java b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/parser/synthetic/OracleJdkTypeIDsPre11.java index 90cfe10258..38e39a6494 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/parser/synthetic/OracleJdkTypeIDsPre11.java +++ b/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/parser/synthetic/OracleJdkTypeIDsPre11.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -118,6 +118,8 @@ public final class OracleJdkTypeIDsPre11 { final static String MONITOR_ENTER = JVM_EVENT_ID_ROOT + "java/monitor_enter"; final static String MONITOR_WAIT = JVM_EVENT_ID_ROOT + "java/monitor_wait"; + private final static String X509_CERTIFICATE = JDK_EVENT_ID_ROOT + "java/x509_certificate"; + private final static String METASPACE_OOM = JVM_EVENT_ID_ROOT + "vm/gc/metaspace/out_of_memory"; private final static String CODE_CACHE_FULL = JVM_EVENT_ID_ROOT + "vm/code_cache/full"; @@ -416,6 +418,8 @@ public static String translate(String typeId) { return JdkTypeIDs.RECORDINGS; case GC_G1MMU: return JdkTypeIDs.GC_G1MMU; + case X509_CERTIFICATE: + return JdkTypeIDs.X509_CERTIFICATE; default: return typeId; } diff --git a/core/org.openjdk.jmc.flightrecorder/src/main/resources/org/openjdk/jmc/flightrecorder/jdk/messages/internal/messages.properties b/core/org.openjdk.jmc.flightrecorder/src/main/resources/org/openjdk/jmc/flightrecorder/jdk/messages/internal/messages.properties index 2ff23f625f..47655a2ebb 100644 --- a/core/org.openjdk.jmc.flightrecorder/src/main/resources/org/openjdk/jmc/flightrecorder/jdk/messages/internal/messages.properties +++ b/core/org.openjdk.jmc.flightrecorder/src/main/resources/org/openjdk/jmc/flightrecorder/jdk/messages/internal/messages.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # @@ -379,6 +379,19 @@ ATTR_THREADS_DAEMON_COUNT=Daemon Threads ATTR_THREADS_DAEMON_COUNT_DESC=The number of live daemon threads ATTR_THREADS_PEAK_COUNT=Peak Threads ATTR_THREADS_PEAK_COUNT_DESC=Peak live thread count since JVM start (or since the peak count was reset) +ATTR_SIGNATURE_ALGORITHM=Signature Algorithm +ATTR_CERTIFICATE_ID=Certificate Id +ATTR_CERTIFICATE_ISSUER=Certificate Issuer +ATTR_KEY_LENGTH=Key Length +ATTR_KEY_TYPE=Key Type +ATTR_SERIAL_NUMBER=Serial Number +ATTR_SUBJECT=Subject +ATTR_VALID_FROM=Valid From +ATTR_VALID_UNTIL=Valid Until +ATTR_CRYPTO_REMARK=Crypto Remark +ATTR_CRYPTO_REMARK_DESC=Crypto Remarks based on the signature algorithm, key type and key length +ATTR_CRYPTO_ICON=Status +ATTR_CRYPTO_ICON_DESC=Crypto Icon will be used to show the action / attention needed in specific domain AGGR_MAX_USED_MEMORY=Maximum used memory AGGR_MIN_TOTAL_MEMORY=Available physical memory AGGR_ADDRESSES_COUNT=Addresses @@ -544,3 +557,4 @@ AGGR_VM_OPERATION_DURATION=VM Operation Duration AGGR_VM_OPERATION_DURATION_DESC=The sum of the durations for the selected VM operation events. AGGR_COMPILATIONS_COUNT=Compilations AGGR_COMPILATIONS_COUNT_DESC=The number of compilations +AGGR_X509_CERTIFICATES_COUNT=X509 Certificates \ No newline at end of file diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDecorator.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDecorator.java new file mode 100644 index 0000000000..9ef0a45ad1 --- /dev/null +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDecorator.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at https://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.rjmx.common; + +import java.io.IOException; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServerConnection; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; + +/** + * Simple decorator for inserting JMC specific handling to a server connection + */ +public class ConnectionDecorator implements MBeanServerConnection { + + private final MBeanServerConnection delegate; + + public ConnectionDecorator(final MBeanServerConnection delegate) { + this.delegate = delegate; + } + + public ObjectInstance createMBean(String className, ObjectName name) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, IOException { + return delegate.createMBean(className, name); + } + + public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException, IOException { + return delegate.createMBean(className, name, loaderName); + } + + public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, IOException { + return delegate.createMBean(className, name, params, signature); + } + + public ObjectInstance createMBean( + String className, ObjectName name, ObjectName loaderName, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, + NotCompliantMBeanException, InstanceNotFoundException, IOException { + return delegate.createMBean(className, name, loaderName, params, signature); + } + + public void unregisterMBean(ObjectName name) + throws InstanceNotFoundException, MBeanRegistrationException, IOException { + delegate.unregisterMBean(name); + } + + public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException, IOException { + return delegate.getObjectInstance(name); + } + + public Set queryMBeans(ObjectName name, QueryExp query) throws IOException { + return delegate.queryMBeans(name, query); + } + + public Set queryNames(ObjectName name, QueryExp query) throws IOException { + return delegate.queryNames(name, query); + } + + public boolean isRegistered(ObjectName name) throws IOException { + return delegate.isRegistered(name); + } + + public Integer getMBeanCount() throws IOException { + return delegate.getMBeanCount(); + } + + public Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, + InstanceNotFoundException, ReflectionException, IOException { + return delegate.getAttribute(name, attribute); + } + + public AttributeList getAttributes(ObjectName name, String[] attributes) + throws InstanceNotFoundException, ReflectionException, IOException { + return delegate.getAttributes(name, attributes); + } + + public void setAttribute(ObjectName name, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, + MBeanException, ReflectionException, IOException { + delegate.setAttribute(name, attribute); + } + + public AttributeList setAttributes(ObjectName name, AttributeList attributes) + throws InstanceNotFoundException, ReflectionException, IOException { + return delegate.setAttributes(name, attributes); + } + + public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) + throws InstanceNotFoundException, MBeanException, ReflectionException, IOException { + return delegate.invoke(name, operationName, params, signature); + } + + public String getDefaultDomain() throws IOException { + return delegate.getDefaultDomain(); + } + + public String[] getDomains() throws IOException { + return delegate.getDomains(); + } + + public void addNotificationListener( + ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException { + delegate.addNotificationListener(name, listener, filter, handback); + } + + public void addNotificationListener( + ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException { + delegate.addNotificationListener(name, listener, filter, handback); + } + + public void removeNotificationListener(ObjectName name, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException, IOException { + delegate.removeNotificationListener(name, listener); + } + + public void removeNotificationListener( + ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException { + delegate.removeNotificationListener(name, listener, filter, handback); + } + + public void removeNotificationListener(ObjectName name, NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException, IOException { + delegate.removeNotificationListener(name, listener); + } + + public void removeNotificationListener( + ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException { + delegate.removeNotificationListener(name, listener, filter, handback); + } + + public MBeanInfo getMBeanInfo(ObjectName name) + throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException { + return delegate.getMBeanInfo(name); + } + + public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException, IOException { + return delegate.isInstanceOf(name, className); + } + +} diff --git a/core/pom.xml b/core/pom.xml index f4f78f10b5..20ba671aa7 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -76,6 +76,7 @@ 3.1.1 3.1.0 0.8.10 + 3.6.1 4.13.2 @@ -409,6 +410,24 @@ + + org.codehaus.mojo + build-helper-maven-plugin + ${build.helper.maven.version} + + + prepare-package + + add-source + + + + ${rootDir}/license + + + + + org.apache.maven.plugins maven-source-plugin diff --git a/core/tests/org.openjdk.jmc.flightrecorder.rules.jdk.test/src/main/resources/baseline/JfrRuleBaseline.xml b/core/tests/org.openjdk.jmc.flightrecorder.rules.jdk.test/src/main/resources/baseline/JfrRuleBaseline.xml index 09d4ff0a82..d8dcec3976 100644 --- a/core/tests/org.openjdk.jmc.flightrecorder.rules.jdk.test/src/main/resources/baseline/JfrRuleBaseline.xml +++ b/core/tests/org.openjdk.jmc.flightrecorder.rules.jdk.test/src/main/resources/baseline/JfrRuleBaseline.xml @@ -331,6 +331,10 @@ This recording is only 9.903 s long, consider creating a recording longer than ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -637,6 +641,10 @@ This recording is only 9.903 s long, consider creating a recording longer than ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -915,6 +923,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -1191,6 +1203,10 @@ Events of the following types have truncated stack traces: <ul><li>O ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -1487,6 +1503,10 @@ Events of the following types have truncated stack traces: <ul><li>O ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -1762,6 +1782,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -2035,6 +2059,10 @@ Events of the following types have truncated stack traces: <ul><li>O ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -2362,6 +2390,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -2662,6 +2694,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -2951,6 +2987,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -3240,6 +3280,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -3531,6 +3575,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -3800,6 +3848,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -4068,6 +4120,10 @@ Events of the following types have truncated stack traces: <ul><li>A ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -4392,6 +4448,10 @@ Some of these inspections were caused by the 'Object Count' JFR event. It trigge ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -4666,6 +4726,10 @@ Some of these inspections were caused by the 'Object Count' JFR event. It trigge ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -4940,6 +5004,10 @@ Some of these inspections were caused by the 'Object Count' JFR event. It trigge ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -5263,6 +5331,10 @@ Enabling the following event types would improve the accuracy of this rule: jdk. ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -5585,6 +5657,10 @@ Enabling the following event types would improve the accuracy of this rule: jdk. ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -5863,6 +5939,10 @@ Enabling the following event types would improve the accuracy of this rule: jdk. ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -6189,6 +6269,10 @@ To improve rule accuracy and/or get more details for further investigation, it i ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -6572,6 +6656,10 @@ String deduplication is only supported when using the G1 (JDK 8u20+) or Shenando ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable @@ -6852,6 +6940,10 @@ Events of the following types have truncated stack traces: <ul><li>D ZGCAllocationStallRule Not Applicable + + + CryptoSecurityRule + Not Applicable diff --git a/releng/platform-definitions/platform-definition-2024-12/platform-definition-2024-12.target b/releng/platform-definitions/platform-definition-2024-12/platform-definition-2024-12.target index ccb1f991c8..822f6c9208 100644 --- a/releng/platform-definitions/platform-definition-2024-12/platform-definition-2024-12.target +++ b/releng/platform-definitions/platform-definition-2024-12/platform-definition-2024-12.target @@ -45,9 +45,13 @@ - - - + + + + + + + diff --git a/releng/platform-definitions/platform-definition-2025-03/platform-definition-2025-03.target b/releng/platform-definitions/platform-definition-2025-03/platform-definition-2025-03.target index 4e5d410fe8..c04a6c55e2 100644 --- a/releng/platform-definitions/platform-definition-2025-03/platform-definition-2025-03.target +++ b/releng/platform-definitions/platform-definition-2025-03/platform-definition-2025-03.target @@ -45,9 +45,14 @@ - - - + + + + + + + + diff --git a/releng/platform-definitions/platform-definition-2025-06/platform-definition-2025-06.target b/releng/platform-definitions/platform-definition-2025-06/platform-definition-2025-06.target index 45c1e7321a..4dae437229 100644 --- a/releng/platform-definitions/platform-definition-2025-06/platform-definition-2025-06.target +++ b/releng/platform-definitions/platform-definition-2025-06/platform-definition-2025-06.target @@ -45,9 +45,13 @@ - - - + + + + + + + diff --git a/releng/platform-definitions/platform-definition-2025-09/platform-definition-2025-09.target b/releng/platform-definitions/platform-definition-2025-09/platform-definition-2025-09.target index 1348992bc2..d0259eb22c 100644 --- a/releng/platform-definitions/platform-definition-2025-09/platform-definition-2025-09.target +++ b/releng/platform-definitions/platform-definition-2025-09/platform-definition-2025-09.target @@ -44,9 +44,13 @@ - - - + + + + + + + diff --git a/releng/third-party/pom.xml b/releng/third-party/pom.xml index 01dfeba4a7..e9b2e78a21 100644 --- a/releng/third-party/pom.xml +++ b/releng/third-party/pom.xml @@ -61,7 +61,7 @@ 2.0.0 12.1.0 1.3.7 - 2.4.2 + 2.5.1 2.27.2 0.0.1-rc.9 8.5.0 @@ -166,14 +166,20 @@ org.jolokia:jolokia-service-discovery:${jolokia.version} - true - - org.jolokia.service.discovery - + + org.jolokia:jolokia-service-serializer:${jolokia.version} + + + org.jolokia:jolokia-core:${jolokia.version} + org.jolokia:jolokia-server-core:${jolokia.version} + + org.jolokia:jolokia-client-java:${jolokia.version} + false + org.osgi:org.osgi.service.servlet:2.0.0 diff --git a/scripts/checkcopyrightyear.sh b/scripts/checkcopyrightyear.sh index c2d27a36dd..d10a0d4313 100755 --- a/scripts/checkcopyrightyear.sh +++ b/scripts/checkcopyrightyear.sh @@ -5,7 +5,9 @@ git remote -v | grep -w upstream || git remote add upstream https://github.com/o git fetch upstream CURRENT_YEAR=$(date +'%Y') -MODIFIED_FILES=$(git diff --name-only upstream/master) +COMMITTED_FILES=$(git diff --name-only --diff-filter=d upstream/master...HEAD) +UNCOMMITTED_FILES=$(git diff --name-only --diff-filter=d) +MODIFIED_FILES=$(echo -e "$COMMITTED_FILES\n$UNCOMMITTED_FILES" | sort -u | grep -v '^$') counter=0 for fileToCheck in $MODIFIED_FILES