diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebGlobalProjectRegistryProxy.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebGlobalProjectRegistryProxy.java index 1c7fcf5cc5..6eb54b4f78 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebGlobalProjectRegistryProxy.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebGlobalProjectRegistryProxy.java @@ -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 storages, @NotNull DataSourceConfigurationManager manager, @Nullable Collection dataSourceIds, boolean refresh, diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java index acd11ef022..7e5ec7a754 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java @@ -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 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); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java index 847ed829a2..0e18ed7f0c 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -149,27 +149,37 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String projectId, @NotNull String configuration, @Nullable List dataSourceIds + ) throws DBException { + return updateProjectDataSourcesConfig(projectId, configuration, dataSourceIds) != null; + } + + @Nullable + protected DataSourceParseResults updateProjectDataSourcesConfig( + @NotNull String projectId, + @NotNull String configuration, + @Nullable List 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; } ); diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java index 67f2aac53e..8138830b8f 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/LocalResourceController.java @@ -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 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 dataSourceIds + ) throws DBException { + DBPDataSourceRegistry registry = getWebProject(projectId, false).getDataSourceRegistry(); + Map 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 oldDataSources, + @Nullable DataSourceParseResults parseResults + ) { + if (parseResults == null || credentialsProvider.getActiveUserCredentials() == null || oldDataSources.isEmpty()) { + return; + } + List updatedConfigurationDataSourceIds = new ArrayList<>(); + List updatedNameDataSourceIds = new ArrayList<>(); + List updatedInternalConfigurationDataSourceIds = new ArrayList<>(); + + for (Map.Entry 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( diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java index dfeb3996da..bcd6ff7ce8 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/utils/WebConnectionFolderUtils.java @@ -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; - } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java index e9f60f7e03..ae50682a82 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/WebServiceUtils.java @@ -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) { diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java index 8887d2d984..6bde4ea246 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/server/events/WSDataSourceUpdatedEventHandlerImpl.java @@ -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); diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java index 6e21e85bf8..be2fa613cc 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/ConnectionControllerCE.java @@ -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, diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java index 2710ec36b2..e094affe4e 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/core/impl/WebServiceCore.java @@ -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())); - } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java index b817174230..3c0c6888b8 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/navigator/impl/WebServiceNavigator.java @@ -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;