From 0ec4c5db870aa226a7b09f44df6b766a9def5c20 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Mon, 13 Apr 2026 11:04:51 +0530 Subject: [PATCH] Fix Payara remote instance handling: improve error reporting and version mismatch detection Fix Payara remote instance handling: improve error reporting and version mismatch detection - Add RemoteInstanceStateListener to handle remote server-specific warnings - Introduce new TaskEvent types: VERSION_MISMATCH, CONNECTION_FAILED, CMD_URL_FAILED - Improve CommandVersion logic with isVersionMismatch() for accurate comparison - Treat version mismatch on remote servers as SUCCESS with warning instead of failure - Update Runner to emit CONNECTION_FAILED instead of generic EXCEPTION - Add user-facing warning messages for: - version mismatch - connection failure (host/port issues) - command URL construction errors - Prevent popup spam with throttling in RemoteInstanceStateListener --- .../modules/payara/common/PayaraState.java | 6 + .../payara/common/status/Bundle.properties | 12 ++ .../status/RemoteInstanceStateListener.java | 169 ++++++++++++++++++ .../modules/payara/tooling/TaskEvent.java | 23 ++- .../payara/tooling/admin/CommandVersion.java | 41 ++++- .../modules/payara/tooling/admin/Runner.java | 2 +- .../tooling/server/state/StatusJob.java | 15 ++ 7 files changed, 260 insertions(+), 8 deletions(-) create mode 100644 enterprise/payara.common/src/org/netbeans/modules/payara/common/status/RemoteInstanceStateListener.java diff --git a/enterprise/payara.common/src/org/netbeans/modules/payara/common/PayaraState.java b/enterprise/payara.common/src/org/netbeans/modules/payara/common/PayaraState.java index eb3ea12e0a43..8ff06297e738 100644 --- a/enterprise/payara.common/src/org/netbeans/modules/payara/common/PayaraState.java +++ b/enterprise/payara.common/src/org/netbeans/modules/payara/common/PayaraState.java @@ -24,6 +24,7 @@ import org.netbeans.modules.payara.tooling.data.PayaraPlatformVersionAPI; import org.netbeans.modules.payara.common.status.AuthFailureStateListener; import org.netbeans.modules.payara.common.status.MonitoringInitStateListener; +import org.netbeans.modules.payara.common.status.RemoteInstanceStateListener; import org.openide.util.NbBundle; import org.netbeans.modules.payara.tooling.data.PayaraServer; import org.netbeans.modules.payara.tooling.data.PayaraServerStatus; @@ -114,6 +115,11 @@ public static boolean monitor(final PayaraServer instance) { PayaraStatus.addChangeListener(instance, authListener, PayaraStatus.STARTUP); PayaraStatus.addErrorListener(instance, authListener); } + if (instance.isRemote()) { + RemoteInstanceStateListener remoteInstanceListener + = new RemoteInstanceStateListener(); + PayaraStatus.addErrorListener(instance, remoteInstanceListener); + } try { long startTime = System.currentTimeMillis(); long waitTime = INIT_MONITORING_TIMEOUT; diff --git a/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/Bundle.properties b/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/Bundle.properties index f000c7be7dfa..34bc3a081548 100644 --- a/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/Bundle.properties +++ b/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/Bundle.properties @@ -19,4 +19,16 @@ AuthFailureStateListener.message=Authorization failed while checking {0} \ status. Please provide valid administrator credentials. AuthFailureStateListener.error.exception=Exception was thrown in server status \ check. +RemoteInstanceStateListener.versionMismatch=The version of the remote \ +Payara server {0} does not match the version of the local Payara \ +installation. Some features may not work correctly. Consider updating the \ +server registration or aligning the server versions. +RemoteInstanceStateListener.connectionFailed=Could not connect to the \ +administration interface of Payara server {0} at {1}:{2}. Please \ +verify that the host name and administration port are correct in the server \ +registration. +RemoteInstanceStateListener.commandException=Could not build the \ +administration command URL for Payara server {0}. The configured \ +host {1} or port {2} may be malformed. Please verify the \ +server registration settings. diff --git a/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/RemoteInstanceStateListener.java b/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/RemoteInstanceStateListener.java new file mode 100644 index 000000000000..b5b77fc88be7 --- /dev/null +++ b/enterprise/payara.common/src/org/netbeans/modules/payara/common/status/RemoteInstanceStateListener.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.payara.common.status; + +import org.netbeans.modules.payara.tooling.PayaraStatus; +import static org.netbeans.modules.payara.tooling.data.PayaraStatusCheck.VERSION; +import org.netbeans.modules.payara.tooling.TaskEvent; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.NbBundle; +import org.netbeans.modules.payara.tooling.data.PayaraServer; +import org.netbeans.modules.payara.tooling.data.PayaraStatusTask; + +/** + * Handle version mismatch and connection/URL failures in administration command + * calls during server status monitoring. + *

+ * Displays a warning notification to the user in three cases: + *

+ *

+ * For every Payara server instance being monitored there must be its own + * {@code RemoteInstanceStateListener} instance to avoid duplicate popups. + *

+ * @author Gaurav Gupta + */ +public class RemoteInstanceStateListener extends BasicStateListener { + + // Class attributes // + /** Minimal delay between displaying warning popups [ms]. + *

+ * Currently it shall not open a popup again sooner than after 60 seconds + * to avoid spamming the user during periodic status checks. */ + private static final long POPUP_DELAY = 60000; + + // Instance attributes // + /** Timestamp of last popup window. */ + private volatile long lastTm; + + // Constructors // + /** + * Constructs an instance of the remote instance state notification handler. + */ + public RemoteInstanceStateListener() { + super(); + this.lastTm = 0; + } + + // Methods // + /** + * Callback to notify about current server status after every check + * when enabled. + *

+ * Not used. + *

+ * @param server Payara server instance being monitored. + * @param status Current server status. + * @param task Last Payara server status check task details. + */ + @Override + public void currentState(final PayaraServer server, + final PayaraStatus status, final PayaraStatusTask task) { + // Not used. + } + + /** + * Callback to notify about server status change when enabled. + *

+ * Not used. + *

+ * @param server Payara server instance being monitored. + * @param status Current server status. + * @param task Last Payara server status check task details. + */ + @Override + public void newState(final PayaraServer server, + final PayaraStatus status, final PayaraStatusTask task) { + // Not used. + } + + /** + * Callback to notify about server status check failures. + *

+ * Shows a non-blocking warning popup on the VERSION check for: + *

+ *

+ * @param server Payara server instance being monitored. + * @param task Payara server status check task details. + */ + @Override + public void error(final PayaraServer server, + final PayaraStatusTask task) { + if (task.getType() != VERSION) { + return; + } + TaskEvent event = task.getEvent(); + if (event == TaskEvent.VERSION_MISMATCH) { + showWarningNotification(server, "RemoteInstanceStateListener.versionMismatch", + server.getName()); + } else if (event == TaskEvent.CONNECTION_FAILED) { + // Connection failed — wrong host or port configured for the server. + showWarningNotification(server, "RemoteInstanceStateListener.connectionFailed", + server.getName(), server.getHost(), + Integer.toString(server.getAdminPort())); + } else if (event == TaskEvent.CMD_EXCEPTION) { + // constructCommandUrl() threw a CommandException — host/port + // value may be malformed. + showWarningNotification(server, "RemoteInstanceStateListener.commandException", + server.getName(), server.getHost(), + Integer.toString(server.getAdminPort())); + } + } + + /** + * Shows a non-blocking warning notification at most once per + * {@link #POPUP_DELAY} milliseconds. + *

+ * @param server Payara server instance (unused, for future use). + * @param bundleKey NbBundle key for the message text. + * @param args Arguments to the bundle message. + */ + private void showWarningNotification(final PayaraServer server, + final String bundleKey, final Object... args) { + long now = System.currentTimeMillis(); + synchronized (this) { + if (now - lastTm < POPUP_DELAY) { + return; + } + lastTm = now; + } + String message = NbBundle.getMessage( + RemoteInstanceStateListener.class, bundleKey, args); + NotifyDescriptor nd = new NotifyDescriptor.Message( + message, NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notifyLater(nd); + } + +} diff --git a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/TaskEvent.java b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/TaskEvent.java index af6bf09e77fb..c606d068b218 100644 --- a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/TaskEvent.java +++ b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/TaskEvent.java @@ -65,7 +65,14 @@ public enum TaskEvent { /** Java VM execution failed. */ JAVA_VM_EXEC_FAILED, /** Signals wrong proxy settings. */ - BAD_GATEWAY; + BAD_GATEWAY, + + /** Version returned by server does not match expected version. */ + VERSION_MISMATCH, + /** Connection to the server administration interface failed. */ + CONNECTION_FAILED, + /** Administration command URL could not be constructed. */ + CMD_URL_FAILED; // Class attributes // /** A String representation of SUBMIT value. */ @@ -119,6 +126,15 @@ public enum TaskEvent { /** A String representation of BAD_GATEWAY value. */ private static final String BAD_GATEWAY_STR = "BadGateway"; + /** A String representation of VERSION_MISMATCH value. */ + private static final String VERSION_MISMATCH_STR = "VersionMismatch"; + + /** A String representation of CONNECTION_FAILED value. */ + private static final String CONNECTION_FAILED_STR = "ConnectionFailed"; + + /** A String representation of CMD_URL_FAILED value. */ + private static final String CMD_URL_FAILED_STR = "CmdUrlFailed"; + /** * Stored String values for backward String * conversion. @@ -180,7 +196,10 @@ public String toString() { case WRONG_JAVA_VM: return WRONG_JAVA_VM_STR; case JAVA_VM_EXEC_FAILED: return JAVA_VM_EXEC_FAILED_STR; case BAD_GATEWAY: return BAD_GATEWAY_STR; - // This is unrecheable. Returned null value means that some + case VERSION_MISMATCH: return VERSION_MISMATCH_STR; + case CONNECTION_FAILED: return CONNECTION_FAILED_STR; + case CMD_URL_FAILED: return CMD_URL_FAILED_STR; + // This is unreachable. Returned null value means that some // enum value is not handled correctly. default: return null; } diff --git a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/CommandVersion.java b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/CommandVersion.java index 4fbe3eda2719..bf67e24d4451 100644 --- a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/CommandVersion.java +++ b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/CommandVersion.java @@ -117,14 +117,17 @@ public static PayaraPlatformVersionAPI getPayaraPlatformVersion( } /** - * Verifies if domain directory returned by version command result matches - * domain directory of provided Payara server entity. + * Verifies if version command result confirms Payara server is running. + *

+ * The version returned by the server must match the version of the local + * Payara installation. Both major and minor version values are compared. *

* @param result Version command result. * @param server Payara server entity. - * @return For local server value of true means that server - * major and minor version value matches values returned by version - * command and value of false that they differs. + * @return Value of true means that server major and minor + * version value matches values returned by version command and + * value of false that they differ or a comparison + * could not be made. */ public static boolean verifyResult( final ResultString result, final PayaraServer server) { @@ -140,6 +143,34 @@ public static boolean verifyResult( return verifyResult; } + /** + * Checks whether the version returned by the server is a known mismatch + * against the locally registered Payara installation. + *

+ * A version mismatch is reported only when both the server response and + * the local installation carry parseable version information that differs. + * When either side is unknown this method returns false. + *

+ * @param result Version command result. + * @param server Payara server entity. + * @return Value of true when the server responded with a + * version that is different from the locally registered version, + * or false when the versions match or comparison + * could not be performed. + */ + public static boolean isVersionMismatch( + final ResultString result, final PayaraServer server) { + String value = ServerUtils.getVersionString(result.getValue()); + if (value != null) { + PayaraPlatformVersionAPI valueVersion = PayaraPlatformVersion.toValue(value); + PayaraPlatformVersionAPI serverVersion = server.getPlatformVersion(); + if (valueVersion != null && serverVersion != null) { + return !serverVersion.equals(valueVersion); + } + } + return false; + } + // Constructors // /** * Constructs an instance of Payara server version command entity. diff --git a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/Runner.java b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/Runner.java index 88b303c926bb..ef2ab2210a57 100644 --- a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/Runner.java +++ b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/admin/Runner.java @@ -902,7 +902,7 @@ public Result call() { retries = 0; } catch (ConnectException ce) { return handleStateChange(TaskState.FAILED, - TaskEvent.EXCEPTION, + TaskEvent.CONNECTION_FAILED, stateChangeArgs(ce.getLocalizedMessage())); } catch (IOException ex) { if (retries <= 0) { diff --git a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/server/state/StatusJob.java b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/server/state/StatusJob.java index 52331471334a..5fe9ba785d7c 100644 --- a/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/server/state/StatusJob.java +++ b/enterprise/payara.tooling/src/org/netbeans/modules/payara/tooling/server/state/StatusJob.java @@ -298,6 +298,21 @@ public void operationStateChanged(final TaskState newState, event)); break; } + // COMPLETED but versions do not match. + // For remote servers, accept the server as + // running (SUCCESS) but fire VERSION_MISMATCH + // so the user is notified via a warning popup. + if (job.status.getServer().isRemote() + && taskResult != null + && CommandVersion.isVersionMismatch( + taskResult, job.status.getServer())) { + job.version.setResult(new StatusResultVersion( + taskResult, + PayaraStatusCheckResult.SUCCESS, + TaskEvent.VERSION_MISMATCH)); + notifyError = true; + break; + } case FAILED: job.version.setResult(new StatusResultVersion(taskResult, PayaraStatusCheckResult.FAILED,