diff --git a/source/java/org/alfresco/util/BaseAlfrescoTestCase.java b/source/java/org/alfresco/util/BaseAlfrescoTestCase.java
index 51fb5051cc..ab12016078 100644
--- a/source/java/org/alfresco/util/BaseAlfrescoTestCase.java
+++ b/source/java/org/alfresco/util/BaseAlfrescoTestCase.java
@@ -19,9 +19,8 @@
package org.alfresco.util;
-import junit.framework.TestCase;
-
import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.repository.ContentService;
@@ -38,7 +37,7 @@ import org.springframework.context.ApplicationContext;
*
* @author Roy Wetherall
*/
-public abstract class BaseAlfrescoTestCase extends TestCase
+public abstract class BaseAlfrescoTestCase extends RetryingTransactionHelperTestCase
{
/**
* This context will be fetched each time, but almost always
@@ -64,10 +63,15 @@ public abstract class BaseAlfrescoTestCase extends TestCase
/** The root node reference */
protected NodeRef rootNodeRef;
-
+ /** Action service */
protected ActionService actionService;
+
+ /** Transaction service */
protected TransactionService transactionService;
+ /** Retrying transaction helper */
+ protected RetryingTransactionHelper retryingTransactionHelper;
+
/**
* By default will return the full context.
* Override this if your test needs a different one.
@@ -78,20 +82,25 @@ public abstract class BaseAlfrescoTestCase extends TestCase
ctx = ApplicationContextHelper.getApplicationContext();
}
+ /**
+ * @see junit.framework.TestCase#setUp()
+ */
@Override
protected void setUp() throws Exception
{
super.setUp();
setUpContext();
- // get the service register
+ // Get the service register
this.serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
- //Get a reference to the node service
+
+ // Get content services
this.nodeService = serviceRegistry.getNodeService();
this.contentService = serviceRegistry.getContentService();
this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
this.actionService = (ActionService)ctx.getBean("actionService");
this.transactionService = serviceRegistry.getTransactionService();
+ this.retryingTransactionHelper = (RetryingTransactionHelper)ctx.getBean("retryingTransactionHelper");
// Authenticate as the system user - this must be done before we create the store
authenticationComponent.setSystemUserAsCurrentUser();
@@ -99,11 +108,21 @@ public abstract class BaseAlfrescoTestCase extends TestCase
// Create the store and get the root node
this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
this.rootNodeRef = this.nodeService.getRootNode(this.storeRef);
-
-
+
}
+ /**
+ * @see org.alfresco.util.RetryingTransactionHelperTestCase#getRetryingTransactionHelper()
+ */
+ @Override
+ public RetryingTransactionHelper getRetryingTransactionHelper()
+ {
+ return this.retryingTransactionHelper;
+ }
+ /**
+ * @see junit.framework.TestCase#tearDown()
+ */
@Override
protected void tearDown() throws Exception
{
@@ -117,6 +136,5 @@ public abstract class BaseAlfrescoTestCase extends TestCase
// Don't let this mask any previous exceptions
}
super.tearDown();
- }
-
+ }
}
\ No newline at end of file
diff --git a/source/java/org/alfresco/util/RetryingTransactionHelperTestCase.java b/source/java/org/alfresco/util/RetryingTransactionHelperTestCase.java
new file mode 100644
index 0000000000..428b7cb321
--- /dev/null
+++ b/source/java/org/alfresco/util/RetryingTransactionHelperTestCase.java
@@ -0,0 +1,308 @@
+/**
+ *
+ */
+package org.alfresco.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Test case base class with helper methods for transactional tests.
+ *
+ * @author Roy Wetherall
+ */
+public abstract class RetryingTransactionHelperTestCase extends TestCase
+{
+ /**
+ * @return retrying transaction helper
+ */
+ public abstract RetryingTransactionHelper getRetryingTransactionHelper();
+
+ /**
+ * Executes a test in a retrying transaction as the admin user.
+ *
+ * @param type of the object resulting from the test, can be set to {@link Void} if none.
+ * @param test test object to be executed within a retrying transaction
+ * @return A the result of the test
+ */
+ protected A doTestInTransaction(final Test test)
+ {
+ return doTestInTransaction(test, AuthenticationUtil.getAdminUserName());
+ }
+
+ /**
+ * Executes a test in a retrying transaction as the admin user.
+ *
+ * @param type of the object resulting from the test, can be set to {@link Void} if none.
+ * @param test test object to be executed within a retrying transaction
+ * @param asUser user to execute the test as
+ * @return A the result of the test
+ */
+ protected A doTestInTransaction(final Test test, final String asUser)
+ {
+ String origUser = AuthenticationUtil.getFullyAuthenticatedUser();
+ AuthenticationUtil.setFullyAuthenticatedUser(asUser);
+ try
+ {
+ // Execute the run() method within a retrying transaction
+ RetryingTransactionCallback doRun = new RetryingTransactionCallback()
+ {
+ @Override
+ public A execute() throws Throwable
+ {
+ // Run test as user
+ return test.run();
+ }
+ };
+ final A result = getRetryingTransactionHelper().doInTransaction(doRun);
+
+ // Execute the test() method within a retrying transaction
+ RetryingTransactionCallback doTest = new RetryingTransactionCallback()
+ {
+ @Override
+ public Void execute() throws Throwable
+ {
+ // pass the result of the run into the test
+ test.test(result);
+ return null;
+ }
+
+ };
+ getRetryingTransactionHelper().doInTransaction(doTest);
+
+ return result;
+ }
+ finally
+ {
+ if (origUser != null)
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(origUser);
+ }
+ else
+ {
+ AuthenticationUtil.clearCurrentSecurityContext();
+ }
+ }
+ }
+
+ /**
+ * Executes a test in a retrying transaction. Run as admin user.
+ *
+ * @param test failure test object
+ */
+ protected void doTestInTransaction(final FailureTest test)
+ {
+ doTestInTransaction(test, AuthenticationUtil.getAdminUserName());
+ }
+
+ /**
+ * Executes a test in a retrying transaction.
+ *
+ * @param test failure test object
+ * @param asUser user to run test as
+ */
+ protected void doTestInTransaction(final FailureTest test, final String asUser)
+ {
+ String origUser = AuthenticationUtil.getFullyAuthenticatedUser();
+ AuthenticationUtil.setFullyAuthenticatedUser(asUser);
+ try
+ {
+ RetryingTransactionCallback doRun = new RetryingTransactionCallback()
+ {
+ @Override
+ public Void execute() throws Throwable
+ {
+ Class> eType = test.getExpectedExceptionClass();
+ try
+ {
+ test.run();
+ }
+ catch (Throwable exception)
+ {
+ if (eType.isInstance(exception) == false)
+ {
+ // Genuine error so re-throw
+ throw exception;
+ }
+
+ // Otherwise, it's an expected failure
+ return null;
+ }
+
+ // Fail since not expected to succeed
+ fail(test.getMessage());
+ return null;
+ }
+ };
+ getRetryingTransactionHelper().doInTransaction(doRun);
+ }
+ finally
+ {
+ if (origUser != null)
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(origUser);
+ }
+ else
+ {
+ AuthenticationUtil.clearCurrentSecurityContext();
+ }
+ }
+ }
+
+ /**
+ * Class containing the code to run as test and the optional code to check the results of the
+ * test in a separate transaction if required.
+ */
+ protected abstract class Test
+ {
+ /** Map containing model values. Used to pass data between run and test methods. */
+ protected Map model = new HashMap(5);
+
+ /**
+ * Helper method to set a string vaule in the model
+ * @param key model key
+ * @param value model value
+ * @return String model value
+ */
+ protected String setString(String key, String value)
+ {
+ if (value != null)
+ {
+ model.put(key, value);
+ }
+ return value;
+ }
+
+ /**
+ * Helper method to get a string value from the model
+ * @param key model key
+ * @return String model value, null if none
+ */
+ protected String getString(String key)
+ {
+ return (String)model.get(key);
+ }
+
+ /**
+ * Helper method to set node reference value in model
+ * @param key model key
+ * @param nodeRef node reference
+ * @return {@link NodeRef} node reference
+ */
+ protected NodeRef setNodeRef(String key, NodeRef nodeRef)
+ {
+ if (nodeRef != null)
+ {
+ model.put(key, nodeRef);
+ }
+ return nodeRef;
+ }
+
+ /**
+ * Helper method to get node reference value from model
+ * @param key mode key
+ * @return {@link NodeRef} node reference
+ */
+ protected NodeRef getNodeRef(String key)
+ {
+ return (NodeRef)model.get(key);
+ }
+
+ /**
+ * Body of the test is implemented here.
+ * @return A result of the test
+ * @throws Exception
+ */
+ public abstract A run() throws Exception;
+
+ /**
+ * If you wish to test the results of the above method within a new and separate
+ * transaction then implement the tests here.
+ * @param result result of the above method
+ * @throws Exception
+ */
+ public void test(A result) throws Exception
+ {
+ // Default implementation does nothing.
+ // Override this if you want to test the results of the
+ // run method in a new transaction.
+ }
+ }
+
+ /**
+ * Class containing test code that is expected to fail.
+ */
+ protected abstract class FailureTest
+ {
+ /** Failure message */
+ private String message = "This test was expected to fail.";
+
+ /** Expected failure exception */
+ private Class> expectedExceptionClass = AlfrescoRuntimeException.class;
+
+ /**
+ * @param message failure message
+ */
+ public FailureTest(String message)
+ {
+ this.message = message;
+ }
+
+ /**
+ * @param expectedExceptionClass expected exception class
+ */
+ public FailureTest(Class> expectedExceptionClass)
+ {
+ this.expectedExceptionClass = expectedExceptionClass;
+ }
+
+ /**
+ * @param message failure message
+ * @param expectedExceptionClass expected exception class
+ */
+ public FailureTest(String message, Class> expectedExceptionClass)
+ {
+ this.message = message;
+ this.expectedExceptionClass = expectedExceptionClass;
+ }
+
+ /**
+ * @return expected exception class
+ */
+ public Class> getExpectedExceptionClass()
+ {
+ return expectedExceptionClass;
+ }
+
+ /**
+ * @return failure message
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * Default constructor
+ */
+ public FailureTest()
+ {
+ super();
+ }
+
+ /**
+ * Code to test for failure
+ *
+ * @throws Exception
+ */
+ public abstract void run() throws Exception;
+ }
+}
diff --git a/source/java/org/alfresco/util/TestWithUserUtils.java b/source/java/org/alfresco/util/TestWithUserUtils.java
index 3c39703cf8..70d18fa5bf 100644
--- a/source/java/org/alfresco/util/TestWithUserUtils.java
+++ b/source/java/org/alfresco/util/TestWithUserUtils.java
@@ -31,7 +31,7 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
- * Utility class containing some useful methods to help when writing tets that require authenticated users
+ * Utility class containing some useful methods to help when writing tests that require authenticated users
*
* @author Roy Wetherall
*/
@@ -77,7 +77,7 @@ public abstract class TestWithUserUtils
}
/**
- * Autneticate the user with the specified password
+ * Authenticate the user with the specified password
*
* @param userName the user name
* @param password the password