diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 96a8be786b..d1c98514ea 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -28,6 +28,20 @@ + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java index 7565c401b0..806f36adc6 100644 --- a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java +++ b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java @@ -28,6 +28,7 @@ import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.StoreArchiveMap; +import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.AssociationRef; @@ -62,6 +63,7 @@ public class ArchiveAndRestoreTest extends TestCase private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + private NodeArchiveService nodeArchiveService; private NodeService nodeService; private PermissionService permissionService; private AuthenticationComponent authenticationComponent; @@ -95,6 +97,7 @@ public class ArchiveAndRestoreTest extends TestCase public void setUp() throws Exception { ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); + nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService"); nodeService = serviceRegistry.getNodeService(); permissionService = serviceRegistry.getPermissionService(); authenticationService = serviceRegistry.getAuthenticationService(); @@ -142,10 +145,7 @@ public class ArchiveAndRestoreTest extends TestCase { try { - if (txn.getStatus() == Status.STATUS_ACTIVE) - { - txn.rollback(); - } + txn.rollback(); } catch (Throwable e) { @@ -461,4 +461,84 @@ public class ArchiveAndRestoreTest extends TestCase System.out.println("Average delete time: " + averageDeleteTimeMs + " ms"); System.out.println("Average create time: " + averageCreateTimeMs + " ms"); } + + public void testInTransactionRestore() throws Exception + { + RestoreNodeReport report = nodeArchiveService.restoreArchivedNode(a); + // expect a failure due to missing archive node + assertEquals("Expected failure", RestoreStatus.FAILURE_INVALID_ARCHIVE_NODE, report.getStatus()); + // check that our transaction was not affected + assertEquals("Transaction should still be valid", Status.STATUS_ACTIVE, txn.getStatus()); + } + + public void testInTransactionPurge() throws Exception + { + nodeArchiveService.purgeArchivedNode(a); + // the node should still be there (it was not available to the purge transaction) + assertTrue("Node should not have been touched", nodeService.exists(a)); + // check that our transaction was not affected + assertEquals("Transaction should still be valid", Status.STATUS_ACTIVE, txn.getStatus()); + } + + private void commitAndBeginNewTransaction() throws Exception + { + txn.commit(); + txn = transactionService.getUserTransaction(); + txn.begin(); + } + + public void testMassRestore() throws Exception + { + nodeService.deleteNode(a); + nodeService.deleteNode(b); + commitAndBeginNewTransaction(); + + List reports = nodeArchiveService.restoreAllArchivedNodes(workStoreRef); + // check that both a and b were restored + assertEquals("Incorrect number of node reports", 2, reports.size()); + commitAndBeginNewTransaction(); + // all nodes must be restored, but some of the inter a-b assocs might not be + verifyNodeExistence(a, true); + verifyNodeExistence(b, true); + verifyNodeExistence(aa, true); + verifyNodeExistence(bb, true); + verifyNodeExistence(a_, false); + verifyNodeExistence(b_, false); + verifyNodeExistence(aa_, false); + verifyNodeExistence(bb_, false); + } + + public void testMassPurge() throws Exception + { + nodeService.deleteNode(a); + nodeService.deleteNode(b); + commitAndBeginNewTransaction(); + + nodeArchiveService.purgeAllArchivedNodes(workStoreRef); + + commitAndBeginNewTransaction(); + // all nodes must be gone + verifyNodeExistence(a, false); + verifyNodeExistence(b, false); + verifyNodeExistence(aa, false); + verifyNodeExistence(bb, false); + verifyNodeExistence(a_, false); + verifyNodeExistence(b_, false); + verifyNodeExistence(aa_, false); + verifyNodeExistence(bb_, false); + } +// +// public void testPermissionsForRestore() throws Exception +// { +// // user A deletes 'a' +// authenticationService.authenticate(USER_A, USER_A.toCharArray()); +// nodeService.deleteNode(a); +// // user B deletes 'b' +// authenticationService.authenticate(USER_B, USER_B.toCharArray()); +// nodeService.deleteNode(b); +// +// // user B can't see archived 'a' +// List restoredByB = nodeArchiveService.restoreAllArchivedNodes(workStoreRef); +// assertEquals("User B should not have seen A's delete", 1, restoredByB.size()); +// } } diff --git a/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java b/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java index b35c9fd677..258fa1d04d 100644 --- a/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java +++ b/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java @@ -19,11 +19,23 @@ package org.alfresco.repo.node.archive; import java.util.ArrayList; import java.util.List; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; 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; /** * Implementation of the node archive abstraction. @@ -32,7 +44,10 @@ import org.alfresco.service.transaction.TransactionService; */ public class NodeArchiveServiceImpl implements NodeArchiveService { + private static Log logger = LogFactory.getLog(NodeArchiveServiceImpl.class); + private NodeService nodeService; + private SearchService searchService; private TransactionService transactionService; public void setNodeService(NodeService nodeService) @@ -45,11 +60,99 @@ public class NodeArchiveServiceImpl implements NodeArchiveService this.transactionService = transactionService; } + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + public NodeRef getStoreArchiveNode(StoreRef storeRef) { return nodeService.getStoreArchiveNode(storeRef); } + /** + * Get all the nodes that were archived from the given store. + */ + private ResultSet getArchivedNodes(StoreRef originalStoreRef) + { + // Get the archive location + NodeRef archiveParentNodeRef = nodeService.getStoreArchiveNode(originalStoreRef); + StoreRef archiveStoreRef = archiveParentNodeRef.getStoreRef(); + // build the query + String query = String.format("PARENT:\"%s\" AND ASPECT:\"%s\"", archiveParentNodeRef, ContentModel.ASPECT_ARCHIVED); + // search parameters + SearchParameters params = new SearchParameters(); + params.addStore(archiveStoreRef); + params.setLanguage(SearchService.LANGUAGE_LUCENE); + params.setQuery(query); +// params.addSort(ContentModel.PROP_ARCHIVED_DATE.toString(), false); + // get all archived children using a search + ResultSet rs = searchService.query(params); + // done + return rs; + } + + /** + * This is the primary restore method that all restore methods fall back on. + * It executes the restore for the node in a separate transaction and attempts to catch + * the known conditions that can be reported back to the client. + */ + public RestoreNodeReport restoreArchivedNode( + final NodeRef archivedNodeRef, + final NodeRef destinationNodeRef, + final QName assocTypeQName, + final QName assocQName) + { + RestoreNodeReport report = new RestoreNodeReport(archivedNodeRef); + report.setTargetParentNodeRef(destinationNodeRef); + try + { + // Transactional wrapper to attempt the restore + TransactionWork restoreWork = new TransactionWork() + { + public NodeRef doWork() throws Exception + { + return nodeService.restoreNode(archivedNodeRef, destinationNodeRef, assocTypeQName, assocQName); + } + }; + NodeRef newNodeRef = TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, restoreWork); + // success + report.setRestoredNodeRef(newNodeRef); + report.setStatus(RestoreStatus.SUCCESS); + } + catch (InvalidNodeRefException e) + { + report.setCause(e); + NodeRef invalidNodeRef = e.getNodeRef(); + if (archivedNodeRef.equals(invalidNodeRef)) + { + // not too serious, but the node to archive is missing + report.setStatus(RestoreStatus.FAILURE_INVALID_ARCHIVE_NODE); + } + else if (EqualsHelper.nullSafeEquals(destinationNodeRef, invalidNodeRef)) + { + report.setStatus(RestoreStatus.FAILURE_INVALID_PARENT); + } + else + { + // some other invalid node was detected + report.setStatus(RestoreStatus.FAILURE_OTHER); + } + } + // TODO: Catch permission exceptions + catch (Throwable e) + { + report.setCause(e); + report.setStatus(RestoreStatus.FAILURE_OTHER); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Attempted node restore: "+ report); + } + return report; + } + /** * @see #restoreArchivedNode(NodeRef, NodeRef, QName, QName) */ @@ -58,27 +161,12 @@ public class NodeArchiveServiceImpl implements NodeArchiveService return restoreArchivedNode(archivedNodeRef, null, null, null); } - public RestoreNodeReport restoreArchivedNode( - NodeRef archivedNodeRef, - NodeRef destinationNodeRef, - QName assocTypeQName, - QName assocQName) - { - throw new UnsupportedOperationException(); - } - /** - * @see #restoreArchivedNode(NodeRef, NodeRef, QName, QName) + * @see #restoreArchivedNodes(List, NodeRef, QName, QName) */ public List restoreArchivedNodes(List archivedNodeRefs) { - List results = new ArrayList(archivedNodeRefs.size()); - for (NodeRef nodeRef : archivedNodeRefs) - { - RestoreNodeReport result = restoreArchivedNode(nodeRef, null, null, null); - results.add(result); - } - return results; + return restoreArchivedNodes(archivedNodeRefs, null, null, null); } /** @@ -100,35 +188,96 @@ public class NodeArchiveServiceImpl implements NodeArchiveService } /** - * @see #restoreArchivedNode(NodeRef, NodeRef, QName, QName) + * @see #restoreAllArchivedNodes(StoreRef, NodeRef, QName, QName) */ public List restoreAllArchivedNodes(StoreRef originalStoreRef) { - throw new UnsupportedOperationException(); + return restoreAllArchivedNodes(originalStoreRef, null, null, null); } + /** + * Finds the archive location for nodes that were deleted from the given store + * and attempt to restore each node. + * + * @see NodeService#getStoreArchiveNode(StoreRef) + * @see #restoreArchivedNode(NodeRef, NodeRef, QName, QName) + */ public List restoreAllArchivedNodes( StoreRef originalStoreRef, NodeRef destinationNodeRef, QName assocTypeQName, QName assocQName) { - throw new UnsupportedOperationException(); + // get all archived children using a search + ResultSet rs = getArchivedNodes(originalStoreRef); + // loop through the resultset and attempt to restore all the nodes + List results = new ArrayList(1000); + for (ResultSetRow row : rs) + { + NodeRef archivedNodeRef = row.getNodeRef(); + RestoreNodeReport result = restoreArchivedNode(archivedNodeRef, destinationNodeRef, assocTypeQName, assocQName); + results.add(result); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Restored " + results.size() + " nodes into store " + originalStoreRef); + } + return results; } - public void purgeArchivedNode(NodeRef archivedNodeRef) + /** + * This is the primary purge methd that all purge methods fall back on. It isolates the delete + * work in a new transaction. + */ + public void purgeArchivedNode(final NodeRef archivedNodeRef) { - throw new UnsupportedOperationException(); + TransactionWork deleteWork = new TransactionWork() + { + public Object doWork() throws Exception + { + try + { + nodeService.deleteNode(archivedNodeRef); + } + catch (InvalidNodeRefException e) + { + // ignore + } + return null; + } + }; + TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, deleteWork); } + /** + * @see #purgeArchivedNode(NodeRef) + */ public void purgeArchivedNodes(List archivedNodes) { - throw new UnsupportedOperationException(); + for (NodeRef archivedNodeRef : archivedNodes) + { + purgeArchivedNode(archivedNodeRef); + } + // done } public void purgeAllArchivedNodes(StoreRef originalStoreRef) { - throw new UnsupportedOperationException(); + // get all archived children using a search + ResultSet rs = getArchivedNodes(originalStoreRef); + // loop through the resultset and attempt to restore all the nodes + List results = new ArrayList(1000); + for (ResultSetRow row : rs) + { + NodeRef archivedNodeRef = row.getNodeRef(); + purgeArchivedNode(archivedNodeRef); + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Deleted " + results.size() + " nodes originally in store " + originalStoreRef); + } } } diff --git a/source/java/org/alfresco/repo/node/archive/RestoreNodeReport.java b/source/java/org/alfresco/repo/node/archive/RestoreNodeReport.java index 9b83e6f832..c21b90d61a 100644 --- a/source/java/org/alfresco/repo/node/archive/RestoreNodeReport.java +++ b/source/java/org/alfresco/repo/node/archive/RestoreNodeReport.java @@ -34,6 +34,7 @@ public class RestoreNodeReport implements Serializable */ public static enum RestoreStatus { + /** the operation was a success */ SUCCESS { @Override @@ -43,15 +44,23 @@ public class RestoreNodeReport implements Serializable } }, + /** the node to restore was missing */ + FAILURE_INVALID_ARCHIVE_NODE + { + }, + /** the destination parent of the restore operation was missing */ FAILURE_INVALID_PARENT { }, + /** the permissions required for either reading or writing were invalid */ FAILURE_PERMISSION { }, + /** there was an integrity failure after the node was restored */ FAILURE_INTEGRITY { }, + /** the problem was not well-recognized */ FAILURE_OTHER { }; @@ -72,18 +81,22 @@ public class RestoreNodeReport implements Serializable private RestoreStatus status; private Throwable cause; - /* package */ RestoreNodeReport( - RestoreStatus status, - NodeRef archivedNodeRef, - NodeRef targetParentNodeRef, - NodeRef restoredNodeRef, - Throwable cause) + /* package */ RestoreNodeReport(NodeRef archivedNodeRef) { - this.status = status; this.archivedNodeRef = archivedNodeRef; - this.targetParentNodeRef = targetParentNodeRef; - this.restoredNodeRef = restoredNodeRef; - this.cause = cause; + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(100); + sb.append("RestoreNodeReport") + .append("[ archived=").append(archivedNodeRef) + .append(", restored=").append(restoredNodeRef) + .append(", parent=").append(targetParentNodeRef) + .append(", status=").append(status) + .append(", err=").append((cause == null ? "" : cause.getMessage())); + return sb.toString(); } public NodeRef getArchivedNodeRef() @@ -96,18 +109,38 @@ public class RestoreNodeReport implements Serializable return targetParentNodeRef; } + /* package */ void setTargetParentNodeRef(NodeRef targetParentNodeRef) + { + this.targetParentNodeRef = targetParentNodeRef; + } + public NodeRef getRestoredNodeRef() { return restoredNodeRef; } + /* package */ void setRestoredNodeRef(NodeRef restoredNodeRef) + { + this.restoredNodeRef = restoredNodeRef; + } + public RestoreStatus getStatus() { return status; } + /* package */ void setStatus(RestoreStatus status) + { + this.status = status; + } + public Throwable getCause() { return cause; } + + /* package */ void setCause(Throwable cause) + { + this.cause = cause; + } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 9ae8aca26d..4e9be3bbbb 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -1514,7 +1514,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } } - public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef targetParentNodeRef, QName assocTypeQName, QName assocQName) + public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) { Node archivedNode = getNodeNotNull(archivedNodeRef); Set aspects = archivedNode.getAspects(); @@ -1533,10 +1533,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl properties.remove(ContentModel.PROP_ARCHIVED_BY); properties.remove(ContentModel.PROP_ARCHIVED_DATE); - if (targetParentNodeRef == null) + if (destinationParentNodeRef == null) { // we must restore to the original location - targetParentNodeRef = originalPrimaryParentAssocRef.getParentRef(); + destinationParentNodeRef = originalPrimaryParentAssocRef.getParentRef(); } // check the associations if (assocTypeQName == null) @@ -1551,7 +1551,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // move the node to the target parent, which may or may not be the original parent moveNode( archivedNodeRef, - targetParentNodeRef, + destinationParentNodeRef, assocTypeQName, assocQName); @@ -1572,7 +1572,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl logger.debug("Restored node: \n" + " original noderef: " + archivedNodeRef + "\n" + " restored noderef: " + restoredNodeRef + "\n" + - " new parent: " + targetParentNodeRef); + " new parent: " + destinationParentNodeRef); } return restoredNodeRef; } diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java index 2abdd03dd6..151f8c9c9d 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java @@ -109,6 +109,11 @@ public class AuthenticationServiceImpl implements AuthenticationService throw ae; } } + + public boolean authenticationExists(String userName) + { + return authenticationDao.userExists(userName); + } public String getCurrentUserName() throws AuthenticationException { diff --git a/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java index cb6276ca44..409d87f13d 100644 --- a/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceImpl.java @@ -180,7 +180,19 @@ public class ChainingAuthenticationServiceImpl implements AuthenticationService } } throw new AuthenticationException("Guest authentication not supported"); + } + public boolean authenticationExists(String userName) + { + for (AuthenticationService authService : getUsableAuthenticationServices()) + { + if (authService.authenticationExists(userName)) + { + return true; + } + } + // it doesn't exist in any of the authentication components + return false; } public String getCurrentUserName() throws AuthenticationException diff --git a/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java index 2668a38325..817109ffd5 100644 --- a/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java @@ -230,6 +230,11 @@ public class TestAuthenticationServiceImpl implements AuthenticationService } } + public boolean authenticationExists(String userName) + { + return userNamesAndPasswords.containsKey(userName); + } + public String getCurrentUserName() throws AuthenticationException { Context context = ContextHolder.getContext(); diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index e6c3d6e792..db10928a2b 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -494,14 +494,17 @@ public interface NodeService * set against it. * * @param archivedNodeRef the archived node - * @param targetParentNodeRef - * @param assocTypeQName - * @param assocQName + * @param destinationParentNodeRef the parent to move the node into + * or null to use the original + * @param assocTypeQName the primary association type name to use in the new location + * or null to use the original + * @param assocQName the primary association name to use in the new location + * or null to use the original * @return Returns the reference to the newly created node */ public NodeRef restoreNode( NodeRef archivedNodeRef, - NodeRef targetParentNodeRef, + NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName); } diff --git a/source/java/org/alfresco/service/cmr/search/ResultSetRow.java b/source/java/org/alfresco/service/cmr/search/ResultSetRow.java index 6db782b7f6..4201e65263 100644 --- a/source/java/org/alfresco/service/cmr/search/ResultSetRow.java +++ b/source/java/org/alfresco/service/cmr/search/ResultSetRow.java @@ -35,39 +35,41 @@ import org.alfresco.service.namespace.QName; public interface ResultSetRow { /** - * Get the values of all available node properties + * Get the values of all available node properties. These are only properties + * that were stored in the query results and can vary depending on the query + * language that was used. * - * @return + * @return Returns all the available node properties */ public Map getValues(); /** * Get a node property by path * - * @param path - * @return + * @param path the path to the value required + * @return Returns the value of the property at the given path */ public Serializable getValue(Path path); /** - * Get a node value by name + * Get a node property value by name * - * @param qname - * @return + * @param qname the property name + * @return Returns the node property for the given name */ public Serializable getValue(QName qname); /** - * The refernce to the node that equates to this row in the result set + * The reference to the node that equates to this row in the result set * - * @return + * @return Returns the reference to the node that makes this result */ public NodeRef getNodeRef(); /** * Get the score for this row in the result set * - * @return + * @return Returns the score for this row in the resultset */ public float getScore(); // Score is score + rank + potentially other // stuff @@ -75,26 +77,25 @@ public interface ResultSetRow /** * Get the containing result set * - * @return + * @return Returns the containing resultset */ public ResultSet getResultSet(); /** - * Return the QName of the node in the context in which it was found. - * @return + * @return Returns the name of the child association leading down to the + * node represented by this row */ - public QName getQName(); /** * Get the position of this row in the containing set. - * @return + * + * @return Returns the position of this row in the containing resultset */ public int getIndex(); /** - * Return the child assoc ref for this row - * @return + * @return Returns the child assoc ref for this row */ public ChildAssociationRef getChildAssocRef(); diff --git a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java index 1d56d09cda..2cd7811e4c 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java @@ -86,8 +86,8 @@ public interface AuthenticationService * Carry out an authentication attempt. If successful the user is set to the current user. * The current user is a part of the thread context. * - * @param userName - * @param password + * @param userName the username + * @param password the passowrd * @throws AuthenticationException */ public void authenticate(String userName, char[] password) throws AuthenticationException; @@ -99,6 +99,14 @@ public interface AuthenticationService */ public void authenticateAsGuest() throws AuthenticationException; + /** + * Check if the given authentication exists. + * + * @param userName the username + * @return Returns true if the authentication exists + */ + public boolean authenticationExists(String userName); + /** * Get the name of the currently authenticated user. * diff --git a/source/java/org/alfresco/util/TestWithUserUtils.java b/source/java/org/alfresco/util/TestWithUserUtils.java index 89d973a8be..e8b5bd8eb2 100644 --- a/source/java/org/alfresco/util/TestWithUserUtils.java +++ b/source/java/org/alfresco/util/TestWithUserUtils.java @@ -48,7 +48,13 @@ public abstract class TestWithUserUtils extends BaseSpringTest NodeRef rootNodeRef, NodeService nodeService, AuthenticationService authenticationService) - { + { + // ignore if the user's authentication already exists + if (authenticationService.authenticationExists(userName)) + { + // ignore + return; + } QName children = ContentModel.ASSOC_CHILDREN; QName system = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system"); QName container = ContentModel.TYPE_CONTAINER; @@ -59,7 +65,7 @@ public abstract class TestWithUserUtils extends BaseSpringTest HashMap properties = new HashMap(); properties.put(ContentModel.PROP_USERNAME, userName); - NodeRef goodUserPerson = nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, properties).getChildRef(); + nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, properties); // Create the users