Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 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.replication; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.transaction.UserTransaction; import junit.framework.TestCase; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionImpl; import org.alfresco.repo.action.RuntimeActionService; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.model.Repository; import org.alfresco.repo.replication.script.ScriptReplicationDefinition; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transfer.TransferServiceImpl2; import org.alfresco.repo.transfer.TransferTransmitter; import org.alfresco.repo.transfer.UnitTestInProcessTransmitterImpl; import org.alfresco.repo.transfer.UnitTestTransferManifestNodeFactory; import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionStatus; import org.alfresco.service.cmr.action.ActionTrackingService; import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedActionService; import org.alfresco.service.cmr.action.scheduled.SchedulableAction.IntervalPeriod; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.UnableToReleaseLockException; import org.alfresco.service.cmr.replication.DisabledReplicationJobException; import org.alfresco.service.cmr.replication.ReplicationDefinition; import org.alfresco.service.cmr.replication.ReplicationService; import org.alfresco.service.cmr.replication.ReplicationServiceException; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.transfer.TransferDefinition; import org.alfresco.service.cmr.transfer.TransferException; import org.alfresco.service.cmr.transfer.TransferReceiver; import org.alfresco.service.cmr.transfer.TransferService2; import org.alfresco.service.cmr.transfer.TransferTarget; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.experimental.categories.Category; import org.springframework.context.ConfigurableApplicationContext; /** * Unit tests for the Replication Service. * Handles its own transactions, as in a few cases it needs * to run async actions and know how they'll behave * @author Nick Burch */ @Category(OwnJVMTestsCategory.class) public class ReplicationServiceIntegrationTest extends TestCase { private static ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper .getApplicationContext(); private static final Log log = LogFactory.getLog(ReplicationServiceIntegrationTest.class); private ReplicationActionExecutor replicationActionExecutor; private ReplicationService replicationService; private ReplicationParams replicationParams; private TransactionService transactionService; private TransferService2 transferService; private ContentService contentService; private JobLockService jobLockService; private ScriptService scriptService; private ActionService actionService; private NodeService nodeService; private LockService lockService; private Repository repositoryHelper; private ActionTrackingService actionTrackingService; private ScheduledPersistedActionService scheduledPersistedActionService; private NodeRef replicationRoot; private NodeRef destinationFolder; private NodeRef folder1; private NodeRef folder2; private NodeRef folder2a; private NodeRef folder2b; private NodeRef content1_1; private NodeRef content1_2; private NodeRef thumbnail1_3; // Thumbnail extends content private NodeRef authority1_4; // Authority doesn't private NodeRef content2a_1; private NodeRef thumbnail2a_2; // Thumbnail extends content private NodeRef zone2a_3; // Zone doesn't private NodeRef deletedFolder; private final String ACTION_NAME = "testName"; private final String ACTION_NAME2 = "testName2"; private final String ACTION_NAME3 = "testName3"; private final QName ACTION_QNAME = QName.createQName(null, ACTION_NAME); private final QName ACTION_QNAME2 = QName.createQName(null, ACTION_NAME2); private final String TRANSFER_TARGET = "TestTransferTarget"; @Override protected void setUp() throws Exception { if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE) { fail("Dangling transaction detected, left by a previous test."); } replicationActionExecutor = (ReplicationActionExecutor) ctx.getBean("replicationActionExecutor"); replicationService = (ReplicationService) ctx.getBean("replicationService"); replicationParams = (ReplicationParams) ctx.getBean("replicationParams"); transactionService = (TransactionService) ctx.getBean("transactionService"); transferService = (TransferService2) ctx.getBean("transferService2"); contentService = (ContentService) ctx.getBean("contentService"); jobLockService = (JobLockService) ctx.getBean("jobLockService"); actionService = (ActionService) ctx.getBean("actionService"); scriptService = (ScriptService) ctx.getBean("scriptService"); nodeService = (NodeService) ctx.getBean("NodeService"); lockService = (LockService) ctx.getBean("lockService"); repositoryHelper = (Repository) ctx.getBean("repositoryHelper"); actionTrackingService = (ActionTrackingService) ctx.getBean("actionTrackingService"); scheduledPersistedActionService = (ScheduledPersistedActionService) ctx .getBean("scheduledPersistedActionService"); // Set the current security context as admin AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); replicationParams.setEnabled(true); UserTransaction txn = transactionService.getUserTransaction(); txn.begin(); // Zap any existing replication entries replicationRoot = ReplicationDefinitionPersisterImpl.REPLICATION_ACTION_ROOT_NODE_REF; for (ChildAssociationRef child : nodeService.getChildAssocs(replicationRoot)) { QName type = nodeService.getType(child.getChildRef()); if (ReplicationDefinitionPersisterImpl.ACTION_TYPES.contains(type)) { nodeService.deleteNode(child.getChildRef()); } } // Create the test folder structure destinationFolder = makeNode(repositoryHelper.getCompanyHome(), ContentModel.TYPE_FOLDER, "ReplicationTransferDestination"); folder1 = makeNode(repositoryHelper.getCompanyHome(), ContentModel.TYPE_FOLDER); folder2 = makeNode(repositoryHelper.getCompanyHome(), ContentModel.TYPE_FOLDER); folder2a = makeNode(folder2, ContentModel.TYPE_FOLDER); folder2b = makeNode(folder2, ContentModel.TYPE_FOLDER); content1_1 = makeNode(folder1, ContentModel.TYPE_CONTENT); content1_2 = makeNode(folder1, ContentModel.TYPE_CONTENT); thumbnail1_3 = makeNode(folder1, ContentModel.TYPE_THUMBNAIL); authority1_4 = makeNode(folder1, ContentModel.TYPE_AUTHORITY); content2a_1 = makeNode(folder2a, ContentModel.TYPE_CONTENT); thumbnail2a_2 = makeNode(folder2a, ContentModel.TYPE_THUMBNAIL); zone2a_3 = makeNode(folder2a, ContentModel.TYPE_ZONE); deletedFolder = makeNode(repositoryHelper.getCompanyHome(), ContentModel.TYPE_FOLDER); nodeService.deleteNode(deletedFolder); // Tell the transfer service not to use HTTP makeTransferServiceLocal(); // Finish setup txn.commit(); } @Override protected void tearDown() throws Exception { RetryingTransactionCallback<Void> cleanupCallback = new RetryingTransactionCallback<Void>() { @Override public Void execute() throws Throwable { // Zap our test folders if (folder1 != null) { nodeService.deleteNode(folder1); } if (folder2 != null) { nodeService.deleteNode(folder2); } // Zap the destination folder, which may well contain entries transfered over which are locked if (destinationFolder != null) { lockService.unlock(destinationFolder, true); nodeService.deleteNode(destinationFolder); } return null; } }; transactionService.getRetryingTransactionHelper().doInTransaction(cleanupCallback); RetryingTransactionCallback<Void> cleanupTargetCallback = new RetryingTransactionCallback<Void>() { @Override public Void execute() throws Throwable { // Zap our test transfer target transferService.deleteTransferTarget(TRANSFER_TARGET); return null; } }; try { transactionService.getRetryingTransactionHelper().doInTransaction(cleanupTargetCallback); } catch (TransferException e) { // Ignore } if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE) { fail("Dangling transaction detected, current test failed to tidy up."); } } public void testCreation() throws Exception { ReplicationDefinition replicationAction = replicationService.createReplicationDefinition(ACTION_NAME, "Test Definition"); assertNotNull(replicationAction); assertEquals("Test Definition", replicationAction.getDescription()); assertEquals(ACTION_NAME, replicationAction.getReplicationName()); assertEquals(ACTION_QNAME, replicationAction.getReplicationQName()); String id = replicationAction.getId(); assertNotNull(id); assertTrue(id.length() > 0); assertNotNull(replicationAction.getPayload()); assertEquals(0, replicationAction.getPayload().size()); assertNull(replicationAction.getLocalTransferReport()); assertNull(replicationAction.getRemoteTransferReport()); } public void testCreateSaveLoad() throws Exception { ReplicationDefinition replicationAction = replicationService.createReplicationDefinition(ACTION_NAME, "Test Definition"); String initialId = replicationAction.getId(); replicationAction.getPayload().add(new NodeRef("workspace://SpacesStore/Testing")); replicationAction.getPayload().add(new NodeRef("workspace://SpacesStore/Testing2")); assertEquals(2, replicationAction.getPayload().size()); replicationService.saveReplicationDefinition(replicationAction); // Load it again, should have the same details still ReplicationDefinition retrieved = replicationService.loadReplicationDefinition(ACTION_NAME); assertNotNull(retrieved); assertEquals(initialId, retrieved.getId()); assertEquals(ACTION_NAME, retrieved.getReplicationName()); assertEquals(ACTION_QNAME, retrieved.getReplicationQName()); assertEquals("Test Definition", retrieved.getDescription()); assertEquals(2, retrieved.getPayload().size()); // Load a 2nd copy, won't be any changes ReplicationDefinition second = replicationService.loadReplicationDefinition(ACTION_NAME); assertNotNull(second); assertEquals(initialId, second.getId()); assertEquals(ACTION_NAME, second.getReplicationName()); assertEquals(ACTION_QNAME, second.getReplicationQName()); assertEquals("Test Definition", second.getDescription()); assertEquals(2, second.getPayload().size()); } public void testLoadList() throws Exception { assertEquals(0, replicationService.loadReplicationDefinitions().size()); // Create and store ReplicationDefinition rd1 = replicationService.createReplicationDefinition(ACTION_NAME, "Test 1"); ReplicationDefinition rd2 = replicationService.createReplicationDefinition(ACTION_NAME2, "Test 2"); assertEquals(0, replicationService.loadReplicationDefinitions().size()); replicationService.saveReplicationDefinition(rd1); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(ACTION_NAME, replicationService.loadReplicationDefinitions().get(0).getReplicationName()); replicationService.saveReplicationDefinition(rd2); assertEquals(2, replicationService.loadReplicationDefinitions().size()); } public void testLoadByTarget() throws Exception { assertEquals(0, replicationService.loadReplicationDefinitions().size()); assertEquals(0, replicationService.loadReplicationDefinitions("TestTarget").size()); assertEquals(0, replicationService.loadReplicationDefinitions("TestTarget2").size()); // Store some ReplicationDefinition rdTT = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rdTT.setTargetName("TestTarget"); replicationService.saveReplicationDefinition(rdTT); // Check it shows up correctly assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(1, replicationService.loadReplicationDefinitions("TestTarget").size()); assertEquals(0, replicationService.loadReplicationDefinitions("TestTarget2").size()); } /** * Ensures that deletion works correctly */ public void testDeletion() throws Exception { // Delete does nothing if not persisted assertEquals(0, replicationService.loadReplicationDefinitions().size()); ReplicationDefinition rd1 = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); assertEquals(0, replicationService.loadReplicationDefinitions().size()); replicationService.deleteReplicationDefinition(rd1); assertEquals(0, replicationService.loadReplicationDefinitions().size()); // Create and save two ReplicationDefinition rd2 = replicationService.createReplicationDefinition(ACTION_NAME2, "Test2"); replicationService.saveReplicationDefinition(rd1); replicationService.saveReplicationDefinition(rd2); assertEquals(2, replicationService.loadReplicationDefinitions().size()); // Delete one - the correct one goes! replicationService.deleteReplicationDefinition(rd2); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(ACTION_NAME, replicationService.loadReplicationDefinitions().get(0).getReplicationName()); assertNotNull(replicationService.loadReplicationDefinition(ACTION_NAME)); assertNull(replicationService.loadReplicationDefinition(ACTION_NAME2)); // Re-delete already deleted, no change replicationService.deleteReplicationDefinition(rd2); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(ACTION_NAME, replicationService.loadReplicationDefinitions().get(0).getReplicationName()); assertNotNull(replicationService.loadReplicationDefinition(ACTION_NAME)); assertNull(replicationService.loadReplicationDefinition(ACTION_NAME2)); // Delete the 2nd replicationService.deleteReplicationDefinition(rd1); assertEquals(0, replicationService.loadReplicationDefinitions().size()); assertNull(replicationService.loadReplicationDefinition(ACTION_NAME)); assertNull(replicationService.loadReplicationDefinition(ACTION_NAME2)); // Can add back in again after being deleted replicationService.saveReplicationDefinition(rd1); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(ACTION_NAME, replicationService.loadReplicationDefinitions().get(0).getReplicationName()); } /** * Ensures that we can create, save, edit, save * load, edit, save, load etc, all without * problems, and without creating duplicates */ public void testEditing() throws Exception { ReplicationDefinition rdTT = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rdTT.setTargetName("TestTarget"); replicationService.saveReplicationDefinition(rdTT); // Load, and check it hasn't changed rdTT = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(ACTION_NAME, rdTT.getReplicationName()); assertEquals("Test", rdTT.getDescription()); assertEquals("TestTarget", rdTT.getTargetName()); assertEquals(true, rdTT.isEnabled()); assertEquals(0, rdTT.getPayload().size()); // Save and re-load without changes replicationService.saveReplicationDefinition(rdTT); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(ACTION_NAME, rdTT.getReplicationName()); assertEquals("Test", rdTT.getDescription()); assertEquals("TestTarget", rdTT.getTargetName()); assertEquals(true, rdTT.isEnabled()); assertEquals(0, rdTT.getPayload().size()); // Make some small changes rdTT.setDescription("Test Description"); rdTT.getPayload().add(folder2a); rdTT.setEnabled(false); // Check we see them on save/load replicationService.saveReplicationDefinition(rdTT); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(ACTION_NAME, rdTT.getReplicationName()); assertEquals("Test Description", rdTT.getDescription()); assertEquals("TestTarget", rdTT.getTargetName()); assertEquals(false, rdTT.isEnabled()); assertEquals(1, rdTT.getPayload().size()); assertEquals(folder2a, rdTT.getPayload().get(0)); // And some more changes rdTT.setDescription("Another One"); rdTT.getPayload().clear(); rdTT.getPayload().add(folder1); rdTT.getPayload().add(folder2b); assertEquals(2, rdTT.getPayload().size()); // Ensure these also come with save/load replicationService.saveReplicationDefinition(rdTT); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(ACTION_NAME, rdTT.getReplicationName()); assertEquals("Another One", rdTT.getDescription()); assertEquals("TestTarget", rdTT.getTargetName()); assertEquals(false, rdTT.isEnabled()); assertEquals(2, rdTT.getPayload().size()); assertEquals(folder1, rdTT.getPayload().get(0)); assertEquals(folder2b, rdTT.getPayload().get(1)); // And more payload changes rdTT.getPayload().clear(); rdTT.getPayload().add(content1_1); assertEquals(1, rdTT.getPayload().size()); rdTT.setEnabled(true); replicationService.saveReplicationDefinition(rdTT); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(ACTION_NAME, rdTT.getReplicationName()); assertEquals("Another One", rdTT.getDescription()); assertEquals("TestTarget", rdTT.getTargetName()); assertEquals(true, rdTT.isEnabled()); assertEquals(1, rdTT.getPayload().size()); assertEquals(content1_1, rdTT.getPayload().get(0)); } /** * Tests that we can rename definitions */ public void testRenaming() throws Exception { // Create one instance ReplicationDefinition rdTT = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rdTT.setTargetName("TestTarget"); replicationService.saveReplicationDefinition(rdTT); assertEquals(1, replicationService.loadReplicationDefinitions().size()); // Rename it replicationService.renameReplicationDefinition(ACTION_NAME, ACTION_NAME2); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(null, replicationService.loadReplicationDefinition(ACTION_NAME)); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME2); assertNotNull(rdTT); assertEquals(ACTION_NAME2, rdTT.getReplicationName()); assertEquals(ACTION_QNAME2, rdTT.getReplicationQName()); // If the source name doesn't exist, does nothing replicationService.renameReplicationDefinition(ACTION_NAME, ACTION_NAME2); assertEquals(1, replicationService.loadReplicationDefinitions().size()); assertEquals(null, replicationService.loadReplicationDefinition(ACTION_NAME)); rdTT = replicationService.loadReplicationDefinition(ACTION_NAME2); assertNotNull(rdTT); assertEquals(ACTION_NAME2, rdTT.getReplicationName()); assertEquals(ACTION_QNAME2, rdTT.getReplicationQName()); // Renaming to a duplicate name breaks rdTT = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rdTT.setTargetName("TestTarget"); replicationService.saveReplicationDefinition(rdTT); assertEquals(2, replicationService.loadReplicationDefinitions().size()); try { replicationService.renameReplicationDefinition(ACTION_NAME, ACTION_NAME2); fail("Shouldn't be able to rename onto a duplicate name"); } catch (ReplicationServiceException e) { } } /** * Test that the action service can find the executor * for us, and that it has everything it needs */ public void testBasicExecution() throws Exception { // We need the test transfer target for this test makeTransferTarget(); // Ensure the destination is empty // (don't want to get confused with older runs) assertEquals(0, nodeService.getChildAssocs(destinationFolder).size()); // First one with no target, which isn't allowed ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); UserTransaction txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); fail("Shouldn't be permitted with no Target defined"); } catch (ReplicationServiceException e) { } txn.rollback(); // Now no payload, also not allowed rd.setTargetName(TRANSFER_TARGET); txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); fail("Shouldn't be permitted with no payload defined"); } catch (ReplicationServiceException e) { } txn.rollback(); // Invalid Transfer Target, not allowed rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName("I am an invalid target that isn't there"); rd.getPayload().add(folder1); txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); fail("Shouldn't be permitted with an invalid transfer target"); } catch (ReplicationServiceException e) { } txn.rollback(); // Can't send Folder2a if Folder2 isn't there, as it // won't have anywhere to put it rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName(TRANSFER_TARGET); rd.getPayload().add(folder2a); txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); fail("Shouldn't be able to send Folder2a when Folder2 is missing!"); } catch (ReplicationServiceException e) { } txn.rollback(); // Next a proper one with a transient definition, // and a sensible set of folders rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName(TRANSFER_TARGET); rd.getPayload().add(folder1); // A deleted folder is fine, will be skipped rd.getPayload().add(deletedFolder); // Will execute without error txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); } catch (ReplicationServiceException e) { // This shouldn't happen normally! Something is wrong! // Tidy up before we throw the exception txn.rollback(); throw e; } txn.commit(); // Now with one that's in the repo ReplicationDefinition rd2 = replicationService.createReplicationDefinition(ACTION_NAME2, "Test"); rd2.setTargetName(TRANSFER_TARGET); rd2.getPayload().add(folder2); replicationService.saveReplicationDefinition(rd2); rd2 = replicationService.loadReplicationDefinition(ACTION_NAME2); // Again no errors txn = transactionService.getUserTransaction(); txn.begin(); actionService.executeAction(rd2, replicationRoot); txn.commit(); // Now disabled, not allowed assertEquals(true, rd.isEnabled()); rd.setEnabled(false); assertEquals(false, rd.isEnabled()); txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); fail("Shouldn't be permitted when disabled"); } catch (ReplicationServiceException e) { //check if throwed exception is of expected type assertTrue(e instanceof DisabledReplicationJobException); assertTrue(actionService instanceof RuntimeActionService); if (actionService instanceof RuntimeActionService) { RuntimeActionService runtimeActionService = (RuntimeActionService) actionService; //check if throwed exception is considered handled assertTrue(runtimeActionService.onLogException(rd, log, e, e.getMessage())); } } txn.rollback(); rd.setEnabled(true); // Schedule it for 0.5 seconds into the future // Ensure that it is run to completion txn = transactionService.getUserTransaction(); txn.begin(); ((ActionImpl) rd2).setExecutionStatus(ActionStatus.New); replicationService.enableScheduling(rd2); rd2.setScheduleStart(new Date(System.currentTimeMillis() + 500)); replicationService.saveReplicationDefinition(rd2); txn.commit(); // Wait for it to run Thread.sleep(2000); for (int i = 0; i < 100; i++) { txn = transactionService.getUserTransaction(); txn.begin(); rd2 = replicationService.loadReplicationDefinition(ACTION_NAME2); txn.commit(); if (rd2.getExecutionStatus().equals(ActionStatus.New) || rd2.getExecutionStatus().equals(ActionStatus.Pending) || rd2.getExecutionStatus().equals(ActionStatus.Running)) { Thread.sleep(50); } } // Check it worked assertEquals(ActionStatus.Completed, rd2.getExecutionStatus()); } /** * Check that the locking works. * Take a 10 second lock on the job, then execute. * Ensure that we really wait a little over 10 seconds. */ public void testReplicationExecutionLocking() throws Exception { // We need the test transfer target for this test makeTransferTarget(); // Create a task ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName(TRANSFER_TARGET); rd.getPayload().add(folder1); rd.getPayload().add(folder2); // Get the lock, and run long start = System.currentTimeMillis(); String token = jobLockService.getLock(rd.getReplicationQName(), 10 * 1000, 1, 1); UserTransaction txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); } catch (ReplicationServiceException e) { // This shouldn't happen normally! Something is wrong! // Tidy up before we throw the exception txn.rollback(); throw e; } txn.commit(); long end = System.currentTimeMillis(); assertTrue("Should wait for the lock, but didn't (waited " + ((end - start) / 1000.0) + " seconds, not 10)", end - start > 10000); } /** * Check that cancelling works. * Does this by taking a lock on the job, cancelling, * releasing and seeing it abort. * * Tests that when we ask for a replication task to be cancelled, * that it starts, cancels, and the status is correctly recorded * for it. */ public void testReplicationExecutionCancelling() throws Exception { // We need the test transfer target for this test makeTransferTarget(); // Create a task ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName(TRANSFER_TARGET); rd.getPayload().add(folder1); rd.getPayload().add(folder2); // Get the lock for 2 seconds String token = jobLockService.getLock(rd.getReplicationQName(), 2 * 1000, 1, 1); // Request it be run async UserTransaction txn = transactionService.getUserTransaction(); txn.begin(); actionService.executeAction(rd, replicationRoot, false, true); assertEquals(ActionStatus.Pending, rd.getExecutionStatus()); assertEquals(false, actionTrackingService.isCancellationRequested(rd)); actionTrackingService.requestActionCancellation(rd); assertEquals(true, actionTrackingService.isCancellationRequested(rd)); txn.commit(); // Let it get going, will be waiting for the lock // having registered with the action tracking service for (int i = 0; i < 100; i++) { // Keep asking for it to be cancelled ASAP actionTrackingService.requestActionCancellation(rd); if (rd.getExecutionStatus().equals(ActionStatus.Running)) { // Good, has started up // Stop waiting and do the cancel break; } else { // Still pending, wait a bit more Thread.sleep(10); } } // Ensure it started, and should shortly stop assertEquals(ActionStatus.Running, rd.getExecutionStatus()); assertEquals(true, actionTrackingService.isCancellationRequested(rd)); // Release our lock, should allow the replication task // to get going and spot the cancel jobLockService.releaseLock(token, rd.getReplicationQName()); // Let the main replication task run to cancelled/completed // This can take quite some time though... for (int i = 0; i < 10; i++) { if (rd.getExecutionStatus() == ActionStatus.Running) { Thread.sleep(1000); } else { // It has finished running, check it break; } } // Ensure it was cancelled assertEquals(null, rd.getExecutionFailureMessage()); assertNotNull(rd.getLocalTransferReport()); assertNotNull(rd.getRemoteTransferReport()); assertEquals(ActionStatus.Cancelled, rd.getExecutionStatus()); } /** * Test that when we execute a replication task, the * right stuff ends up being moved for us */ public void testExecutionResult() throws Exception { UserTransaction txn = transactionService.getUserTransaction(); txn.begin(); // Destination is empty assertEquals(0, nodeService.getChildAssocs(destinationFolder).size()); // We need the test transfer target for this test makeTransferTarget(); // Put in Folder 2, so we can send Folder 2a String folder2Name = (String) nodeService.getProperties(folder2).get(ContentModel.PROP_NAME); NodeRef folderT2 = makeNode(destinationFolder, ContentModel.TYPE_FOLDER, folder2Name); txn.commit(); // Run a transfer ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName(TRANSFER_TARGET); rd.getPayload().add(folder1); rd.getPayload().add(folder2a); assertEquals(null, rd.getLocalTransferReport()); assertEquals(null, rd.getRemoteTransferReport()); txn = transactionService.getUserTransaction(); txn.begin(); try { actionService.executeAction(rd, replicationRoot); } catch (ReplicationServiceException e) { // This shouldn't happen normally! Something is wrong! // Tidy up before we throw the exception txn.rollback(); throw e; } txn.commit(); // Correct things have turned up assertEquals(2, nodeService.getChildAssocs(destinationFolder).size()); NodeRef c1 = nodeService.getChildAssocs(destinationFolder).get(0).getChildRef(); NodeRef c2 = nodeService.getChildAssocs(destinationFolder).get(1).getChildRef(); // The destination should have folder 1 (transfered) // and folder 2 (created). folder 2 will have // folder 2a (transfered) but not 2b NodeRef folderT1 = null; boolean foundT1 = false; boolean foundT2 = false; if (nodeService.getProperty(folder1, ContentModel.PROP_NAME) .equals(nodeService.getProperty(c1, ContentModel.PROP_NAME))) { folderT1 = c1; foundT1 = true; } if (nodeService.getProperty(folder1, ContentModel.PROP_NAME) .equals(nodeService.getProperty(c2, ContentModel.PROP_NAME))) { folderT1 = c2; foundT1 = true; } if (c1.equals(folderT2) || c2.equals(folderT2)) { foundT2 = true; } if (!foundT1) { fail("Folder 1 not found in the destination"); } if (!foundT2) { fail("Folder 2 not found in the destination"); } // Folder 1 has 2*content + thumbnail assertEquals(3, nodeService.getChildAssocs(folderT1).size()); // Won't have the authority, as that gets skipped for (ChildAssociationRef r : nodeService.getChildAssocs(folderT1)) { if (nodeService.getType(r.getChildRef()).equals(ContentModel.TYPE_AUTHORITY)) { fail("Found authority as " + r.getChildRef() + " but it shouldn't be transfered!"); } } // Folder 2 has 2a but not 2b, since only // 2a was transfered assertEquals(1, nodeService.getChildAssocs(folderT2).size()); NodeRef folderT2a = nodeService.getChildAssocs(folderT2).get(0).getChildRef(); assertEquals(nodeService.getProperty(folder2a, ContentModel.PROP_NAME), nodeService.getProperty(folderT2a, ContentModel.PROP_NAME)); // Won't have Folder 2b, as it wasn't on the payload for (ChildAssociationRef r : nodeService.getChildAssocs(folderT2)) { assertNotSame(nodeService.getProperty(folder2b, ContentModel.PROP_NAME), nodeService.getProperty(r.getChildRef(), ContentModel.PROP_NAME)); } // Folder 2a has content + thumbnail assertEquals(2, nodeService.getChildAssocs(folderT2a).size()); // Won't have the zone, as that gets skipped for (ChildAssociationRef r : nodeService.getChildAssocs(folderT2a)) { if (nodeService.getType(r.getChildRef()).equals(ContentModel.TYPE_ZONE)) { fail("Found zone as " + r.getChildRef() + " but it shouldn't be transfered!"); } } // Check we got transfer reports, and they look sensible NodeRef localReport = rd.getLocalTransferReport(); assertNotNull(localReport); NodeRef remoteReport = rd.getRemoteTransferReport(); assertNotNull(remoteReport); txn = transactionService.getUserTransaction(); txn.begin(); ContentReader localReader = contentService.getReader(localReport, ContentModel.PROP_CONTENT); String localReportContent = localReader.getContentString(); assertTrue("XML not found in:\n" + localReportContent, localReportContent.contains("<?xml")); assertTrue("Report XML not found in:\n" + localReportContent, localReportContent.contains("<report:transferReport")); ContentReader remoteReader = contentService.getReader(remoteReport, ContentModel.PROP_CONTENT); String remoteReportContent = remoteReader.getContentString(); assertTrue("XML not found in:\n" + remoteReportContent, remoteReportContent.contains("<?xml")); assertTrue("Report Status not found in:\n" + remoteReportContent, remoteReportContent.contains("state=\"COMPLETE\"")); txn.commit(); } /** * Test that we turn a list of payload node starting points * into the correct set of nodes to pass to the * transfer service. */ public void testReplicationPayloadExpansion() throws Exception { ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); Set<NodeRef> expanded; // Empty folder -> just itself rd.getPayload().clear(); rd.getPayload().add(folder2b); expanded = replicationActionExecutor.expandPayload(rd); assertEquals(1, expanded.size()); assertTrue(expanded.contains(folder2b)); // Folder with content and thumbnails - just content + thumbnail + folder rd.getPayload().clear(); rd.getPayload().add(folder1); expanded = replicationActionExecutor.expandPayload(rd); assertEquals(4, expanded.size()); assertTrue(expanded.contains(folder1)); assertTrue(expanded.contains(content1_1)); assertTrue(expanded.contains(content1_2)); assertTrue(expanded.contains(thumbnail1_3)); assertFalse(expanded.contains(authority1_4)); // Wrong type, won't be there // Folder with folders - descends properly rd.getPayload().clear(); rd.getPayload().add(folder2); expanded = replicationActionExecutor.expandPayload(rd); assertEquals(5, expanded.size()); assertTrue(expanded.contains(folder2)); assertTrue(expanded.contains(folder2a)); assertTrue(expanded.contains(content2a_1)); assertTrue(expanded.contains(thumbnail2a_2)); assertFalse(expanded.contains(zone2a_3)); // Wrong type, won't be there assertTrue(expanded.contains(folder2b)); // Multiple things - gets each in turn rd.getPayload().clear(); rd.getPayload().add(folder1); rd.getPayload().add(folder2); expanded = replicationActionExecutor.expandPayload(rd); assertEquals(9, expanded.size()); assertTrue(expanded.contains(folder1)); assertTrue(expanded.contains(content1_1)); assertTrue(expanded.contains(content1_2)); assertTrue(expanded.contains(thumbnail1_3)); assertTrue(expanded.contains(folder2)); assertTrue(expanded.contains(folder2a)); assertTrue(expanded.contains(content2a_1)); assertTrue(expanded.contains(thumbnail2a_2)); assertTrue(expanded.contains(folder2b)); // TODO Test how options like permissions and renditions // affects what gets sent back } /** * Test that we turn a replication definition correctly * into a transfer definition */ public void testTransferDefinitionBuilding() throws Exception { ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); Set<NodeRef> nodes = new HashSet<NodeRef>(); nodes.add(folder1); nodes.add(content1_1); TransferDefinition td = replicationActionExecutor.buildTransferDefinition(rd, nodes); assertEquals(true, td.isSync()); assertEquals(replicationParams.getTransferReadOnly(), td.isReadOnly()); assertEquals(2, td.getNodes().size()); assertEquals(true, td.getNodes().contains(folder1)); assertEquals(true, td.getNodes().contains(content1_1)); } private abstract class DoInTransaction implements RetryingTransactionCallback<Void> { protected final ReplicationDefinition replicationDefinition; private DoInTransaction(ReplicationDefinition rd) { this.replicationDefinition = rd; } } /** * Test that the schedule related parts work properly */ public void testScheduling() throws Exception { // A new definition doesn't have scheduling ReplicationDefinition rd = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); rd.setTargetName("Target"); assertFalse(rd.isSchedulingEnabled()); // Disable does nothing replicationService.disableScheduling(rd); assertFalse(rd.isSchedulingEnabled()); // Enable it transactionService.getRetryingTransactionHelper().doInTransaction(new DoInTransaction(rd) { public Void execute() throws Throwable { replicationService.saveReplicationDefinition(replicationDefinition); replicationService.enableScheduling(replicationDefinition); assertTrue(replicationDefinition.isSchedulingEnabled()); return null; } }, false, true); assertTrue(rd.isSchedulingEnabled()); // Double enabling does nothing replicationService.enableScheduling(rd); assertTrue(rd.isSchedulingEnabled()); // Change it assertNull(rd.getScheduleStart()); assertNull(rd.getScheduleIntervalCount()); assertNull(rd.getScheduleIntervalPeriod()); rd.setScheduleStart(new Date(1)); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(null, rd.getScheduleIntervalCount()); assertEquals(null, rd.getScheduleIntervalPeriod()); // Won't show up until saved ReplicationDefinition rd2 = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(false, rd2.isSchedulingEnabled()); assertEquals(null, rd2.getScheduleStart()); assertEquals(null, rd2.getScheduleIntervalCount()); assertEquals(null, rd2.getScheduleIntervalPeriod()); // Save and check assertEquals(true, rd.isSchedulingEnabled()); transactionService.getRetryingTransactionHelper().doInTransaction(new DoInTransaction(rd) { public Void execute() throws Throwable { replicationService.saveReplicationDefinition(replicationDefinition); return null; } }, false, true); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(null, rd.getScheduleIntervalCount()); assertEquals(null, rd.getScheduleIntervalPeriod()); rd = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(null, rd.getScheduleIntervalCount()); assertEquals(null, rd.getScheduleIntervalPeriod()); // Change, save, check rd.setScheduleIntervalCount(2); rd.setScheduleIntervalPeriod(IntervalPeriod.Hour); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(2, rd.getScheduleIntervalCount().intValue()); assertEquals(IntervalPeriod.Hour, rd.getScheduleIntervalPeriod()); transactionService.getRetryingTransactionHelper().doInTransaction(new DoInTransaction(rd) { public Void execute() throws Throwable { replicationService.saveReplicationDefinition(replicationDefinition); return null; } }, false, true); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(2, rd.getScheduleIntervalCount().intValue()); assertEquals(IntervalPeriod.Hour, rd.getScheduleIntervalPeriod()); rd = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(2, rd.getScheduleIntervalCount().intValue()); assertEquals(IntervalPeriod.Hour, rd.getScheduleIntervalPeriod()); // Re-load and enable is fine rd2 = replicationService.loadReplicationDefinition(ACTION_NAME); assertEquals(true, rd2.isSchedulingEnabled()); replicationService.enableScheduling(rd2); assertEquals(true, rd2.isSchedulingEnabled()); // Check on the listing methods assertEquals(1, replicationService.loadReplicationDefinitions().size()); rd = replicationService.loadReplicationDefinitions().get(0); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(2, rd.getScheduleIntervalCount().intValue()); assertEquals(IntervalPeriod.Hour, rd.getScheduleIntervalPeriod()); assertEquals(1, replicationService.loadReplicationDefinitions("Target").size()); rd = replicationService.loadReplicationDefinitions("Target").get(0); assertEquals(true, rd.isSchedulingEnabled()); assertEquals(1, rd.getScheduleStart().getTime()); assertEquals(2, rd.getScheduleIntervalCount().intValue()); assertEquals(IntervalPeriod.Hour, rd.getScheduleIntervalPeriod()); // Disable it transactionService.getRetryingTransactionHelper().doInTransaction(new DoInTransaction(rd) { public Void execute() throws Throwable { replicationService.disableScheduling(replicationDefinition); return null; } }); assertEquals(false, rd.isSchedulingEnabled()); // Check listings again rd = replicationService.loadReplicationDefinitions().get(0); assertEquals(false, rd.isSchedulingEnabled()); rd = replicationService.loadReplicationDefinitions("Target").get(0); assertEquals(false, rd.isSchedulingEnabled()); // Enable it, and check the scheduled service final int count = scheduledPersistedActionService.listSchedules().size(); transactionService.getRetryingTransactionHelper().doInTransaction(new DoInTransaction(rd) { public Void execute() throws Throwable { replicationService.enableScheduling(replicationDefinition); replicationService.saveReplicationDefinition(replicationDefinition); assertEquals(count + 1, scheduledPersistedActionService.listSchedules().size()); return null; } }, false, true); // Delete it, and check the scheduled service transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() { public Void execute() throws Throwable { ReplicationDefinition replicationDefinition; replicationDefinition = replicationService.loadReplicationDefinition(ACTION_NAME); replicationService.deleteReplicationDefinition(replicationDefinition); assertEquals(count, scheduledPersistedActionService.listSchedules().size()); return null; } }, false, true); assertEquals(count, scheduledPersistedActionService.listSchedules().size()); // Ask for it to run scheduled // Should fire up and then fail due to missing definitions transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() { public Void execute() throws Throwable { ReplicationDefinition replicationDefinition; replicationDefinition = replicationService.createReplicationDefinition(ACTION_NAME, "Test"); replicationService.enableScheduling(replicationDefinition); replicationDefinition.setScheduleStart(new Date(System.currentTimeMillis() + 50)); replicationService.saveReplicationDefinition(replicationDefinition); assertEquals(ActionStatus.New, replicationDefinition.getExecutionStatus()); return null; } }, false, true); // Let it fire up, wait up to 1.5 seconds for (int i = 0; i < 150; i++) { rd = replicationService.loadReplicationDefinition(ACTION_NAME); if (rd.getExecutionStatus().equals(ActionStatus.Failed)) break; if (rd.getExecutionStatus().equals(ActionStatus.Completed)) break; Thread.sleep(10); } // Should have failed, as missing target + payload assertEquals(ActionStatus.Failed, rd.getExecutionStatus()); } public void testJavascriptAPI() throws Exception { ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); // Setup some replication tasks ReplicationDefinition empty = replicationService.createReplicationDefinition(ACTION_NAME, "Empty"); ReplicationDefinition persisted = replicationService.createReplicationDefinition(ACTION_NAME2, "Persisted"); persisted.setTargetName(TRANSFER_TARGET); persisted.getPayload().add(new NodeRef("workspace://SpacesStore/Testing")); persisted.getPayload().add(new NodeRef("workspace://SpacesStore/Testing2")); replicationService.saveReplicationDefinition(persisted); ReplicationDefinition persisted2 = replicationService.createReplicationDefinition(ACTION_NAME3, "Persisted2"); persisted2.setTargetName("AnotherTarget"); replicationService.saveReplicationDefinition(persisted2); // Call the test Map<String, Object> model = new HashMap<String, Object>(); model.put("Empty", new ScriptReplicationDefinition(serviceRegistry, replicationService, null, empty)); model.put("EmptyName", ACTION_NAME); model.put("Persisted", new ScriptReplicationDefinition(serviceRegistry, replicationService, null, persisted)); model.put("PersistedName", ACTION_NAME2); model.put("PersistedNodeRef", persisted.getNodeRef().toString()); model.put("PersistedTarget", persisted.getTargetName()); model.put("Persisted2", new ScriptReplicationDefinition(serviceRegistry, replicationService, null, persisted2)); model.put("Persisted2Name", ACTION_NAME3); model.put("Persisted2NodeRef", persisted2.getNodeRef().toString()); model.put("Persisted2Target", persisted2.getTargetName()); ScriptLocation location = new ClasspathScriptLocation( "org/alfresco/repo/replication/script/test_replicationService.js"); this.scriptService.executeScript(location, model); } // ============================================= private NodeRef makeNode(NodeRef parent, QName nodeType) { String uuid = GUID.generate(); return makeNode(parent, nodeType, uuid); } private NodeRef makeNode(NodeRef parent, QName nodeType, String name) { Map<QName, Serializable> props = new HashMap<QName, Serializable>(); QName newName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, name); NodeRef existing = nodeService.getChildByName(parent, ContentModel.ASSOC_CONTAINS, name); if (existing != null) { System.err.println("Zapped existing node " + existing + " for name " + name); try { lockService.unlock(existing, true); } catch (UnableToReleaseLockException e) { } nodeService.deleteNode(existing); } props.put(ContentModel.PROP_NAME, name); ChildAssociationRef assoc = nodeService.createNode(parent, ContentModel.ASSOC_CONTAINS, newName, nodeType, props); return assoc.getChildRef(); } private void makeTransferTarget() { String name = TRANSFER_TARGET; String title = "title"; String description = "description"; String endpointProtocol = "http"; String endpointHost = "localhost"; int endpointPort = 8080; String endpointPath = "rhubarb"; String username = "admin"; char[] password = "password".toCharArray(); TransferTarget ret = transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password); assertNotNull("Transfer Target not correctly built", ret); } private void makeTransferServiceLocal() { TransferReceiver receiver = (TransferReceiver) ctx.getBean("transferReceiver"); TransferManifestNodeFactory transferManifestNodeFactory = (TransferManifestNodeFactory) ctx .getBean("transferManifestNodeFactory"); TransferServiceImpl2 transferServiceImpl = (TransferServiceImpl2) ctx.getBean("transferService2"); ContentService contentService = (ContentService) ctx.getBean("contentService"); TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService); transferServiceImpl.setTransmitter(transmitter); UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory( transferManifestNodeFactory); transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory); // Map company_home to the special destination folder List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap(); pathMap.add(new Pair<Path, Path>(nodeService.getPath(repositoryHelper.getCompanyHome()), nodeService.getPath(destinationFolder))); } }