From 14ce2fce3ccafb1c455d88a358b2b51971288e23 Mon Sep 17 00:00:00 2001 From: Aleksandr Skoblikov Date: Thu, 3 Oct 2024 21:47:33 +0400 Subject: [PATCH] dbeaver/dbeaver-vscode#19 map custom folder as project in vc code product --- .../io/cloudbeaver/BaseWebProjectImpl.java | 6 +- .../WebHeadlessSessionProjectImpl.java | 4 +- .../src/io/cloudbeaver/WebProjectImpl.java | 8 +- .../io/cloudbeaver/WebSessionProjectImpl.java | 21 +- .../model/app/WebAppConfiguration.java | 2 + .../rm/local/BaseLocalResourceController.java | 352 ++++++++++++++++++ .../rm/local/LocalResourceController.java | 264 +------------ .../model/session/BaseWebSession.java | 7 +- .../cloudbeaver/model/session/WebSession.java | 12 +- .../service/core/impl/WebServiceCore.java | 7 +- 10 files changed, 411 insertions(+), 272 deletions(-) create mode 100644 server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java index fb066b6719..ddbd377c4e 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/BaseWebProjectImpl.java @@ -23,7 +23,6 @@ import org.jkiss.dbeaver.model.rm.RMController; import org.jkiss.dbeaver.model.rm.RMControllerProvider; import org.jkiss.dbeaver.model.rm.RMProject; -import org.jkiss.dbeaver.model.rm.RMUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.Pair; @@ -44,11 +43,12 @@ public BaseWebProjectImpl( @NotNull DBPWorkspace workspace, @NotNull RMController resourceController, @NotNull SMSessionContext sessionContext, - @NotNull RMProject project + @NotNull RMProject project, + @NotNull Path path ) { super(workspace, sessionContext); this.resourceController = resourceController; - this.path = RMUtils.getProjectPath(project); + this.path = path; this.project = project; } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebHeadlessSessionProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebHeadlessSessionProjectImpl.java index 10dfc6acce..852520ef2f 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebHeadlessSessionProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebHeadlessSessionProjectImpl.java @@ -19,6 +19,7 @@ import io.cloudbeaver.model.session.WebHeadlessSession; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.model.rm.RMProject; +import org.jkiss.dbeaver.model.rm.RMUtils; public class WebHeadlessSessionProjectImpl extends WebProjectImpl { public WebHeadlessSessionProjectImpl( @@ -30,7 +31,8 @@ public WebHeadlessSessionProjectImpl( session.getUserContext().getRmController(), session.getSessionContext(), project, - session.getUserContext().getPreferenceStore() + session.getUserContext().getPreferenceStore(), + RMUtils.getProjectPath(project) ); } } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebProjectImpl.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebProjectImpl.java index 00a4c64136..c6c7144198 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebProjectImpl.java @@ -30,18 +30,22 @@ import org.jkiss.dbeaver.registry.rm.DataSourceRegistryRM; import org.jkiss.dbeaver.runtime.DBWorkbench; +import java.nio.file.Path; + public abstract class WebProjectImpl extends BaseWebProjectImpl { private static final Log log = Log.getLog(WebProjectImpl.class); @NotNull protected final DBPPreferenceStore preferenceStore; + public WebProjectImpl( @NotNull DBPWorkspace workspace, @NotNull RMController resourceController, @NotNull SMSessionContext sessionContext, @NotNull RMProject project, - @NotNull DBPPreferenceStore preferenceStore + @NotNull DBPPreferenceStore preferenceStore, + @NotNull Path path ) { - super(workspace, resourceController, sessionContext, project); + super(workspace, resourceController, sessionContext, project, path); this.preferenceStore = preferenceStore; } 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 5dacda453f..b55860c05b 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/WebSessionProjectImpl.java @@ -27,10 +27,12 @@ import org.jkiss.dbeaver.model.app.DBPDataSourceRegistryCache; 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.WSEventType; import org.jkiss.dbeaver.registry.DataSourceDescriptor; import org.jkiss.dbeaver.runtime.jobs.DisconnectJob; +import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; @@ -49,7 +51,24 @@ public WebSessionProjectImpl( webSession.getRmController(), webSession.getSessionContext(), project, - webSession.getUserPreferenceStore() + webSession.getUserPreferenceStore(), + RMUtils.getProjectPath(project) + ); + this.webSession = webSession; + } + + public WebSessionProjectImpl( + @NotNull WebSession webSession, + @NotNull RMProject project, + @NotNull Path path + ) { + super( + webSession.getWorkspace(), + webSession.getRmController(), + webSession.getSessionContext(), + project, + webSession.getUserPreferenceStore(), + path ); this.webSession = webSession; } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java index 92c2388921..ca09529a49 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/app/WebAppConfiguration.java @@ -17,6 +17,7 @@ package io.cloudbeaver.model.app; import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; import java.util.Map; @@ -28,6 +29,7 @@ public interface WebAppConfiguration { boolean isAnonymousAccessEnabled(); + @Nullable T getResourceQuota(String quotaId); String getDefaultUserTeam(); 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 new file mode 100644 index 0000000000..fbea3749e0 --- /dev/null +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/local/BaseLocalResourceController.java @@ -0,0 +1,352 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2024 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.model.rm.local; + +import io.cloudbeaver.BaseWebProjectImpl; +import io.cloudbeaver.model.rm.lock.RMFileLockController; +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.DBPDataSourceConfigurationStorage; +import org.jkiss.dbeaver.model.DBPDataSourceContainer; +import org.jkiss.dbeaver.model.DBPDataSourceFolder; +import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; +import org.jkiss.dbeaver.model.app.DBPProject; +import org.jkiss.dbeaver.model.app.DBPWorkspace; +import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl; +import org.jkiss.dbeaver.model.rm.RMController; +import org.jkiss.dbeaver.model.rm.RMEvent; +import org.jkiss.dbeaver.model.rm.RMEventManager; +import org.jkiss.dbeaver.model.rm.RMProject; +import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; +import org.jkiss.dbeaver.registry.*; +import org.jkiss.dbeaver.utils.GeneralUtils; +import org.jkiss.utils.ArrayUtils; +import org.jkiss.utils.IOUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.function.Predicate; + +public abstract class BaseLocalResourceController implements RMController { + private static final Log log = Log.getLog(BaseLocalResourceController.class); + + public static final String DEFAULT_CHANGE_ID = "0"; + private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+"; + private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name + + @NotNull + protected final DBPWorkspace workspace; + @NotNull + protected final RMFileLockController lockController; + + protected BaseLocalResourceController( + @NotNull DBPWorkspace workspace, + @NotNull RMFileLockController lockController + ) { + this.workspace = workspace; + this.lockController = lockController; + } + + @Override + public RMProject getProject(@NotNull String projectId, boolean readResources, boolean readProperties) + throws DBException { + RMProject project = makeProjectFromId(projectId, true); + if (project == null) { + return null; + } + if (readResources) { + doProjectOperation(projectId, () -> { + project.setChildren( + listResources(projectId, null, null, readProperties, false, true) + ); + return null; + }); + } + return project; + } + + @Override + public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException { + var project = getWebProject(projectId, false); + return doFileReadOperation(projectId, + project.getMetadataFilePath(), + () -> project.getProjectProperty(propName)); + } + + @Override + public void setProjectProperty( + @NotNull String projectId, + @NotNull String propName, + @NotNull Object propValue + ) throws DBException { + BaseWebProjectImpl webProject = getWebProject(projectId, false); + doFileWriteOperation(projectId, webProject.getMetadataFilePath(), + () -> { + log.debug("Updating value for property '" + propName + "' in project '" + projectId + "'"); + webProject.setProjectProperty(propName, propValue); + return null; + } + ); + } + + @Override + public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) + throws DBException { + DBPProject projectMetadata = getWebProject(projectId, false); + return doFileReadOperation( + projectId, + projectMetadata.getMetadataFolder(false), + () -> { + DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry(); + registry.refreshConfig(); + registry.checkForErrors(); + DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); + Predicate 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); + } + ); + } + + @Override + public void createProjectDataSources( + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + updateProjectDataSources(projectId, configuration, dataSourceIds); + } + + @Override + public boolean updateProjectDataSources( + @NotNull String projectId, + @NotNull String configuration, + @Nullable List dataSourceIds + ) throws DBException { + try (var lock = lockController.lockProject(projectId, "updateProjectDataSources")) { + DBPProject project = getWebProject(projectId, 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( + List.of(storage), + manager, + dataSourceIds, + true, + false + ); + registry.checkForErrors(); + log.debug("Save data sources configuration in project '" + projectId + "'"); + ((DataSourcePersistentRegistry) registry).saveDataSources(); + registry.checkForErrors(); + return configChanged; + } + ); + } + } + + @Override + public void deleteProjectDataSources( + @NotNull String projectId, + @NotNull String[] dataSourceIds + ) throws DBException { + try (var projectLock = lockController.lockProject(projectId, "deleteDatasources")) { + DBPProject project = getWebProject(projectId, false); + doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + for (String dataSourceId : dataSourceIds) { + DBPDataSourceContainer dataSource = registry.getDataSource(dataSourceId); + + if (dataSource != null) { + log.debug("Deleting data source '" + dataSourceId + "' in project '" + projectId + "'"); + registry.removeDataSource(dataSource); + } else { + log.warn("Could not find datasource " + dataSourceId + " for deletion"); + } + } + registry.checkForErrors(); + return null; + }); + } + } + + @Override + public void createProjectDataSourceFolder( + @NotNull String projectId, + @NotNull String folderPath + ) throws DBException { + try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + DBPProject project = getWebProject(projectId, false); + log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'"); + doFileWriteOperation(projectId, project.getMetadataFolder(false), + () -> { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + var result = Path.of(folderPath); + var newName = result.getFileName().toString(); + GeneralUtils.validateResourceName(newName); + var parent = result.getParent(); + var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/")); + DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName); + registry.checkForErrors(); + return null; + } + ); + } + } + + @Override + public void deleteProjectDataSourceFolders( + @NotNull String projectId, + @NotNull String[] folderPaths, + boolean dropContents + ) throws DBException { + try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + DBPProject project = getWebProject(projectId, false); + doFileWriteOperation(projectId, project.getMetadataFolder(false), + () -> { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + for (String folderPath : folderPaths) { + DBPDataSourceFolder folder = registry.getFolder(folderPath); + if (folder != null) { + log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'"); + registry.removeFolder(folder, dropContents); + } else { + log.warn("Can not find folder by path [" + folderPath + "] for deletion"); + } + } + registry.checkForErrors(); + return null; + } + ); + } + } + + @Override + public void moveProjectDataSourceFolder( + @NotNull String projectId, + @NotNull String oldPath, + @NotNull String newPath + ) throws DBException { + try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + DBPProject project = getWebProject(projectId, false); + log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'"); + doFileWriteOperation(projectId, project.getMetadataFolder(false), + () -> { + DBPDataSourceRegistry registry = project.getDataSourceRegistry(); + registry.moveFolder(oldPath, newPath); + registry.checkForErrors(); + return null; + } + ); + } + } + + protected abstract BaseWebProjectImpl getWebProject(String projectId, boolean refresh) throws DBException; + + protected abstract T doFileWriteOperation(String projectId, Path file, RMFileOperation operation) + throws DBException; + + protected abstract T doFileReadOperation(String projectId, Path file, RMFileOperation operation) + throws DBException; + + protected abstract T doProjectOperation(String projectId, RMFileOperation operation) throws DBException; + + protected abstract RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException; + + protected void validateResourcePath(String resourcePath) throws DBException { + var fullPath = Paths.get(resourcePath); + for (Path path : fullPath) { + String fileName = IOUtils.getFileNameWithoutExtension(path); + GeneralUtils.validateResourceName(fileName); + } + } + + protected void createFolder(Path targetPath) throws DBException { + if (!Files.exists(targetPath)) { + try { + Files.createDirectories(targetPath); + } catch (IOException e) { + throw new DBException("Error creating folder '" + targetPath + "'"); + } + } + } + + protected class InternalWebProjectImpl extends BaseWebProjectImpl { + public InternalWebProjectImpl( + @NotNull SessionContextImpl sessionContext, + @NotNull RMProject rmProject, + @NotNull Path projectPath + ) { + super( + BaseLocalResourceController.this.workspace, + BaseLocalResourceController.this, + sessionContext, + rmProject, + projectPath + ); + } + + @NotNull + @Override + protected DBPDataSourceRegistry createDataSourceRegistry() { + return new DataSourceRegistry(this); + } + } + + protected void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_ADD, + getProject(projectId, false, false), + resourcePath) + ); + } + + protected void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath) + throws DBException { + RMEventManager.fireEvent( + new RMEvent(RMEvent.Action.RESOURCE_DELETE, + makeProjectFromId(projectId, false), + resourcePath + ) + ); + } + + protected void fireRmProjectAddEvent(@NotNull RMProject project) { + RMEventManager.fireEvent( + new RMEvent( + RMEvent.Action.RESOURCE_ADD, + project + ) + ); + } +} 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 21657250bc..0e42353e31 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 @@ -29,40 +29,31 @@ import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.model.DBPDataSourceConfigurationStorage; -import org.jkiss.dbeaver.model.DBPDataSourceContainer; -import org.jkiss.dbeaver.model.DBPDataSourceFolder; -import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry; -import org.jkiss.dbeaver.model.app.DBPProject; 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.impl.app.BaseProjectImpl; import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl; import org.jkiss.dbeaver.model.rm.*; -import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; 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.WSEventType; import org.jkiss.dbeaver.model.websocket.event.WSSessionLogUpdatedEvent; -import org.jkiss.dbeaver.registry.*; +import org.jkiss.dbeaver.registry.ResourceTypeDescriptor; +import org.jkiss.dbeaver.registry.ResourceTypeRegistry; import org.jkiss.dbeaver.runtime.DBWorkbench; -import org.jkiss.dbeaver.utils.GeneralUtils; -import org.jkiss.utils.ArrayUtils; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.IOUtils; import org.jkiss.utils.Pair; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.text.MessageFormat; 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; @@ -70,15 +61,10 @@ /** * Resource manager API */ -public class LocalResourceController implements RMController { +public class LocalResourceController extends BaseLocalResourceController { private static final Log log = Log.getLog(LocalResourceController.class); - private static final String FILE_REGEX = "(?U)[\\w.$()@/\\\\ -]+"; - private static final String PROJECT_REGEX = "(?U)[\\w.$()@ -]+"; // slash not allowed in project name - public static final String DEFAULT_CHANGE_ID = "0"; - - private final DBPWorkspace workspace; protected final SMCredentialsProvider credentialsProvider; private final Path rootPath; @@ -86,7 +72,6 @@ public class LocalResourceController implements RMController { private final Path sharedProjectsPath; private final String globalProjectName; private Supplier smControllerSupplier; - protected final RMFileLockController lockController; protected final List fileHandlers; private final Map projectRegistries = new LinkedHashMap<>(); @@ -99,13 +84,12 @@ public LocalResourceController( Path sharedProjectsPath, Supplier smControllerSupplier ) throws DBException { - this.workspace = workspace; + super(workspace, new RMFileLockController(WebAppUtils.getWebApplication())); this.credentialsProvider = credentialsProvider; this.rootPath = rootPath; this.userProjectsPath = userProjectsPath; this.sharedProjectsPath = sharedProjectsPath; this.smControllerSupplier = smControllerSupplier; - this.lockController = new RMFileLockController(WebAppUtils.getWebApplication()); this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName(); this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers(); @@ -131,7 +115,7 @@ protected BaseWebProjectImpl getWebProject(String projectId, boolean refresh) th if (project == null || refresh) { SessionContextImpl sessionContext = new SessionContextImpl(null); RMProject rmProject = makeProjectFromId(projectId, false); - project = new InternalWebProjectImpl(sessionContext, rmProject); + project = new InternalWebProjectImpl(sessionContext, rmProject, getProjectPath(projectId)); projectRegistries.put(projectId, project); } return project; @@ -342,182 +326,6 @@ public RMProject getProject(@NotNull String projectId, boolean readResources, bo return project; } - @Override - public Object getProjectProperty(@NotNull String projectId, @NotNull String propName) throws DBException { - var project = getWebProject(projectId, false); - return doFileReadOperation(projectId, project.getMetadataFilePath(), () -> project.getProjectProperty(propName)); - } - - @Override - public void setProjectProperty( - @NotNull String projectId, - @NotNull String propName, - @NotNull Object propValue - ) throws DBException { - BaseWebProjectImpl webProject = getWebProject(projectId, false); - doFileWriteOperation(projectId, webProject.getMetadataFilePath(), - () -> { - log.debug("Updating value for property '" + propName + "' in project '" + projectId + "'"); - webProject.setProjectProperty(propName, propValue); - return null; - } - ); - } - - @Override - public String getProjectsDataSources(@NotNull String projectId, @Nullable String[] dataSourceIds) throws DBException { - DBPProject projectMetadata = getWebProject(projectId, false); - return doFileReadOperation( - projectId, - projectMetadata.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = projectMetadata.getDataSourceRegistry(); - registry.refreshConfig(); - registry.checkForErrors(); - DataSourceConfigurationManagerBuffer buffer = new DataSourceConfigurationManagerBuffer(); - Predicate 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); - } - ); - } - - @Override - public void createProjectDataSources( - @NotNull String projectId, - @NotNull String configuration, - @Nullable List dataSourceIds - ) throws DBException { - updateProjectDataSources(projectId, configuration, dataSourceIds); - } - - @Override - public boolean updateProjectDataSources( - @NotNull String projectId, - @NotNull String configuration, - @Nullable List dataSourceIds - ) throws DBException { - try (var lock = lockController.lockProject(projectId, "updateProjectDataSources")) { - DBPProject project = getWebProject(projectId, 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( - List.of(storage), - manager, - dataSourceIds, - true, - false - ); - registry.checkForErrors(); - log.debug("Save data sources configuration in project '" + projectId + "'"); - ((DataSourcePersistentRegistry) registry).saveDataSources(); - registry.checkForErrors(); - return configChanged; - } - ); - } - } - - @Override - public void deleteProjectDataSources(@NotNull String projectId, - @NotNull String[] dataSourceIds) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "deleteDatasources")) { - DBPProject project = getWebProject(projectId, false); - doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - for (String dataSourceId : dataSourceIds) { - DBPDataSourceContainer dataSource = registry.getDataSource(dataSourceId); - - if (dataSource != null) { - log.debug("Deleting data source '" + dataSourceId + "' in project '" + projectId + "'"); - registry.removeDataSource(dataSource); - } else { - log.warn("Could not find datasource " + dataSourceId + " for deletion"); - } - } - registry.checkForErrors(); - return null; - }); - } - } - - @Override - public void createProjectDataSourceFolder(@NotNull String projectId, - @NotNull String folderPath) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { - DBPProject project = getWebProject(projectId, false); - log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'"); - doFileWriteOperation(projectId, project.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - var result = Path.of(folderPath); - var newName = result.getFileName().toString(); - GeneralUtils.validateResourceName(newName); - var parent = result.getParent(); - var parentFolder = parent == null ? null : registry.getFolder(parent.toString().replace("\\", "/")); - DBPDataSourceFolder newFolder = registry.addFolder(parentFolder, newName); - registry.checkForErrors(); - return null; - } - ); - } - } - - @Override - public void deleteProjectDataSourceFolders( - @NotNull String projectId, - @NotNull String[] folderPaths, - boolean dropContents - ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { - DBPProject project = getWebProject(projectId, false); - doFileWriteOperation(projectId, project.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - for (String folderPath : folderPaths) { - DBPDataSourceFolder folder = registry.getFolder(folderPath); - if (folder != null) { - log.debug("Deleting data source folder '" + folderPath + "' in project '" + projectId + "'"); - registry.removeFolder(folder, dropContents); - } else { - log.warn("Can not find folder by path [" + folderPath + "] for deletion"); - } - } - registry.checkForErrors(); - return null; - } - ); - } - } - - @Override - public void moveProjectDataSourceFolder( - @NotNull String projectId, - @NotNull String oldPath, - @NotNull String newPath - ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { - DBPProject project = getWebProject(projectId, false); - log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'"); - doFileWriteOperation(projectId, project.getMetadataFolder(false), - () -> { - DBPDataSourceRegistry registry = project.getDataSourceRegistry(); - registry.moveFolder(oldPath, newPath); - registry.checkForErrors(); - return null; - } - ); - } - } - @NotNull @Override public RMResource[] listResources( @@ -804,15 +612,6 @@ public String setResourceContents( return DEFAULT_CHANGE_ID; } - protected void createFolder(Path targetPath) throws DBException { - if (!Files.exists(targetPath)) { - try { - Files.createDirectories(targetPath); - } catch (IOException e) { - throw new DBException("Error creating folder '" + targetPath + "'"); - } - } - } @NotNull @Override @@ -857,14 +656,6 @@ public String setResourceProperties( } } - private void validateResourcePath(String resourcePath) throws DBException { - var fullPath = Paths.get(resourcePath); - for (Path path : fullPath) { - String fileName = IOUtils.getFileNameWithoutExtension(path); - GeneralUtils.validateResourceName(fileName); - } - } - @NotNull private Path getTargetPath(@NotNull String projectId, @NotNull String resourcePath) throws DBException { Path projectPath = getProjectPath(projectId); @@ -894,7 +685,7 @@ private String makeProjectIdFromPath(Path path, RMProjectType type) { } @Nullable - private RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException { + protected RMProject makeProjectFromId(String projectId, boolean loadPermissions) throws DBException { var projectName = parseProjectName(projectId); var projectPath = getProjectPath(projectId); if (!Files.exists(projectPath)) { @@ -1138,32 +929,6 @@ private String getProjectRelativePath(@NotNull String projectId, @NotNull Path p return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR); } - private void fireRmResourceAddEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException { - RMEventManager.fireEvent( - new RMEvent(RMEvent.Action.RESOURCE_ADD, - getProject(projectId, false, false), - resourcePath) - ); - } - - private void fireRmResourceDeleteEvent(@NotNull String projectId, @NotNull String resourcePath) throws DBException { - RMEventManager.fireEvent( - new RMEvent(RMEvent.Action.RESOURCE_DELETE, - makeProjectFromId(projectId, false), - resourcePath - ) - ); - } - - private void fireRmProjectAddEvent(@NotNull RMProject project) { - RMEventManager.fireEvent( - new RMEvent( - RMEvent.Action.RESOURCE_ADD, - project - ) - ); - } - protected void handleProjectOpened(String projectId) throws DBException { createResourceTypeFolders(getProjectPath(projectId)); } @@ -1284,21 +1049,4 @@ public static boolean isProjectOwner(String projectId, String userId) { rmProjectName.name.equals(userId); } - - private class InternalWebProjectImpl extends BaseWebProjectImpl { - public InternalWebProjectImpl(SessionContextImpl sessionContext, RMProject rmProject) { - super( - LocalResourceController.this.workspace, - LocalResourceController.this, - sessionContext, - rmProject); - } - - @NotNull - @Override - protected DBPDataSourceRegistry createDataSourceRegistry() { - return new DataSourceRegistry(this); - } - } - } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/BaseWebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/BaseWebSession.java index 9cad92722f..43b69e137b 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/BaseWebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/BaseWebSession.java @@ -63,11 +63,16 @@ public BaseWebSession(@NotNull String id, @NotNull WebApplication application) t this.application = application; this.createTime = System.currentTimeMillis(); this.lastAccessTime = this.createTime; - this.workspace = new WebSessionWorkspace(this); + this.workspace = createWebWorkspace(); this.workspace.getAuthContext().addSession(this); this.userContext = createUserContext(); } + @NotNull + protected WebSessionWorkspace createWebWorkspace() { + return new WebSessionWorkspace(this); + } + protected WebUserContext createUserContext() throws DBException { return new WebUserContext(this.application, this.workspace); } diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java index e78b45d9b2..ce10a80553 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/session/WebSession.java @@ -69,6 +69,7 @@ import org.jkiss.utils.CommonUtils; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; import java.time.Instant; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -297,7 +298,7 @@ private void loadProjects() { for (RMProject project : rmProjects) { createWebProject(project); } - if (user == null) { + if (user == null && application.getAppConfiguration().isAnonymousAccessEnabled()) { WebProjectImpl anonymousProject = createWebProject(RMUtils.createAnonymousProject()); anonymousProject.setInMemory(true); } @@ -310,12 +311,12 @@ private void loadProjects() { } } - private WebSessionProjectImpl createWebProject(RMProject project) { + private WebSessionProjectImpl createWebProject(RMProject project) throws DBException { WebSessionProjectImpl sessionProject; if (project.isGlobal()) { sessionProject = createGlobalProject(project); } else { - sessionProject = new WebSessionProjectImpl(this, project); + sessionProject = new WebSessionProjectImpl(this, project, getProjectPath(project)); } // do not load data sources for anonymous project if (project.getType() == RMProjectType.USER && userContext.getUser() == null) { @@ -328,6 +329,11 @@ private WebSessionProjectImpl createWebProject(RMProject project) { return sessionProject; } + @NotNull + protected Path getProjectPath(@NotNull RMProject project) throws DBException { + return RMUtils.getProjectPath(project); + } + protected WebSessionProjectImpl createGlobalProject(RMProject project) { globalProject = new WebSessionGlobalProjectImpl(this, project); globalProject.refreshAccessibleConnectionIds(); 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 90c097cbe9..77a5a40fc7 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 @@ -26,6 +26,7 @@ import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.service.core.DBWServiceCore; import io.cloudbeaver.service.security.SMUtils; +import io.cloudbeaver.utils.WebAppUtils; import io.cloudbeaver.utils.WebConnectionFolderUtils; import io.cloudbeaver.utils.WebDataSourceUtils; import io.cloudbeaver.utils.WebEventUtils; @@ -209,7 +210,7 @@ private List getConnectionFoldersFromProject( @Override public String[] getSessionPermissions(@NotNull WebSession webSession) throws DBWebException { - if (CBApplication.getInstance().isConfigurationMode()) { + if (WebAppUtils.getWebApplication().isConfigurationMode()) { return new String[]{ DBWConstants.PERMISSION_ADMIN }; @@ -437,7 +438,7 @@ public WebConnectionInfo createConnection( var rmProject = project.getRMProject(); if (rmProject.getType() == RMProjectType.USER && !webSession.hasPermission(DBWConstants.PERMISSION_ADMIN) - && !CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections() + && !WebAppUtils.getWebApplication().getAppConfiguration().isSupportsCustomConnections() ) { throw new DBWebException("New connection create is restricted by server configuration"); } @@ -857,7 +858,7 @@ private WebConnectionInfo closeAndDeleteConnection( @Override public List getProjects(@NotNull WebSession session) { var customConnectionsEnabled = - CBApplication.getInstance().getAppConfiguration().isSupportsCustomConnections() + WebAppUtils.getWebApplication().getAppConfiguration().isSupportsCustomConnections() || SMUtils.isRMAdmin(session); return session.getAccessibleProjects().stream() .map(pr -> new WebProjectInfo(session, pr, customConnectionsEnabled))