diff --git a/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/ConnectionWizardPage.java b/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/ConnectionWizardPage.java index 618370963..3f8fd0f3b 100644 --- a/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/ConnectionWizardPage.java +++ b/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/ConnectionWizardPage.java @@ -93,6 +93,7 @@ public class ConnectionWizardPage extends RelinkableWizardPage { private final ConnectionWizardModel model; private Text hostNameField; private Text usernameField; + private Button requireSecureConnectionButton; private Text portField; private Text javaCommandField; private Label javaCommandCaption; @@ -408,7 +409,7 @@ private boolean hasServiceUrl() { private void createHostPortServiceURLComposite(Composite outer) { // FIXME: Make sure to fix the layout to align for all components, for example commandline, pid and service url (for JDP) Composite inner = new Composite(outer, SWT.NONE); - GridLayout l = new GridLayout(1, false); + GridLayout l = new GridLayout(2, false); inner.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 2)); l.marginWidth = 0; @@ -416,7 +417,7 @@ private void createHostPortServiceURLComposite(Composite outer) { inner.setLayout(l); fieldStack = new Composite(inner, SWT.NONE); - fieldStack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + fieldStack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); fieldStackLayout = new StackLayout(); fieldStack.setLayout(fieldStackLayout); createHostPortComposite(fieldStack); @@ -425,6 +426,10 @@ private void createHostPortServiceURLComposite(Composite outer) { } private void createCustomURLButtonComposite(Composite inner) { + requireSecureConnectionButton = new Button(inner, SWT.CHECK); + requireSecureConnectionButton.setText(Messages.ConnectionWizardPage_REQUIRE_SECURE_CONNECTION_LABEL); + requireSecureConnectionButton + .setLayoutData(new GridData(SWT.LEFT, GridData.VERTICAL_ALIGN_BEGINNING, false, false)); CustomURLSelector customURLSelector = new CustomURLSelector(); customUrlButton = new Button(inner, SWT.TOGGLE); customUrlButton.setText(Messages.ConnectionWizardPage_BUTTON_CUSTOM_JMX_SERVICE_URL_TEXT); @@ -487,6 +492,9 @@ private void initializeFields() { if (server != null) { currentUrl = server.getConnectionUrl(); name = server.getServerHandle().getServerDescriptor().getDisplayName(); + if (server.getServerHandle().getConnectionDescriptor().requireSecureConnection()) { + this.requireSecureConnectionButton.setSelection(true); + } ICredentials credential = server.getCredentials(); if (credential != null) { try { @@ -670,7 +678,7 @@ void updateModel() { ServerModelCredentials credentials = new ServerModelCredentials(username, password, storePassword); IConnectionDescriptor cd = new ConnectionDescriptorBuilder().url(currentUrl).credentials(credentials) - .build(); + .requireSecureConnection(isSecureConnectionRequired()).build(); Server newServer; if (server != null) { @@ -694,6 +702,10 @@ void updateModel() { } } + private boolean isSecureConnectionRequired() { + return requireSecureConnectionButton.getSelection(); + } + private Exception testConnection() { if (model.createdServer == null) { updateModel(); diff --git a/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/Messages.java b/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/Messages.java index 10cfdaacc..3e9919dcc 100644 --- a/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/Messages.java +++ b/application/org.openjdk.jmc.browser/src/main/java/org/openjdk/jmc/browser/wizards/Messages.java @@ -74,6 +74,7 @@ public class Messages extends NLS { public static String ConnectionWizardPage_PASSWORD_TOOLTIP; public static String ConnectionWizardPage_PORT_CAPTION; public static String ConnectionWizardPage_PORT_TOOLTIP; + public static String ConnectionWizardPage_REQUIRE_SECURE_CONNECTION_LABEL; public static String ConnectionWizardPage_SERVICE_URL_CAPTION; public static String ConnectionWizardPage_SERVICE_URL_TOOLTIP; public static String ConnectionWizardPage_STATUS_CAPTION; diff --git a/application/org.openjdk.jmc.browser/src/main/resources/org/openjdk/jmc/browser/wizards/messages.properties b/application/org.openjdk.jmc.browser/src/main/resources/org/openjdk/jmc/browser/wizards/messages.properties index 70aa09f74..b20e646f5 100644 --- a/application/org.openjdk.jmc.browser/src/main/resources/org/openjdk/jmc/browser/wizards/messages.properties +++ b/application/org.openjdk.jmc.browser/src/main/resources/org/openjdk/jmc/browser/wizards/messages.properties @@ -85,7 +85,7 @@ ConnectionWizardPage_STATUS_IS_UNTESTED=Untested ConnectionWizardPage_STATUS_IS_CONNECTED=OK ConnectionWizardPage_STATUS_IS_NOT_CONNECTED=Unable to connect ConnectionWizardPage_COULD_NOT_CONNECT_DISABLE_NEXT=Could not connect to {0}. Click Finish to create the connection without connecting. - +ConnectionWizardPage_REQUIRE_SECURE_CONNECTION_LABEL=Require secure connection ServerSelectionWizardPage_NEW_CONNECTION=Create a new connection ServerConnectWizardPage_SERVER_SELECT_DESCRIPTION=Select a JVM to connect to ServerConnectWizardPage_TOOL_SELECT_DESCRIPTION=Select a tool to connect to {0} diff --git a/application/org.openjdk.jmc.console.ui/src/main/java/org/openjdk/jmc/console/ui/editor/internal/ConsoleEditor.java b/application/org.openjdk.jmc.console.ui/src/main/java/org/openjdk/jmc/console/ui/editor/internal/ConsoleEditor.java index bbc6d25e0..2ed5644bb 100644 --- a/application/org.openjdk.jmc.console.ui/src/main/java/org/openjdk/jmc/console/ui/editor/internal/ConsoleEditor.java +++ b/application/org.openjdk.jmc.console.ui/src/main/java/org/openjdk/jmc/console/ui/editor/internal/ConsoleEditor.java @@ -123,8 +123,7 @@ public void run() { WorkbenchToolkit.asyncCloseEditor(ConsoleEditor.this); // FIXME: Show stacktrace? (Need to show our own ExceptionDialog in that case, or maybe create our own DetailsAreaProvider, see WorkbenchStatusDialogManager.setDetailsAreaProvider) return new Status(IStatus.ERROR, ConsolePlugin.PLUGIN_ID, IStatus.ERROR, - NLS.bind(Messages.ConsoleEditor_COULD_NOT_CONNECT, getEditorInput().getName(), e.getMessage()), - e); + NLS.bind(e.getLocalizedMessage(), getEditorInput().getName(), e.getMessage()), e); } } } diff --git a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java index 0e8a47382..a013f2568 100644 --- a/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java +++ b/application/org.openjdk.jmc.jolokia/src/main/java/org/openjdk/jmc/jolokia/JmcJolokiaPlugin.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, 2025, Kantega AS. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Kantega AS. All rights reserved. * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDescriptorBuilder.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDescriptorBuilder.java index 4c0858b3a..85a728dcf 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDescriptorBuilder.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/ConnectionDescriptorBuilder.java @@ -53,6 +53,7 @@ public class ConnectionDescriptorBuilder { private String password; private int port = DEFAULT_PORT; private ICredentials credentials; + private boolean requireSecureConnection; /** * Port number designator meaning that the default port for the selected protocol should be @@ -142,6 +143,16 @@ public ConnectionDescriptorBuilder password(String password) { return this; } + /** + * @param requireSecureConnection + * force connection to use TLS. Fail if not possible. + * @return the Builder currently being configured. + */ + public ConnectionDescriptorBuilder requireSecureConnection(boolean requireSecureConnection) { + this.requireSecureConnection = requireSecureConnection; + return this; + } + /** * Builds the {@link IConnectionDescriptor}. * @@ -169,6 +180,6 @@ public IConnectionDescriptor build() throws IllegalStateException { } } - return new JMXConnectionDescriptor(url, credentials); + return new JMXConnectionDescriptor(url, credentials, requireSecureConnection); } } diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/IConnectionDescriptor.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/IConnectionDescriptor.java index 1c5f1945e..79c8a348a 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/IConnectionDescriptor.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/IConnectionDescriptor.java @@ -52,6 +52,10 @@ */ public interface IConnectionDescriptor { + // JMX convention. See e.g. + // https://github.com/openjdk/jdk/blob/master/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java + static final String JMX_REMOTE_X_CHECK_STUB = "jmx.remote.x.check.stub"; //$NON-NLS-1$ + /** * Returns a JMX service URL based on the settings in the descriptor. Some implementations may * want to just return a pre-configured service URL, whilst others may want to resolve the URL @@ -70,4 +74,8 @@ public interface IConnectionDescriptor { * @return the JMX environment. Usually contains credentials and similar. */ Map getEnvironment(); + + default boolean requireSecureConnection() { + return "true".equals(getEnvironment().get(JMX_REMOTE_X_CHECK_STUB)); + } } diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/JMXConnectionDescriptor.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/JMXConnectionDescriptor.java index 5248836b0..d449e5b5c 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/JMXConnectionDescriptor.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/JMXConnectionDescriptor.java @@ -67,6 +67,10 @@ */ public final class JMXConnectionDescriptor implements IConnectionDescriptor { + // JMX convention. See e.g. + // https://github.com/openjdk/jdk/blob/master/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java + private static final String JMX_REMOTE_X_CHECK_STUB = "jmx.remote.x.check.stub"; //$NON-NLS-1$ + /** * The JMX service URL. */ @@ -74,12 +78,15 @@ public final class JMXConnectionDescriptor implements IConnectionDescriptor { private final ICredentials credentials; + private boolean requireSecureConnection; + /** * Full constructor. */ - public JMXConnectionDescriptor(JMXServiceURL url, ICredentials credentials) { + public JMXConnectionDescriptor(JMXServiceURL url, ICredentials credentials, boolean requireSecureConnection) { this.url = url; this.credentials = credentials; + this.requireSecureConnection = requireSecureConnection; } @Override @@ -112,6 +119,9 @@ public Map getEnvironment() { } catch (SecurityException e) { throw new RuntimeException(e); } + if (requireSecureConnection) { + env.put(JMX_REMOTE_X_CHECK_STUB, "true"); //$NON-NLS-1$ + } return env; } diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/RJMXConnection.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/RJMXConnection.java index 40d17360b..b626dd2a9 100755 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/RJMXConnection.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/RJMXConnection.java @@ -361,7 +361,8 @@ public boolean connect() throws ConnectionException { return true; } catch (Exception e) { m_server = null; - throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), url, e); + throw new WrappedConnectionException(m_serverDescriptor.getDisplayName(), url, + m_connectionDescriptor.requireSecureConnection(), e); } } } diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/WrappedConnectionException.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/WrappedConnectionException.java index f8efedf05..8b8e53634 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/WrappedConnectionException.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/internal/WrappedConnectionException.java @@ -51,13 +51,19 @@ public class WrappedConnectionException extends ConnectionException { private final JMXServiceURL url; private final String serverName; + private boolean requireSecureConnection; public WrappedConnectionException(String serverName, JMXServiceURL url, Exception cause) { super(cause.getMessage()); initCause(cause); // yes, still 1.4 compatible this.url = url; this.serverName = serverName; + } + public WrappedConnectionException(String serverName, JMXServiceURL url, boolean requireSecureConnection, + Exception cause) { + this(serverName, url, cause); + this.requireSecureConnection = requireSecureConnection; } @Override @@ -89,6 +95,10 @@ public String getLocalizedMessage() { return String.format(Messages.getString(Messages.ConnectionException_MSARMI_CHECK_PASSWORD), serverName, url); } + if (rootCause instanceof SecurityException && requireSecureConnection) { + return String.format(Messages.getString(Messages.ConnectionException_COULD_NOT_MAKE_SECURE_CONNECTION), + serverName, rootCause.getLocalizedMessage()); //$NON-NLS-1$ + } if (rootCause instanceof SecurityException || rootCause instanceof GeneralSecurityException) { return String.format(Messages.getString(Messages.ConnectionException_UNABLE_TO_RESOLVE_CREDENTIALS), serverName, rootCause.getLocalizedMessage()); diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/messages/internal/Messages.java b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/messages/internal/Messages.java index 41a8332b0..809f4e286 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/messages/internal/Messages.java +++ b/core/org.openjdk.jmc.rjmx.common/src/main/java/org/openjdk/jmc/rjmx/common/messages/internal/Messages.java @@ -49,6 +49,7 @@ public class Messages { public static final String ConnectionException_UNABLE_TO_CREATE_INITIAL_CONTEXT = "ConnectionException_UNABLE_TO_CREATE_INITIAL_CONTEXT"; //$NON-NLS-1$ public static final String ConnectionException_UNABLE_TO_RESOLVE_CREDENTIALS = "ConnectionException_UNABLE_TO_RESOLVE_CREDENTIALS"; //$NON-NLS-1$ public static final String ConnectionException_UNRESOLVED = "ConnectionException_UNRESOLVED"; //$NON-NLS-1$ + public static final String ConnectionException_COULD_NOT_MAKE_SECURE_CONNECTION = "ConnectionException_COULD_NOT_MAKE_SECURE_CONNECTION"; //$NON-NLS-1$ public static final String LABEL_NOT_AVAILABLE = "LABEL_NOT_AVAILABLE"; //$NON-NLS-1$ public static final String MBeanOperationsWrapper_DESCRIPTOR = "MBeanOperationsWrapper_DESCRIPTOR"; //$NON-NLS-1$ diff --git a/core/org.openjdk.jmc.rjmx.common/src/main/resources/org/openjdk/jmc/rjmx/common/messages/internal/messages.properties b/core/org.openjdk.jmc.rjmx.common/src/main/resources/org/openjdk/jmc/rjmx/common/messages/internal/messages.properties index 1bab5a1a1..cbae88e70 100644 --- a/core/org.openjdk.jmc.rjmx.common/src/main/resources/org/openjdk/jmc/rjmx/common/messages/internal/messages.properties +++ b/core/org.openjdk.jmc.rjmx.common/src/main/resources/org/openjdk/jmc/rjmx/common/messages/internal/messages.properties @@ -39,5 +39,6 @@ ConnectionException_UNABLE_TO_RESOLVE_CREDENTIALS=Unable to resolve the connecti ConnectionException_MSARMI_CHECK_PASSWORD=Unable to connect with msarmi protocol for {0}, using Service URL {1}. Verify that you have entered the correct password. ConnectionException_ATTACH_NOT_SUPPORTED=Attaching to the local JVM {0} is not supported: {1} ConnectionException_UNRESOLVED=Unresolved +ConnectionException_COULD_NOT_MAKE_SECURE_CONNECTION=Unable to make secure connection to {0} , check that the connection is secure and credentials are correct. Problem was: {1} LABEL_NOT_AVAILABLE=N/A MBeanOperationsWrapper_DESCRIPTOR=Descriptor