From 601d7c52e39c09e7e79b0aeee4b6ea951f559a72 Mon Sep 17 00:00:00 2001 From: jurgen Date: Fri, 17 Apr 2015 18:04:27 +0000 Subject: [PATCH] Txn close & disconnect in detached job to avoid UI freeze Former-commit-id: 2a01f48836b8d8c669c33b9a186a467c485b3d39 --- .../ApplicationWorkbenchAdvisor.java | 26 ---- .../registry/DataSourceDescriptor.java | 146 +++++++++++------- .../dbeaver/registry/DataSourceRegistry.java | 38 +---- .../jkiss/dbeaver/runtime/RuntimeUtils.java | 62 +++++++- .../dbeaver/runtime/jobs/DisconnectJob.java | 5 +- .../dbeaver/runtime/sql/SQLQueryJob.java | 6 +- 6 files changed, 157 insertions(+), 126 deletions(-) diff --git a/plugins/org.jkiss.dbeaver.core.application/src/org/jkiss/dbeaver/core/application/ApplicationWorkbenchAdvisor.java b/plugins/org.jkiss.dbeaver.core.application/src/org/jkiss/dbeaver/core/application/ApplicationWorkbenchAdvisor.java index 8772bae306..8c697c2a3d 100644 --- a/plugins/org.jkiss.dbeaver.core.application/src/org/jkiss/dbeaver/core/application/ApplicationWorkbenchAdvisor.java +++ b/plugins/org.jkiss.dbeaver.core.application/src/org/jkiss/dbeaver/core/application/ApplicationWorkbenchAdvisor.java @@ -140,32 +140,6 @@ public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor if (!closeActiveTransactions()) { return false; } -/* - final IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); - DBRProgressMonitor nullMonitor = VoidProgressMonitor.INSTANCE; - for (IWorkbenchPage workbenchPage : workbenchWindow.getPages()) { - for (IEditorReference editorRef : workbenchPage.getEditorReferences()) { - try { - if (editorRef.getEditor(false) == null) { - continue; - } - IEditorInput editorInput = editorRef.getEditorInput(); - - if (editorInput instanceof IAutoSaveEditorInput && ((IAutoSaveEditorInput) editorInput).isAutoSaveEnabled()) { - IEditorPart editor = editorRef.getEditor(false); - if (editor != null) { - if (!RuntimeUtils.validateAndSave(nullMonitor, editor)) { - return false; - } - } - } - } catch (CoreException ex) { - log.error("Can't obtain editor storage", ex); //$NON-NLS-1$ - } - } - } -*/ - return true; } diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceDescriptor.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceDescriptor.java index aa6251a001..e88d83c425 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceDescriptor.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceDescriptor.java @@ -81,6 +81,8 @@ public class DataSourceDescriptor { static final Log log = Log.getLog(DataSourceDescriptor.class); + public static final int END_TRANSACTION_WAIT_TIME = 3000; + public static class FilterMapping { public final Class type; public DBSObjectFilter defaultFilter; @@ -864,62 +866,67 @@ public class DataSourceDescriptor } // First rollback active transaction - monitor.subTask("Rollback active transaction"); DBCTransactionManager txnManager = DBUtils.getTransactionManager(dataSource); try { - if (txnManager != null && !txnManager.isAutoCommit()) { - // Check current transaction - - // If there are some executions in last savepoint then ask user about commit/rollback - QMMCollector qmm = DBeaverCore.getInstance().getQueryManager().getMetaCollector(); - if (qmm != null) { - QMMSessionInfo qmmSession = qmm.getSessionInfo(dataSource); - QMMTransactionInfo txn = qmmSession == null ? null : qmmSession.getTransaction(); - QMMTransactionSavepointInfo sp = txn == null ? null : txn.getCurrentSavepoint(); - if (sp != null && (sp.getPrevious() != null || sp.getLastExecute() != null)) { - boolean hasUserExec = false; - if (true) { - // Do not check whether we have user queries, just ask for confirmation - hasUserExec = true; - } else { - for (QMMTransactionSavepointInfo psp = sp; psp != null; psp = psp.getPrevious()) { - if (psp.hasUserExecutions()) { - hasUserExec = true; - break; - } + if (txnManager == null || txnManager.isAutoCommit()) { + return true; + } + + // If there are some executions in last savepoint then ask user about commit/rollback + QMMCollector qmm = DBeaverCore.getInstance().getQueryManager().getMetaCollector(); + if (qmm != null) { + QMMSessionInfo qmmSession = qmm.getSessionInfo(dataSource); + QMMTransactionInfo txn = qmmSession == null ? null : qmmSession.getTransaction(); + QMMTransactionSavepointInfo sp = txn == null ? null : txn.getCurrentSavepoint(); + if (sp != null && (sp.getPrevious() != null || sp.getLastExecute() != null)) { + boolean hasUserExec = false; + if (true) { + // Do not check whether we have user queries, just ask for confirmation + hasUserExec = true; + } else { + for (QMMTransactionSavepointInfo psp = sp; psp != null; psp = psp.getPrevious()) { + if (psp.hasUserExecutions()) { + hasUserExec = true; + break; } } - if (hasUserExec) { - // Ask for confirmation - TransactionCloseConfirmer closeConfirmer = new TransactionCloseConfirmer(); - UIUtils.runInUI(null, closeConfirmer); - DBCSession session = dataSource.openSession(monitor, DBCExecutionPurpose.UTIL, "End active transaction"); - try { - switch (closeConfirmer.result) { - case IDialogConstants.YES_ID: - txnManager.commit(session); - break; - case IDialogConstants.NO_ID: - txnManager.rollback(session, null); - break; - default: - return false; - } - } finally { - session.close(); + } + if (hasUserExec) { + // Ask for confirmation + TransactionCloseConfirmer closeConfirmer = new TransactionCloseConfirmer(getName()); + UIUtils.runInUI(null, closeConfirmer); + DBCSession session = dataSource.openSession(monitor, DBCExecutionPurpose.UTIL, "End active transaction"); + try { + boolean commit; + switch (closeConfirmer.result) { + case IDialogConstants.YES_ID: + commit = true; + break; + case IDialogConstants.NO_ID: + commit = false; + break; + default: + return false; } + monitor.subTask("End active transaction"); + EndTransactionTask task = new EndTransactionTask(session, commit); + RuntimeUtils.runTask(task, END_TRANSACTION_WAIT_TIME); + } finally { + session.close(); } + return true; } } } + return true; } catch (Throwable e) { log.warn("Could not rollback active transaction before disconnect", e); + return true; } finally { monitor.worked(1); } - return true; } @Override @@ -1149,19 +1156,6 @@ public class DataSourceDescriptor } } - private class TransactionCloseConfirmer implements Runnable { - int result = IDialogConstants.NO_ID; - @Override - public void run() - { - result = ConfirmationDialog.showConfirmDialog( - null, - DBeaverPreferences.CONFIRM_TXN_DISCONNECT, - ConfirmationDialog.QUESTION_WITH_CANCEL, - getName()); - } - } - public void copyFrom(DataSourceDescriptor descriptor) { filterMap.clear(); for (FilterMapping mapping : descriptor.getObjectFilters()) { @@ -1180,4 +1174,50 @@ public class DataSourceDescriptor public String toString() { return name + " [" + driver + "]"; } + + private static class EndTransactionTask implements DBRRunnableWithProgress { + private final DBCSession session; + private final boolean commit; + + private EndTransactionTask(DBCSession session, boolean commit) { + this.session = session; + this.commit = commit; + } + + @Override + public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + DBCTransactionManager txnManager = DBUtils.getTransactionManager(session.getDataSource()); + if (txnManager != null) { + try { + if (commit) { + txnManager.commit(session); + } else { + txnManager.rollback(session, null); + } + } catch (DBCException e) { + throw new InvocationTargetException(e); + } + } + } + } + + private static class TransactionCloseConfirmer implements Runnable { + final String name; + int result = IDialogConstants.NO_ID; + + private TransactionCloseConfirmer(String name) { + this.name = name; + } + + @Override + public void run() + { + result = ConfirmationDialog.showConfirmDialog( + null, + DBeaverPreferences.CONFIRM_TXN_DISCONNECT, + ConfirmationDialog.QUESTION_WITH_CANCEL, + name); + } + } + } diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceRegistry.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceRegistry.java index c581fdd741..1965e92013 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceRegistry.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/registry/DataSourceRegistry.java @@ -18,19 +18,15 @@ */ package org.jkiss.dbeaver.registry; -import org.jkiss.dbeaver.core.Log; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.resource.StringConverter; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; -import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.core.DBeaverUI; +import org.jkiss.dbeaver.core.Log; import org.jkiss.dbeaver.model.*; import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; @@ -43,7 +39,6 @@ import org.jkiss.dbeaver.model.struct.rdb.DBSSchema; import org.jkiss.dbeaver.registry.encode.EncryptionException; import org.jkiss.dbeaver.registry.encode.PasswordEncrypter; import org.jkiss.dbeaver.registry.encode.SimpleStringEncrypter; -import org.jkiss.dbeaver.runtime.AbstractJob; import org.jkiss.dbeaver.runtime.RuntimeUtils; import org.jkiss.dbeaver.runtime.VoidProgressMonitor; import org.jkiss.dbeaver.utils.AbstractPreferenceStore; @@ -128,34 +123,7 @@ public class DataSourceRegistry implements DBPDataSourceRegistry return; } final DisconnectTask disconnectTask = new DisconnectTask(); - Job disconnectJob = new AbstractJob("Disconnect from data sources") { - @Override - protected IStatus run(DBRProgressMonitor monitor) - { - try { - disconnectTask.run(monitor); - } catch (InvocationTargetException e) { - return RuntimeUtils.makeExceptionStatus(e.getTargetException()); - } catch (InterruptedException e) { - // do nothing - } - return Status.OK_STATUS; - } - }; - disconnectJob.schedule(); - - // Wait for job to finish - long startTime = System.currentTimeMillis(); - if (waitTime > 0) { - while (!disconnectTask.finished && System.currentTimeMillis() - startTime < waitTime) { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - break; - } - } - } - if (!disconnectTask.finished) { + if (!RuntimeUtils.runTask(disconnectTask, waitTime)) { log.warn("Some data source connections wasn't closed on shutdown in " + waitTime + "ms. Probably network timeout occurred."); } } @@ -810,7 +778,6 @@ public class DataSourceRegistry implements DBPDataSourceRegistry private class DisconnectTask implements DBRRunnableWithProgress { boolean disconnected; - volatile boolean finished; @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { List dsSnapshot; @@ -836,7 +803,6 @@ public class DataSourceRegistry implements DBPDataSourceRegistry } } finally { monitor.done(); - finished = true; } } } diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/RuntimeUtils.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/RuntimeUtils.java index 0f89b4dfc2..3c03acf49a 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/RuntimeUtils.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/RuntimeUtils.java @@ -24,6 +24,7 @@ import org.apache.commons.jexl2.JexlException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; @@ -44,6 +45,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProcessDescriptor; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.runtime.DBRShellCommand; +import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.ui.UIUtils; import org.jkiss.dbeaver.ui.dialogs.ConfirmationDialog; import org.jkiss.dbeaver.ui.views.process.ShellProcessView; @@ -59,10 +61,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; +import java.util.*; /** * RuntimeUtils @@ -624,4 +623,59 @@ public class RuntimeUtils { } } + public static boolean runTask(final DBRRunnableWithProgress task, final long waitTime) { + final MonitoringTask monitoringTask = new MonitoringTask(task); + Job monitorJob = new AbstractJob("Disconnect from data sources") { + @Override + protected IStatus run(DBRProgressMonitor monitor) + { + try { + monitoringTask.run(monitor); + } catch (InvocationTargetException e) { + return RuntimeUtils.makeExceptionStatus(e.getTargetException()); + } catch (InterruptedException e) { + // do nothing + } + return Status.OK_STATUS; + } + }; + monitorJob.schedule(); + + // Wait for job to finish + long startTime = System.currentTimeMillis(); + if (waitTime > 0) { + while (!monitoringTask.finished && System.currentTimeMillis() - startTime < waitTime) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + break; + } + } + } + return monitoringTask.finished; + } + + private static class MonitoringTask implements DBRRunnableWithProgress { + private final DBRRunnableWithProgress task; + volatile boolean finished; + + private MonitoringTask(DBRRunnableWithProgress task) { + this.task = task; + } + + public boolean isFinished() { + return finished; + } + + @Override + public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + try { + task.run(monitor); + } finally { + monitor.done(); + finished = true; + } + } + } + } diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/jobs/DisconnectJob.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/jobs/DisconnectJob.java index d705d28fc2..e02da288fa 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/jobs/DisconnectJob.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/jobs/DisconnectJob.java @@ -28,13 +28,12 @@ import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.runtime.RuntimeUtils; /** - * DisconnectJob + * Disconnect Job */ public class DisconnectJob extends EventProcessorJob { - public DisconnectJob( - DataSourceDescriptor container) + public DisconnectJob(DataSourceDescriptor container) { super(NLS.bind(CoreMessages.runtime_jobs_disconnect_name, container.getName()), container); setUser(true); diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/sql/SQLQueryJob.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/sql/SQLQueryJob.java index 3f952a8397..a20b17f781 100644 --- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/sql/SQLQueryJob.java +++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/runtime/sql/SQLQueryJob.java @@ -533,7 +533,7 @@ public class SQLQueryJob extends DataSourceJob // Retrieve source entity if (result != null) { DBCResultSetMetaData rsMeta = resultSet.getMeta(); - String sourceName = null; + String sourceName = null;//resultSet.getResultSetName(); for (DBCAttributeMetaData attr : rsMeta.getAttributes()) { String entityName = attr.getEntityName(); if (!CommonUtils.isEmpty(entityName)) { @@ -610,10 +610,8 @@ public class SQLQueryJob extends DataSourceJob private boolean keepStatementOpen() { // Only in single query mode and if pref option set to true - DBSDataSourceContainer container = getDataSourceContainer(); return queries.size() == 1 && - container != null && - container.getPreferenceStore().getBoolean(DBeaverPreferences.KEEP_STATEMENT_OPEN); + getDataSourceContainer().getPreferenceStore().getBoolean(DBeaverPreferences.KEEP_STATEMENT_OPEN); } private void closeStatement() -- GitLab