Merge V3.2 To HEAD

17894 : Merge from NEILM to V3.2



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18233 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2010-01-21 17:48:10 +00:00
parent ac8529fc24
commit 41e574f3d5
11 changed files with 600 additions and 29 deletions

View File

@@ -34,6 +34,17 @@
</property>
</bean>
<!-- this bean supports the registration of 'action filter' beans. These beans should extend the
baseActionFilter bean below, which provides automatic registration with the
default AsynchronousActionService. These filter beans are used to detect equivalent pending
actions and to drop unnecessary repeat actions.
See bean createThumbnailActionFilter for an example. -->
<bean name="baseActionFilter" abstract="true" init-method="init" class="org.alfresco.repo.action.AbstractAsynchronousActionFilter">
<property name="asynchronousActionExecutionQueue">
<ref bean="defaultAsynchronousActionExecutionQueue"/>
</property>
</bean>
<bean id="defaultAsynchronousActionExecutionQueue" class="org.alfresco.repo.action.AsynchronousActionExecutionQueueImpl" init-method="init">
<property name="threadPoolExecutor">
<ref bean="defaultAsyncThreadPool"/>

View File

@@ -387,6 +387,7 @@ deployment.rmi.service.port=50507
mbean.server.locateExistingServerIfPossible=true
# External executable locations
#ooo.exe=/Applications/OpenOffice.org.app/Contents/MacOS/soffice
ooo.exe=soffice
ooo.user=${dir.root}/oouser

View File

@@ -183,5 +183,18 @@
</property>
<property name="serviceRegistry" ref="ServiceRegistry"/>
</bean>
<!-- This action filter bean prevents multiple equivalent create-thumbnail actions from executing
simultaneously in the Asynchronous Action Execution Service -->
<bean id="createThumbnailActionFilter" class="org.alfresco.repo.action.CreateThumbnailActionFilter" parent="baseActionFilter">
<property name="name">
<value>preventMultipleCreateThumbnailActions</value>
</property>
<!-- The action-definition-name against which this bean will be registered. -->
<property name="actionDefinitionName">
<value>create-thumbnail</value>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.action;
import java.util.Comparator;
/**
* This class is the base filter class for asynchronous actions. These filters are used in identifying
* 'equivalent' actions in the asynchronous action execution service. By registering
* a subclass of this type, all actions of a given action-definition-name that are still pending
* (i.e. currently executing or in the queue awaiting execution) will be compared to any new action
* and if they are equal (as determined by the compare implementation defined herein) the newly
* submitted action will not be added to the queue and will be dropped.
*
* Concrete subclasses can be implemented and then dependency-injected using the spring-bean
* baseActionFilter as their parent.
*
* @author Neil McErlean
*/
public abstract class AbstractAsynchronousActionFilter implements Comparator<OngoingAsyncAction>
{
private String name;
private String actionDefinitionName;
private AsynchronousActionExecutionQueueImpl asynchronousActionExecutionQueue;
/**
* Gets the name of this comparator.
* @return
*/
public String getName()
{
return this.name;
}
/**
* Sets the name of this comparator.
* @param name
*/
public void setName(String name)
{
this.name = name;
}
/**
* Gets the action definition name against which this comparator is registered.
* @return
*/
public String getActionDefinitionName()
{
return this.actionDefinitionName;
}
public void setActionDefinitionName(String actionDefinitionName)
{
this.actionDefinitionName = actionDefinitionName;
}
public void setAsynchronousActionExecutionQueue(
AsynchronousActionExecutionQueueImpl asynchronousActionExecutionQueue)
{
this.asynchronousActionExecutionQueue = asynchronousActionExecutionQueue;
}
public void init()
{
this.asynchronousActionExecutionQueue.registerActionFilter(this);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,6 +25,8 @@
package org.alfresco.repo.action;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -35,6 +37,7 @@ import org.alfresco.repo.action.evaluator.InCategoryEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
import org.alfresco.repo.action.executer.ActionExecuter;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.action.executer.AddFeaturesActionExecuter;
import org.alfresco.repo.action.executer.CheckInActionExecuter;
import org.alfresco.repo.action.executer.CheckOutActionExecuter;
@@ -42,6 +45,7 @@ import org.alfresco.repo.action.executer.CompositeActionExecuter;
import org.alfresco.repo.action.executer.MoveActionExecuter;
import org.alfresco.repo.action.executer.ScriptActionExecuter;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition;
@@ -49,6 +53,7 @@ import org.alfresco.service.cmr.action.ActionConditionDefinition;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.action.CompositeActionCondition;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -56,6 +61,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.BaseAlfrescoSpringTest;
/**
@@ -69,12 +75,28 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
private NodeRef nodeRef;
private NodeRef folder;
private RetryingTransactionHelper transactionHelper;
@Override
protected String[] getConfigLocations()
{
String[] existingConfigLocations = ApplicationContextHelper.CONFIG_LOCATIONS;
List<String> locations = Arrays.asList(existingConfigLocations);
List<String> mutableLocationsList = new ArrayList<String>(locations);
mutableLocationsList.add("classpath:org/alfresco/repo/action/test-action-services-context.xml");
String[] result = mutableLocationsList.toArray(new String[mutableLocationsList.size()]);
return (String[]) result;
}
@Override
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper");
// Create the node used for tests
this.nodeRef = this.nodeService.createNode(
this.rootNodeRef,
@@ -761,6 +783,85 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
* Test asynchronous actions
*/
/**
* This test checks that a series of "equivalent" actions submitted for asynchronous execution
* will be correctly filtered so that no 2 equivalent actions are executed at the same time.
*/
public void testAsyncLongRunningActionsFilter()
{
setComplete();
endTransaction();
final SleepActionExecuter sleepAction = (SleepActionExecuter)applicationContext.getBean("sleep-action");
assertNotNull(sleepAction);
final int actionSubmissonCount = 4; // Rather arbitrary count.
for (int i = 0; i < actionSubmissonCount; i ++)
{
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Action action = actionService.createAction(SleepActionExecuter.NAME);
action.setExecuteAsynchronously(true);
actionService.executeAction(action, nodeRef);
return null;
}
});
}
// Wait long enough for previous action(s) to have executed and then submit another
try
{
Thread.sleep(sleepAction.getSleepMs() * actionSubmissonCount + 1000); // Enough time for all actions and an extra second for luck.
}
catch (InterruptedException ignored)
{
// intentionally empty
}
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Action action = actionService.createAction(SleepActionExecuter.NAME);
action.setExecuteAsynchronously(true);
actionService.executeAction(action, nodeRef);
return null;
}
});
try
{
Thread.sleep(sleepAction.getSleepMs() + 2000); // Enough time for latest action and an extra 2 seconds for luck.
}
catch (InterruptedException ignored)
{
// intentionally empty
}
int sleepTime = 0; // Do not sleep during execution as the Action itself sleeps.
int maxTries = 1;
postAsyncActionTest(
this.transactionService,
sleepTime,
maxTries,
new AsyncTest()
{
public String executeTest()
{
final int expectedResult = 2;
int actualResult = sleepAction.getTimesExecuted();
return actualResult == expectedResult ? null : "Expected timesExecuted " + expectedResult + " was " + actualResult;
};
});
}
/**
* Test asynchronous execute action
*/
@@ -786,10 +887,10 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
10,
new AsyncTest()
{
public boolean executeTest()
public String executeTest()
{
return (
finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_CLASSIFIABLE));
boolean result = finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_CLASSIFIABLE);
return result ? null : "Expected aspect Classifiable";
};
});
}
@@ -828,11 +929,11 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
10,
new AsyncTest()
{
public boolean executeTest()
public String executeTest()
{
return (
finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_VERSIONABLE) &&
finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_LOCKABLE));
boolean result = finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_VERSIONABLE) &&
finalNodeService.hasAspect(finalNodeRef, ContentModel.ASPECT_LOCKABLE);
return result ? null : "Expected aspects Versionable & Lockable";
};
});
}
@@ -872,8 +973,8 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
try
{
int tries = 0;
boolean done = false;
while (done == false && tries < maxTries)
String errorMsg = null;
while (errorMsg == null && tries < maxTries)
{
try
{
@@ -883,16 +984,16 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
// Sleep for a bit
Thread.sleep(sleepTime);
done = (transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<Boolean>()
errorMsg = (transactionService.getRetryingTransactionHelper().doInTransaction(
new RetryingTransactionCallback<String>()
{
public Boolean execute()
public String execute()
{
// See if the action has been performed
boolean done = test.executeTest();
String done = test.executeTest();
return done;
}
})).booleanValue();
}));
}
catch (InterruptedException e)
{
@@ -901,9 +1002,9 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
}
}
if (done == false)
if (errorMsg != null)
{
throw new RuntimeException("Asynchronous action was not executed.");
throw new RuntimeException("Asynchronous action was not executed. " + errorMsg);
}
}
catch (Throwable exception)
@@ -918,7 +1019,11 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
*/
public interface AsyncTest
{
boolean executeTest();
/**
*
* @return <code>null</code> if the test succeeded, else an error message for use in JUnit report.
*/
String executeTest();
}
/** ===================================================================================
@@ -1009,10 +1114,10 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
10,
new AsyncTest()
{
public boolean executeTest()
public String executeTest()
{
return (
ActionServiceImplTest.this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CLASSIFIABLE));
boolean result = ActionServiceImplTest.this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CLASSIFIABLE);
return result == true ? null : "Expected aspect Classifiable";
};
});
@@ -1040,4 +1145,68 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
});
}
/**
* This class is only used during JUnit testing.
*
* @author Neil Mc Erlean
*/
public static class SleepActionFilter extends AbstractAsynchronousActionFilter
{
public int compare(OngoingAsyncAction sae1, OngoingAsyncAction sae2)
{
// Sleep actions are always equivalent.
return 0;
}
}
/**
* This class is only intended for use in JUnit tests.
*
* @author Neil McErlean.
*/
public static class SleepActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "sleep-action";
private int sleepMs;
private int timesExecuted = 0;
private void incrementTimesExecutedCount() {timesExecuted++;}
public int getTimesExecuted() {return timesExecuted;}
public int getSleepMs()
{
return sleepMs;
}
public void setSleepMs(int sleepMs)
{
this.sleepMs = sleepMs;
}
/**
* Add parameter definitions
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
// Intentionally empty
}
@Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
try
{
Thread.sleep(sleepMs);
}
catch (InterruptedException ignored)
{
// Intentionally empty
}
finally
{
incrementTimesExecutedCount();
}
}
}
}

View File

@@ -25,8 +25,13 @@
package org.alfresco.repo.action;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.Map.Entry;
import java.util.concurrent.ThreadPoolExecutor;
import org.alfresco.error.StackTraceUtil;
@@ -47,6 +52,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.EqualsHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -64,7 +70,17 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
private TransactionService transactionService;
private AuthenticationContext authenticationContext;
private PolicyComponent policyComponent;
private NodeService nodeService;
private Map<String, AbstractAsynchronousActionFilter>
actionFilters = new HashMap<String, AbstractAsynchronousActionFilter>();
private NodeService nodeService;
/**
* We keep a record of ongoing asynchronous actions (this includes those being executed and
* those that are in the queue).
* This needs to be thread-safe - hence the Vector.
*/
List<OngoingAsyncAction> ongoingActions = new Vector<OngoingAsyncAction>();
// Policy delegates
private ClassPolicyDelegate<OnAsyncActionExecute> onAsyncActionExecuteDelegate;
@@ -169,6 +185,37 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
return qnames;
}
/**
* This method registers an action filter, which can be used to prevent unwanted or unnecessary
* asynchronous actions from being scheduled for execution.
*
* @param filter the filter implementation.
*/
public void registerActionFilter(AbstractAsynchronousActionFilter filter)
{
String filterName = filter.getName();
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Registered asynchronous action filter ")
.append(filter.getName()).append(" for action ")
.append(filter.getActionDefinitionName());
logger.debug(msg.toString());
}
AbstractAsynchronousActionFilter existingFilter = actionFilters.get(filterName);
if (logger.isDebugEnabled() && existingFilter != null)
{
StringBuilder msg = new StringBuilder();
msg.append("This replaces previous filter ")
.append(existingFilter.getName());
logger.debug(msg.toString());
}
this.actionFilters.put(filter.getName(), filter);
}
/**
* {@inheritDoc}
*/
@@ -185,6 +232,22 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
public void executeAction(RuntimeActionService actionService, Action action, NodeRef actionedUponNodeRef,
boolean checkConditions, Set<String> actionChain, NodeRef actionExecutionHistoryNodeRef)
{
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Received request to execute async action ").append(action.getActionDefinitionName())
.append(" on ").append(actionedUponNodeRef);
logger.debug(msg.toString());
msg = new StringBuilder();
msg.append("ThreadPool's active count = ").append(this.threadPoolExecutor.getActiveCount());
logger.debug(msg.toString());
msg = new StringBuilder();
msg.append("ThreadPool's queue size = ").append(this.threadPoolExecutor.getQueue().size());
logger.debug(msg.toString());
}
Set<RuleServiceImpl.ExecutedRuleData> executedRules =
(Set<RuleServiceImpl.ExecutedRuleData>) AlfrescoTransactionSupport.getResource("RuleServiceImpl.ExecutedRules");
Runnable runnable = new ActionExecutionWrapper(
@@ -195,7 +258,60 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
actionExecutionHistoryNodeRef,
actionChain,
executedRules);
threadPoolExecutor.execute(runnable);
// Consider whether this action should be filtered out by one of the registered filters.
boolean newActionShouldBeFilteredOut = false;
OngoingAsyncAction nodeBeingNewlyActioned = new OngoingAsyncAction(actionedUponNodeRef, action);
for (Entry<String, AbstractAsynchronousActionFilter> entry : actionFilters.entrySet())
{
AbstractAsynchronousActionFilter comparator = entry.getValue();
String actionDefinitionName = comparator.getActionDefinitionName();
if (actionDefinitionName.equals(action.getActionDefinitionName()) == false)
{
// We're only interested in registered actions with the same name as this one.
continue;
}
else
{
// Now we've found a registered action that matches the current one.
// So we'll go through the actions that are ongoing and consider them for matches with this one.
for (OngoingAsyncAction ongoingAction : this.ongoingActions)
{
if (comparator.compare(ongoingAction, nodeBeingNewlyActioned) == 0)
{
newActionShouldBeFilteredOut = true;
break;
}
}
}
}
if (newActionShouldBeFilteredOut)
{
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Dropping action ").append(action).append(" as equivalent is ongoing.");
logger.debug(msg.toString());
}
return;
}
else
{
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Executing action ").append(action);
logger.debug(msg.toString());
}
// Queue it and do it.
ongoingActions.add(nodeBeingNewlyActioned);
threadPoolExecutor.execute(runnable);
}
// Done
if (logger.isDebugEnabled())
{
@@ -214,8 +330,19 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
}
}
private void handleAsyncActionIsCompleted(NodeRef n, Action action) {
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Completed action ").append(action);
logger.debug(msg.toString());
}
OngoingAsyncAction ongoing = new OngoingAsyncAction(n, action);
ongoingActions.remove(ongoing);
}
/**
* Tansaction listener used to invoke callback policies
* Transaction listener used to invoke callback policies
*/
public class CallbackTransactionListener extends TransactionListenerAdapter
{
@@ -384,6 +511,7 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE
{
logger.error("Failed to execute asynchronous action: " + action, exception);
}
}
handleAsyncActionIsCompleted(actionedUponNodeRef, action);
}
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.action;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* This filter class is used to identify equivalent create-thumbnail actions.
* Two create-thumbnail actions are considered equivalent if they are on the same NodeRef and
* if they have the same thumbnail-name parameter.
*
* @author Neil McErlean
*/
public class CreateThumbnailActionFilter extends AbstractAsynchronousActionFilter
{
private static final String PARAM_THUMBNAIL_NAME = "thumbnail-name";
public int compare(OngoingAsyncAction nodeAction1, OngoingAsyncAction nodeAction2)
{
NodeRef n1 = nodeAction1.getNodeRef();
NodeRef n2 = nodeAction2.getNodeRef();
if (n1.equals(n2) == false)
{
return -1;
}
else
{
String thName1 = (String)nodeAction1.getAction().getParameterValue(PARAM_THUMBNAIL_NAME);
String thName2 = (String)nodeAction2.getAction().getParameterValue(PARAM_THUMBNAIL_NAME);
return thName1.compareTo(thName2);
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.action;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.EqualsHelper;
/**
* This class represents an ongoing asynchronous action.
*/
public class OngoingAsyncAction
{
private final NodeRef node;
private final Action action;
public OngoingAsyncAction(NodeRef node, Action action)
{
this.node = node;
this.action = action;
}
public NodeRef getNodeRef()
{
return node;
}
public Action getAction()
{
return action;
}
@Override
public boolean equals(Object otherObj)
{
if (otherObj == null || !otherObj.getClass().equals(this.getClass()))
{
return false;
}
OngoingAsyncAction otherNodeBeingActioned = (OngoingAsyncAction)otherObj;
return EqualsHelper.nullSafeEquals(this.node, otherNodeBeingActioned.node) &&
EqualsHelper.nullSafeEquals(this.action.getActionDefinitionName(), otherNodeBeingActioned.action.getActionDefinitionName());
}
@Override
public int hashCode() {
return this.node.hashCode() + 7 * this.action.getActionDefinitionName().hashCode();
}
@Override
public String toString()
{
StringBuilder msg = new StringBuilder();
msg.append(node).append(", ").append(action.getActionDefinitionName());
return msg.toString();
}
}

View File

@@ -0,0 +1,22 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="sleep-action" class="org.alfresco.repo.action.ActionServiceImplTest$SleepActionExecuter" parent="action-executer">
<property name="publicAction">
<value>false</value>
</property>
<property name="sleepMs">
<value>1000</value>
</property>
</bean>
<bean id="sleepActionFilter" class="org.alfresco.repo.action.ActionServiceImplTest$SleepActionFilter" parent="baseActionFilter">
<property name="name">
<value>preventMultipleSleepActions</value>
</property>
<property name="actionDefinitionName">
<value>sleep-action</value>
</property>
</bean>
</beans>

View File

@@ -171,7 +171,7 @@ public abstract class AbstractContentTransformer2 extends ContentTransformerHelp
// Make sure that this transformation gets set back i.t.o. time taken.
// This will ensure that transformers that compete for the same transformation
// will be prejudiced against transformers that tend to fail
recordTime(10000); // 10 seconds, i.e. rubbish
recordTime(60 * 1000); // 1 minute, i.e. rubbish
throw new ContentIOException("Content conversion failed: \n" +
" reader: " + reader + "\n" +

View File

@@ -295,11 +295,12 @@ public class RuleServiceCoverageTest extends TestCase
100,
new AsyncTest()
{
public boolean executeTest()
public String executeTest()
{
return RuleServiceCoverageTest.this.nodeService.hasAspect(
boolean result = RuleServiceCoverageTest.this.nodeService.hasAspect(
newNodeRef,
ContentModel.ASPECT_VERSIONABLE);
return result ? "" : "Expected aspect Versionable";
};
});
}