dbeaver/pro#143 te remote event handle (#1409)

* CB-3054 send event if connection name was changed

* CB-3053 use smSession id in events

* CB-3054 fix rename event

* CB-3054 use equalSettings method to compare changes

* dbeaver/pro#143 Datasources refresh API

* dbeaver/pro#143 Handle datasource events + partial ds load

* CB-3054 update headless sessions on pink

Co-authored-by: Aleksandr Skoblikov <aleksandr.skoblikov@dbeaver.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
This commit is contained in:
Serge Rider
2023-01-16 18:04:16 +03:00
committed by GitHub
co-authored by Aleksandr Skoblikov kseniaguzeeva
parent 157fde96b0
commit b814110578
14 changed files with 138 additions and 52 deletions
@@ -35,6 +35,7 @@ import org.jkiss.dbeaver.registry.DataSourceConfigurationManager;
import org.jkiss.dbeaver.registry.DataSourcePersistentRegistry;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
@@ -254,6 +255,11 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
dataSourceRegistry.refreshConfig();
}
@Override
public void refreshConfig(@Nullable Collection<String> dataSourceIds) {
dataSourceRegistry.refreshConfig(dataSourceIds);
}
@Override
public Throwable getLastError() {
return dataSourceRegistry.getLastError();
@@ -299,10 +305,10 @@ public class WebDataSourceRegistryProxy implements DBPDataSourceRegistry, DataSo
public boolean loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@NotNull DataSourceConfigurationManager manager,
boolean refresh,
@Nullable Collection<String> dataSourceIds, boolean refresh,
boolean purgeUntouched
) {
return dataSourceRegistry.loadDataSources(storages, manager, refresh, purgeUntouched);
return dataSourceRegistry.loadDataSources(storages, manager, dataSourceIds, refresh, purgeUntouched);
}
@Override
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.cloudbeaver.service.session;
package io.cloudbeaver.auth;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.auth.SMCredentials;
@@ -32,6 +32,6 @@ public class SMTokenCredentialProvider implements SMCredentialsProvider {
@Nullable
@Override
public SMCredentials getActiveUserCredentials() {
return new SMCredentials(smAccessToken, null, Set.of());
return new SMCredentials(smAccessToken, null, null, Set.of());
}
}
@@ -42,6 +42,7 @@ import org.jkiss.dbeaver.model.security.SMObjects;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.registry.*;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import org.jkiss.utils.IOUtils;
@@ -51,6 +52,7 @@ import java.nio.file.*;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -287,12 +289,16 @@ public class LocalResourceController implements RMController {
}
@Override
public String getProjectsDataSources(@NotNull String projectId) throws DBException {
public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException {
DBPProject projectMetadata = getProjectMetadata(projectId, false);
DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry();
registry.checkForErrors();
DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer();
((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, null);
Predicate<DBPDataSourceContainer> filter = null;
if (!ArrayUtils.isEmpty(dataSourceIds)) {
filter = ds -> ArrayUtils.contains(dataSourceIds, ds.getId());
}
((DataSourcePersistentRegistry) registry).saveConfigurationToManager(new VoidProgressMonitor(), buffer, filter);
registry.checkForErrors();
return new String(buffer.getData(), StandardCharsets.UTF_8);
}
@@ -316,7 +322,7 @@ public class LocalResourceController implements RMController {
final DBPDataSourceRegistry registry = project.getDataSourceRegistry();
final DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(StandardCharsets.UTF_8));
final DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer();
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(List.of(storage), manager, true, false);
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(List.of(storage), manager, dataSourceIds, true, false);
registry.checkForErrors();
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
@@ -100,6 +100,7 @@ public class WebUserContext implements SMCredentialsProvider {
this.smCredentials = new SMCredentials(
smAccessToken,
smAuthPermissions.getUserId(),
smAuthPermissions.getSessionId(),
smAuthPermissions.getPermissions()
);
this.refreshToken = smRefreshToken;
@@ -128,6 +129,7 @@ public class WebUserContext implements SMCredentialsProvider {
this.smCredentials = new SMCredentials(
newTokens.getSmAccessToken(),
smCredentials.getUserId(),
smCredentials.getSmSessionId(),
smCredentials.getPermissions()
);
}
@@ -26,7 +26,6 @@ import io.cloudbeaver.model.app.WebAuthApplication;
import io.cloudbeaver.model.app.WebAuthConfiguration;
import io.cloudbeaver.model.rm.local.LocalResourceController;
import io.cloudbeaver.model.session.WebAuthInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.registry.WebDriverRegistry;
import io.cloudbeaver.registry.WebServiceRegistry;
import io.cloudbeaver.server.jetty.CBJettyServer;
@@ -833,8 +832,8 @@ public class CBApplication extends BaseWebApplication implements WebAuthApplicat
configurationMode = CommonUtils.isEmpty(serverName);
String sessionId = null;
if (credentialsProvider instanceof WebSession) {
sessionId = ((WebSession) credentialsProvider).getSessionId();
if (credentialsProvider != null && credentialsProvider.getActiveUserCredentials() != null) {
sessionId = credentialsProvider.getActiveUserCredentials().getSmSessionId();
}
eventController.addEvent(new WSServerConfigurationChangedEvent(sessionId));
}
@@ -23,7 +23,6 @@ import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.dbeaver.model.websocket.event.WSEventTopic;
import org.jkiss.utils.CommonUtils;
import java.util.Collection;
@@ -45,7 +44,7 @@ public class WSConfigurationChangedEventHandler implements WSEventHandler {
Collection<BaseWebSession> allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (CommonUtils.equalObjects(activeUserSession.getSessionId(), event.getSessionId())) {
if (WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId())) {
continue;
}
activeUserSession.addSessionEvent(event);
@@ -21,6 +21,7 @@ import io.cloudbeaver.server.CBPlatform;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.WSEventHandler;
import org.jkiss.dbeaver.model.websocket.WSUtils;
import org.jkiss.dbeaver.model.websocket.event.WSEvent;
import org.jkiss.utils.CommonUtils;
@@ -41,7 +42,7 @@ public abstract class WSProjectUpdatedEventHandler implements WSEventHandler {
log.debug(getSupportedTopicId() + " event handled");
Collection<BaseWebSession> allSessions = CBPlatform.getInstance().getSessionManager().getAllActiveSessions();
for (var activeUserSession : allSessions) {
if (CommonUtils.equalObjects(activeUserSession.getSessionId(), event.getSessionId())) {
if (WSWebUtils.isSessionIdEquals(activeUserSession, event.getSessionId())) {
continue;
}
updateSessionData(activeUserSession, event);
@@ -0,0 +1,39 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* 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 io.cloudbeaver.server.events;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import java.util.Objects;
public class WSWebUtils {
public static boolean isSessionIdEquals(@NotNull BaseWebSession webSession, @Nullable String sessionId) {
if (sessionId == null) {
return false;
}
if (Objects.equals(webSession.getSessionId(), sessionId)) {
return true;
}
if (webSession instanceof WebSession) {
return Objects.equals(webSession.getUserContext().getSmSessionId(), sessionId);
}
return false;
}
}
@@ -22,6 +22,7 @@ import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.websocket.CBWebSessionEventHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.websocket.WSConstants;
@@ -40,8 +41,12 @@ public class CBEventsWebSocket extends WebSocketAdapter implements CBWebSessionE
@NotNull
private final BaseWebSession webSession;
@NotNull
private final WriteCallback callback;
public CBEventsWebSocket(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
this.callback = new WebSocketPingPongCallback(webSession);
}
@Override
@@ -124,4 +129,9 @@ public class CBEventsWebSocket extends WebSocketAdapter implements CBWebSessionE
public BaseWebSession getWebSession() {
return webSession;
}
@NotNull
public WriteCallback getCallback() {
return callback;
}
}
@@ -92,7 +92,10 @@ public class CBJettyWebSocketManager implements JettyWebSocketCreator {
var sessionId = entry.getKey();
var webSocket = entry.getValue();
try {
webSocket.getRemote().sendPing(ByteBuffer.wrap("cb-ping".getBytes(StandardCharsets.UTF_8)));
webSocket.getRemote().sendPing(
ByteBuffer.wrap("cb-ping".getBytes(StandardCharsets.UTF_8)),
webSocket.getCallback()
);
} catch (Exception e) {
log.error("Failed to send ping in web socket: " + sessionId);
}
@@ -0,0 +1,38 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2022 DBeaver Corp and others
*
* 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 io.cloudbeaver.server.websockets;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.jkiss.code.NotNull;
public class WebSocketPingPongCallback implements WriteCallback {
@NotNull
private final BaseWebSession webSession;
public WebSocketPingPongCallback(@NotNull BaseWebSession webSession) {
this.webSession = webSession;
}
@Override
public void writeSuccess() {
if (webSession instanceof WebHeadlessSession) {
webSession.touchSession();
}
}
}
@@ -61,7 +61,10 @@ import org.jkiss.utils.CommonUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -405,7 +408,7 @@ public class WebServiceCore implements DBWServiceCore {
webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo(newDataSource));
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession.getSessionId(),
webSession.getUserContext().getSmSessionId(),
connectionInfo.getId(),
WSConstants.EventAction.CREATE
);
@@ -427,12 +430,12 @@ public class WebServiceCore implements DBWServiceCore {
WebConnectionInfo connectionInfo = webSession.getWebConnectionInfo(projectId, config.getConnectionId());
DBPDataSourceContainer dataSource = connectionInfo.getDataSourceContainer();
webSession.addInfoMessage("Update connection - " + WebServiceUtils.getConnectionContainerInfo(dataSource));
DBPConnectionConfiguration oldConnectionConfig = new DBPConnectionConfiguration(dataSource.getConnectionConfiguration());
var oldDataSource = new DataSourceDescriptor((DataSourceDescriptor) dataSource, dataSource.getRegistry());
if (!CommonUtils.isEmpty(config.getName())) {
dataSource.setName(config.getName());
}
String oldDescription = dataSource.getDescription();
if (config.getDescription() != null) {
dataSource.setDescription(config.getDescription());
}
@@ -441,7 +444,7 @@ public class WebServiceCore implements DBWServiceCore {
WebServiceUtils.setConnectionConfiguration(dataSource.getDriver(), dataSource.getConnectionConfiguration(), config);
boolean sendEvent = sendUpdateConnectionEvent(config, dataSource, oldConnectionConfig, oldDescription);
boolean sendEvent = !((DataSourceDescriptor) dataSource).equalSettings(oldDataSource);
WebServiceUtils.saveAuthProperties(
dataSource,
@@ -460,7 +463,7 @@ public class WebServiceCore implements DBWServiceCore {
if (sendEvent) {
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession.getSessionId(),
webSession.getUserContext().getSmSessionId(),
connectionInfo.getId(),
WSConstants.EventAction.UPDATE
);
@@ -468,27 +471,6 @@ public class WebServiceCore implements DBWServiceCore {
return connectionInfo;
}
/**
* Checks if only user credentials were changed
*/
private boolean sendUpdateConnectionEvent(
@NotNull WebConnectionConfig config,
@NotNull DBPDataSourceContainer dataSource,
@NotNull DBPConnectionConfiguration oldConnectionConfig,
@Nullable String oldDescription
) {
if (!oldConnectionConfig.equals(dataSource.getConnectionConfiguration())) {
return true;
}
if (!Objects.equals(oldDescription, dataSource.getDescription())) {
return true;
}
if (dataSource.getProject().isUseSecretStorage()) {
return dataSource.isSharedCredentials() != config.isSharedCredentials();
}
return dataSource.isSavePassword() != config.isSaveCredentials();
}
@Override
public boolean deleteConnection(
@NotNull WebSession webSession, @Nullable String projectId, @NotNull String connectionId
@@ -502,7 +484,7 @@ public class WebServiceCore implements DBWServiceCore {
closeAndDeleteConnection(webSession, projectId, connectionId, true);
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession.getSessionId(),
webSession.getUserContext().getSmSessionId(),
connectionId,
WSConstants.EventAction.DELETE
);
@@ -583,7 +565,7 @@ public class WebServiceCore implements DBWServiceCore {
webSession.addConnection(connectionInfo);
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession.getSessionId(),
webSession.getUserContext().getSmSessionId(),
connectionInfo.getId(),
WSConstants.EventAction.CREATE
);
@@ -786,7 +768,7 @@ public class WebServiceCore implements DBWServiceCore {
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
DBNLocalFolder.makeLocalFolderItemPath(newFolder),
WSConstants.EventAction.CREATE
);
@@ -811,13 +793,13 @@ public class WebServiceCore implements DBWServiceCore {
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
oldFolderNode,
WSConstants.EventAction.DELETE
);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
newFolderNode,
WSConstants.EventAction.CREATE
);
@@ -842,7 +824,7 @@ public class WebServiceCore implements DBWServiceCore {
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
folderNode,
WSConstants.EventAction.DELETE
);
@@ -862,7 +844,7 @@ public class WebServiceCore implements DBWServiceCore {
dataSourceDescriptor.persistConfiguration();
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession.getSessionId(),
webSession.getUserContext().getSmSessionId(),
id,
WSConstants.EventAction.UPDATE);
return connectionInfo;
@@ -391,13 +391,13 @@ public class WebServiceNavigator implements DBWServiceNavigator {
private void addNavigatorNodeMoveEvent(@NotNull WebSession session, DBNNode node, String oldNodePath, String newNodePath) {
WebEventUtils.addNavigatorNodeUpdatedEvent(
node.getOwnerProject(),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
oldNodePath,
WSConstants.EventAction.DELETE
);
WebEventUtils.addNavigatorNodeUpdatedEvent(
node.getOwnerProject(),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
newNodePath,
WSConstants.EventAction.CREATE
);
@@ -492,7 +492,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
node.getOwnerProject().getDataSourceRegistry().removeFolder(((DBNLocalFolder) node).getFolder(), false);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
nodePath,
WSConstants.EventAction.DELETE
);
@@ -561,7 +561,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
((DBNDataSource) node).getDataSourceContainer());
WebEventUtils.addDataSourceUpdatedEvent(
node.getOwnerProject(),
session.getSessionId(),
session.getUserContext().getSmSessionId(),
((DBNDataSource) node).getDataSourceContainer().getId(),
WSConstants.EventAction.UPDATE
);
@@ -17,6 +17,7 @@
package io.cloudbeaver.service.session;
import io.cloudbeaver.DBWebException;
import io.cloudbeaver.auth.SMTokenCredentialProvider;
import io.cloudbeaver.model.session.BaseWebSession;
import io.cloudbeaver.model.session.WebHeadlessSession;
import io.cloudbeaver.model.session.WebSession;