Skip to content

Commit c368768

Browse files
committed
Extract LoginPanel interface with SPI discovery
- Create LoginPanel interface with LoginSuccessHandler callback, 5-parameter initialize, setStatus, setVisible, isVisible, and showLoginNotification methods - Update DefaultLoginPanel: extends JFrame implements LoginPanel, package-private constructor, store onSuccess callback, add showLoginNotification with proper EDT marshalling via invokeAndWait - Rewrite LoginPanelFactory with ServiceLoader (SPI) discovery and getLoginPanel() API, falling back to DefaultLoginPanel - Add Mirth.showLogin() as single entry point for login initiation, passing Mirth::handleLoginSuccess as the callback - Make Mirth.handleLoginSuccess private, replace CustomBannerPanelDialog with loginPanel.showLoginNotification() delegation - Decouple Frame from login panel: setupFrame accepts Consumer<String> statusCallback, re-login sites use Mirth.showLogin() - Delete AbstractLoginPanel.java
1 parent 14dc6d9 commit c368768

File tree

7 files changed

+273
-76
lines changed

7 files changed

+273
-76
lines changed

client/src/com/mirth/connect/client/ui/AbstractLoginPanel.java

Lines changed: 0 additions & 17 deletions
This file was deleted.

client/src/com/mirth/connect/client/ui/LoginPanel.form renamed to client/src/com/mirth/connect/client/ui/DefaultLoginPanel.form

File renamed without changes.

client/src/com/mirth/connect/client/ui/DefaultLoginPanel.java

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66

77
import java.awt.Color;
88
import java.awt.Cursor;
9+
import java.lang.reflect.InvocationTargetException;
910
import java.util.Collections;
1011
import java.util.HashMap;
1112
import java.util.List;
1213
import java.util.Map;
13-
import java.util.Set;
14+
import java.util.concurrent.atomic.AtomicBoolean;
1415

1516
import javax.swing.ImageIcon;
17+
import javax.swing.SwingUtilities;
1618
import javax.swing.SwingWorker;
1719

1820
import org.apache.commons.lang3.StringUtils;
21+
import org.apache.logging.log4j.LogManager;
22+
import org.apache.logging.log4j.Logger;
1923

2024
import com.mirth.connect.client.core.Client;
2125
import com.mirth.connect.client.core.ClientException;
@@ -26,11 +30,14 @@
2630
import com.mirth.connect.model.LoginStatus;
2731
import com.mirth.connect.plugins.MultiFactorAuthenticationClientPlugin;
2832

29-
public class DefaultLoginPanel extends AbstractLoginPanel {
33+
class DefaultLoginPanel extends javax.swing.JFrame implements LoginPanel {
3034

35+
private static final Logger logger = LogManager.getLogger(DefaultLoginPanel.class);
3136
private static final String ERROR_MESSAGE = "There was an error connecting to the server at the specified address. Please verify that the server is up and running.";
3237

33-
public DefaultLoginPanel() {
38+
private LoginSuccessHandler onSuccess;
39+
40+
DefaultLoginPanel() {
3441
initComponents();
3542
DisplayUtil.setResizable(this, false);
3643
jLabel2.setForeground(UIConstants.HEADER_TITLE_TEXT_COLOR);
@@ -69,12 +76,21 @@ public void mouseClicked(java.awt.event.MouseEvent evt) {
6976
}
7077

7178
@Override
72-
public void initialize(String mirthServer, String version, String user, String pass) {
79+
public void initialize(String mirthServer, String version, String user, String pass, LoginSuccessHandler onSuccess) {
80+
if (SwingUtilities.isEventDispatchThread()) {
81+
doInitialize(mirthServer, version, user, pass, onSuccess);
82+
} else {
83+
SwingUtilities.invokeLater(() -> doInitialize(mirthServer, version, user, pass, onSuccess));
84+
}
85+
}
86+
87+
private void doInitialize(String mirthServer, String version, String user, String pass, LoginSuccessHandler onSuccess) {
7388
synchronized (this) {
7489
// Do not initialize another login window if one is already visible
7590
if (isVisible()) {
7691
return;
7792
}
93+
this.onSuccess = onSuccess;
7894

7995
setTitle(String.format("%s %s - Login", BrandingConstants.PRODUCT_NAME, version));
8096
setIconImage(BrandingConstants.FAVICON.getImage());
@@ -392,7 +408,7 @@ private void passwordActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST
392408
private void loginButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FIRST:event_loginButtonActionPerformed
393409
{// GEN-HEADEREND:event_loginButtonActionPerformed
394410
errorPane.setVisible(false);
395-
LoginPanel loginPanel = this;
411+
DefaultLoginPanel loginPanel = this;
396412

397413
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
398414

@@ -425,9 +441,9 @@ public Void doInBackground() {
425441

426442
// If SUCCESS or SUCCESS_GRACE_PERIOD
427443
if (loginStatus != null && loginStatus.isSuccess()) {
428-
if (!Mirth.handleLoginSuccess(client, loginStatus, username.getText())) {
444+
if (!onSuccess.handle(client, loginStatus, username.getText())) {
429445
loginPanel.setVisible(false);
430-
loginPanel.initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "");
446+
loginPanel.initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "", onSuccess);
431447
}
432448
} else {
433449
// Assume failure unless overridden by a plugin
@@ -444,9 +460,9 @@ public Void doInBackground() {
444460

445461
if (loginStatus != null && loginStatus.isSuccess()) {
446462
errorOccurred = false;
447-
if (!Mirth.handleLoginSuccess(client, loginStatus, username.getText())) {
463+
if (!onSuccess.handle(client, loginStatus, username.getText())) {
448464
loginPanel.setVisible(false);
449-
loginPanel.initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "");
465+
loginPanel.initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "", onSuccess);
450466
}
451467
}
452468
}
@@ -487,18 +503,48 @@ private void closeButtonActionPerformed(java.awt.event.ActionEvent evt)// GEN-FI
487503
System.exit(0);
488504
}// GEN-LAST:event_closeButtonActionPerformed
489505

506+
@Override
507+
public void setVisible(boolean visible) {
508+
if (SwingUtilities.isEventDispatchThread()) {
509+
super.setVisible(visible);
510+
} else {
511+
SwingUtilities.invokeLater(() -> super.setVisible(visible));
512+
}
513+
}
514+
490515
@Override
491516
public void setStatus(String status) {
492-
this.status.setText("Please wait: " + status);
517+
SwingUtilities.invokeLater(() -> this.status.setText("Please wait: " + status));
493518
}
494519

495-
public void setError(String status) {
496-
errorTextArea.setText(status);
497-
errorPane.setVisible(true);
498-
loggingIn.setVisible(false);
499-
loginMain.setVisible(true);
500-
loginProgress.setIndeterminate(false);
501-
password.grabFocus();
520+
private void setError(String status) {
521+
SwingUtilities.invokeLater(() -> {
522+
errorTextArea.setText(status);
523+
errorPane.setVisible(true);
524+
loggingIn.setVisible(false);
525+
loginMain.setVisible(true);
526+
loginProgress.setIndeterminate(false);
527+
password.grabFocus();
528+
});
529+
}
530+
531+
@Override
532+
public boolean showLoginNotification(String title, String message) {
533+
// Called from a background thread (SwingWorker.doInBackground via handleLoginSuccess).
534+
// Swing dialogs must be created and shown on the EDT to avoid deadlocks.
535+
final AtomicBoolean accepted = new AtomicBoolean(false);
536+
try {
537+
SwingUtilities.invokeAndWait(() -> {
538+
CustomBannerPanelDialog dialog = new CustomBannerPanelDialog(this, title, message);
539+
accepted.set(dialog.isAccepted());
540+
});
541+
} catch (InterruptedException e) {
542+
logger.warn("Login notification dialog interrupted; treating as declined");
543+
Thread.currentThread().interrupt();
544+
} catch (InvocationTargetException e) {
545+
throw new RuntimeException(e.getCause());
546+
}
547+
return accepted.get();
502548
}
503549

504550
// Variables declaration - do not modify//GEN-BEGIN:variables

client/src/com/mirth/connect/client/ui/Frame.java

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
/*
2-
* Copyright (c) Mirth Corporation. All rights reserved.
3-
*
4-
* http://www.mirthcorp.com
5-
*
6-
* The software in this package is published under the terms of the MPL license a copy of which has
7-
* been included with this distribution in the LICENSE.txt file.
8-
*/
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: Mirth Corporation
3+
// SPDX-FileCopyrightText: 2025-2026 Open Integration Engine Contributors
94

105
package com.mirth.connect.client.ui;
116

@@ -57,6 +52,7 @@
5752
import java.util.concurrent.ExecutorService;
5853
import java.util.concurrent.Executors;
5954
import java.util.concurrent.Future;
55+
import java.util.function.Consumer;
6056
import java.util.prefs.Preferences;
6157
import java.util.regex.Matcher;
6258
import java.util.regex.Pattern;
@@ -541,15 +537,13 @@ public void eventDispatched(AWTEvent e)
541537
/**
542538
* Called to set up this main window frame.
543539
*/
544-
public void setupFrame(Client mirthClient) throws ClientException {
545-
546-
AbstractLoginPanel login = LoginPanelFactory.getInstance();
540+
public void setupFrame(Client mirthClient, Consumer<String> statusCallback) throws ClientException {
547541

548542
// Initialize the send message dialog
549543
editMessageDialog = new EditMessageDialog();
550544

551545
this.mirthClient = mirthClient;
552-
login.setStatus("Loading extensions...");
546+
statusCallback.accept("Loading extensions...");
553547
try {
554548
loadExtensionMetaData();
555549
} catch (ClientException e) {
@@ -589,10 +583,10 @@ public void setupFrame(Client mirthClient) throws ClientException {
589583
}
590584

591585
setInitialVisibleTasks();
592-
login.setStatus("Loading preferences...");
586+
statusCallback.accept("Loading preferences...");
593587
userPreferences = Preferences.userNodeForPackage(Mirth.class);
594588
userPreferences.put("defaultServer", PlatformUI.SERVER_URL);
595-
login.setStatus("Loading GUI components...");
589+
statusCallback.accept("Loading GUI components...");
596590
splitPane.setDividerSize(0);
597591
splitPane.setBorder(BorderFactory.createEmptyBorder());
598592

@@ -661,15 +655,15 @@ public void setupFrame(Client mirthClient) throws ClientException {
661655
}
662656

663657
setCurrentTaskPaneContainer(taskPaneContainer);
664-
login.setStatus("Loading dashboard...");
658+
statusCallback.accept("Loading dashboard...");
665659
doShowDashboard();
666-
login.setStatus("Loading channel editor...");
660+
statusCallback.accept("Loading channel editor...");
667661
channelEditPanel = new ChannelSetup();
668-
login.setStatus("Loading alert editor...");
662+
statusCallback.accept("Loading alert editor...");
669663
if (alertEditPanel == null) {
670664
alertEditPanel = new DefaultAlertEditPanel();
671665
}
672-
login.setStatus("Loading message browser...");
666+
statusCallback.accept("Loading message browser...");
673667
messageBrowser = new MessageBrowser();
674668

675669
// Refresh code templates after extensions have been loaded
@@ -1524,7 +1518,7 @@ public void alertThrowable(Component parentComponent, Throwable t, String custom
15241518
}
15251519
mirthClient.close();
15261520
this.dispose();
1527-
LoginPanelFactory.getInstance().initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "");
1521+
Mirth.showLogin();
15281522
return;
15291523
} else if (t.getCause() != null && t.getCause() instanceof HttpHostConnectException && (StringUtils.contains(t.getCause().getMessage(), "Connection refused") || StringUtils.contains(t.getCause().getMessage(), "Host is down"))) {
15301524
connectionError = true;
@@ -1542,7 +1536,7 @@ public void alertThrowable(Component parentComponent, Throwable t, String custom
15421536
}
15431537
mirthClient.close();
15441538
this.dispose();
1545-
LoginPanelFactory.getInstance().initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "");
1539+
Mirth.showLogin();
15461540
return;
15471541
}
15481542
}
@@ -2292,7 +2286,7 @@ public boolean logout(boolean quit, boolean confirmFirst) {
22922286
this.dispose();
22932287

22942288
if (!quit) {
2295-
LoginPanelFactory.getInstance().initialize(PlatformUI.SERVER_URL, PlatformUI.CLIENT_VERSION, "", "");
2289+
Mirth.showLogin();
22962290
}
22972291

22982292
return true;

0 commit comments

Comments
 (0)