mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Merge branch 'master' of https://github.com/Alfresco/alfresco-community-repo into MNT-23926_add_sevenpass_dod_5220.22-M_ags_deletion
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -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
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -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,
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -9,6 +9,6 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -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
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<organization>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
10
pom.xml
10
pom.xml
@@ -2,7 +2,7 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Alfresco Community Repo Parent</name>
|
||||
|
||||
@@ -51,8 +51,8 @@
|
||||
<dependency.alfresco-server-root.version>7.0.1</dependency.alfresco-server-root.version>
|
||||
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
|
||||
<dependency.activiti.version>5.23.0</dependency.activiti.version>
|
||||
<dependency.alfresco-transform-core.version>5.1.7-A.3</dependency.alfresco-transform-core.version>
|
||||
<dependency.alfresco-transform-service.version>4.1.7-A.1</dependency.alfresco-transform-service.version>
|
||||
<dependency.alfresco-transform-core.version>5.1.7</dependency.alfresco-transform-core.version>
|
||||
<dependency.alfresco-transform-service.version>4.1.7</dependency.alfresco-transform-service.version>
|
||||
<dependency.alfresco-greenmail.version>7.1</dependency.alfresco-greenmail.version>
|
||||
<dependency.acs-event-model.version>1.0.2</dependency.acs-event-model.version>
|
||||
|
||||
@@ -112,10 +112,10 @@
|
||||
<dependency.jakarta-ee-json-api.version>2.1.3</dependency.jakarta-ee-json-api.version>
|
||||
<dependency.jakarta-ee-json-impl.version>1.1.7</dependency.jakarta-ee-json-impl.version>
|
||||
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
|
||||
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
|
||||
<dependency.json-smart.version>2.5.2</dependency.json-smart.version>
|
||||
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
|
||||
<alfresco.aos-module.version>3.2.0</alfresco.aos-module.version>
|
||||
<alfresco.api-explorer.version>25.1.0-A1</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
|
||||
<alfresco.api-explorer.version>25.1.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
|
||||
|
||||
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
|
||||
<license-maven-plugin.version>2.4.0</license-maven-plugin.version>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>25.1.0.61-SNAPSHOT</version>
|
||||
<version>25.1.0.70-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -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<String, Object> 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<String, Object> model, final String scriptProcessorName, final String runAsUser)
|
||||
{
|
||||
// execute as specified runAsUser
|
||||
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
|
||||
{
|
||||
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<>() {
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
return executeScript(theScript, model, scriptProcessorName);
|
||||
}
|
||||
}, runAsUser);
|
||||
}
|
||||
|
||||
|
||||
protected Object executeScript(String theScript, Map<String, Object> 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<String, Object> 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<String, Object> 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<Boolean>()
|
||||
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;
|
||||
}
|
||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
* #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<T extends LockStore>
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>Start outer txn</li>
|
||||
* <li>Modify lock in outer txn</li>
|
||||
* <li>Start inner txn</li>
|
||||
* <li>Modify lock in inner txn</li>
|
||||
* </ul>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* #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<T extends LockStore>
|
||||
{
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>Start outer txn</li>
|
||||
* <li>Modify lock in outer txn</li>
|
||||
* <li>Start inner txn</li>
|
||||
* <li>Modify lock in inner txn</li>
|
||||
* </ul>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user