dbeaver/pro#6249 fix events for changing extensions (#3667)

* dbeaver/pro#6249 fix events for changing extensions

* dbeaver/pro#6249 send connection updated info event on rm side

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
This commit is contained in:
Ainur
2025-09-10 14:33:34 +02:00
committed by GitHub
co-authored by Daria Marutkina
parent e896789cb0
commit c5190b2567
10 changed files with 304 additions and 196 deletions
@@ -35,6 +35,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
import org.jkiss.dbeaver.registry.DataSourceConfigurationManager;
import org.jkiss.dbeaver.registry.DataSourceParseResults;
import org.jkiss.dbeaver.registry.DataSourcePersistentRegistry;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
@@ -377,7 +378,7 @@ public class WebGlobalProjectRegistryProxy implements DBPDataSourceRegistry, Dat
}
@Override
public boolean loadDataSources(
public DataSourceParseResults loadDataSources(
@NotNull List<DBPDataSourceConfigurationStorage> storages,
@NotNull DataSourceConfigurationManager manager,
@Nullable Collection<String> dataSourceIds, boolean refresh,
@@ -29,13 +29,16 @@ import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.rm.RMProject;
import org.jkiss.dbeaver.model.rm.RMUtils;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceRegistry;
import org.jkiss.dbeaver.runtime.jobs.DisconnectJob;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WebSessionProjectImpl extends WebProjectImpl {
private static final Log log = Log.getLog(WebSessionProjectImpl.class);
@@ -194,40 +197,31 @@ public class WebSessionProjectImpl extends WebProjectImpl {
/**
* updates data sources based on event in web session
*
* @param dataSourceIds list of updated connections
* @param eventId id of event
* @param event data source updated event
*/
public synchronized boolean updateProjectDataSources(@NotNull List<String> dataSourceIds, @NotNull String eventId) {
public synchronized boolean updateProjectDataSources(@NotNull WSDataSourceEvent event) {
var sendDataSourceUpdatedEvent = false;
DBPDataSourceRegistry registry = getDataSourceRegistry();
// save old connections
var oldDataSources = dataSourceIds.stream()
.map(registry::getDataSource)
.filter(Objects::nonNull)
.collect(Collectors.toMap(
DBPDataSourceContainer::getId,
registry::createDataSource)
);
if (WSDataSourceEvent.CREATED.equals(eventId) || WSDataSourceEvent.UPDATED.equals(eventId)) {
registry.refreshConfig(dataSourceIds);
if (WSDataSourceEvent.CREATED.equals(event.getId()) || WSDataSourceEvent.UPDATED.equals(event.getId())) {
registry.refreshConfig(event.getDataSourceIds());
}
for (String dsId : dataSourceIds) {
for (String dsId : event.getDataSourceIds()) {
DataSourceDescriptor ds = (DataSourceDescriptor) registry.getDataSource(dsId);
if (ds == null) {
continue;
}
switch (eventId) {
switch (event.getId()) {
case WSDataSourceEvent.CREATED -> {
addConnection(ds);
sendDataSourceUpdatedEvent = true;
}
case WSDataSourceEvent.UPDATED -> {
boolean connectionUpdated = !ds.equalSettings(oldDataSources.get(dsId));
if (connectionUpdated) {
sendDataSourceUpdatedEvent = true;
if (event.getProperty() == WSDataSourceProperty.CONFIGURATION) {
WebDataSourceUtils.disconnectDataSource(webSession, ds);
}
// if settings were changed we need to send event
if (event.getProperty() != WSDataSourceProperty.INTERNAL) {
sendDataSourceUpdatedEvent = true;
}
}
case WSDataSourceEvent.DELETED -> {
WebDataSourceUtils.disconnectDataSource(webSession, ds);
@@ -149,27 +149,37 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull String projectId,
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
return updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds) != null;
}
@Nullable
protected DataSourceParseResults updateProjectDataSourcesConfig(
@NotNull String projectId,
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
try (var lock = lockController.lock(projectId, "updateProjectDataSources")) {
DBPProject project = getWebProject(projectId, false);
return doFileWriteOperation(projectId, project.getMetadataFolder(false),
return doFileWriteOperation(
projectId, project.getMetadataFolder(false),
() -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
DBPDataSourceConfigurationStorage storage = new DataSourceMemoryStorage(configuration.getBytes(
StandardCharsets.UTF_8));
DataSourceConfigurationManager manager = new DataSourceConfigurationManagerBuffer();
var configChanged = ((DataSourcePersistentRegistry) registry).loadDataSources(
final DataSourceParseResults parseResults = ((DataSourcePersistentRegistry) registry).loadDataSources(
List.of(storage),
manager,
dataSourceIds,
true,
false
dataSourceIds == null
);
registry.checkForErrors();
log.debug("Save data sources configuration in project '" + projectId + "'");
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
return configChanged;
return parseResults;
}
);
}
@@ -217,6 +227,7 @@ public abstract class BaseLocalResourceController implements RMController {
var parent = result.getParent();
var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/"));
DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName);
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
return null;
}
@@ -244,6 +255,7 @@ public abstract class BaseLocalResourceController implements RMController {
log.warn("Can not find folder by path [" + folderPath + "] for deletion");
}
}
((DataSourcePersistentRegistry) registry).saveDataSources();
registry.checkForErrors();
return null;
}
@@ -265,6 +277,7 @@ public abstract class BaseLocalResourceController implements RMController {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
registry.moveFolder(oldPath, newPath);
registry.checkForErrors();
((DataSourcePersistentRegistry) registry).saveDataSources();
return null;
}
);
@@ -28,18 +28,26 @@ import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentials;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
import org.jkiss.dbeaver.model.rm.*;
import org.jkiss.dbeaver.model.security.SMController;
import org.jkiss.dbeaver.model.security.SMObjectType;
import org.jkiss.dbeaver.model.sql.DBQuotaException;
import org.jkiss.dbeaver.model.websocket.event.MessageType;
import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceEvent;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDatasourceFolderEvent;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceParseResults;
import org.jkiss.dbeaver.registry.ResourceTypeDescriptor;
import org.jkiss.dbeaver.registry.ResourceTypeRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
@@ -334,6 +342,239 @@ public class LocalResourceController extends BaseLocalResourceController {
return project;
}
@Override
public void createProjectDataSources(
@NotNull String projectId,
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
super.createProjectDataSources(projectId, configuration, dataSourceIds);
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds != null) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
dataSourceIds,
WSDataSourceProperty.CONFIGURATION
)
);
}
}
@Override
public boolean updateProjectDataSources(
@NotNull String projectId,
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
DBPDataSourceRegistry registry = getWebProject(projectId, false).getDataSourceRegistry();
Map<String, DataSourceDescriptor> oldDataSources = registry.getDataSources().stream()
.filter(ds -> dataSourceIds == null || dataSourceIds.contains(ds.getId()))
.collect(Collectors.toMap(
DBPDataSourceContainer::getId,
registry::createDataSource
)
);
DataSourceParseResults parseResults = super.updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds);
sendDataSourcesConfigUpdatedEvent(registry, oldDataSources, parseResults);
return parseResults != null;
}
@Override
public void deleteProjectDataSources(@NotNull String projectId, @NotNull String[] dataSourceIds) throws DBException {
super.deleteProjectDataSources(projectId, dataSourceIds);
if (credentialsProvider.getActiveUserCredentials() != null && dataSourceIds.length > 0) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.delete(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
Arrays.asList(dataSourceIds),
WSDataSourceProperty.CONFIGURATION
)
);
}
}
@Override
public void createProjectDataSourceFolder(@NotNull String projectId, @NotNull String folderPath) throws DBException {
super.createProjectDataSourceFolder(projectId, folderPath);
if (credentialsProvider.getActiveUserCredentials() != null) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
List.of(createNodePathFromFolderPath(projectId, folderPath))
)
);
}
}
@Override
public void moveProjectDataSourceFolder(
@NotNull String projectId,
@NotNull String oldPath,
@NotNull String newPath
) throws DBException {
super.moveProjectDataSourceFolder(projectId, oldPath, newPath);
if (credentialsProvider.getActiveUserCredentials() != null) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.delete(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
List.of(createNodePathFromFolderPath(projectId, oldPath))
)
);
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
List.of(createNodePathFromFolderPath(projectId, newPath))
)
);
}
}
@Override
public void deleteProjectDataSourceFolders(@NotNull String projectId, @NotNull String[] folderPaths, boolean dropContents)
throws DBException {
super.deleteProjectDataSourceFolders(projectId, folderPaths, dropContents);
if (credentialsProvider.getActiveUserCredentials() != null) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
projectId,
Arrays.stream(folderPaths).map(
p -> createNodePathFromFolderPath(projectId, p)
).collect(Collectors.toList())
)
);
}
}
private String createNodePathFromFolderPath(String projectId, String folderPath) {
return DBNLocalFolder.makeLocalFolderItemPath(projectId, folderPath);
}
private void sendDataSourcesConfigUpdatedEvent(
@NotNull DBPDataSourceRegistry registry,
@NotNull Map<String, DataSourceDescriptor> oldDataSources,
@Nullable DataSourceParseResults parseResults
) {
if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null || oldDataSources.isEmpty()) {
return;
}
List<String> updatedConfigurationDataSourceIds = new ArrayList<>();
List<String> updatedNameDataSourceIds = new ArrayList<>();
List<String> updatedInternalConfigurationDataSourceIds = new ArrayList<>();
for (Map.Entry<String, DataSourceDescriptor> entry : oldDataSources.entrySet()) {
String dsId = entry.getKey();
DataSourceDescriptor oldDs = entry.getValue();
DataSourceDescriptor newDs = (DataSourceDescriptor) registry.getDataSource(dsId);
if (newDs == null) {
continue;
}
if (!oldDs.equalConfiguration(newDs)) {
updatedConfigurationDataSourceIds.add(dsId);
} else if (!oldDs.isLooselyEqualTo(newDs)) {
updatedNameDataSourceIds.add(dsId);
} else if (!oldDs.equalInternalConfiguration(newDs)) {
updatedInternalConfigurationDataSourceIds.add(dsId);
}
}
if (!updatedConfigurationDataSourceIds.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.update(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
updatedConfigurationDataSourceIds,
WSDataSourceProperty.CONFIGURATION
)
);
}
if (!updatedNameDataSourceIds.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.update(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
updatedNameDataSourceIds,
WSDataSourceProperty.NAME
)
);
}
if (!updatedInternalConfigurationDataSourceIds.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.update(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
updatedInternalConfigurationDataSourceIds,
WSDataSourceProperty.INTERNAL
)
);
}
if (!parseResults.addedDataSources.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
updatedNameDataSourceIds,
WSDataSourceProperty.CONFIGURATION
)
);
}
if (!parseResults.removedDataSources.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDataSourceEvent.delete(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
updatedNameDataSourceIds,
WSDataSourceProperty.CONFIGURATION
)
);
}
if (!parseResults.addedFolders.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.create(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
parseResults.addedFolders.stream().map(
f -> createNodePathFromFolderPath(registry.getProject().getId(), f.getFolderPath())
).toList()
)
);
}
if (!parseResults.removedFolders.isEmpty()) {
ServletAppUtils.getServletApplication().getEventController().addEvent(
WSDatasourceFolderEvent.delete(
credentialsProvider.getActiveUserCredentials().getSmSessionId(),
credentialsProvider.getActiveUserCredentials().getUserId(),
registry.getProject().getId(),
parseResults.removedFolders.stream().map(
f -> createNodePathFromFolderPath(registry.getProject().getId(), f.getFolderPath())
).toList()
)
);
}
}
@NotNull
@Override
public RMResource[] listResources(
@@ -21,7 +21,6 @@ import io.cloudbeaver.model.WebConnectionFolderInfo;
import io.cloudbeaver.model.session.WebSession;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.model.DBPDataSourceFolder;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.utils.CommonUtils;
public class WebConnectionFolderUtils {
@@ -45,9 +44,4 @@ public class WebConnectionFolderUtils {
throw new DBWebException("Folder name '" + folderName + "' contains illegal characters: /");
}
}
public static DBPDataSourceFolder createFolder(WebConnectionFolderInfo parentFolder, String newName, DBPDataSourceRegistry registry) throws DBWebException {
DBPDataSourceFolder folder = registry.addFolder(parentFolder == null ? null : parentFolder.getDataSourceFolder(), newName);
return folder;
}
}
@@ -318,9 +318,11 @@ public class WebServiceUtils extends WebCommonUtils {
return container.getName() + " [" + container.getId() + "]";
}
public static void updateConfigAndRefreshDatabases(WebSession session, String projectId) throws DBWebException {
public static void refreshDatabases(WebSession session, String projectId) throws DBWebException {
DBNProject projectNode = session.getNavigatorModelOrThrow().getRoot().getProjectNode(session.getProjectById(projectId));
DBNModel.updateConfigAndRefreshDatabases(projectNode.getDatabases());
if (projectNode != null) {
projectNode.getDatabases().refreshChildren();
}
}
public static boolean isGlobalProject(DBPProject project) {
@@ -39,10 +39,7 @@ public class WSDataSourceUpdatedEventHandlerImpl extends WSAbstractProjectEventH
log.debug("Project " + event.getProjectId() + " is not found in session " + webSession.getSessionId());
return;
}
sendEvent = project.updateProjectDataSources(
event.getDataSourceIds(),
event.getId()
);
sendEvent = project.updateProjectDataSources(event);
}
if (sendEvent) {
activeUserSession.addSessionEvent(event);
@@ -24,7 +24,6 @@ import io.cloudbeaver.model.WebPropertyInfo;
import io.cloudbeaver.model.session.WebSession;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import io.cloudbeaver.utils.WebEventUtils;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
@@ -38,8 +37,6 @@ import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.secret.DBSSecretController;
import org.jkiss.dbeaver.model.secret.DBSSecretValue;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.runtime.jobs.ConnectionTestJob;
@@ -100,13 +97,6 @@ public class ConnectionControllerCE implements ConnectionController {
WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
webSession.addInfoMessage("New connection was created - " + WebServiceUtils.getConnectionContainerInfo(
newDataSource));
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
connectionInfo.getId(),
WSConstants.EventAction.CREATE,
WSDataSourceProperty.CONFIGURATION
);
log.info(String.format(
"New connection was created: [info=%s, user=%s]",
WebServiceUtils.getConnectionContainerInfo(newDataSource),
@@ -190,7 +180,6 @@ public class ConnectionControllerCE implements ConnectionController {
DBPDataSourceRegistry sessionRegistry
) throws DBWebException {
WebConnectionInfo connectionInfo = WebDataSourceUtils.getWebConnectionInfo(webSession, projectId, config.getConnectionId());
boolean sendEvent = !((DataSourceDescriptor) dataSource).equalSettings(getOldDataSource(dataSource));
if (!isSharedCredentials(dataSource)) {
// secret controller is responsible for notification, password changes applied after checks
WebServiceUtils.saveAuthProperties(
@@ -201,24 +190,12 @@ public class ConnectionControllerCE implements ConnectionController {
config.isSharedCredentials()
);
}
WSDataSourceProperty property = getDatasourceEventProperty(getOldDataSource(dataSource), dataSource);
try {
sessionRegistry.updateDataSource(dataSource);
sessionRegistry.checkForErrors();
} catch (DBException e) {
throw new DBWebException("Failed to update connection", e);
}
if (sendEvent) {
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
connectionInfo.getId(),
WSConstants.EventAction.UPDATE,
property
);
}
log.info(String.format(
"Connection updated: [info=%s, userId=%s]",
WebServiceUtils.getConnectionContainerInfo(dataSource),
@@ -238,13 +215,6 @@ public class ConnectionControllerCE implements ConnectionController {
webSession.addInfoMessage("Delete connection - " +
WebServiceUtils.getConnectionContainerInfo(connectionInfo.getDataSourceContainer()));
closeAndDeleteConnection(webSession, projectId, connectionId, true);
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
connectionId,
WSConstants.EventAction.DELETE,
WSDataSourceProperty.CONFIGURATION
);
log.info(String.format(
"Connection deleted: [info=%s, userId=%s]",
@@ -361,23 +331,6 @@ public class ConnectionControllerCE implements ConnectionController {
return project;
}
private WSDataSourceProperty getDatasourceEventProperty(
DataSourceDescriptor oldDataSource,
DBPDataSourceContainer dataSource
) {
if (!oldDataSource.equalConfiguration((DataSourceDescriptor) dataSource)) {
return WSDataSourceProperty.CONFIGURATION;
}
var nameChanged = !CommonUtils.equalObjects(oldDataSource.getName(), dataSource.getName());
var descriptionChanged = !CommonUtils.equalObjects(oldDataSource.getDescription(), dataSource.getDescription());
if (nameChanged && descriptionChanged) {
return WSDataSourceProperty.CONFIGURATION;
}
return nameChanged ? WSDataSourceProperty.NAME : WSDataSourceProperty.CONFIGURATION;
}
@NotNull
private WebConnectionInfo closeAndDeleteConnection(
@NotNull WebSession webSession,
@@ -27,7 +27,10 @@ import io.cloudbeaver.server.WebAppUtils;
import io.cloudbeaver.server.WebApplication;
import io.cloudbeaver.service.core.DBWServiceCore;
import io.cloudbeaver.service.security.SMUtils;
import io.cloudbeaver.utils.*;
import io.cloudbeaver.utils.ServletAppUtils;
import io.cloudbeaver.utils.WebCommonUtils;
import io.cloudbeaver.utils.WebConnectionFolderUtils;
import io.cloudbeaver.utils.WebDataSourceUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jkiss.code.NotNull;
@@ -41,14 +44,16 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.app.DBPProject;
import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration;
import org.jkiss.dbeaver.model.connection.DBPDriver;
import org.jkiss.dbeaver.model.navigator.*;
import org.jkiss.dbeaver.model.navigator.DBNBrowseSettings;
import org.jkiss.dbeaver.model.navigator.DBNDataSource;
import org.jkiss.dbeaver.model.navigator.DBNModel;
import org.jkiss.dbeaver.model.navigator.DBNNode;
import org.jkiss.dbeaver.model.net.DBWHandlerConfiguration;
import org.jkiss.dbeaver.model.net.DBWNetworkHandler;
import org.jkiss.dbeaver.model.net.DBWTunnel;
import org.jkiss.dbeaver.model.net.ssh.SSHSession;
import org.jkiss.dbeaver.model.rm.RMProjectType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.registry.DataSourceDescriptor;
import org.jkiss.dbeaver.registry.DataSourceProviderRegistry;
@@ -56,7 +61,6 @@ import org.jkiss.dbeaver.registry.network.NetworkHandlerDescriptor;
import org.jkiss.dbeaver.registry.network.NetworkHandlerRegistry;
import org.jkiss.dbeaver.registry.settings.ProductSettingDescriptor;
import org.jkiss.dbeaver.registry.settings.ProductSettingsRegistry;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
import java.util.*;
@@ -393,15 +397,7 @@ public class WebServiceCore implements DBWServiceCore {
dataSourceRegistry.addDataSource(newDataSource);
dataSourceRegistry.checkForErrors();
WebConnectionInfo connectionInfo = project.addConnection(newDataSource);
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
connectionInfo.getId(),
WSConstants.EventAction.CREATE,
WSDataSourceProperty.CONFIGURATION
);
return connectionInfo;
return project.addConnection(newDataSource);
} catch (DBException e) {
throw new DBWebException("Error copying connection", e);
}
@@ -536,25 +532,18 @@ public class WebServiceCore implements DBWServiceCore {
) throws DBWebException {
WebConnectionFolderUtils.validateConnectionFolder(folderName);
session.addInfoMessage("Create new folder");
WebConnectionFolderInfo parentNode = null;
WebConnectionFolderInfo parentFolder = null;
try {
if (parentPath != null) {
parentNode = WebConnectionFolderUtils.getFolderInfo(session, projectId, parentPath);
parentFolder = WebConnectionFolderUtils.getFolderInfo(session, projectId, parentPath);
}
WebProjectImpl project = getProjectById(session, projectId);
DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry();
DBPDataSourceFolder newFolder = WebConnectionFolderUtils.createFolder(parentNode,
folderName,
sessionRegistry);
WebConnectionFolderInfo folderInfo = new WebConnectionFolderInfo(session, newFolder);
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session,
DBNLocalFolder.makeLocalFolderItemPath(newFolder),
WSConstants.EventAction.CREATE
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
DBPDataSourceFolder newFolder = registry.addFolder(
parentFolder == null ? null : parentFolder.getDataSourceFolder(), folderName
);
return folderInfo;
WebServiceUtils.refreshDatabases(session, projectId);
return new WebConnectionFolderInfo(session, newFolder);
} catch (DBException e) {
throw new DBWebException(e.getMessage(), e);
}
@@ -565,26 +554,12 @@ public class WebServiceCore implements DBWServiceCore {
@NotNull WebSession session,
@Nullable String projectId,
@NotNull String folderPath,
@NotNull String newName
@NotNull String newPath
) throws DBWebException {
WebConnectionFolderUtils.validateConnectionFolder(newName);
WebConnectionFolderUtils.validateConnectionFolder(newPath);
WebConnectionFolderInfo folderInfo = WebConnectionFolderUtils.getFolderInfo(session, projectId, folderPath);
var oldFolderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
folderInfo.getDataSourceFolder().setName(newName);
var newFolderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session,
oldFolderNode,
WSConstants.EventAction.DELETE
);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session,
newFolderNode,
WSConstants.EventAction.CREATE
);
folderInfo.getDataSourceFolder().setName(newPath);
WebServiceUtils.refreshDatabases(session, projectId);
return folderInfo;
}
@@ -599,17 +574,10 @@ public class WebServiceCore implements DBWServiceCore {
if (folder.getDataSourceRegistry().getProject() != project) {
throw new DBWebException("Global folder '" + folderInfo.getId() + "' cannot be deleted");
}
var folderNode = DBNLocalFolder.makeLocalFolderItemPath(folderInfo.getDataSourceFolder());
session.addInfoMessage("Delete folder");
DBPDataSourceRegistry sessionRegistry = project.getDataSourceRegistry();
sessionRegistry.removeFolder(folderInfo.getDataSourceFolder(), false);
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session,
folderNode,
WSConstants.EventAction.DELETE
);
WebServiceUtils.refreshDatabases(session, projectId);
} catch (DBException e) {
throw new DBWebException(e.getMessage(), e);
}
@@ -624,12 +592,6 @@ public class WebServiceCore implements DBWServiceCore {
DataSourceDescriptor dataSourceDescriptor = ((DataSourceDescriptor) connectionInfo.getDataSourceContainer());
dataSourceDescriptor.setNavigatorSettings(settings);
dataSourceDescriptor.persistConfiguration();
WebEventUtils.addDataSourceUpdatedEvent(
webSession.getProjectById(projectId),
webSession,
id,
WSConstants.EventAction.UPDATE,
WSDataSourceProperty.CONFIGURATION);
return connectionInfo;
}
@@ -656,16 +618,4 @@ public class WebServiceCore implements DBWServiceCore {
}
return project;
}
private void validateDriverLibrariesPresence(@NotNull DBPDataSourceContainer container) throws DBWebException {
if (!DBWorkbench.isDistributed() && container.getDriver().getDriverLoader(container).needsExternalDependencies()) {
throwDriverNotFoundException(container);
}
}
@NotNull
private static String throwDriverNotFoundException(@NotNull DBPDataSourceContainer container) throws DBWebException {
throw new DBWebException("Driver files for %s are not found. Please ask the administrator to download it."
.formatted(container.getDriver().getName()));
}
}
@@ -55,7 +55,6 @@ import org.jkiss.dbeaver.model.struct.DBSObjectFilter;
import org.jkiss.dbeaver.model.struct.rdb.DBSCatalog;
import org.jkiss.dbeaver.model.struct.rdb.DBSSchema;
import org.jkiss.dbeaver.model.websocket.WSConstants;
import org.jkiss.dbeaver.model.websocket.event.datasource.WSDataSourceProperty;
import org.jkiss.dbeaver.model.websocket.event.resource.WSResourceProperty;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.utils.CommonUtils;
@@ -461,28 +460,10 @@ public class WebServiceNavigator implements DBWServiceNavigator {
if (siblings.contains(newName)) {
throw new DBWebException("Name " + newName + " is unavailable or invalid");
}
var oldNodePath = node.getNodeItemPath();
node.rename(session.getProgressMonitor(), newName);
var newNodePath = node.getNodeItemPath();
addNavigatorNodeMoveEvent(session, node, oldNodePath, newNodePath);
return node.getName();
}
private void addNavigatorNodeMoveEvent(@NotNull WebSession session, DBNNode node, String oldNodePath, String newNodePath) {
WebEventUtils.addNavigatorNodeUpdatedEvent(
node.getOwnerProject(),
session,
oldNodePath,
WSConstants.EventAction.DELETE
);
WebEventUtils.addNavigatorNodeUpdatedEvent(
node.getOwnerProject(),
session,
newNodePath,
WSConstants.EventAction.CREATE
);
}
@NotNull
private String renameRmResourceNode(@NotNull WebSession session, DBNNode node, @NotNull String newName) throws DBException {
if (newName.contains("/") || newName.contains("\\")) {
@@ -570,14 +551,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
throw e;
}
} else if (node instanceof DBNLocalFolder) {
var nodePath = node.getNodeItemPath();
node.getOwnerProject().getDataSourceRegistry().removeFolder(((DBNLocalFolder) node).getFolder(), false);
WebEventUtils.addNavigatorNodeUpdatedEvent(
session.getProjectById(projectId),
session,
nodePath,
WSConstants.EventAction.DELETE
);
} else if (node instanceof DBNResourceManagerResource) {
DBNResourceManagerResource rmResource = ((DBNResourceManagerResource) node);
String resourceProjectId = rmResource.getResourceProject().getId();
@@ -592,7 +566,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
}
}
if (containsFolderNodes) {
WebServiceUtils.updateConfigAndRefreshDatabases(session, projectId);
WebServiceUtils.refreshDatabases(session, projectId);
}
return nodes.size();
@@ -645,15 +619,7 @@ public class WebServiceNavigator implements DBWServiceNavigator {
folder = localFolderNode.getFolder();
}
dataSourceNode.moveToFolder(folderNode.getOwnerProject(), folder);
node.getOwnerProject().getDataSourceRegistry().updateDataSource(
dataSourceNode.getDataSourceContainer());
WebEventUtils.addDataSourceUpdatedEvent(
node.getOwnerProject(),
session,
dataSourceNode.getDataSourceContainer().getId(),
WSConstants.EventAction.UPDATE,
WSDataSourceProperty.CONFIGURATION
);
node.getOwnerProject().getDataSourceRegistry().updateDataSource(dataSourceNode.getDataSourceContainer());
} else if (node instanceof DBNLocalFolder dbnLocalFolder) {
DBPDataSourceFolder parentFolder = null;
if (folderNode instanceof DBNLocalFolder parentFolderNode) {
@@ -667,15 +633,12 @@ public class WebServiceNavigator implements DBWServiceNavigator {
throw new DBWebException("Node " + folderNodePath + " contains folder with name '" + node.getName() + "'");
}
}
var oldNodePath = node.getNodeItemPath();
node.getOwnerProject().getDataSourceRegistry().moveFolder(
dbnLocalFolder.getFolder().getFolderPath(),
dbnLocalFolder.generateNewFolderPath(parentFolder, dbnLocalFolder.getNodeDisplayName())
);
node.getOwnerProject().getDataSourceRegistry().checkForErrors();
var newNodePath = node.getNodeItemPath();
WebServiceUtils.updateConfigAndRefreshDatabases(session, node.getOwnerProject().getId());
addNavigatorNodeMoveEvent(session, node, oldNodePath, newNodePath);
WebServiceUtils.refreshDatabases(session, node.getOwnerProject().getId());
} else if (node instanceof DBNResourceManagerResource) {
boolean rmNewNode = folderNode instanceof DBNAbstractResourceManagerNode;
DBNResourceManagerResource rmOldNode = (DBNResourceManagerResource) node;