diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000000..0cd87fae44
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,142 @@
+# JDK Mission Control (JMC) - Agent Instructions
+
+## Project Overview
+- Java Mission Control (JMC) is an OpenJDK project with core libraries and an Eclipse RCP UI.
+- Main areas:
+ - `core/` - Headless Java 17 libraries (can run standalone, read recordings from JDK 7+)
+ - `org.openjdk.jmc.common` - Core APIs and utilities
+ - `org.openjdk.jmc.flightrecorder` - JFR parsing and analysis
+ - `org.openjdk.jmc.flightrecorder.rules` - Rule framework
+ - `org.openjdk.jmc.flightrecorder.rules.jdk` - JDK-specific analysis rules
+ - `org.openjdk.jmc.testlib` - Test utilities
+ - `application/` - Eclipse RCP UI components (Java 21)
+ - `agent/` - JFR Agent for bytecode instrumentation
+
+## Build & Test
+- `./build.sh --installCore` - Install core libraries
+- `./build.sh --packageJmc` - Package full JMC application
+- `./build.sh --test` - Run standard tests
+- `./build.sh --testUi` - Run UI tests
+- `mvn verify -Dtest.includes=**/*TestName*` - Run specific tests
+- `mvn verify -Dspotbugs.skip=true` - Skip SpotBugs during verification
+
+### Build Scripts
+- `scripts/runcoretests.sh` - Run core library tests
+- `scripts/runapptests.sh` - Run application tests
+- `scripts/runagenttests.sh` - Run agent tests
+- `scripts/startp2.sh` - Start local P2 repository for application build
+
+### Eclipse Platform Profiles
+Build against specific Eclipse platform versions using Maven profiles:
+```bash
+mvn verify -P 2024-12
+```
+
+## Java Version & Toolchains
+- **Java Version**: JDK 21 for application build, JDK 17 for core components
+- Requires both JDK 17 and JDK 21 configured in `~/.m2/toolchains.xml`.
+
+Example `~/.m2/toolchains.xml`:
+```xml
+
+
+ jdk
+
+ JavaSE-17
+ 17
+
+
+ /path/to/jdk17
+
+
+
+ jdk
+
+ JavaSE-21
+ 21
+
+
+ /path/to/jdk21
+
+
+
+```
+
+## Code Style & Formatting
+- Java formatting follows the Eclipse profile in `configuration/ide/eclipse/formatting/formatting.xml`.
+- Java cleanup rules are in `configuration/ide/eclipse/formatting/clean-up-with-formatting.xml`.
+- JavaScript formatting follows `configuration/ide/eclipse/formatting/formattingjs.xml`.
+- Key formatter settings (from the Eclipse profiles):
+ - Tabs for indentation (`tabulation.char=tab`, size 4).
+ - Line length 120 (`lineSplit=120`).
+ - Javadoc/comment line length 100 (`comment.line_length=100`).
+- Avoid star imports; remove unused imports.
+- **Naming**: Follow Eclipse/Java standard conventions, be consistent.
+
+## Logging / Error Handling
+- Use `Level.FINE` for expected exceptions (e.g., `CancellationException`).
+- Use `Level.SEVERE` for unexpected failures.
+- Filter cancellations from error logs during model rebuilding.
+- Check SpotBugs exceptions for guidance.
+
+## Commit Messages
+- Format: `JIRA_NUMBER: Commit message` (example: `6789: Fix bug`).
+- Issue tracker: https://bugs.openjdk.org/projects/JMC/issues
+
+## Copyright Headers
+- All modified files must have the current year in the copyright header.
+- CI validates this via `scripts/checkcopyrightyear.sh`.
+- Affected file types: `*.java`, `*.htm`, `pom.xml`, `*.properties`.
+
+## Static Analysis
+- **Checkstyle**: Enforces no star imports, no redundant/unused imports (`configuration/checkstyle/checkstyle.xml`).
+- **SpotBugs**: Static bug detection with exclusions in `configuration/spotbugs/spotbugs-exclude.xml`.
+- **Spotless**: Code formatting enforced during Maven validate phase.
+
+## Internationalization (i18n)
+- Core modules: Place `messages.properties`, `messages_ja.properties`, `messages_zh.properties` in `internal` packages.
+- Application modules: Use separate l10n plugin modules (e.g., `org.openjdk.jmc.*.ja`, `org.openjdk.jmc.*.zh_CN`).
+- Access strings via `Messages.getString(Messages.MESSAGE_KEY)`.
+
+## Writing Flight Recorder Rules
+Rules analyze JFR recordings and provide recommendations to users.
+
+### Creating a New Rule
+1. Extend `AbstractRule`:
+```java
+public class MyRule extends AbstractRule {
+ public MyRule() {
+ super("MyRuleId", Messages.getString(Messages.MY_RULE_NAME),
+ JfrRuleTopics.TOPIC_NAME, CONFIGURATION_ATTRIBUTES,
+ RESULT_ATTRIBUTES, Collections.emptyMap());
+ }
+
+ @Override
+ protected IResult getResult(IItemCollection items, IPreferenceValueProvider vp,
+ IResultValueProvider rp) {
+ // Analyze items using ItemFilters, Aggregators, etc.
+ return ResultBuilder.createFor(this, vp)
+ .setSeverity(Severity.get(score))
+ .setSummary("Summary message")
+ .setExplanation("Detailed explanation")
+ .build();
+ }
+}
+```
+
+2. Register the rule in `META-INF/services/org.openjdk.jmc.flightrecorder.rules.IRule`:
+```
+org.openjdk.jmc.flightrecorder.rules.jdk.mypackage.MyRule
+```
+
+### Core Item Processing APIs
+- `IItemCollection` - Collection of JFR events to query
+- `IItemFilter` - Filter events (use `ItemFilters` factory)
+- `IAggregator` - Aggregate values (use `Aggregators` factory)
+- `IAttribute` - Access event attributes (see `JdkAttributes`)
+- `RulesToolkit` - Utility methods for rule implementations
+
+## Testing
+- Unit tests in `src/test/java` using JUnit 4.
+- Test resources in `src/test/resources`.
+- JDP multicast tests are automatically skipped on macOS.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bc94eb7ef0..5235d2c186 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,7 +17,7 @@ message should be "6789: Commit message".
The mailing list for JDK Mission Control is `jmc-dev@openjdk.java.net`. See
for instructions
-on how to subscribe of if you want to read the archives.
+on how to subscribe or if you want to read the archives.
## Issues
@@ -45,4 +45,3 @@ If not, ask for an invite in the jmc-dev mailing list.
If you have a question or need help, please send an email to our mailing list
`jmc-dev@openjdk.java.net` or stop by the JMC slack channel.
-
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index bd4ccad0aa..9ae18cd268 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,4 +1,4 @@
-#Contributors
+# Contributors
A lot of people have been involved with Mission Control over the years.
This document is a thank you to everyone who has ever contributed to
@@ -31,4 +31,4 @@ List of contributors, approximately in order of time spent on project:
* Suchita Chaturvedi
* Guru Hb
* Sharath Ballal
-* Peter Boström
\ No newline at end of file
+* Peter Boström
diff --git a/README.md b/README.md
index fc861c571b..c3227b9783 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Mission Control is an open source production time profiling and diagnostics tool for Java.
-Builds of Mission Control can currently be found in the Oracle JDK on supported platforms and in the Eclipse marketplace.
+Builds of Mission Control can currently be found in the Oracle JDK on supported platforms and in the Eclipse Marketplace.
For more information on Mission Control, see https://www.oracle.com/missioncontrol.
@@ -217,7 +217,7 @@ Prerequisites for building Mission Control:
2. Install a JDK 21 distribution and make sure that it too is declared in the local maven toolchain.
-3. Install Maven (version 3.5.x. or above)
+3. Install Maven (version 3.5.x or above)
On Linux or macOS you can use the `build.sh` script to build JMC:
```
@@ -268,7 +268,7 @@ If maven reports a toolchain error, e.g. :
[ERROR] Please make sure you define the required toolchains in your ~/.m2/toolchains.xml file.
```
-Create or amend the local maven toolchain file by pointing to the right/any JDK 17.
+Create or amend the local maven toolchain file by pointing to any JDK 17 installation.
~/.m2/toolchains.xml
@@ -346,7 +346,7 @@ mvn verify -P uitests -Dspotbugs.skip=true
```
## Filtering Test Runs
-Aside from the from the simple -test Maven flag test classes that should be run/not run can be specified by means of the system properties "test.includes" and/or "test.excludes". Multiple patterns can be specified by comma separation.
+Aside from the simple -test Maven flag, test classes that should be run/not run can be specified by means of the system properties "test.includes" and/or "test.excludes". Multiple patterns can be specified by comma separation.
For example:
@@ -422,7 +422,7 @@ There is another update site for the Eclipse plug-ins, providing plug-ins for ru
application/org.openjdk.jmc.updatesite.ide/target/
```
-To install it into Eclipe, simply open Eclipse and select Help | Install New Software... In the dialog, click Add... and then click the Archive... button. Select the built update site, e.g.
+To install it into Eclipse, simply open Eclipse and select Help | Install New Software... In the dialog, click Add... and then click the Archive... button. Select the built update site, e.g.
```bash
application/org.openjdk.jmc.updatesite.ide/target/org.openjdk.jmc.updatesite.ide-10.0.0-SNAPSHOT.zip
diff --git a/application/coverage/pom.xml b/application/coverage/pom.xml
index 3b0a7b32a7..b86a865921 100644
--- a/application/coverage/pom.xml
+++ b/application/coverage/pom.xml
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/org.openjdk.jmc.ui.websocket/pom.xml b/application/org.openjdk.jmc.ui.websocket/pom.xml
new file mode 100644
index 0000000000..1bca6528d9
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+ 4.0.0
+
+ org.openjdk.jmc
+ missioncontrol.application
+ ${revision}${changelist}
+
+ org.openjdk.jmc.ui.websocket
+ eclipse-plugin
+
+ ${project.basedir}/../../configuration
+
+
diff --git a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/websocket/WebsocketServer.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/MCWebsocketServer.java
similarity index 72%
rename from application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/websocket/WebsocketServer.java
rename to application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/MCWebsocketServer.java
index f8356e7922..d49334a4cc 100644
--- a/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/websocket/WebsocketServer.java
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/MCWebsocketServer.java
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2021, 2025, Datadog, Inc. All rights reserved.
+ * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, Datadog, Inc. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -31,9 +31,10 @@
* 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.ui.websocket;
+package org.openjdk.jmc.ui.websocket;
import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
@@ -45,13 +46,12 @@
import java.util.logging.Level;
import java.util.stream.Collectors;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
import org.eclipse.jetty.ee9.websocket.api.Session;
import org.eclipse.jetty.ee9.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketServletContainerInitializer;
-import org.eclipse.jetty.ee9.websocket.servlet.WebSocketUpgradeFilter;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.flightrecorder.serializers.dot.DotSerializer;
import org.openjdk.jmc.flightrecorder.serializers.json.FlameGraphJsonSerializer;
@@ -60,14 +60,8 @@
import org.openjdk.jmc.flightrecorder.stacktrace.FrameSeparator.FrameCategorization;
import org.openjdk.jmc.flightrecorder.stacktrace.graph.StacktraceGraphModel;
import org.openjdk.jmc.flightrecorder.stacktrace.tree.StacktraceTreeModel;
-import org.openjdk.jmc.flightrecorder.ui.FlightRecorderUI;
-
-public class WebsocketServer {
-
- private static int MAX_MESSAGE_SIZE = 1024 * 1024 * 1024;
- private static int IDLE_TIMEOUT_MINUTES = 5;
- private final int port;
+public class MCWebsocketServer {
private Server server;
private List handlers = new CopyOnWriteArrayList<>();
private List treeHandlers = new CopyOnWriteArrayList<>();
@@ -75,16 +69,11 @@ public class WebsocketServer {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private IItemCollection currentSelection = null;
- public WebsocketServer(int port) {
- this.port = port;
- executorService.execute(() -> startServer());
+ public MCWebsocketServer(int port) {
+ executorService.execute(() -> startServer(port));
}
- public int getPort() {
- return port;
- }
-
- private void startServer() {
+ public void startServer(int port) {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setHost("127.0.0.1");
@@ -96,24 +85,22 @@ private void startServer() {
server.setHandler(context);
JettyWebSocketServletContainerInitializer.configure(context, (servletContext, container) -> {
- container.setMaxBinaryMessageSize(MAX_MESSAGE_SIZE);
- container.setIdleTimeout(Duration.ofMinutes(IDLE_TIMEOUT_MINUTES));
+ container.setMaxBinaryMessageSize(Long.MAX_VALUE);
+ container.setIdleTimeout(Duration.ofMinutes(Long.MAX_VALUE));
container.addMapping("/events/*", (req, resp) -> {
- // try to send the current selection when the client connects
- // for simplicity, we serialise for every new connection
- String eventsJson = WebsocketServer.toEventsJsonString(currentSelection);
+ String eventsJson = MCWebsocketServer.toEventsJsonString(currentSelection);
WebsocketConnectionHandler handler = new WebsocketConnectionHandler(eventsJson);
handlers.add(handler);
return handler;
});
container.addMapping("/tree/*", (req, resp) -> {
- String treeJson = WebsocketServer.toTreeModelJsonString(currentSelection);
+ String treeJson = MCWebsocketServer.toTreeModelJsonString(currentSelection);
WebsocketConnectionHandler handler = new WebsocketConnectionHandler(treeJson);
treeHandlers.add(handler);
return handler;
});
container.addMapping("/graph/*", (req, resp) -> {
- String dot = WebsocketServer.toGraphModelDotString(currentSelection);
+ String dot = MCWebsocketServer.toGraphModelDotString(currentSelection);
WebsocketConnectionHandler handler = new WebsocketConnectionHandler(dot);
graphHandlers.add(handler);
return handler;
@@ -121,13 +108,9 @@ private void startServer() {
});
try {
- WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO,
- "Starting websocket server listening on port " + port);
server.start();
- server.join();
} catch (Exception e) {
- FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to start websocket server", e);
+ WebsocketPlugin.getLogger().log(Level.SEVERE, "Failed to start websocket server", e);
}
}
@@ -139,15 +122,15 @@ public void notifyAll(IItemCollection events) {
}
private void notifyAllEventHandlers(IItemCollection events) {
- handlers = notifyAllHandlers(events, handlers, WebsocketServer::toEventsJsonString);
+ handlers = notifyAllHandlers(events, handlers, MCWebsocketServer::toEventsJsonString);
}
private void notifyAllGraphHandlers(IItemCollection events) {
- graphHandlers = notifyAllHandlers(events, graphHandlers, WebsocketServer::toGraphModelDotString);
+ graphHandlers = notifyAllHandlers(events, graphHandlers, MCWebsocketServer::toGraphModelDotString);
}
private void notifyAllTreeHandlers(IItemCollection events) {
- treeHandlers = notifyAllHandlers(events, treeHandlers, WebsocketServer::toTreeModelJsonString);
+ treeHandlers = notifyAllHandlers(events, treeHandlers, MCWebsocketServer::toTreeModelJsonString);
}
private static String toEventsJsonString(IItemCollection items) {
@@ -187,15 +170,8 @@ private List notifyAllHandlers(
return handlers;
}
- public void shutdown() {
- try {
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO,
- "Stopping websocket server listening on port " + port);
- server.stop();
- // TODO: see if we need to cleanup executor service and thread
- } catch (Exception e) {
- FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to stop websocket server", e);
- }
+ public void shutdown() throws Exception {
+ server.stop();
}
private static class WebsocketConnectionHandler extends WebSocketAdapter {
@@ -207,12 +183,12 @@ private static class WebsocketConnectionHandler extends WebSocketAdapter {
public void sendMessage(String message) {
if (getSession() != null && isConnected()) {
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO,
+ WebsocketPlugin.getLogger().log(Level.INFO,
"Sending message to " + getSession().getRemoteAddress().toString());
try {
getSession().getRemote().sendString(message);
} catch (IOException e) {
- FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to send websocket message", e);
+ WebsocketPlugin.getLogger().log(Level.SEVERE, "Failed to send websocket message", e);
}
}
}
@@ -220,15 +196,14 @@ public void sendMessage(String message) {
@Override
public void onWebSocketConnect(Session sess) {
super.onWebSocketConnect(sess);
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO,
- "Socket connected to " + sess.getRemoteAddress().toString());
+ WebsocketPlugin.getLogger().log(Level.INFO, "Socket connected to " + sess.getRemoteAddress().toString());
try {
if (firstMessage != null) {
getSession().getRemote().sendString(firstMessage);
firstMessage = null;
}
} catch (IOException e) {
- FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Failed to show outline view", e);
+ WebsocketPlugin.getLogger().log(Level.SEVERE, "Failed to show outline view", e);
}
}
@@ -240,16 +215,18 @@ public void onWebSocketText(String message) {
@Override
public void onWebSocketClose(int statusCode, String reason) {
super.onWebSocketClose(statusCode, reason);
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO, "Socket closed: [" + statusCode + "] " + reason);
+ WebsocketPlugin.getLogger().log(Level.INFO, "Socket closed: [" + statusCode + "] " + reason);
}
@Override
public void onWebSocketError(Throwable cause) {
super.onWebSocketError(cause);
- if (cause.getCause() instanceof TimeoutException) {
- FlightRecorderUI.getDefault().getLogger().log(Level.INFO, "Websocket timed out");
+ if (cause instanceof TimeoutException) {
+ WebsocketPlugin.getLogger().log(Level.INFO, "Websocket timed out");
+ } else if (cause instanceof ClosedChannelException) {
+ WebsocketPlugin.getLogger().log(Level.INFO, "Websocket channel has closed");
} else {
- FlightRecorderUI.getDefault().getLogger().log(Level.SEVERE, "Websocket error", cause);
+ WebsocketPlugin.getLogger().log(Level.SEVERE, "Websocket error", cause);
}
}
}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/WebsocketPlugin.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/WebsocketPlugin.java
new file mode 100644
index 0000000000..ac7a9ab1a5
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/WebsocketPlugin.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 IBM Corporation. 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.ui.websocket;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.openjdk.jmc.common.item.IItemCollection;
+import org.openjdk.jmc.ui.websocket.preferences.PreferenceConstants;
+import org.osgi.framework.BundleContext;
+
+public class WebsocketPlugin extends AbstractUIPlugin implements IStartup {
+
+ public final static String PLUGIN_ID = "org.openjdk.jmc.ui.websocket"; //$NON-NLS-1$
+ private static final Logger LOGGER = Logger.getLogger(PLUGIN_ID);
+
+ private static WebsocketPlugin plugin;
+ private MCWebsocketServer server;
+
+ public WebsocketPlugin() {
+ }
+
+ public static WebsocketPlugin getDefault() {
+ return plugin;
+ }
+
+ public static Logger getLogger() {
+ return LOGGER;
+ }
+
+ public void start(BundleContext bundleContext) throws Exception {
+ super.start(bundleContext);
+ this.getPreferenceStore().addPropertyChangeListener(preferenceChangeListener);
+ plugin = this;
+ startServer(getCryostatPort());
+ LOGGER.log(Level.INFO, "JMC Websocket Server is live!");
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception {
+ if (server != null) {
+ server.shutdown();
+ server = null;
+ }
+ plugin = null;
+ super.stop(bundleContext);
+ }
+
+ public void notifyAll(IItemCollection events) {
+ if (server != null) {
+ server.notifyAll(events);
+ }
+ }
+
+ private void startServer(int port) {
+ if (getServerEnabled()) {
+ server = new MCWebsocketServer(port);
+ }
+ }
+
+ private void stopServer() {
+ if (server != null) {
+ try {
+ server.shutdown();
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Error shutting down the Jetty WebSocket server", e);
+ }
+ }
+ }
+
+ private IPropertyChangeListener preferenceChangeListener = new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(PreferenceConstants.P_SERVER_ENABLED)) {
+ if (getServerEnabled()) {
+ startServer(getCryostatPort());
+ } else {
+ stopServer();
+ }
+ }
+ if (event.getProperty().equals(PreferenceConstants.P_SERVER_PORT) && getServerEnabled()) {
+ stopServer();
+ startServer(getCryostatPort());
+ }
+ }
+ };
+
+ private int getCryostatPort() {
+ return this.getPreferenceStore().getInt(PreferenceConstants.P_SERVER_PORT);
+ }
+
+ public boolean getServerEnabled() {
+ return this.getPreferenceStore().getBoolean(PreferenceConstants.P_SERVER_ENABLED);
+ }
+
+ @Override
+ public void earlyStartup() {
+ // do nothing, we're only implementing IStartup to force a load of the plugin
+ }
+}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/Messages.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/Messages.java
new file mode 100644
index 0000000000..60b35bc07c
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/Messages.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 IBM Corporation. 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.ui.websocket.preferences;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.openjdk.jmc.ui.websocket.preferences.messages"; //$NON-NLS-1$
+
+ public static String WebsocketPreferencePage_DESCRIPTION;
+ public static String WebsocketPreferencePage_ENABLE;
+ public static String WebsocketPreferencePage_PORT;
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceConstants.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceConstants.java
new file mode 100644
index 0000000000..ed7ef192a2
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceConstants.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 IBM Corporation. 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.ui.websocket.preferences;
+
+public class PreferenceConstants {
+ public static final String P_SERVER_ENABLED = "websocket.server.enabled";
+ public static final String P_SERVER_PORT = "websocket.server.port";
+ public static final boolean DEFAULT_ENABLED = false;
+ public static final int DEFAULT_PORT = 8029;
+}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceInitializer.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceInitializer.java
new file mode 100644
index 0000000000..fb8335ff2e
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/PreferenceInitializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 IBM Corporation. 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.ui.websocket.preferences;
+
+import org.openjdk.jmc.ui.websocket.WebsocketPlugin;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = WebsocketPlugin.getDefault().getPreferenceStore();
+ store.setDefault(PreferenceConstants.P_SERVER_PORT, PreferenceConstants.DEFAULT_PORT);
+ store.setDefault(PreferenceConstants.P_SERVER_ENABLED, PreferenceConstants.DEFAULT_ENABLED);
+ }
+}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/WebsocketPreferencePage.java b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/WebsocketPreferencePage.java
new file mode 100644
index 0000000000..b7fc4ccf44
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/java/org/openjdk/jmc/ui/websocket/preferences/WebsocketPreferencePage.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2026 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026 IBM Corporation. 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.ui.websocket.preferences;
+
+import org.openjdk.jmc.ui.websocket.WebsocketPlugin;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class WebsocketPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ private IntegerFieldEditor portField;
+
+ public WebsocketPreferencePage() {
+ super(GRID);
+ setPreferenceStore(WebsocketPlugin.getDefault().getPreferenceStore());
+ setDescription(Messages.WebsocketPreferencePage_DESCRIPTION);
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ addField(new BooleanFieldEditor(PreferenceConstants.P_SERVER_ENABLED, Messages.WebsocketPreferencePage_ENABLE,
+ getFieldEditorParent()));
+ portField = new IntegerFieldEditor(PreferenceConstants.P_SERVER_PORT, Messages.WebsocketPreferencePage_PORT,
+ getFieldEditorParent());
+ addField(portField);
+ enableWebsocketFields(isWebsocketPluginEnabled());
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(FieldEditor.VALUE)) {
+ FieldEditor editor = (FieldEditor) event.getSource();
+ if (PreferenceConstants.P_SERVER_ENABLED.equals(editor.getPreferenceName())) {
+ enableWebsocketFields((boolean) event.getNewValue());
+ }
+ }
+ }
+
+ private boolean isWebsocketPluginEnabled() {
+ return WebsocketPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.P_SERVER_ENABLED);
+ }
+
+ private void enableWebsocketFields(boolean enable) {
+ portField.setEnabled(enable, getFieldEditorParent());
+ }
+}
diff --git a/application/org.openjdk.jmc.ui.websocket/src/main/resources/org/openjdk/jmc/ui/websocket/preferences/messages.properties b/application/org.openjdk.jmc.ui.websocket/src/main/resources/org/openjdk/jmc/ui/websocket/preferences/messages.properties
new file mode 100644
index 0000000000..19710c4b5d
--- /dev/null
+++ b/application/org.openjdk.jmc.ui.websocket/src/main/resources/org/openjdk/jmc/ui/websocket/preferences/messages.properties
@@ -0,0 +1,3 @@
+WebsocketPreferencePage_DESCRIPTION=Settings for the JMC Websocket Server
+WebsocketPreferencePage_ENABLE=Enable
+WebsocketPreferencePage_PORT=WebSocket Port for JFR transfer:
diff --git a/application/pom.xml b/application/pom.xml
index fbd9cc8b2d..f96e309eeb 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -1,6 +1,6 @@
+
+
diff --git a/core/license/LICENSE.txt b/core/license/LICENSE.txt
index 0134be2728..a84a6f60fe 100644
--- a/core/license/LICENSE.txt
+++ b/core/license/LICENSE.txt
@@ -1,6 +1,6 @@
Oracle provides this software under a dual license, either the UPL or the BSD:
-Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
+Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
The Universal Permissive License (UPL), Version 1.0
@@ -40,7 +40,7 @@ SOFTWARE.
The BSD 3-Clause License (BSD):
-Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
+Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/core/license/THIRD_PARTY_LICENSES.txt b/core/license/THIRD_PARTY_LICENSES.txt
index 6c78f0f145..af3ffa0a02 100644
--- a/core/license/THIRD_PARTY_LICENSES.txt
+++ b/core/license/THIRD_PARTY_LICENSES.txt
@@ -47,7 +47,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
===============================================================================
-## org.lz4:lz4-java@1.8.0 (Apache-2.0)
+## at.yawk.lz4:lz4-java@1.10.2 (Apache-2.0)
URL for License – http://opensource.org/licenses/Apache-2.0
diff --git a/core/org.openjdk.jmc.common/pom.xml b/core/org.openjdk.jmc.common/pom.xml
index a80a8a1b22..157589e1ff 100644
--- a/core/org.openjdk.jmc.common/pom.xml
+++ b/core/org.openjdk.jmc.common/pom.xml
@@ -1,6 +1,6 @@
- 2.0.3
- 2.0.2
- 2.1.3
+ 2.0.5
+ 2.0.3
+ 2.1.4
1.0.3
1.2.3
- 1.8.0
+ 1.10.2
2.2.2
2.0.0
12.1.0
@@ -104,7 +104,7 @@
org.owasp.encoder:encoder:${owasp.encoder.version}
- org.lz4:lz4-java:${lz4.version}
+ at.yawk.lz4:lz4-java:${lz4.version}
org.hdrhistogram:HdrHistogram:${hdrhistogram.version}
diff --git a/releng/tools/README.md b/releng/tools/README.md
index 79136b3ef3..43d00a2e15 100644
--- a/releng/tools/README.md
+++ b/releng/tools/README.md
@@ -1,2 +1,2 @@
-#Releng Tools
-This is the location to put release engineering related tools.
\ No newline at end of file
+# Releng Tools
+This is the location to put release engineering related tools.