Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 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.
Expand All @@ -25,10 +25,13 @@
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBFileController;
import org.jkiss.dbeaver.model.app.DBPLockManagerProvider;
import org.jkiss.dbeaver.model.app.DBPWorkspace;
import org.jkiss.dbeaver.model.auth.SMCredentialsProvider;
import org.jkiss.dbeaver.model.auth.SMSessionContext;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.shared.SharedFileLockManager;
import org.jkiss.dbeaver.model.impl.app.ApplicationRegistry;
import org.jkiss.dbeaver.model.impl.app.BaseApplicationImpl;
import org.jkiss.dbeaver.model.impl.app.BaseWorkspaceImpl;
Expand All @@ -48,7 +51,7 @@
/**
* Servlet application
*/
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication {
public abstract class BaseServletApplication extends BaseApplicationImpl implements ServletApplication, DBPLockManagerProvider {

public static final String DEFAULT_CONFIG_FILE_PATH = "/etc/cloudbeaver.conf";
public static final String CUSTOM_CONFIG_FOLDER = "custom";
Expand All @@ -59,6 +62,7 @@ public abstract class BaseServletApplication extends BaseApplicationImpl impleme

private String instanceId;

@NotNull
@Override
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
Expand All @@ -73,6 +77,18 @@ public DBFileController createFileController(@NotNull SMCredentialsProvider cred
throw new IllegalStateException("File controller is not supported by " + getClass().getSimpleName());
}

@NotNull
@Override
public LockManager createLockManager(@NotNull Path metadataFolder) throws DBException {
return new SharedFileLockManager(getApplicationInstanceId(), metadataFolder);
}

@NotNull
@Override
public LockManager createLockManager() throws DBException {
return new SharedFileLockManager(getApplicationInstanceId());
}

@Nullable
@Override
public Path getDefaultWorkingFolder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
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.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.rm.RMController;
import org.jkiss.dbeaver.model.rm.RMEvent;
import org.jkiss.dbeaver.model.rm.RMEventManager;
Expand All @@ -53,11 +55,11 @@ public abstract class BaseLocalResourceController implements RMController {
@NotNull
protected final DBPWorkspace workspace;
@NotNull
protected final FileLockController lockController;
protected final LockManager lockController;

protected BaseLocalResourceController(
@NotNull DBPWorkspace workspace,
@NotNull FileLockController lockController
@NotNull LockManager lockController
) {
this.workspace = workspace;
this.lockController = lockController;
Expand Down Expand Up @@ -157,7 +159,7 @@ protected DataSourceParseResults updateProjectDataSourcesConfig(
@NotNull String configuration,
@Nullable List<String> dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "updateProjectDataSources")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProjectDataSources"))) {
DBPProject project = getWebProject(projectId, false);
return doFileWriteOperation(
projectId, project.getMetadataFolder(false),
Expand Down Expand Up @@ -188,7 +190,7 @@ public void deleteProjectDataSources(
@NotNull String projectId,
@NotNull String[] dataSourceIds
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteDataSources")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteDataSources"))) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> {
DBPDataSourceRegistry registry = project.getDataSourceRegistry();
Expand All @@ -213,7 +215,7 @@ public void createProjectDataSourceFolder(
@NotNull String projectId,
@NotNull String folderPath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
Expand Down Expand Up @@ -241,7 +243,7 @@ public void deleteProjectDataSourceFolders(
@NotNull String[] folderPaths,
boolean dropContents
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
doFileWriteOperation(projectId, project.getMetadataFolder(false),
() -> {
Expand All @@ -267,7 +269,7 @@ public void moveProjectDataSourceFolder(
@NotNull String oldPath,
@NotNull String newPath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createDatasourceFolder")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createDatasourceFolder"))) {
DBPProject project = getWebProject(projectId, false);
log.debug("Moving data source folder from '" + oldPath + "' to '" + newPath + "' in project '" + projectId + "'");
doFileWriteOperation(projectId, project.getMetadataFolder(false),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2025 DBeaver Corp and others
* Copyright (C) 2010-2026 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.
Expand Down Expand Up @@ -33,7 +33,9 @@
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.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl;
import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
Expand Down Expand Up @@ -90,9 +92,10 @@ public LocalResourceController(
@NotNull Path rootPath,
@NotNull Path userProjectsPath,
@NotNull Path sharedProjectsPath,
@NotNull Supplier<SMAdminController> smControllerSupplier
@NotNull Supplier<SMAdminController> smControllerSupplier,
@NotNull LockManager lockController
) throws DBException {
super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId()));
super(workspace, lockController);
this.credentialsProvider = credentialsProvider;
this.rootPath = rootPath;
this.userProjectsPath = userProjectsPath;
Expand All @@ -101,7 +104,7 @@ public LocalResourceController(

this.globalProjectName = DBWorkbench.getPlatform().getApplication().getDefaultProjectName();
this.fileHandlers = RMFileOperationHandlersRegistry.getInstance().getFileHandlers();
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath);
this.sharedProjectsMetadataInfo = new ProjectsMetadataInfo(sharedProjectsPath, lockController);
}

@NotNull
Expand Down Expand Up @@ -303,7 +306,7 @@ public RMProject createProject(@NotNull String name, @Nullable String descriptio
@Override
public RMProject updateProject(@NotNull String projectId, @NotNull RMProjectInfo projectInfo) throws DBException {
validateProjectName(projectId, projectInfo.getName());
try (var ignoredLock = lockController.lock(projectId, "updateProject")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("updateProject"))) {
RMLocalProject project = getWebProject(projectId, false);
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
Expand All @@ -330,7 +333,7 @@ private void validateProjectName(@Nullable String projectId, @Nullable String na

@Override
public void deleteProject(@NotNull String projectId) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteProject")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteProject"))) {
Path targetPath = getProjectPath(projectId);
if (!Files.exists(targetPath)) {
log.error(MessageFormat.format("Project folder ''{0}'' is not found", projectId));
Expand Down Expand Up @@ -699,7 +702,7 @@ public String createResource(
@NotNull String resourcePath,
boolean isFolder
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "createResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("createResource"))) {
validateResourcePath(resourcePath);
Path targetPath = getTargetPath(projectId, resourcePath);
if (Files.exists(targetPath)) {
Expand Down Expand Up @@ -731,7 +734,7 @@ public String moveResource(
@NotNull String oldResourcePath,
@NotNull String newResourcePath
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "moveResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("moveResource"))) {
var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath);
var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath);
if (log.isDebugEnabled()) {
Expand Down Expand Up @@ -803,7 +806,7 @@ private void movePropertiesRecursive(

@Override
public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "deleteResource")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("deleteResource"))) {
if (log.isDebugEnabled()) {
log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : ""));
}
Expand Down Expand Up @@ -891,7 +894,7 @@ public String setResourceContents(
@NotNull byte[] data,
boolean forceOverwrite
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("setResourceContents"))) {
validateResourcePath(resourcePath);
Number fileSizeLimit = ServletAppUtils.getServletApplication()
.getAppConfiguration()
Expand Down Expand Up @@ -936,7 +939,7 @@ public String setResourceProperty(
@NotNull String propertyName,
@Nullable Object propertyValue
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
Expand All @@ -957,7 +960,7 @@ public String setResourceProperties(
@NotNull String resourcePath,
@NotNull Map<String, Object> properties
) throws DBException {
try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) {
try (var ignoredLock = lockController.lock(LockTarget.of(projectId), LockOptions.of("resourcePropertyUpdate"))) {
validateResourcePath(resourcePath);
RMLocalProject webProject = getWebProject(projectId, false);
doFileWriteOperation(projectId, webProject.getMetadataFilePath(),
Expand Down Expand Up @@ -1204,12 +1207,14 @@ private String getProjectRelativePath(@NotNull String projectId, @NotNull Path p
return getProjectPath(projectId).toAbsolutePath().relativize(path).toString().replace('\\', IPath.SEPARATOR);
}

@NotNull
public static Builder builder(
SMCredentialsProvider credentialsProvider,
DBPWorkspace workspace,
Supplier<SMAdminController> smControllerSupplier
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
) {
return new Builder(workspace, credentialsProvider, smControllerSupplier);
return new Builder(workspace, credentialsProvider, lockController, smControllerSupplier);
}

@Override
Expand All @@ -1225,36 +1230,52 @@ public static class Builder {
protected Path rootPath;
protected Path userProjectsPath;
protected Path sharedProjectsPath;
protected LockManager lockController;

protected Builder(
DBPWorkspace workspace, SMCredentialsProvider credentialsProvider,
Supplier<SMAdminController> smControllerSupplier
@NotNull DBPWorkspace workspace,
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull LockManager lockController,
@NotNull Supplier<SMAdminController> smControllerSupplier
) {
this.workspace = workspace;
this.credentialsProvider = credentialsProvider;
this.smController = smControllerSupplier;
this.rootPath = RMUtils.getRootPath();
this.userProjectsPath = RMUtils.getUserProjectsPath();
this.sharedProjectsPath = RMUtils.getSharedProjectsPath();
this.lockController = lockController;
}

public Builder setRootPath(Path rootPath) {
@NotNull
public Builder setRootPath(@NotNull Path rootPath) {
this.rootPath = rootPath;
return this;
}

public Builder setUserProjectsPath(Path userProjectsPath) {
@NotNull
public Builder setUserProjectsPath(@NotNull Path userProjectsPath) {
this.userProjectsPath = userProjectsPath;
return this;
}

public Builder setSharedProjectsPath(Path sharedProjectsPath) {
@NotNull
public Builder setSharedProjectsPath(@NotNull Path sharedProjectsPath) {
this.sharedProjectsPath = sharedProjectsPath;
return this;
}

@NotNull
public LocalResourceController build() throws DBException {
return new LocalResourceController(workspace, credentialsProvider, rootPath, userProjectsPath, sharedProjectsPath, smController);
return new LocalResourceController(
workspace,
credentialsProvider,
rootPath,
userProjectsPath,
sharedProjectsPath,
smController,
lockController
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package io.cloudbeaver.model.rm.local;

import com.google.gson.reflect.TypeToken;
import io.cloudbeaver.utils.ServletAppUtils;
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.app.DBPProject;
import org.jkiss.dbeaver.model.data.json.JSONUtils;
import org.jkiss.dbeaver.model.fs.lock.FileLockController;
import org.jkiss.dbeaver.model.fs.lock.LockManager;
import org.jkiss.dbeaver.model.fs.lock.LockOptions;
import org.jkiss.dbeaver.model.fs.lock.LockTarget;
import org.jkiss.dbeaver.model.impl.app.BaseProjectImpl;
import org.jkiss.dbeaver.model.rm.RMProjectInfo;
import org.jkiss.dbeaver.model.rm.RMProjectType;
Expand All @@ -45,12 +46,12 @@ public class ProjectsMetadataInfo {

private final Map<String, RMProjectInfo> projectsInfo = new LinkedHashMap<>();
private final Path projectsPath;
private final FileLockController lockController;
private final LockManager lockController;


public ProjectsMetadataInfo(@NotNull Path projectsPath) throws DBException {
public ProjectsMetadataInfo(@NotNull Path projectsPath, @NotNull LockManager lockController) throws DBException {
this.projectsPath = projectsPath;
this.lockController = new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId());
this.lockController = lockController;
readProjectInfos(projectsPath);
}

Expand Down Expand Up @@ -139,7 +140,7 @@ private RMProjectInfo getProjectInfoFromProjectSettings(@NotNull Path projectPat
}

private void saveProjectsInfo() {
try (var lock = lockController.lock(PROJECTS_INFO_FILE_NAME, "saveProjectsInfo")) {
try (var lock = lockController.lock(LockTarget.of(PROJECTS_INFO_FILE_NAME), LockOptions.of("saveProjectsInfo"))) {
log.info("Saving project information");
Files.writeString(projectsPath.resolve(PROJECTS_INFO_FILE_NAME), JSONUtils.GSON.toJson(projectsInfo));
} catch (IOException e) {
Expand All @@ -148,4 +149,4 @@ private void saveProjectsInfo() {
log.error("Error locking file " + PROJECTS_INFO_FILE_NAME + ": " + e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2024 DBeaver Corp and others
* Copyright (C) 2010-2026 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.
Expand Down Expand Up @@ -75,13 +75,16 @@ protected SMAdminController createGlobalSecurityController() throws DBException
);
}



@NotNull
@Override
public RMController createResourceController(@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace) throws DBException {
return LocalResourceController.builder(credentialsProvider, workspace, this::getSecurityController).build();
public RMController createResourceController(
@NotNull SMCredentialsProvider credentialsProvider,
@NotNull DBPWorkspace workspace
) throws DBException {
var lockManager = createLockManager();
return LocalResourceController
.builder(credentialsProvider, workspace, lockManager, this::getSecurityController)
.build();
}

@NotNull
Expand Down
Loading