Skip to content
Draft
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ work/
!/.idea/runConfigurations
*.iml
.vscode
.cursor
.zed

# OS specific files
.DS_Store
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.client;

import org.exist.xmldb.XmldbURI;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.XMLDBException;

/**
* Loads document content for the Java Admin Client editor off the EDT.
* UI construction stays in {@link InteractiveClient#scheduleEditResource(org.exist.xmldb.XmldbURI)} (#4355).
*/
public final class ClientDocumentEditSupport {

private ClientDocumentEditSupport() {
}

/**
* Result of loading a resource for editing (XML:DB work only).
*/
public record DocumentEditPayload(XmldbURI name, Resource resource) {
}

/**
* Retrieves a resource to be shown in {@link DocumentView}.
*/
@FunctionalInterface
public interface DocumentRetriever {
Resource retrieve() throws XMLDBException;
}

/**
* Performs XML:DB retrieval only; must be called from a background thread.
*/
public static DocumentEditPayload load(final XmldbURI name, final DocumentRetriever retriever) throws XMLDBException {
return new DocumentEditPayload(name, retriever.retrieve());
}
}
373 changes: 209 additions & 164 deletions exist-core/src/main/java/org/exist/client/ClientFrame.java

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions exist-core/src/main/java/org/exist/client/ClientSwingEdt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.client;

import javax.swing.SwingUtilities;
import java.lang.reflect.InvocationTargetException;

/**
* Helpers for running work on the Swing event dispatch thread (EDT).
* Used by the Java Admin Client to satisfy Swing single-thread rules
* (<a href="https://github.com/eXist-db/exist/issues/4355">#4355</a>).
*/
final class ClientSwingEdt {

private ClientSwingEdt() {
}

/**
* Runs {@code task} on the EDT, blocking the caller until it completes.
* If the caller is already on the EDT, {@code task} runs immediately.
*/
static void invokeAndWaitIfNeeded(final Runnable task) {
if (SwingUtilities.isEventDispatchThread()) {
task.run();
} else {
try {
SwingUtilities.invokeAndWait(task);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException runtimeException) {
throw runtimeException;
}
if (cause instanceof Error error) {
throw error;
}
if (cause != null) {
throw new IllegalStateException("EDT task failed", cause);
}
throw new IllegalStateException("EDT task failed", e);
}
}
}

/**
* Runs {@code task} asynchronously on the EDT when the caller is not on the EDT.
* If the caller is already on the EDT, {@code task} runs immediately.
*/
static void invokeLaterIfNeeded(final Runnable task) {
if (SwingUtilities.isEventDispatchThread()) {
task.run();
} else {
SwingUtilities.invokeLater(task);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* info@exist-db.org
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.client;

import javax.swing.SwingWorker;
import java.util.concurrent.ExecutionException;

/**
* Shared {@link SwingWorker} pattern for XML:DB work off the EDT with EDT completion (#4355).
*
* @param <T> result type produced in {@link #loadInBackground()}
*/
public abstract class ClientSwingXmlWorker<T> extends SwingWorker<T, Void> {

/**
* Runs off the EDT; perform XML:DB / blocking I/O here.
*/
protected abstract T loadInBackground() throws Exception;

@Override
protected final T doInBackground() throws Exception {
return loadInBackground();
}

/**
* Runs on the EDT after {@link #loadInBackground()} completed successfully.
* Default implementation is intentionally empty; subclasses override to update the UI.
*/
protected void onSuccess(final T result) {
// no-op — override in subclasses
}

/**
* Runs on the EDT when {@link #loadInBackground()} failed.
*/
protected void onFailure(final Throwable t) {
if (t instanceof Exception ex) {
ClientFrame.showErrorMessage(ex.getMessage(), ex);
} else {
ClientFrame.showErrorMessage(t.toString(), new Exception(t));
}
}

@Override
protected final void done() {
try {
onSuccess(get());
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
} catch (final ExecutionException e) {
final Throwable cause = e.getCause() != null ? e.getCause() : e;
onFailure(cause);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public ConnectionDialog(final java.awt.Frame parent, final boolean modal, final
this.defaultConnectionSettings = defaultConnectionSettings;
this.config = Path.of(defaultConnectionSettings.getConfiguration());
this.disableEmbeddedConnectionType = disableEmbeddedConnectionType;
this.setIconImage(InteractiveClient.getExistIcon(getClass()).getImage());
InteractiveClient.setExistImage(getClass(), this::setIconImage);
initComponents();

if (disableEmbeddedConnectionType) {
Expand Down Expand Up @@ -239,7 +239,7 @@ protected final void paintTabBorder(final java.awt.Graphics g, final int tabPlac
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle("Database Connection");

lblExistLogo.setIcon(InteractiveClient.getExistIcon(getClass()));
InteractiveClient.setExistImageIcon(getClass(), lblExistLogo::setIcon);

lblUsername.setText(getLabelText("LoginPanel.2"));

Expand Down
Loading
Loading