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 fbea3749e0..847ed829a2 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 @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 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. @@ -17,7 +17,6 @@ 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; @@ -28,6 +27,7 @@ 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.fs.lock.FileLockController; import org.jkiss.dbeaver.model.impl.auth.SessionContextImpl; import org.jkiss.dbeaver.model.rm.RMController; import org.jkiss.dbeaver.model.rm.RMEvent; @@ -57,11 +57,11 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull protected final DBPWorkspace workspace; @NotNull - protected final RMFileLockController lockController; + protected final FileLockController lockController; protected BaseLocalResourceController( @NotNull DBPWorkspace workspace, - @NotNull RMFileLockController lockController + @NotNull FileLockController lockController ) { this.workspace = workspace; this.lockController = lockController; @@ -150,7 +150,7 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String configuration, @Nullable List dataSourceIds ) throws DBException { - try (var lock = lockController.lockProject(projectId, "updateProjectDataSources")) { + try (var lock = lockController.lock(projectId, "updateProjectDataSources")) { DBPProject project = getWebProject(projectId, false); return doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> { @@ -180,7 +180,7 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String projectId, @NotNull String[] dataSourceIds ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "deleteDatasources")) { + try (var projectLock = lockController.lock(projectId, "deleteDatasources")) { DBPProject project = getWebProject(projectId, false); doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> { DBPDataSourceRegistry registry = project.getDataSourceRegistry(); @@ -205,7 +205,7 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String projectId, @NotNull String folderPath ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + try (var projectLock = lockController.lock(projectId, "createDatasourceFolder")) { DBPProject project = getWebProject(projectId, false); log.debug("Creating data source folder '" + folderPath + "' in project '" + projectId + "'"); doFileWriteOperation(projectId, project.getMetadataFolder(false), @@ -230,7 +230,7 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String[] folderPaths, boolean dropContents ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + try (var projectLock = lockController.lock(projectId, "createDatasourceFolder")) { DBPProject project = getWebProject(projectId, false); doFileWriteOperation(projectId, project.getMetadataFolder(false), () -> { @@ -257,7 +257,7 @@ public abstract class BaseLocalResourceController implements RMController { @NotNull String oldPath, @NotNull String newPath ) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "createDatasourceFolder")) { + try (var projectLock = lockController.lock(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), 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 a8f956761f..67f2aac53e 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 @@ -19,7 +19,6 @@ package io.cloudbeaver.model.rm.local; import io.cloudbeaver.BaseWebProjectImpl; import io.cloudbeaver.DBWConstants; import io.cloudbeaver.model.app.ServletApplication; -import io.cloudbeaver.model.rm.lock.RMFileLockController; import io.cloudbeaver.service.security.SMUtils; import io.cloudbeaver.service.sql.WebSQLConstants; import io.cloudbeaver.utils.ServletAppUtils; @@ -32,6 +31,7 @@ import org.jkiss.dbeaver.Log; 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.rm.*; @@ -83,7 +83,7 @@ public class LocalResourceController extends BaseLocalResourceController { Path sharedProjectsPath, Supplier smControllerSupplier ) throws DBException { - super(workspace, new RMFileLockController(ServletAppUtils.getServletApplication())); + super(workspace, new FileLockController(ServletAppUtils.getServletApplication().getApplicationInstanceId())); this.credentialsProvider = credentialsProvider; this.rootPath = rootPath; this.userProjectsPath = userProjectsPath; @@ -297,7 +297,7 @@ public class LocalResourceController extends BaseLocalResourceController { @Override public void deleteProject(@NotNull String projectId) throws DBException { - try (var projectLock = lockController.lockProject(projectId, "deleteProject")) { + try (var projectLock = lockController.lock(projectId, "deleteProject")) { RMProject project = makeProjectFromId(projectId, false); Path targetPath = getProjectPath(projectId); if (!Files.exists(targetPath)) { @@ -395,7 +395,7 @@ public class LocalResourceController extends BaseLocalResourceController { @NotNull String resourcePath, boolean isFolder ) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "createResource")) { + try (var ignoredLock = lockController.lock(projectId, "createResource")) { validateResourcePath(resourcePath); Path targetPath = getTargetPath(projectId, resourcePath); if (Files.exists(targetPath)) { @@ -427,7 +427,7 @@ public class LocalResourceController extends BaseLocalResourceController { @NotNull String oldResourcePath, @NotNull String newResourcePath ) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "moveResource")) { + try (var ignoredLock = lockController.lock(projectId, "moveResource")) { var normalizedOldResourcePath = CommonUtils.normalizeResourcePath(oldResourcePath); var normalizedNewResourcePath = CommonUtils.normalizeResourcePath(newResourcePath); if (log.isDebugEnabled()) { @@ -499,7 +499,7 @@ public class LocalResourceController extends BaseLocalResourceController { @Override public void deleteResource(@NotNull String projectId, @NotNull String resourcePath, boolean recursive) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "deleteResource")) { + try (var ignoredLock = lockController.lock(projectId, "deleteResource")) { if (log.isDebugEnabled()) { log.debug("Removing resource from '" + resourcePath + "' in project '" + projectId + "'" + (recursive ? " recursive" : "")); } @@ -587,7 +587,7 @@ public class LocalResourceController extends BaseLocalResourceController { @NotNull byte[] data, boolean forceOverwrite ) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "setResourceContents")) { + try (var ignoredLock = lockController.lock(projectId, "setResourceContents")) { validateResourcePath(resourcePath); Number fileSizeLimit = ServletAppUtils.getServletApplication() .getAppConfiguration() @@ -632,7 +632,7 @@ public class LocalResourceController extends BaseLocalResourceController { @NotNull String propertyName, @Nullable Object propertyValue ) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) { + try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) { validateResourcePath(resourcePath); BaseWebProjectImpl webProject = getWebProject(projectId, false); doFileWriteOperation(projectId, webProject.getMetadataFilePath(), @@ -653,7 +653,7 @@ public class LocalResourceController extends BaseLocalResourceController { @NotNull String resourcePath, @NotNull Map properties ) throws DBException { - try (var ignoredLock = lockController.lockProject(projectId, "resourcePropertyUpdate")) { + try (var ignoredLock = lockController.lock(projectId, "resourcePropertyUpdate")) { validateResourcePath(resourcePath); BaseWebProjectImpl webProject = getWebProject(projectId, false); doFileWriteOperation(projectId, webProject.getMetadataFilePath(), diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMFileLockController.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMFileLockController.java deleted file mode 100644 index c160071d73..0000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMFileLockController.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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.lock; - -import com.google.gson.Gson; -import io.cloudbeaver.model.app.ServletApplication; -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.DBPWorkspace; -import org.jkiss.utils.IOUtils; - -import java.io.IOException; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.UUID; - -/** - * File based resource locks - */ -public class RMFileLockController { - private static final Log log = Log.getLog(RMFileLockController.class); - private static final int DEFAULT_MAX_LOCK_TIME = 1 * 60 * 1000; // 1 min - private static final int CHECK_PERIOD = 10; - - private static final String LOCK_META_FOLDER = ".locks"; - private static final String LOCK_FILE_EXTENSION = ".lock"; - - private final Gson gson = new Gson(); - - private final Path lockFolderPath; - private final String applicationId; - private final int maxLockTime; - - - public RMFileLockController(ServletApplication application) throws DBException { - this(application, DEFAULT_MAX_LOCK_TIME); - } - - // for tests - public RMFileLockController(ServletApplication application, int maxLockTime) throws DBException { - this.lockFolderPath = application.getWorkspaceDirectory() - .resolve(DBPWorkspace.METADATA_FOLDER) - .resolve(LOCK_META_FOLDER); - this.applicationId = application.getApplicationInstanceId(); - this.maxLockTime = maxLockTime; - } - - /** - * Lock the project for the duration of any operation. - * Other threads/processes will also see this lock, and will wait for it to end - * or force intercept lock, if the operation will take too long and - * exceeds the maximum available locking time {@link #maxLockTime} or the lock is invalid {@link #awaitUnlock)}. - * - * @param projectId - project to be locked - * @param operationName - executed operation name - * @return - lock - */ - @NotNull - public RMLock lockProject(@NotNull String projectId, @NotNull String operationName) throws DBException { - synchronized (RMFileLockController.class) { - try { - RMLockInfo lockInfo = new RMLockInfo.Builder(projectId, UUID.randomUUID().toString()) - .setApplicationId(applicationId) - .setOperationName(operationName) - .setOperationStartTime(System.currentTimeMillis()) - .build(); - Path projectLockFile = getProjectLockFilePath(projectId); - - if (!IOUtils.isFileFromDefaultFS(lockFolderPath)) { - // fake lock for external file system? - return new RMLock(projectLockFile); - } - createLockFolderIfNeeded(); - createProjectFolder(projectId); - - createLockFile(projectLockFile, lockInfo); - return new RMLock(projectLockFile); - } catch (Exception e) { - throw new DBException("Failed to lock project: " + projectId, e); - } - } - } - - /** - * if the project is already locked, the operation will be executed as a child of the first lock, - * otherwise it creates its own lock. - * - * @param projectId - project to be locked - * @param operationName - executed operation name - * @return - lock - */ - @Nullable - public RMLock lockIfNotLocked(@NotNull String projectId, @NotNull String operationName) throws DBException { - synchronized (RMFileLockController.class) { - if (isProjectLocked(projectId)) { - return null; - } - return lockProject(projectId, operationName); - } - } - - /** - * Check that project locked - */ - public boolean isProjectLocked(String projectId) { - Path projectLockFilePath = getProjectLockFilePath(projectId); - return isLocked(projectLockFilePath); - } - - protected boolean isLocked(Path lockFilePath) { - return Files.exists(lockFilePath); - } - - private void createLockFile(Path projectLockFile, RMLockInfo lockInfo) throws DBException, InterruptedException { - boolean lockFileCreated = false; - while (!lockFileCreated) { - if (Files.exists(projectLockFile)) { - awaitUnlock(lockInfo.getProjectId(), projectLockFile); - } - try { - Files.createFile(projectLockFile); - lockFileCreated = true; - } catch (IOException e) { - if (Files.exists(projectLockFile)) { - log.info("Looks like file was locked by another rm instance at the same time"); - continue; - } else { - throw new DBException("Failed to create lock file: " + projectLockFile, e); - } - } - - try { - Files.write(projectLockFile, gson.toJson(lockInfo).getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - log.error("Failed to write lock info, unlock project: " + lockInfo.getProjectId()); - try { - Files.deleteIfExists(lockFolderPath); - } catch (IOException ex) { - throw new DBException("Failed to remove invalid lock file: " + projectLockFile, ex); - } - throw new DBException("Failed to lock project: " + lockInfo.getProjectId(), e); - } - } - - } - - private void createProjectFolder(String projectId) throws DBException { - Path projectLocksFolder = lockFolderPath.resolve(projectId); - if (Files.exists(projectLocksFolder)) { - return; - } - try { - Files.createDirectories(projectLocksFolder); - } catch (Exception e) { - if (Files.exists(projectLocksFolder)) { - // ignore, because file can be created by another server - } else { - throw new DBException("Failed to create project lock folder: " + projectId, e); - } - } - } - - protected void awaitUnlock(String projectId, Path projectLockFile) throws InterruptedException, DBException { - if (!isLocked(projectLockFile)) { - return; - } - awaitingUnlock(projectId, projectLockFile); - } - - protected void awaitingUnlock(String projectId, Path projectLockFile) throws DBException, InterruptedException { - log.info("Waiting for a file to be unlocked: " + projectLockFile); - RMLockInfo originalLockInfo = readLockInfo(projectId, projectLockFile); - boolean fileUnlocked = originalLockInfo == null; //lock can be removed at the moment when we try to read lock file info - int maxIterations = maxLockTime / CHECK_PERIOD; - int currentCheckCount = 0; - - while (!fileUnlocked) { - fileUnlocked = !isLocked(projectLockFile); - if (currentCheckCount >= maxIterations || fileUnlocked) { - break; - } - if (originalLockInfo != null & originalLockInfo.isBlank()) { - // possible in situation where the project has just been locked - // and the lock information has not yet been written - originalLockInfo = readLockInfo(projectId, projectLockFile); - } - currentCheckCount++; - Thread.sleep(CHECK_PERIOD); - } - if (fileUnlocked) { - return; - } - - RMLockInfo currentLockInfo = readLockInfo(projectId, projectLockFile); - if (currentLockInfo == null) { - // file unlocked now - return; - } - - //checking that this is not a new lock from another operation - if (originalLockInfo.getOperationId().equals(currentLockInfo.getOperationId())) { - forceUnlock(projectLockFile); - } else { - awaitUnlock(projectId, lockFolderPath); - } - } - - protected void forceUnlock(Path projectLockFile) { - // something went wrong and lock is invalid - log.warn("File has not been unlocked within the expected period, force unlock"); - try { - Files.deleteIfExists(projectLockFile); - } catch (IOException e) { - log.error(e); - } - } - - @Nullable - /** - @return - - null if lock not exist; - - empty lock info if the lock has just been created and the information has not yet been written; - - lock info - */ - private RMLockInfo readLockInfo(String projectId, Path projectLockFile) throws DBException { - if (Files.notExists(projectLockFile)) { - return null; - } - try (Reader reader = Files.newBufferedReader(projectLockFile, StandardCharsets.UTF_8)) { - return gson.fromJson(reader, RMLockInfo.class); - } catch (IOException e) { - if (!isLocked(projectLockFile)) { - return null; - } - log.warn("Failed to read lock file info, but lock file still exist: " + projectLockFile); - return RMLockInfo.emptyLock(projectId); - } - } - - private Path getProjectLockFilePath(String projectId) { - return lockFolderPath.resolve(projectId).resolve(projectId + LOCK_FILE_EXTENSION); - } - - private void createLockFolderIfNeeded() throws IOException { - synchronized (RMFileLockController.class) { - if (Files.notExists(lockFolderPath)) { - Files.createDirectories(lockFolderPath); - } - } - } -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLock.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLock.java deleted file mode 100644 index 733327e594..0000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLock.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.lock; - -import org.jkiss.dbeaver.Log; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -/** - * Resource Manager resource lock - */ -public class RMLock implements AutoCloseable { - private static final Log log = Log.getLog(RMLock.class); - - private final Path lockFilePath; - - public RMLock(Path lockFilePath) { - this.lockFilePath = lockFilePath; - } - - /** - * Unlock resource and remove .lock file - */ - public void unlock() { - try { - Files.deleteIfExists(lockFilePath); - } catch (IOException e) { - log.error("Failed to unlock file: " + lockFilePath, e); - if (Files.exists(lockFilePath)) { - // file still locket, try to unlock again - unlock(); - } - } - } - - /** - * @return path to the lock file - */ - protected Path getLockFilePath() { - return lockFilePath; - } - - @Override - public void close() { - unlock(); - } -} diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLockInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLockInfo.java deleted file mode 100644 index 380cd1fc5c..0000000000 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/model/rm/lock/RMLockInfo.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.lock; - -public class RMLockInfo { - private final String applicationId; - private final String projectId; - private final String operationId; - private final String operationName; - private final long operationStartTime; - - private RMLockInfo( - String applicationId, - String projectId, - String operationId, - String operationName, - long operationStartTime - ) { - this.applicationId = applicationId; - this.projectId = projectId; - this.operationId = operationId; - this.operationName = operationName; - this.operationStartTime = operationStartTime; - } - - static RMLockInfo emptyLock( - String projectId - ) { - return new RMLockInfo( - "", - projectId, - "", - "", - System.currentTimeMillis() - ); - } - - - public boolean isBlank() { - return operationId.isEmpty(); - } - - public String getApplicationId() { - return applicationId; - } - - public String getProjectId() { - return projectId; - } - - public String getOperationId() { - return operationId; - } - - public String getOperationName() { - return operationName; - } - - public long getOperationStartTime() { - return operationStartTime; - } - - public static final class Builder { - private String applicationId; - private final String projectId; - private final String operationId; - private String operationName; - private long operationStartTime; - - public Builder(String projectId, String operationId) { - this.projectId = projectId; - this.operationId = operationId; - } - - - public Builder setApplicationId(String applicationId) { - this.applicationId = applicationId; - return this; - } - - - public Builder setOperationName(String operationName) { - this.operationName = operationName; - return this; - } - - public Builder setOperationStartTime(long operationStartTime) { - this.operationStartTime = operationStartTime; - return this; - } - - public RMLockInfo build() { - return new RMLockInfo(applicationId, projectId, operationId, operationName, operationStartTime); - } - } -} diff --git a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/RMLockTest.java b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/RMLockTest.java index 49e67ee4cb..75d7551ef3 100644 --- a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/RMLockTest.java +++ b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/RMLockTest.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 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. @@ -17,7 +17,6 @@ package io.cloudbeaver.model.rm.lock; import io.cloudbeaver.app.CEAppStarter; -import io.cloudbeaver.test.platform.CEServerTestSuite; import org.jkiss.dbeaver.Log; import org.junit.Assert; import org.junit.Test; @@ -50,7 +49,7 @@ public class RMLockTest { AtomicReference exceptionReference = new AtomicReference<>(); Runnable runnable1 = () -> { - try (var lock = lockController1.lockProject(project1, "testThatProjectLocked1")) { + try (var lock = lockController1.lock(project1, "testThatProjectLocked1")) { isLockedByThread1.set(true); thread2CDL.countDown(); thread1CDL.await(1, TimeUnit.MINUTES); @@ -77,15 +76,15 @@ public class RMLockTest { } return invocationOnMock.callRealMethod(); } - }).when(lockController2).awaitingUnlock(Mockito.any(), Mockito.any()); + }).when(lockController2).awaitingUnlock(Mockito.any()); Runnable runnable2 = () -> { try { thread2CDL.await(1, TimeUnit.MINUTES); Assert.assertTrue("Project not locket by thread 1", isLockedByThread1.get()); - Assert.assertTrue("Project not locked", lockController2.isProjectLocked(project1)); - try (var lock = lockController2.lockProject(project1, "testThatProjectLocked2")) { + Assert.assertTrue("Project not locked", lockController2.isFileLocked(project1)); + try (var lock = lockController2.lock(project1, "testThatProjectLocked2")) { //that we were really waiting for the file and the lock was not removed earlier - Mockito.verify(lockController2, Mockito.atLeast(atLeastWaitCalls)).awaitingUnlock(Mockito.any(), Mockito.any()); + Mockito.verify(lockController2, Mockito.atLeast(atLeastWaitCalls)).awaitingUnlock(Mockito.any()); } } catch (Throwable e) { log.error(e); @@ -101,7 +100,7 @@ public class RMLockTest { if (exceptionReference.get() != null) { throw exceptionReference.get(); } - Assert.assertFalse(lockController2.isProjectLocked(project1)); + Assert.assertFalse(lockController2.isFileLocked(project1)); } @Test @@ -117,7 +116,7 @@ public class RMLockTest { AtomicBoolean isLockedByThread2 = new AtomicBoolean(false); AtomicReference exceptionReference = new AtomicReference<>(); Runnable runnable1 = () -> { - try (var lock = lockController1.lockProject(project1, "testAccessToDifferentProjects1")) { + try (var lock = lockController1.lock(project1, "testAccessToDifferentProjects1")) { isLockedByThread1.set(true); thread2InitCDL.countDown(); thread1CDL.await(1, TimeUnit.MINUTES); @@ -135,7 +134,7 @@ public class RMLockTest { var lockController2 = new TestLockController(CEAppStarter.getTestApp(), 1); Runnable runnable2 = () -> { try { - try (var lock = lockController2.lockProject(project2, "testAccessToDifferentProjects2")) { + try (var lock = lockController2.lock(project2, "testAccessToDifferentProjects2")) { thread2InitCDL.await(); Assert.assertTrue("Project1 not locket by thread1", isLockedByThread1.get()); isLockedByThread2.set(true); @@ -158,8 +157,8 @@ public class RMLockTest { throw exceptionReference.get(); } - Assert.assertFalse(lockController2.isProjectLocked(project1)); - Assert.assertFalse(lockController2.isProjectLocked(project2)); + Assert.assertFalse(lockController2.isFileLocked(project1)); + Assert.assertFalse(lockController2.isFileLocked(project2)); } @Test @@ -172,7 +171,7 @@ public class RMLockTest { AtomicBoolean isLockedByThread1 = new AtomicBoolean(false); AtomicReference exceptionReference = new AtomicReference<>(); Runnable runnable1 = () -> { - try (var lock = lockController1.lockProject(project1, "testForceUnlock1")) { + try (var lock = lockController1.lock(project1, "testForceUnlock1")) { isLockedByThread1.set(true); thread1CDL.await(1, TimeUnit.MINUTES); } catch (Throwable e) { @@ -187,7 +186,7 @@ public class RMLockTest { var lockController2 = Mockito.spy(new TestLockController(CEAppStarter.getTestApp(), 100)); Runnable runnable2 = () -> { try { - try (var lock = lockController2.lockProject(project1, "testForceUnlock2")) { + try (var lock = lockController2.lock(project1, "testForceUnlock2")) { Assert.assertTrue("Project1 not locket by thread1", isLockedByThread1.get()); Mockito.verify(lockController2, Mockito.atLeast(5)).isLocked(Mockito.any()); thread1CDL.countDown(); @@ -206,6 +205,6 @@ public class RMLockTest { if (exceptionReference.get() != null) { throw exceptionReference.get(); } - Assert.assertFalse(lockController2.isProjectLocked(project1)); + Assert.assertFalse(lockController2.isFileLocked(project1)); } } diff --git a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/TestLockController.java b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/TestLockController.java index e92ae0e314..bb7094ab97 100644 --- a/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/TestLockController.java +++ b/server/test/io.cloudbeaver.test.platform/src/io/cloudbeaver/model/rm/lock/TestLockController.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 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. @@ -17,28 +17,30 @@ package io.cloudbeaver.model.rm.lock; import io.cloudbeaver.model.app.ServletApplication; +import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.fs.lock.FileLockController; import java.nio.file.Path; -public class TestLockController extends RMFileLockController { +public class TestLockController extends FileLockController { public TestLockController(ServletApplication application) throws DBException { - super(application); + super(application.getApplicationInstanceId()); } public TestLockController(ServletApplication application, int maxTimeout) throws DBException { - super(application, maxTimeout); + super(application.getApplicationInstanceId(), maxTimeout); } //avoid mockito access method error @Override - public void awaitingUnlock(String projectId, Path projectLockFile) throws InterruptedException, DBException { - super.awaitingUnlock(projectId, projectLockFile); + public void awaitingUnlock(@NotNull Path projectLockFile) throws InterruptedException, DBException { + super.awaitingUnlock(projectLockFile); } //avoid mockito access method error @Override - public boolean isLocked(Path lockFilePath) { + public boolean isLocked(@NotNull Path lockFilePath) { return super.isLocked(lockFilePath); } }