diff --git a/amps/ags/pom.xml b/amps/ags/pom.xml index 141fd41d81..f289a0fc1c 100644 --- a/amps/ags/pom.xml +++ b/amps/ags/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-amps - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/ags/rm-automation/pom.xml b/amps/ags/rm-automation/pom.xml index 090c1b8bba..30336f8fda 100644 --- a/amps/ags/rm-automation/pom.xml +++ b/amps/ags/rm-automation/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml index 691fae9704..73ba74c272 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-automation-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/ags/rm-community/pom.xml b/amps/ags/rm-community/pom.xml index 2a8e19f5fa..cfca1c253d 100644 --- a/amps/ags/rm-community/pom.xml +++ b/amps/ags/rm-community/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-parent - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env index 358fd679fc..fb842fec56 100644 --- a/amps/ags/rm-community/rm-community-repo/.env +++ b/amps/ags/rm-community/rm-community-repo/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.14 +SOLR6_TAG=2.0.15 POSTGRES_TAG=16.6 ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml index e33e379e04..f1a2cd0987 100644 --- a/amps/ags/rm-community/rm-community-repo/pom.xml +++ b/amps/ags/rm-community/rm-community-repo/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml index 730e9a42e7..b668184f47 100644 --- a/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml +++ b/amps/ags/rm-community/rm-community-rest-api-explorer/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-governance-services-community-repo-parent - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/pom.xml b/amps/pom.xml index 8f58fe3618..0738e02077 100644 --- a/amps/pom.xml +++ b/amps/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/share-services/pom.xml b/amps/share-services/pom.xml index 4c5441cbcb..1691d76325 100644 --- a/amps/share-services/pom.xml +++ b/amps/share-services/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-amps - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js index 314e1e1b29..c5e909d740 100644 --- a/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js +++ b/amps/share-services/src/main/resources/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/unlock-document.post.json.js @@ -23,10 +23,6 @@ function runAction(p_params) if (p_params.destNode.hasAspect("cm:lockable") && !p_params.destNode.hasAspect("trx:transferred")) { p_params.destNode.unlock(); - if(p_params.destNode.hasAspect("gd2:editingInGoogle")) - { - p_params.destNode.removeAspect("gd2:editingInGoogle"); - } } var resultId = originalDoc.name, diff --git a/core/pom.xml b/core/pom.xml index 2c6fd6bddb..3344c3e60e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/data-model/pom.xml b/data-model/pom.xml index 2e6970001e..9e9be3629d 100644 --- a/data-model/pom.xml +++ b/data-model/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/mmt/pom.xml b/mmt/pom.xml index ccc2e911d4..514d5fe2ae 100644 --- a/mmt/pom.xml +++ b/mmt/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/distribution/pom.xml b/packaging/distribution/pom.xml index 86e563d230..4bdfbb4aef 100644 --- a/packaging/distribution/pom.xml +++ b/packaging/distribution/pom.xml @@ -9,6 +9,6 @@ org.alfresco alfresco-community-repo-packaging - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/docker-alfresco/pom.xml b/packaging/docker-alfresco/pom.xml index 26fff79e35..9a3e0aedc6 100644 --- a/packaging/docker-alfresco/pom.xml +++ b/packaging/docker-alfresco/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/pom.xml b/packaging/pom.xml index b010f42adb..afb7c5aecc 100644 --- a/packaging/pom.xml +++ b/packaging/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/environment/.env b/packaging/tests/environment/.env index 358fd679fc..fb842fec56 100644 --- a/packaging/tests/environment/.env +++ b/packaging/tests/environment/.env @@ -1,3 +1,3 @@ -SOLR6_TAG=2.0.14 +SOLR6_TAG=2.0.15 POSTGRES_TAG=16.6 ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 diff --git a/packaging/tests/pom.xml b/packaging/tests/pom.xml index ba59f7322e..c0b8fdf84d 100644 --- a/packaging/tests/pom.xml +++ b/packaging/tests/pom.xml @@ -6,7 +6,7 @@ org.alfresco alfresco-community-repo-packaging - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/tas-cmis/pom.xml b/packaging/tests/tas-cmis/pom.xml index 20d37ebf7b..316ca4e799 100644 --- a/packaging/tests/tas-cmis/pom.xml +++ b/packaging/tests/tas-cmis/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-tests - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/tas-email/pom.xml b/packaging/tests/tas-email/pom.xml index ec3f68d2ee..a10bc5ded3 100644 --- a/packaging/tests/tas-email/pom.xml +++ b/packaging/tests/tas-email/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/tas-integration/pom.xml b/packaging/tests/tas-integration/pom.xml index 015e2149ff..5e604b956f 100644 --- a/packaging/tests/tas-integration/pom.xml +++ b/packaging/tests/tas-integration/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/tas-restapi/pom.xml b/packaging/tests/tas-restapi/pom.xml index 73c125f226..0ef72b41f5 100644 --- a/packaging/tests/tas-restapi/pom.xml +++ b/packaging/tests/tas-restapi/pom.xml @@ -8,7 +8,7 @@ org.alfresco alfresco-community-repo-tests - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/tests/tas-webdav/pom.xml b/packaging/tests/tas-webdav/pom.xml index dee3e38e6d..9b8276dd8b 100644 --- a/packaging/tests/tas-webdav/pom.xml +++ b/packaging/tests/tas-webdav/pom.xml @@ -9,7 +9,7 @@ org.alfresco alfresco-community-repo-tests - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/packaging/war/pom.xml b/packaging/war/pom.xml index 3cea4e4a84..7f4764e900 100644 --- a/packaging/war/pom.xml +++ b/packaging/war/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo-packaging - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/pom.xml b/pom.xml index 4d0c3457b3..5fc22ca55f 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT pom Alfresco Community Repo Parent @@ -51,8 +51,8 @@ 7.0.1 5.23.0 5.23.0 - 5.1.7-A.3 - 4.1.7-A.1 + 5.1.7 + 4.1.7 7.1 1.0.2 @@ -112,10 +112,10 @@ 2.1.3 1.1.7 2.9.0 - 2.5.1 + 2.5.2 4.1.0 3.2.0 - 25.1.0-A1 + 25.1.0 2.2.0 2.4.0 diff --git a/remote-api/pom.xml b/remote-api/pom.xml index 873b1c30e1..79b065dfd5 100644 --- a/remote-api/pom.xml +++ b/remote-api/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/repository/pom.xml b/repository/pom.xml index f0b3f3ab08..f7e409a4ad 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -7,7 +7,7 @@ org.alfresco alfresco-community-repo - 25.1.0.61-SNAPSHOT + 25.1.0.70-SNAPSHOT diff --git a/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java b/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java index 1dfee2d75f..1123ca1542 100644 --- a/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java +++ b/repository/src/main/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java @@ -28,11 +28,17 @@ package org.alfresco.repo.workflow.activiti.script; import java.util.Map; +import org.activiti.engine.RepositoryService; import org.activiti.engine.delegate.VariableScope; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.el.Expression; import org.activiti.engine.impl.persistence.entity.DeploymentEntity; +import org.activiti.engine.impl.persistence.entity.ExecutionEntity; +import org.activiti.engine.repository.ProcessDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.workflow.WorkflowDeployer; @@ -45,13 +51,12 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowException; /** - * Base class for execution scripts, using {@link ScriptService} as part of - * activiti workflow. + * Base class for execution scripts, using {@link ScriptService} as part of activiti workflow. * * @author Frederik Heremans * @since 3.4.e */ -public class ActivitiScriptBase +public class ActivitiScriptBase { protected static final String PERSON_BINDING_NAME = "person"; protected static final String USERHOME_BINDING_NAME = "userhome"; @@ -61,17 +66,19 @@ public class ActivitiScriptBase protected Expression runAs; protected Expression scriptProcessor; + private static final Logger LOGGER = LoggerFactory.getLogger(ActivitiScriptBase.class); + protected Object executeScript(String theScript, Map model, String scriptProcessorName, String runAsUser) { String user = AuthenticationUtil.getFullyAuthenticatedUser(); - + Object scriptResult = null; if (runAsUser == null && user != null) { // Just execute the script using the current user scriptResult = executeScript(theScript, model, scriptProcessorName); } - else + else { if (runAsUser != null) { @@ -87,26 +94,25 @@ public class ActivitiScriptBase } return scriptResult; } - + protected Object executeScriptAsUser(final String theScript, final Map model, final String scriptProcessorName, final String runAsUser) { // execute as specified runAsUser - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<>() { public Object doWork() throws Exception { return executeScript(theScript, model, scriptProcessorName); } }, runAsUser); } - + protected Object executeScript(String theScript, Map model, String scriptProcessorName) { // Execute the script using the appropriate processor Object scriptResult = null; // Checks if current workflow is secure - boolean secure = isSecure(); + boolean secure = isSecure(model); if (scriptProcessorName != null) { @@ -117,11 +123,11 @@ public class ActivitiScriptBase // Use default script-processor scriptResult = getServiceRegistry().getScriptService().executeScriptString(theScript, model, secure); } - + return scriptResult; } - - protected String getStringValue(Expression expression, VariableScope scope) + + protected String getStringValue(Expression expression, VariableScope scope) { if (expression != null) { @@ -133,15 +139,15 @@ public class ActivitiScriptBase protected ServiceRegistry getServiceRegistry() { ProcessEngineConfigurationImpl config = Context.getProcessEngineConfiguration(); - if (config != null) + if (config != null) { // Fetch the registry that is injected in the activiti spring-configuration ServiceRegistry registry = (ServiceRegistry) config.getBeans().get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY); if (registry == null) { throw new RuntimeException( - "Service-registry not present in ProcessEngineConfiguration beans, expected ServiceRegistry with key" + - ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY); + "Service-registry not present in ProcessEngineConfiguration beans, expected ServiceRegistry with key" + + ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY); } return registry; } @@ -149,42 +155,136 @@ public class ActivitiScriptBase } /** - * Checks whether the workflow must be considered secure or not - based on {@link DeploymentEntity} category. - * If it is not considered secure, the workflow will be executed in sandbox context with more restrictions + * Checks whether the workflow must be considered secure or not - based on {@link DeploymentEntity} category. If it is not considered secure, the workflow will be executed in sandbox context with more restrictions * * @return true if workflow is considered secure, false otherwise */ - private boolean isSecure() + private boolean isSecure(Map model) + { + String category = getDeploymentCategory(model); + + // iF The deployment category matches the condition (either internal or full access) the workflow is considered secure + return category != null && (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(category) || WorkflowDeployer.CATEGORY_FULL_ACCESS.equals(category)); + } + + /** + * Gets the deployment category from the execution context. If no execution context is available, a query to obtain the deployment is performed so the category can be returned. + * + * @param model + * a map with workflow model + * @return the deployment category + */ + private String getDeploymentCategory(Map model) + { + String category = getDeploymentCategoryFromContext(); + + if (category == null) + { + String deploymentId = null; + String processDefinitionId = null; + + if (model != null && model.containsKey(EXECUTION_BINDING_NAME) && model.get(EXECUTION_BINDING_NAME) instanceof ExecutionEntity) + { + ExecutionEntity executionEntity = (ExecutionEntity) model.get(EXECUTION_BINDING_NAME); + deploymentId = executionEntity.getDeploymentId(); + processDefinitionId = executionEntity.getProcessDefinitionId(); + } + + category = getDeploymentCategoryFromQuery(deploymentId, processDefinitionId); + } + + return category; + } + + /** + * Obtains the deployment category from current execution context + * + * @return the category for current execution deployment, otherwise null + */ + private String getDeploymentCategoryFromContext() { String category = null; - try { if (Context.isExecutionContextActive()) { category = Context.getExecutionContext().getDeployment().getCategory(); } + else + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("No execution context available"); + } + } } catch (Exception e) { - // No action required + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Could not obtain deployment category from execution context: {}", e.getMessage()); + } } - // If the workflow is considered secure, the deployment entity category matches the condition (either internal or full access) - return category != null && (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(category) || WorkflowDeployer.CATEGORY_FULL_ACCESS.equals(category)); + return category; } /** - * Checks that the specified 'runAs' field - * specifies a valid username. + * Obtains the deployment category through a query + * + * @param deploymentId + * the deployment id to obtain the category from + * @param processDefinitionId + * if no deployment id is provided, the process definition id can be used to obtain the deployment + * @return the category for the obtained deployment, otherwise null */ - private void validateRunAsUser(final String runAsUser) + private String getDeploymentCategoryFromQuery(String deploymentId, String processDefinitionId) { - Boolean runAsExists = AuthenticationUtil.runAs(new RunAsWork() + String category = null; + + try { + RepositoryService repositoryService = Context.getProcessEngineConfiguration().getRepositoryService(); + + if (deploymentId == null && processDefinitionId != null) + { + ProcessDefinition processDefnition = repositoryService.getProcessDefinition(processDefinitionId); + if (processDefnition != null) + { + deploymentId = processDefnition.getDeploymentId(); + } + } + + if (deploymentId != null) + { + DeploymentEntity deployment = (DeploymentEntity) repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult(); + if (deployment != null) + { + category = deployment.getCategory(); + } + } + } + catch (Exception e) + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Could not obtain deployment category through a query: {}", e.getMessage()); + } + } + + return category; + } + + /** + * Checks that the specified 'runAs' field specifies a valid username. + */ + private void validateRunAsUser(final String runAsUser) + { + Boolean runAsExists = AuthenticationUtil.runAs(new RunAsWork<>() { // Validate using System user to ensure sufficient permissions available to access person node. - public Boolean doWork() throws Exception + @Override + public Boolean doWork() throws Exception { return getServiceRegistry().getPersonService().personExists(runAsUser); } @@ -195,21 +295,21 @@ public class ActivitiScriptBase throw new WorkflowException("runas user '" + runAsUser + "' does not exist."); } } - + protected ActivitiScriptNode getPersonNode(String runAsUser) { String userName = null; - if (runAsUser != null) + if (runAsUser != null) { userName = runAsUser; } - else + else { userName = AuthenticationUtil.getFullyAuthenticatedUser(); } - + // The "System" user is a special case, which has no person object associated with it. - if(userName != null && !AuthenticationUtil.SYSTEM_USER_NAME.equals(userName)) + if (userName != null && !AuthenticationUtil.SYSTEM_USER_NAME.equals(userName)) { ServiceRegistry services = getServiceRegistry(); PersonService personService = services.getPersonService(); @@ -221,18 +321,18 @@ public class ActivitiScriptBase } return null; } - - public void setScript(Expression script) + + public void setScript(Expression script) { this.script = script; } - public void setRunAs(Expression runAs) + public void setRunAs(Expression runAs) { this.runAs = runAs; } - public void setScriptProcessor(Expression scriptProcessor) + public void setScriptProcessor(Expression scriptProcessor) { this.scriptProcessor = scriptProcessor; } diff --git a/repository/src/test/java/org/alfresco/repo/lock/mem/AbstractLockStoreTxTest.java b/repository/src/test/java/org/alfresco/repo/lock/mem/AbstractLockStoreTxTest.java index 484b2d50e4..8e386388c5 100644 --- a/repository/src/test/java/org/alfresco/repo/lock/mem/AbstractLockStoreTxTest.java +++ b/repository/src/test/java/org/alfresco/repo/lock/mem/AbstractLockStoreTxTest.java @@ -1,494 +1,489 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ -package org.alfresco.repo.lock.mem; - -import java.util.Date; - -import jakarta.transaction.NotSupportedException; -import jakarta.transaction.SystemException; -import jakarta.transaction.UserTransaction; - -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.lock.LockType; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.springframework.context.ApplicationContext; -import org.springframework.dao.ConcurrencyFailureException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -/** - * Integration tests that check transaction related functionality of {@link LockStore} implementations. - * @author Matt Ward - */ -public abstract class AbstractLockStoreTxTest -{ - /** - * Instance of the Class Under Test. - */ - protected T lockStore; - - protected static ApplicationContext ctx; - protected static TransactionService transactionService; - - /** - * Concrete subclasses must implement this method to provide the tests with a LockStore instance. - * - * @return LockStore to test - */ - protected abstract T createLockStore(); - - @BeforeClass - public static void setUpSpringContext() - { - ctx = ApplicationContextHelper.getApplicationContext(); - transactionService = (TransactionService) ctx.getBean("TransactionService"); - } - - @Before - public void setUpLockStore() - { - lockStore = createLockStore(); - } - - /** - *
    - *
  • Start outer txn
  • - *
  • Modify lock in outer txn
  • - *
  • Start inner txn
  • - *
  • Modify lock in inner txn
  • - *
- * Inner transaction should fail while outer succeeds - */ - @Test - public void testRepeatableRead_01() throws Exception - { - - } - - @Test - public void testRepeatableReadsInTransaction() throws NotSupportedException, SystemException - { - final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); - UserTransaction txA = txService.getUserTransaction(); - - final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); - final NodeRef nodeRef2 = new NodeRef("workspace://SpacesStore/UUID-2"); - Date now = new Date(); - Date expires = new Date(now.getTime() + 180000); - final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "jbloggs", expires, Lifetime.EPHEMERAL, null); - - - Thread txB = new Thread("TxB") - { - @Override - public void run() - { - Object main = AbstractLockStoreTxTest.this; - UserTransaction tx = txService.getUserTransaction(); - try - { - tx.begin(); - try - { - // txB read lock state - LockState lockState = lockStore.get(nodeRef); - assertEquals("jbloggs", lockState.getOwner()); - assertEquals(Lifetime.EPHEMERAL, lockState.getLifetime()); - - // Wait, while txA changes the lock state - passControl(this, main); - - // assert txB still sees state A - lockState = lockStore.get(nodeRef); - assertEquals("jbloggs", lockState.getOwner()); - - // Wait, while txA checks whether it can see lock for nodeRef2 (though it doesn't exist yet) - passControl(this, main); - - // txB sets a value, already seen as non-existent lock by txA - lockStore.set(nodeRef2, LockState.createLock(nodeRef2, LockType.WRITE_LOCK, - "csmith", null, Lifetime.EPHEMERAL, null)); - } - finally - { - tx.rollback(); - } - } - catch (Throwable e) - { - throw new RuntimeException("Error in transaction B", e); - } - finally - { - // Stop 'main' from waiting - synchronized(main) - { - main.notifyAll(); - } - } - } - }; - - txA.begin(); - try - { - // txA set lock state 1 - lockStore.set(nodeRef, lockState1); - - // Wait while txB reads and checks the LockState - txB.setDaemon(true); - txB.start(); - passControl(this, txB); - - // txA set different lock state - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. - final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); - lockStore.set(nodeRef, lockState2); - - // Wait while txB reads/checks the LockState again for nodeRef - passControl(this, txB); - - // Another update - AuthenticationUtil.setFullyAuthenticatedUser("another"); // Current lock owner needed to change lock. - final LockState lockState3 = LockState.createWithOwner(lockState2, "bsmith"); - lockStore.set(nodeRef, lockState3); - // Check we can see the update. - assertEquals("bsmith", lockStore.get(nodeRef).getOwner()); - - // Perform a read, that we know will retrieve a null value - assertNull("nodeRef2 LockState", lockStore.get(nodeRef2)); - - // Wait while txB populates the store with a value for nodeRef2 - passControl(this, txB); - - // Perform the read again - update should not be visible in this transaction - assertNull("nodeRef2 LockState", lockStore.get(nodeRef2)); - } - finally - { - txA.rollback(); - } - } - - protected void passControl(Object from, Object to) - { - synchronized(to) - { - to.notifyAll(); - } - synchronized(from) - { - try - { - // TODO: wait should be called in a loop with repeated wait condition check, - // but what's the condition we're waiting on? - from.wait(10000); - } - catch (InterruptedException error) - { - throw new RuntimeException(error); - } - } - } - - @Test - public void testCannotSetLockWhenChangedByAnotherTx() throws NotSupportedException, SystemException - { - final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); - UserTransaction txA = txService.getUserTransaction(); - final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); - Date now = new Date(); - Date expires = new Date(now.getTime() + 180000); - final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "jbloggs", expires, Lifetime.EPHEMERAL, null); - - - Thread txB = new Thread("TxB") - { - @Override - public void run() - { - Object main = AbstractLockStoreTxTest.this; - UserTransaction tx = txService.getUserTransaction(); - try - { - tx.begin(); - try - { - // txB read lock state - LockState lockState = lockStore.get(nodeRef); - assertEquals("jbloggs", lockState.getOwner()); - assertEquals(Lifetime.EPHEMERAL, lockState.getLifetime()); - - // Wait, while txA changes the lock state - passControl(this, main); - - try - { - // Attempt to change the lock state for a NodeRef should fail - // when it has been modified by another tx since this tx last inspected it. - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner - lockStore.set(nodeRef, LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "csmith", null, Lifetime.EPHEMERAL, null)); - fail("Exception should have been thrown but was not."); - } - catch (ConcurrencyFailureException e) - { - // Good! - } - } - finally - { - tx.rollback(); - } - } - catch (Throwable e) - { - throw new RuntimeException("Error in transaction B", e); - } - finally - { - // Stop 'main' from waiting - synchronized(main) - { - main.notifyAll(); - } - } - } - }; - - txA.begin(); - try - { - // txA set lock state 1 - lockStore.set(nodeRef, lockState1); - - // Wait while txB reads and checks the LockState - txB.setDaemon(true); - txB.start(); - passControl(this, txB); - - // txA set different lock state - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. - final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); - lockStore.set(nodeRef, lockState2); - - // Wait while txB attempts to modify the lock info - passControl(this, txB); - - // Lock shouldn't have changed since this tx updated it. - assertEquals(lockState2, lockStore.get(nodeRef)); - } - finally - { - txA.rollback(); - } - } - - @Test - public void testCanChangeLockIfLatestValueIsHeldEvenIfAlreadyChangedByAnotherTx() throws NotSupportedException, SystemException - { - final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); - UserTransaction txA = txService.getUserTransaction(); - final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); - final Date now = new Date(); - Date expired = new Date(now.getTime() - 180000); - final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "jbloggs", expired, Lifetime.EPHEMERAL, null); - - final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); - - Thread txB = new Thread("TxB") - { - @Override - public void run() - { - Object main = AbstractLockStoreTxTest.this; - UserTransaction tx = txService.getUserTransaction(); - try - { - tx.begin(); - try - { - AuthenticationUtil.setFullyAuthenticatedUser("new-user"); - - // txB read lock state - LockState readLockState = lockStore.get(nodeRef); - assertEquals(lockState2, readLockState); - - // Set new value, even though txA has already set new values - // (but not since this tx's initial read) - Date expiresFuture = new Date(now.getTime() + 180000); - final LockState newUserLockState = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "new-user", expiresFuture, Lifetime.EPHEMERAL, null); - lockStore.set(nodeRef, newUserLockState); - - // Read - assertEquals(newUserLockState, lockStore.get(nodeRef)); - } - finally - { - tx.rollback(); - } - } - catch (Throwable e) - { - throw new RuntimeException("Error in transaction B", e); - } - finally - { - // Stop 'main' from waiting - synchronized(main) - { - main.notifyAll(); - } - } - } - }; - - txA.begin(); - try - { - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. - - // txA set lock state 1 - lockStore.set(nodeRef, lockState1); - assertEquals(lockState1, lockStore.get(nodeRef)); - - // txA set different lock state - lockStore.set(nodeRef, lockState2); - assertEquals(lockState2, lockStore.get(nodeRef)); - - // Wait while txB modifies the lock info - txB.setDaemon(true); - txB.start(); - passControl(this, txB); - - // This tx should still see the same state, though it has been changed by txB. - assertEquals(lockState2, lockStore.get(nodeRef)); - } - finally - { - txA.rollback(); - } - } - - - @Test - public void testNotOnlyCurrentLockOwnerCanChangeInfo() throws NotSupportedException, SystemException - { - final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); - UserTransaction txA = txService.getUserTransaction(); - final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); - Date now = new Date(); - Date expires = new Date(now.getTime() + 180000); - final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "jbloggs", expires, Lifetime.EPHEMERAL, null); - - txA.begin(); - try - { - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); - - // Set initial lock state - lockStore.set(nodeRef, lockState1); - - // Set different lock state - // Current lock owner is still authenticated (jbloggs) - final LockState lockState2 = LockState.createWithOwner(lockState1, "csmith"); - lockStore.set(nodeRef, lockState2); - - // Check update - assertEquals(lockState2, lockStore.get(nodeRef)); - - // Incorrect lock owner - this shouldn't fail. See ACE-2181 - final LockState lockState3 = LockState.createWithOwner(lockState1, "dsmithers"); - - lockStore.set(nodeRef, lockState3); - - // Check update. - assertEquals(lockState3, lockStore.get(nodeRef)); - } - finally - { - txA.rollback(); - } - } - - @Test - public void testOtherUserCanChangeLockInfoOnceExpired() throws NotSupportedException, SystemException - { - final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); - UserTransaction txA = txService.getUserTransaction(); - final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); - Date now = new Date(); - Date expired = new Date(now.getTime() - 900); - final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "jbloggs", expired, Lifetime.EPHEMERAL, null); - - txA.begin(); - try - { - AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); - - // Set initial lock state - lockStore.set(nodeRef, lockState1); - - // Set different lock state - AuthenticationUtil.setFullyAuthenticatedUser("csmith"); - Date expiresFuture = new Date(now.getTime() + 180000); - final LockState lockState2 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, - "csmith", expiresFuture, Lifetime.EPHEMERAL, null); - lockStore.set(nodeRef, lockState2); - - // Updated, since lock had expired. - assertEquals(lockState2, lockStore.get(nodeRef)); - - // Incorrect lock owner - this shouldn't fail - // LockStore does not check for lock owning - // and is owned by csmith. - AuthenticationUtil.setFullyAuthenticatedUser("dsmithers"); - final LockState lockState3 = LockState.createWithOwner(lockState2, "dsmithers"); - - lockStore.set(nodeRef, lockState3); - - // Check update. - assertEquals(lockState3, lockStore.get(nodeRef)); - } - finally - { - txA.rollback(); - } - } -} +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2023 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.lock.mem; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.util.Date; +import jakarta.transaction.NotSupportedException; +import jakarta.transaction.SystemException; +import jakarta.transaction.UserTransaction; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.dao.ConcurrencyFailureException; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; + +/** + * Integration tests that check transaction related functionality of {@link LockStore} implementations. + * + * @author Matt Ward + */ +public abstract class AbstractLockStoreTxTest +{ + /** + * Instance of the Class Under Test. + */ + protected T lockStore; + + protected static ApplicationContext ctx; + protected static TransactionService transactionService; + + /** + * Concrete subclasses must implement this method to provide the tests with a LockStore instance. + * + * @return LockStore to test + */ + protected abstract T createLockStore(); + + @BeforeClass + public static void setUpSpringContext() + { + ctx = ApplicationContextHelper.getApplicationContext(); + transactionService = (TransactionService) ctx.getBean("TransactionService"); + } + + @Before + public void setUpLockStore() + { + lockStore = createLockStore(); + } + + /** + *
    + *
  • Start outer txn
  • + *
  • Modify lock in outer txn
  • + *
  • Start inner txn
  • + *
  • Modify lock in inner txn
  • + *
+ * Inner transaction should fail while outer succeeds + */ + @Test + public void testRepeatableRead_01() throws Exception + { + + } + + @Test + public void testRepeatableReadsInTransaction() throws NotSupportedException, SystemException + { + final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); + UserTransaction txA = txService.getUserTransaction(); + + final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); + final NodeRef nodeRef2 = new NodeRef("workspace://SpacesStore/UUID-2"); + Date now = new Date(); + Date expires = new Date(now.getTime() + 180000); + final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "jbloggs", expires, Lifetime.EPHEMERAL, null); + + Thread txB = new Thread("TxB") { + @Override + public void run() + { + Object main = AbstractLockStoreTxTest.this; + UserTransaction tx = txService.getUserTransaction(); + try + { + tx.begin(); + try + { + // txB read lock state + LockState lockState = lockStore.get(nodeRef); + assertEquals("jbloggs", lockState.getOwner()); + assertEquals(Lifetime.EPHEMERAL, lockState.getLifetime()); + + // Wait, while txA changes the lock state + passControl(this, main); + + // assert txB still sees state A + lockState = lockStore.get(nodeRef); + assertEquals("jbloggs", lockState.getOwner()); + + // Wait, while txA checks whether it can see lock for nodeRef2 (though it doesn't exist yet) + passControl(this, main); + + // txB sets a value, already seen as non-existent lock by txA + lockStore.set(nodeRef2, LockState.createLock(nodeRef2, LockType.WRITE_LOCK, + "csmith", null, Lifetime.EPHEMERAL, null)); + } + finally + { + tx.rollback(); + } + } + catch (Throwable e) + { + throw new RuntimeException("Error in transaction B", e); + } + finally + { + // Stop 'main' from waiting + synchronized (main) + { + main.notifyAll(); + } + } + } + }; + + txA.begin(); + try + { + // txA set lock state 1 + lockStore.set(nodeRef, lockState1); + + // Perform a read, that we know will retrieve a null value (and null will be cached for this transaction) + assertNull("nodeRef2 LockState", lockStore.get(nodeRef2)); + + // Wait while txB reads and checks the LockState + txB.setDaemon(true); + txB.start(); + passControl(this, txB); + + // txA set different lock state + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. + final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); + lockStore.set(nodeRef, lockState2); + + // Wait while txB reads/checks the LockState again for nodeRef + passControl(this, txB); + + // Another update + AuthenticationUtil.setFullyAuthenticatedUser("another"); // Current lock owner needed to change lock. + final LockState lockState3 = LockState.createWithOwner(lockState2, "bsmith"); + lockStore.set(nodeRef, lockState3); + // Check we can see the update. + assertEquals("bsmith", lockStore.get(nodeRef).getOwner()); + + // Wait while txB populates the store with a value for nodeRef2 + passControl(this, txB); + + // Perform the read again - update should not be visible in this transaction (was already cached) + assertNull("nodeRef2 LockState", lockStore.get(nodeRef2)); + } + finally + { + txA.rollback(); + } + } + + protected void passControl(Object from, Object to) + { + synchronized (to) + { + to.notifyAll(); + } + synchronized (from) + { + try + { + // TODO: wait should be called in a loop with repeated wait condition check, + // but what's the condition we're waiting on? + from.wait(10000); + } + catch (InterruptedException error) + { + throw new RuntimeException(error); + } + } + } + + @Test + public void testCannotSetLockWhenChangedByAnotherTx() throws NotSupportedException, SystemException + { + final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); + UserTransaction txA = txService.getUserTransaction(); + final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); + Date now = new Date(); + Date expires = new Date(now.getTime() + 180000); + final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "jbloggs", expires, Lifetime.EPHEMERAL, null); + + Thread txB = new Thread("TxB") { + @Override + public void run() + { + Object main = AbstractLockStoreTxTest.this; + UserTransaction tx = txService.getUserTransaction(); + try + { + tx.begin(); + try + { + // txB read lock state + LockState lockState = lockStore.get(nodeRef); + assertEquals("jbloggs", lockState.getOwner()); + assertEquals(Lifetime.EPHEMERAL, lockState.getLifetime()); + + // Wait, while txA changes the lock state + passControl(this, main); + + try + { + // Attempt to change the lock state for a NodeRef should fail + // when it has been modified by another tx since this tx last inspected it. + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner + lockStore.set(nodeRef, LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "csmith", null, Lifetime.EPHEMERAL, null)); + fail("Exception should have been thrown but was not."); + } + catch (ConcurrencyFailureException e) + { + // Good! + } + } + finally + { + tx.rollback(); + } + } + catch (Throwable e) + { + throw new RuntimeException("Error in transaction B", e); + } + finally + { + // Stop 'main' from waiting + synchronized (main) + { + main.notifyAll(); + } + } + } + }; + + txA.begin(); + try + { + // txA set lock state 1 + lockStore.set(nodeRef, lockState1); + + // Wait while txB reads and checks the LockState + txB.setDaemon(true); + txB.start(); + passControl(this, txB); + + // txA set different lock state + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. + final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); + lockStore.set(nodeRef, lockState2); + + // Wait while txB attempts to modify the lock info + passControl(this, txB); + + // Lock shouldn't have changed since this tx updated it. + assertEquals(lockState2, lockStore.get(nodeRef)); + } + finally + { + txA.rollback(); + } + } + + @Test + public void testCanChangeLockIfLatestValueIsHeldEvenIfAlreadyChangedByAnotherTx() throws NotSupportedException, SystemException + { + final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); + UserTransaction txA = txService.getUserTransaction(); + final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); + final Date now = new Date(); + Date expired = new Date(now.getTime() - 180000); + final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "jbloggs", expired, Lifetime.EPHEMERAL, null); + + final LockState lockState2 = LockState.createWithOwner(lockState1, "another"); + + Thread txB = new Thread("TxB") { + @Override + public void run() + { + Object main = AbstractLockStoreTxTest.this; + UserTransaction tx = txService.getUserTransaction(); + try + { + tx.begin(); + try + { + AuthenticationUtil.setFullyAuthenticatedUser("new-user"); + + // txB read lock state + LockState readLockState = lockStore.get(nodeRef); + assertEquals(lockState2, readLockState); + + // Set new value, even though txA has already set new values + // (but not since this tx's initial read) + Date expiresFuture = new Date(now.getTime() + 180000); + final LockState newUserLockState = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "new-user", expiresFuture, Lifetime.EPHEMERAL, null); + lockStore.set(nodeRef, newUserLockState); + + // Read + assertEquals(newUserLockState, lockStore.get(nodeRef)); + } + finally + { + tx.rollback(); + } + } + catch (Throwable e) + { + throw new RuntimeException("Error in transaction B", e); + } + finally + { + // Stop 'main' from waiting + synchronized (main) + { + main.notifyAll(); + } + } + } + }; + + txA.begin(); + try + { + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); // Current lock owner needed to change lock. + + // txA set lock state 1 + lockStore.set(nodeRef, lockState1); + assertEquals(lockState1, lockStore.get(nodeRef)); + + // txA set different lock state + lockStore.set(nodeRef, lockState2); + assertEquals(lockState2, lockStore.get(nodeRef)); + + // Wait while txB modifies the lock info + txB.setDaemon(true); + txB.start(); + passControl(this, txB); + + // This tx should still see the same state, though it has been changed by txB. + assertEquals(lockState2, lockStore.get(nodeRef)); + } + finally + { + txA.rollback(); + } + } + + @Test + public void testNotOnlyCurrentLockOwnerCanChangeInfo() throws NotSupportedException, SystemException + { + final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); + UserTransaction txA = txService.getUserTransaction(); + final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); + Date now = new Date(); + Date expires = new Date(now.getTime() + 180000); + final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "jbloggs", expires, Lifetime.EPHEMERAL, null); + + txA.begin(); + try + { + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); + + // Set initial lock state + lockStore.set(nodeRef, lockState1); + + // Set different lock state + // Current lock owner is still authenticated (jbloggs) + final LockState lockState2 = LockState.createWithOwner(lockState1, "csmith"); + lockStore.set(nodeRef, lockState2); + + // Check update + assertEquals(lockState2, lockStore.get(nodeRef)); + + // Incorrect lock owner - this shouldn't fail. See ACE-2181 + final LockState lockState3 = LockState.createWithOwner(lockState1, "dsmithers"); + + lockStore.set(nodeRef, lockState3); + + // Check update. + assertEquals(lockState3, lockStore.get(nodeRef)); + } + finally + { + txA.rollback(); + } + } + + @Test + public void testOtherUserCanChangeLockInfoOnceExpired() throws NotSupportedException, SystemException + { + final TransactionService txService = (TransactionService) ctx.getBean("TransactionService"); + UserTransaction txA = txService.getUserTransaction(); + final NodeRef nodeRef = new NodeRef("workspace://SpacesStore/UUID-1"); + Date now = new Date(); + Date expired = new Date(now.getTime() - 900); + final LockState lockState1 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "jbloggs", expired, Lifetime.EPHEMERAL, null); + + txA.begin(); + try + { + AuthenticationUtil.setFullyAuthenticatedUser("jbloggs"); + + // Set initial lock state + lockStore.set(nodeRef, lockState1); + + // Set different lock state + AuthenticationUtil.setFullyAuthenticatedUser("csmith"); + Date expiresFuture = new Date(now.getTime() + 180000); + final LockState lockState2 = LockState.createLock(nodeRef, LockType.WRITE_LOCK, + "csmith", expiresFuture, Lifetime.EPHEMERAL, null); + lockStore.set(nodeRef, lockState2); + + // Updated, since lock had expired. + assertEquals(lockState2, lockStore.get(nodeRef)); + + // Incorrect lock owner - this shouldn't fail + // LockStore does not check for lock owning + // and is owned by csmith. + AuthenticationUtil.setFullyAuthenticatedUser("dsmithers"); + final LockState lockState3 = LockState.createWithOwner(lockState2, "dsmithers"); + + lockStore.set(nodeRef, lockState3); + + // Check update. + assertEquals(lockState3, lockStore.get(nodeRef)); + } + finally + { + txA.rollback(); + } + } +}