/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed 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.jkiss.dbeaver.ui.actions; import org.eclipse.core.expressions.PropertyTester; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.commands.ICommandService; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.CoreCommands; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.DBPContextProvider; import org.jkiss.dbeaver.model.DBPDataSourceContainer; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.IDataSourceContainerProvider; import org.jkiss.dbeaver.model.exec.*; import org.jkiss.dbeaver.model.qm.QMUtils; import org.jkiss.dbeaver.runtime.IPluginService; import org.jkiss.dbeaver.runtime.qm.DefaultExecutionHandler; import org.jkiss.dbeaver.ui.ActionUtils; import org.jkiss.dbeaver.ui.editors.sql.SQLEditor; /** * DatabaseEditorPropertyTester */ public class DataSourcePropertyTester extends PropertyTester { static protected final Log log = Log.getLog(DataSourcePropertyTester.class); public static final String NAMESPACE = "org.jkiss.dbeaver.core.datasource"; public static final String PROP_CONNECTED = "connected"; public static final String PROP_TRANSACTIONAL = "transactional"; public static final String PROP_TRANSACTION_ACTIVE = "transactionActive"; @Override public boolean test(Object receiver, String property, Object[] args, Object expectedValue) { if (!(receiver instanceof DBPContextProvider)) { return false; } DBPContextProvider contextProvider = (DBPContextProvider)receiver; @Nullable DBCExecutionContext context = contextProvider.getExecutionContext(); switch (property) { case PROP_CONNECTED: boolean isConnected; if (context != null) { isConnected = context.getDataSource().getContainer().isConnected(); } else if (receiver instanceof IDataSourceContainerProvider) { DBPDataSourceContainer container = ((IDataSourceContainerProvider) receiver).getDataSourceContainer(); isConnected = container != null && container.isConnected(); } else { isConnected = false; } boolean checkConnected = Boolean.TRUE.equals(expectedValue); return checkConnected ? isConnected : !isConnected; case PROP_TRANSACTIONAL: if (context == null) { return false; } if (!context.isConnected()) { return Boolean.FALSE.equals(expectedValue); } DBCTransactionManager txnManager = DBUtils.getTransactionManager(context); try { return txnManager != null && Boolean.valueOf(!txnManager.isAutoCommit()).equals(expectedValue); } catch (DBCException e) { log.debug("Error checking auto-commit state", e); return false; } case PROP_TRANSACTION_ACTIVE: if (context != null && context.isConnected()) { boolean active = QMUtils.isTransactionActive(context); return Boolean.valueOf(active).equals(expectedValue); } return Boolean.FALSE.equals(expectedValue); } return false; } public static void firePropertyChange(String propName) { ActionUtils.evaluatePropertyState(NAMESPACE + "." + propName); } public static void fireCommandRefresh(final String commandID) { // Update commands final ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); if (commandService != null) { DBeaverUI.asyncExec(new Runnable() { @Override public void run() { commandService.refreshElements(commandID, null); } }); } } public static class QMService implements IPluginService { private QMEventsHandler qmHandler; @Override public void activateService() { qmHandler = new QMEventsHandler(); QMUtils.registerHandler(qmHandler); } @Override public void deactivateService() { QMUtils.unregisterHandler(qmHandler); } } // QM events handler private static class QMEventsHandler extends DefaultExecutionHandler { @NotNull @Override public String getHandlerName() { return DataSourcePropertyTester.class.getName(); } @Override public synchronized void handleTransactionAutocommit(@NotNull DBCExecutionContext context, boolean autoCommit) { // Fire transactional mode change DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTIONAL); DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE); DataSourcePropertyTester.fireCommandRefresh(CoreCommands.CMD_TOGGLE_AUTOCOMMIT); } @Override public synchronized void handleTransactionCommit(@NotNull DBCExecutionContext context) { DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE); updateEditorsDirtyFlag(); } @Override public synchronized void handleTransactionRollback(@NotNull DBCExecutionContext context, DBCSavepoint savepoint) { DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE); updateEditorsDirtyFlag(); } @Override public synchronized void handleStatementExecuteBegin(@NotNull DBCStatement statement) { DataSourcePropertyTester.firePropertyChange(DataSourcePropertyTester.PROP_TRANSACTION_ACTIVE); } } /** * This is a hack. * Editors should listen txn commit/rollback and update their dirty flag (active transaction makes SQL editor dirty). * Making each editor QM listener is too expensive. */ private static void updateEditorsDirtyFlag() { IEditorReference[] editors = DBeaverUI.getActiveWorkbenchWindow().getActivePage().getEditorReferences(); for (IEditorReference ref : editors) { final IEditorPart editor = ref.getEditor(false); if (editor instanceof SQLEditor) { DBeaverUI.asyncExec(new Runnable() { @Override public void run() { ((SQLEditor) editor).updateDirtyFlag(); } }); } } } }