From 2eeefe0a7248363afff3ea3bcea0a1766dcf8920 Mon Sep 17 00:00:00 2001 From: Jan Vonka Date: Tue, 13 Oct 2009 11:34:23 +0000 Subject: [PATCH] Merged V3.2 to HEAD 15596: Merged V3.1 to V3.2 14017: Fix ETHREEOH-1880 - remove (double-)reverse of WCM staging snapshot order 14112: Fix ETHREEOH-1758 - apply contributed patch 14447: Merged V2.2 to V3.1 14276: ETWOTWO-1224 / WCM-948 - browsing staging area during (commit of) large submit can cause AVMNotFoundException: Path /www/avm_webapps not found. 14452: Add WCM services-based unit test for ETWOTWO-1224 / WCM-948 14589: ETHREEOH-1646 - User Sandboxes aren't visible for Content Publisher 15604: Merged V3.1 to V3.2 14734: Merged V2.2 to V3.1 14718: ETWOTWO-1244 - unable to revert some snapshots ("Does not exist: xxx") 14852: Merged V2.2 to V3.1 14720: ETWOTWO-1183 - "Show All Sandboxes" checkbox should only be visible for "Content Publisher" or "Content Manager" 15032: Fix ETHREEOH-2240 - delete WCM web project (does not clean-up workflow sandboxes and also appears in archive store) 15037: Fix ETHREEOH-2240 - follow-on for Alfresco Explorer's Manage (Review) Task Dialog 15056: Fix ETHREEOH-2297 - WCM layered folder - problem deleting file 15072: Minor - fix remote AVM test 15605: Merged V3.1 to V3.2 15082: Merged V2.2 to V3.1 15081: AVM - add tests only 15083: Fix ETHREEOH-2296 - user conflict when updating an AVM layered file 15118: Merged V2.2 to V3.1 15115: Fix ETWOTWO-1265 - WCM locking not working as expected (+ add WCM services test) 15137: Fix ETHREEOH-2309 and ETHREEOH-227 - including refactor (& clean-up) of WCM-related actions 15156: Fix ETHREEOH-2078 & ETHREEOH-2040 - WCM - deploy to localhost causes "Must have at least one store" errors 15175: Fix ETHREEOH-2309 and ETHREEOH-227 - further clean-up of WCM submit/undo actions (for consistency) 15190: Minor: message updates for WCM submit/undo actions git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@16858 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/action-services-context.xml | 9 + .../alfresco/messages/wcm-services.properties | 17 + config/alfresco/wcm-services-context.xml | 42 +- .../org/alfresco/repo/avm/AVMRepository.java | 4 + .../repo/avm/AVMServiceLocalTest.java | 517 ++++++++++++++- .../repo/avm/AVMServiceRemoteSystemTest.java | 8 +- .../org/alfresco/repo/avm/AVMServiceTest.java | 618 +++++++++++++++++- .../org/alfresco/repo/avm/AVMStoreImpl.java | 57 +- .../alfresco/repo/avm/AVMSyncServiceImpl.java | 48 +- .../avm/actions/AVMDeployWebsiteAction.java | 8 +- .../repo/avm/actions/AVMRevertListAction.java | 28 +- .../avm/actions/AVMRevertStoreAction.java | 27 +- .../avm/actions/AVMRevertToVersionAction.java | 30 +- .../avm/actions/AVMUndoSandboxListAction.java | 28 +- .../avm/actions/SimpleAVMPromoteAction.java | 17 +- .../avm/actions/SimpleAVMSubmitAction.java | 2 + .../avm/actions/StartAVMWorkflowAction.java | 27 +- .../repo/avm/wf/AVMSubmitPackageHandler.java | 135 ++-- .../wcm/AbstractWCMServiceImplTest.java | 16 +- .../org/alfresco/wcm/WCMConcurrentTest.java | 247 +++++++ .../java/org/alfresco/wcm/WCMTestSuite.java | 2 +- .../actions/WCMSandboxRevertListAction.java | 94 +++ .../WCMSandboxRevertSnapshotAction.java | 100 +++ .../wcm/actions/WCMSandboxSubmitAction.java | 101 +++ .../wcm/actions/WCMSandboxUndoAction.java | 101 +++ .../wcm/asset/AssetServiceImplTest.java | 74 ++- .../alfresco/wcm/sandbox/SandboxFactory.java | 67 +- .../wcm/sandbox/SandboxServiceImpl.java | 138 ++-- .../wcm/sandbox/SandboxServiceImplTest.java | 567 +++++++++++++--- .../java/org/alfresco/wcm/util/WCMUtil.java | 24 + .../wcm/webproject/WebProjectServiceImpl.java | 89 ++- .../webproject/WebProjectServiceImplTest.java | 64 +- source/test-resources/wcm/small-61-items.zip | Bin 0 -> 10044 bytes 33 files changed, 2983 insertions(+), 323 deletions(-) create mode 100644 config/alfresco/messages/wcm-services.properties create mode 100644 source/java/org/alfresco/wcm/WCMConcurrentTest.java create mode 100644 source/java/org/alfresco/wcm/actions/WCMSandboxRevertListAction.java create mode 100644 source/java/org/alfresco/wcm/actions/WCMSandboxRevertSnapshotAction.java create mode 100644 source/java/org/alfresco/wcm/actions/WCMSandboxSubmitAction.java create mode 100644 source/java/org/alfresco/wcm/actions/WCMSandboxUndoAction.java create mode 100644 source/test-resources/wcm/small-61-items.zip diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 2e40e81e60..304ee124cf 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -505,6 +505,7 @@ + @@ -520,6 +521,7 @@ + @@ -532,6 +534,7 @@ + @@ -541,6 +544,7 @@ + @@ -550,6 +554,7 @@ + @@ -559,6 +564,7 @@ + @@ -568,6 +574,7 @@ + @@ -580,6 +587,7 @@ + @@ -627,6 +635,7 @@ + diff --git a/config/alfresco/messages/wcm-services.properties b/config/alfresco/messages/wcm-services.properties new file mode 100644 index 0000000000..692d6ae8b4 --- /dev/null +++ b/config/alfresco/messages/wcm-services.properties @@ -0,0 +1,17 @@ +# WCM service related messages + +# WCM actions + +wcm-submit.title=Submit changed assets from a WCM author sandbox to the corresponding staging sandbox +wcm-submit.description=This submits changed assets included in the list or all changed assets (if list is null/empty) +wcm-submit.sandbox-id.display-label=The sandbox store id +wcm-submit.path-list.display-label=The list of asset paths (relative to sandbox store) + +wcm-undo.title=Undo changed assets in a WCM sandbox +wcm-undo.description=This will undo/revert changed assets included in the list or all changed assets (if list is null/empty) +wcm-undo.sandbox-id.display-label=The sandbox store id +wcm-undo.path-list.display-label=The list of asset paths (relative to sandbox store) + +wcm-revert-snapshot.title=Revert (copy forward) a WCM sandbox store to the given snapshot +wcm-revert-snapshot.description=This reverts all of the assets in a WCM sandbox store to the given snapshot version +wcm-revert-snapshot.version.display-label=The snapshot version to revert to diff --git a/config/alfresco/wcm-services-context.xml b/config/alfresco/wcm-services-context.xml index a349a38fb8..b75e49c37e 100644 --- a/config/alfresco/wcm-services-context.xml +++ b/config/alfresco/wcm-services-context.xml @@ -54,6 +54,7 @@ + @@ -198,8 +199,8 @@ - + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + alfresco.messages.wcm-services + + + + diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 401f02e517..359cb5055f 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -2048,6 +2048,10 @@ public class AVMRepository List history = new ArrayList(); for (int i = 0; i < count; i++) { + if (node instanceof LayeredFileNodeImpl) + { + break; + } node = node.getAncestor(); if (node == null) { diff --git a/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java b/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java index 1d1d5ccb33..0806743592 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 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 @@ -23,9 +23,12 @@ package org.alfresco.repo.avm; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,6 +42,7 @@ import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.alfresco.service.cmr.avm.VersionDescriptor; import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.avmsync.AVMSyncException; import org.alfresco.service.cmr.avmsync.AVMSyncService; @@ -982,7 +986,6 @@ public class AVMServiceLocalTest extends TestCase assertEquals("b title", fService.getNodeProperty(-1, "main:/b", ContentModel.PROP_TITLE).getStringValue()); assertEquals("b descrip", fService.getNodeProperty(-1, "main:/b", ContentModel.PROP_DESCRIPTION).getStringValue()); - fService.setNodeProperty("layer:/layer/b", ContentModel.PROP_TITLE, new PropertyValue(DataTypeDefinition.TEXT, "b title2")); fService.setNodeProperty("layer:/layer/b", ContentModel.PROP_DESCRIPTION, new PropertyValue(DataTypeDefinition.TEXT, "b descrip2")); @@ -1010,6 +1013,514 @@ public class AVMServiceLocalTest extends TestCase throw e; } } + + public void testSimpleUpdateLF1() throws Exception + { + try + { + List snapshots = fService.getStoreVersions("main"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + snapshots = fService.getStoreVersions("layer"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + recursiveList("main"); + recursiveList("layer"); + + fService.createDirectory("main:/", "a"); + fService.createDirectory("layer:/", "a"); + + logger.debug("created 2 plain dirs: main:/a, layer:/a"); + + recursiveList("main"); + recursiveList("layer"); + + fService.createFile("main:/a", "foo"); + + assertEquals(1, fService.lookup(-1, "main:/a/foo").getVersionID()); + + PrintStream out = new PrintStream(fService.getFileOutputStream("main:/a/foo")); + out.println("I am main:/a/foo"); + out.close(); + + AVMNodeDescriptor node = fService.lookup(-1, "main:/a/foo"); + assertEquals(1, node.getVersionID()); + List history = fService.getHistory(node, -1); + assertEquals(0, history.size()); + + fService.createSnapshot("main", null, null); + + snapshots = fService.getStoreVersions("main"); + assertEquals(2, snapshots.size()); + assertEquals(1, snapshots.get(snapshots.size()-1).getVersionID()); + + snapshots = fService.getStoreVersions("layer"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + assertEquals(1, fService.lookup(-1, "main:/a/foo").getVersionID()); + assertEquals(1, fService.lookup(1, "main:/a/foo").getVersionID()); + + logger.debug("created plain file: main:/a/foo"); + + recursiveList("main"); + recursiveList("layer"); + + fService.createLayeredFile("main:/a/foo", "layer:/a", "foo"); + + assertEquals(1, fService.lookup(-1, "layer:/a/foo").getVersionID()); + + BufferedReader reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "layer:/a/foo"))); + String line = reader.readLine(); + reader.close(); + assertEquals("I am main:/a/foo", line); + + node = fService.lookup(-1, "layer:/a/foo"); + assertEquals(1, node.getVersionID()); + + history = fService.getHistory(node, -1); + assertEquals(0, history.size()); + + fService.createSnapshot("layer", null, null); + + snapshots = fService.getStoreVersions("main"); + assertEquals(2, snapshots.size()); + assertEquals(1, snapshots.get(snapshots.size()-1).getVersionID()); + + snapshots = fService.getStoreVersions("layer"); + assertEquals(2, snapshots.size()); + assertEquals(1, snapshots.get(snapshots.size()-1).getVersionID()); + + assertEquals(1, fService.lookup(-1, "layer:/a/foo").getVersionID()); + assertEquals(1, fService.lookup(1, "layer:/a/foo").getVersionID()); + + List diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "layer:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/a/foo", line); + + logger.debug("created layered file: layer:/a/foo -> main:/a/foo"); + + recursiveList("main"); + recursiveList("layer"); + + out = new PrintStream(fService.getFileOutputStream("layer:/a/foo")); + out.println("I am layer:/a/foo"); + out.close(); + + logger.debug("modified file: layer:/a/foo"); + + recursiveList("main"); + recursiveList("layer"); + + assertEquals(2, fService.lookup(-1, "layer:/a/foo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/a/foo", line); + + diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(1, diffs.size()); + + // TODO - review behaviour + assertEquals("[layer:/a/foo[-1] > main:/a/foo[-1]]", diffs.toString()); + fSyncService.update(diffs, null, false, false, false, false, "one", "one"); + + // update will implicitly snapshot (src and dst) + snapshots = fService.getStoreVersions("main"); + assertEquals(3, snapshots.size()); + assertEquals(2, snapshots.get(snapshots.size()-1).getVersionID()); + + snapshots = fService.getStoreVersions("layer"); + assertEquals(3, snapshots.size()); + assertEquals(2, snapshots.get(snapshots.size()-1).getVersionID()); + + node = fService.lookup(-1, "layer:/a/foo"); + assertEquals(2, node.getVersionID()); + history = fService.getHistory(node, -1); + + assertEquals(1, history.size()); + assertEquals(1, history.get(0).getVersionID()); + + assertEquals(1, fService.lookup(1, "layer:/a/foo").getVersionID()); + assertEquals(2, fService.lookup(2, "layer:/a/foo").getVersionID()); + + logger.debug("submitted/updated file: layer:/a/foo -> main:/a/foo"); + + recursiveList("main"); + recursiveList("layer"); + + fSyncService.flatten("layer:/a", "main:/a"); + + logger.debug("flatten dir: layer:/a -> main:/a"); + + recursiveList("main"); + recursiveList("layer"); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "layer:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am layer:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am layer:/a/foo", line); + + snapshots = fService.getStoreVersions("main"); + assertEquals(3, snapshots.size()); + assertEquals(2, snapshots.get(snapshots.size()-1).getVersionID()); + + snapshots = fService.getStoreVersions("layer"); + assertEquals(3, snapshots.size()); + assertEquals(2, snapshots.get(snapshots.size()-1).getVersionID()); + } + catch (Exception e) + { + e.printStackTrace(System.err); + throw e; + } + } + + public void testSimpleUpdateLF2() throws Exception + { + try + { + fService.createStore("mainA"); + fService.createStore("mainB"); + fService.createStore("mainB--layer"); + + List snapshots = fService.getStoreVersions("mainA"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + snapshots = fService.getStoreVersions("mainB"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + snapshots = fService.getStoreVersions("mainB--layer"); + assertEquals(1, snapshots.size()); + assertEquals(0, snapshots.get(0).getVersionID()); + + logger.debug("created 3 stores: mainA, mainB, mainB-layer"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + fService.createDirectory("mainA:/", "a"); + fService.createDirectory("mainB:/", "a"); + + logger.debug("created 2 plain dirs: mainA:/a, mainB:/a"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + fService.createLayeredDirectory("mainB:/a", "mainB--layer:/", "a"); + + logger.debug("created layered dir: mainB--layer:/a -> mainB:/a"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + // note: unlike WCM, edit staging directly (ie. don't bother with mainA--layer for now) + fService.createFile("mainA:/a", "foo"); + + assertEquals(1, fService.lookup(-1, "mainA:/a/foo").getVersionID()); + assertNull(fService.lookup(-1, "mainB:/a/foo")); + assertNull(fService.lookup(-1, "mainB--layer:/a/foo")); + + PrintStream out = new PrintStream(fService.getFileOutputStream("mainA:/a/foo")); + out.println("I am mainA:/a/foo"); + out.close(); + + logger.debug("created plain file: mainA:/a/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + fService.createSnapshot("mainA", null, null); + + assertEquals(1, fService.lookup(-1, "mainA:/a/foo").getVersionID()); + assertNull(fService.lookup(-1, "mainB:/a/foo")); + assertNull(fService.lookup(-1, "mainB--layer:/a/foo")); + + snapshots = fService.getStoreVersions("mainA"); + assertEquals(2, snapshots.size()); + assertEquals(1, snapshots.get(snapshots.size()-1).getVersionID()); + + logger.debug("created snapshot: mainA"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + // note: WCM does not expose layered file (between web project staging sandboxes) + fService.createLayeredFile("mainA:/a/foo", "mainB:/a", "foo"); + + assertEquals(1, fService.lookup(-1, "mainA:/a/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "mainB:/a/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "mainB--layer:/a/foo").getVersionID()); + + BufferedReader reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB--layer:/a/foo"))); + String line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainA:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + + logger.debug("created layered file: mainB:/a/foo -> mainA:/a/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + // modify file in user's sandbox + out = new PrintStream(fService.getFileOutputStream("mainB--layer:/a/foo")); + out.println("I am mainB--layer:/a/foo"); + out.close(); + + assertEquals(1, fService.lookup(-1, "mainA:/a/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "mainB:/a/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "mainB--layer:/a/foo").getVersionID()); + + logger.debug("modified file: mainB--layer:/a/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB--layer:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainB--layer:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainA:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + List diffs = fSyncService.compare(-1, "mainB--layer:/a", -1, "mainB:/a", null); + assertEquals(1, diffs.size()); + + // TODO - review behaviour + assertEquals("[mainB--layer:/a/foo[-1] > mainB:/a/foo[-1]]", diffs.toString()); + fSyncService.update(diffs, null, false, false, false, false, "one", "one"); + + fSyncService.flatten("mainB--layer:/a", "mainB:/a"); + + assertEquals(1, fService.lookup(-1, "mainA:/a/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "mainB:/a/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "mainB--layer:/a/foo").getVersionID()); + + snapshots = fService.getStoreVersions("mainB--layer"); + assertEquals(2, snapshots.size()); + assertEquals(1, snapshots.get(snapshots.size()-1).getVersionID()); + + snapshots = fService.getStoreVersions("mainB"); + assertEquals(3, snapshots.size()); + assertEquals(2, snapshots.get(snapshots.size()-1).getVersionID()); + + logger.debug("submit/update file: mainB--layer:/a/foo -> mainB:/a/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + fSyncService.flatten("mainB--layer:/a", "mainB:/a"); + + logger.debug("flatten dir: mainB--layer:/a/foo -> mainB:/a/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB--layer:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainB--layer:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainB--layer:/a/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainA:/a/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/foo", line); + } + catch (Exception e) + { + e.printStackTrace(System.err); + throw e; + } + finally + { + fService.purgeStore("mainA"); + fService.purgeStore("mainB"); + fService.purgeStore("mainB--layer"); + } + } + + public void testLayeredFolder_DeleteFile_mimic_ETHREEOH_2297() throws Exception + { + try + { + fService.createStore("mainA"); + fService.createStore("mainB"); + + fService.createDirectory("mainA:/", "a"); + fService.createDirectory("mainA:/a", "b"); + + fService.createDirectory("mainB:/", "a"); + + fService.createStore("mainB--layer"); + + fService.createLayeredDirectory("mainB:/a", "mainB--layer:/", "a"); + + // note: short-cut - created directly in "staging" area (don't bother with sandbox mainA--layer for now) + fService.createFile("mainA:/a/b", "foo"); + + PrintStream out = new PrintStream(fService.getFileOutputStream("mainA:/a/b/foo")); + out.println("I am mainA:/a/b/foo"); + out.close(); + + logger.debug("created file: mainA:/a/b/c/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + // create equivalent of WCM layered folder between web project staging sandboxes (mainB:/a/b pointing to ,mainA:/a/b) + fService.createLayeredDirectory("mainA:/a/b", "mainB:/a", "b"); + + fService.createSnapshot("mainA", null, null); + fService.createSnapshot("mainB", null, null); + + logger.debug("created layered directory: mainB:/a/b -> mainA:/a/b"); + + recursiveList("mainB"); + recursiveList("mainB--layer"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB--layer:/a/b/foo"))); + String line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/b/foo", line); + + out = new PrintStream(fService.getFileOutputStream("mainB--layer:/a/b/foo")); + out.println("I am mainB--layer:/a/b/foo"); + out.close(); + + fService.createSnapshot("mainB--layer", null, null); + + logger.debug("updated file: mainB--layer:/a/b/foo"); + + recursiveList("mainB"); + recursiveList("mainB--layer"); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainA:/a/b/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/b/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB:/a/b/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainA:/a/b/foo", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB--layer:/a/b/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainB--layer:/a/b/foo", line); + + List diffs = fSyncService.compare(-1, "mainB--layer:/a", -1, "mainB:/a", null); + assertEquals(1, diffs.size()); + assertEquals("[mainB--layer:/a/b/foo[-1] > mainB:/a/b/foo[-1]]", diffs.toString()); + + fSyncService.update(diffs, null, false, false, false, false, "one", "one"); + fSyncService.flatten("mainB--layer:/a", "mainB:/a"); + + logger.debug("updated: created file: mainB:/a/b/foo"); + + recursiveList("mainB"); + recursiveList("mainB--layer"); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "mainB:/a/b/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am mainB--layer:/a/b/foo", line); + + recursiveList("mainA"); + recursiveList("mainB"); + + // note: short-cut - removed directly from "staging" area (don't bother with sandbox mainA--layer for now) + fService.removeNode("mainA:/a/b", "foo"); + + fService.createSnapshot("mainA", null, null); + + logger.debug("removed file: mainA:/a/b/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + + // ETHREEOH-2297 + fService.removeNode("mainB--layer:/a/b", "foo"); + + diffs = fSyncService.compare(-1, "mainB--layer:/a", -1, "mainB:/a", null); + assertEquals(1, diffs.size()); + assertEquals("[mainB--layer:/a/b/foo[-1] > mainB:/a/b/foo[-1]]", diffs.toString()); + + fSyncService.update(diffs, null, false, false, false, false, "one", "one"); + fSyncService.flatten("mainB--layer:/a", "mainB:/a"); + + fService.createSnapshot("mainB", null, null); + + logger.debug("updated: removed file: mainB:/a/b/foo"); + + recursiveList("mainA"); + recursiveList("mainB"); + recursiveList("mainB--layer"); + } + catch (Exception e) + { + e.printStackTrace(System.err); + throw e; + } + finally + { + fService.purgeStore("mainA"); + fService.purgeStore("mainB"); + fService.purgeStore("mainB--layer"); + } + } protected void recursiveContents(String path) { @@ -1077,7 +1588,7 @@ public class AVMServiceLocalTest extends TestCase Map listing = fService.getDirectoryListing(version, path); for (String name : listing.keySet()) { - if (logger.isDebugEnabled()) { logger.debug(name); } + if (logger.isTraceEnabled()) { logger.trace(name); } builder.append(recursiveList(basename + name, version, indent + 2, followLinks)); } } diff --git a/source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java b/source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java index 7ed6e709c0..aeb543fc18 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 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 @@ -24,7 +24,6 @@ package org.alfresco.repo.avm; import org.alfresco.repo.remote.ClientTicketHolder; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.remote.AVMRemote; import org.alfresco.service.cmr.security.AuthenticationService; @@ -36,6 +35,9 @@ import org.springframework.context.support.FileSystemXmlApplicationContext; */ public class AVMServiceRemoteSystemTest extends AVMServiceLocalTest { + private final static String ADMIN_UN = "admin"; + private final static String ADMIN_PW = "admin"; + @Override protected void setUp() throws Exception { @@ -48,7 +50,7 @@ public class AVMServiceRemoteSystemTest extends AVMServiceLocalTest excluder = (NameMatcher) fContext.getBean("globalPathExcluder"); AuthenticationService authService = (AuthenticationService)fContext.getBean("authenticationService"); - authService.authenticate(AuthenticationUtil.getAdminUserName(), "admin".toCharArray()); + authService.authenticate(ADMIN_UN, ADMIN_PW.toCharArray()); String ticket = authService.getCurrentTicket(); ((ClientTicketHolder)fContext.getBean("clientTicketHolder")).setTicket(ticket); } diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index 95e4d26a45..6840049145 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -43,6 +43,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.model.WCMModel; import org.alfresco.repo.action.ActionImpl; import org.alfresco.repo.avm.actions.AVMRevertListAction; +import org.alfresco.repo.avm.actions.AVMRevertStoreAction; import org.alfresco.repo.avm.actions.AVMRevertToVersionAction; import org.alfresco.repo.avm.actions.AVMUndoSandboxListAction; import org.alfresco.repo.avm.actions.SimpleAVMPromoteAction; @@ -59,6 +60,7 @@ import org.alfresco.service.cmr.avm.AVMCycleException; import org.alfresco.service.cmr.avm.AVMException; import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMNotFoundException; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.avm.LayeringDescriptor; @@ -160,7 +162,7 @@ public class AVMServiceTest extends AVMServiceTestBase } - public void testETWOTWO570() throws Exception + public void test_ETWOTWO_570() throws Exception { // Check that read-write methods are properly intercepted RetryingTransactionCallback readOnlyCallback = new RetryingTransactionCallback() @@ -1926,6 +1928,8 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test the revert list action. + * + * @deprecated */ public void testRevertListAction() throws Exception { @@ -1976,9 +1980,76 @@ public class AVMServiceTest extends AVMServiceTestBase fService.purgeStore("area"); } } - + + /** + * Test the revert store action. + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxRevertSnapshotAction or org.alfresco.wcm.SandboxService.revertSnapshot + */ + public void testRevertStoreAction() throws Exception + { + try + { + setupBasicTree(); + + fService.createStore("area"); + fService.createLayeredDirectory("main:/a", "area:/", "a"); + + fService.getFileOutputStream("area:/a/b/c/foo").close(); + + List diffs = fSyncService.compare(-1, "area:/a", -1, "main:/a", null); + assertEquals(1, diffs.size()); + assertEquals("[area:/a/b/c/foo[-1] > main:/a/b/c/foo[-1]]", diffs.toString()); + + fSyncService.update(diffs, null, false, false, false, false, null, null); + + diffs = fSyncService.compare(-1, "area:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + fService.getFileOutputStream("area:/a/b/c/bar").close(); + + diffs = fSyncService.compare(-1, "area:/a", -1, "main:/a", null); + assertEquals(1, diffs.size()); + assertEquals("[area:/a/b/c/bar[-1] > main:/a/b/c/bar[-1]]", diffs.toString()); + + final ActionImpl action = new ActionImpl(null, GUID.generate(), AVMRevertStoreAction.NAME); + action.setParameterValue(AVMRevertStoreAction.PARAM_VERSION, fService.getLatestSnapshotID("area")); + + final AVMRevertStoreAction revert = (AVMRevertStoreAction)fContext.getBean("avm-revert-store"); + + class TxnWork implements RetryingTransactionCallback + { + public Object execute() throws Exception + { + revert.execute(action, AVMNodeConverter.ToNodeRef(-1, "area:/")); + return null; + } + }; + + TransactionService transactionService = (TransactionService) fContext.getBean("transactionService"); + transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); + + diffs = fSyncService.compare(-1, "area:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + System.out.println(recursiveList("area", -1, true)); + System.out.println(recursiveList("main", -1, true)); + } + catch (Exception e) + { + e.printStackTrace(); + throw e; + } + finally + { + fService.purgeStore("area"); + } + } + /** * Test the undo list action. + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxRevertListAction or org.alfresco.wcm.SandboxService.revertList */ public void testUndoListAction() throws Exception { @@ -2031,6 +2102,8 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test the promote action. + * + * @deprecated */ public void testPromoteAction() throws Exception { @@ -2075,13 +2148,17 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test the SimpleAVMSubmitAction. + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxSubmitAction or org.alfesco.wcm.SandboxService.submit */ public void testSubmitAction() throws Exception { - final String STAGING = "foo-staging"; // note: it is implied that the website/webproject name is the same as staging name + // NOTE: it is implied that the sandboxes follow WCM naming conventions + final String STAGING = "foo"; + final String SANDBOX = "foo--sandbox"; + try { - fService.createStore(STAGING); fService.createDirectory(STAGING+":/", JNDIConstants.DIR_DEFAULT_WWW); fService.createDirectory(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, "a"); @@ -2089,30 +2166,33 @@ public class AVMServiceTest extends AVMServiceTestBase fService.createDirectory(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b", "c"); fService.createFile(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c", "foo").close(); fService.createFile(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c", "bar").close(); - fService.createStore("area"); - fService.setStoreProperty("area", SandboxConstants.PROP_WEBSITE_NAME, new PropertyValue(null, STAGING)); // note: it is implied that the website name is the same as staging name - fService.createLayeredDirectory(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, "area:/", JNDIConstants.DIR_DEFAULT_WWW); - fService.createFile("area:/" + JNDIConstants.DIR_DEFAULT_WWW, "figs").close(); - fService.getFileOutputStream("area:/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c/foo").close(); - fService.removeNode("area:/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c/bar"); - List diffs = fSyncService.compare(-1, "area:/" + JNDIConstants.DIR_DEFAULT_WWW, -1, STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, null); + + fService.createStore(SANDBOX); + fService.setStoreProperty(SANDBOX, SandboxConstants.PROP_WEBSITE_NAME, new PropertyValue(null, STAGING)); // note: it is implied that the website name is the same as staging name + fService.createLayeredDirectory(STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, SANDBOX+":/", JNDIConstants.DIR_DEFAULT_WWW); + fService.createFile(SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW, "figs").close(); + fService.getFileOutputStream(SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c/foo").close(); + fService.removeNode(SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW + "/a/b/c/bar"); + + List diffs = fSyncService.compare(-1, SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW, -1, STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, null); assertEquals(3, diffs.size()); - assertEquals("[area:/www/a/b/c/bar[-1] > "+STAGING+":/www/a/b/c/bar[-1], area:/www/a/b/c/foo[-1] > "+STAGING+":/www/a/b/c/foo[-1], area:/www/figs[-1] > "+STAGING+":/www/figs[-1]]", diffs.toString()); + assertEquals("["+SANDBOX+":/www/a/b/c/bar[-1] > "+STAGING+":/www/a/b/c/bar[-1], "+SANDBOX+":/www/a/b/c/foo[-1] > "+STAGING+":/www/a/b/c/foo[-1], "+SANDBOX+":/www/figs[-1] > "+STAGING+":/www/figs[-1]]", diffs.toString()); + final SimpleAVMSubmitAction action = (SimpleAVMSubmitAction) fContext.getBean("simple-avm-submit"); class TxnWork implements RetryingTransactionCallback { public Object execute() throws Exception { - action.execute(null, AVMNodeConverter.ToNodeRef(-1, "area:/" + JNDIConstants.DIR_DEFAULT_WWW)); + action.execute(null, AVMNodeConverter.ToNodeRef(-1, SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW)); return null; } - } - ; + }; + TransactionService transactionService = (TransactionService) fContext.getBean("transactionService"); transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); - - diffs = fSyncService.compare(-1, "area:/" + JNDIConstants.DIR_DEFAULT_WWW, -1, STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, null); - + + diffs = fSyncService.compare(-1, SANDBOX+":/" + JNDIConstants.DIR_DEFAULT_WWW, -1, STAGING+":/" + JNDIConstants.DIR_DEFAULT_WWW, null); + assertEquals(0, diffs.size()); } catch (Exception e) @@ -2123,7 +2203,7 @@ public class AVMServiceTest extends AVMServiceTestBase finally { fService.purgeStore(STAGING); - fService.purgeStore("area"); + fService.purgeStore(SANDBOX); } } @@ -3365,7 +3445,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test creating a layered file. */ - public void testCreateLayeredFile() throws Exception + public void testLayeredFile1() throws Exception { try { @@ -3398,6 +3478,504 @@ public class AVMServiceTest extends AVMServiceTestBase throw e; } } + + public void testLayeredFile2() throws Exception + { + try + { + AVMNodeDescriptor desc = fService.lookup(-1, "main:/foo"); + assertNull(desc); + + try + { + fService.getFileOutputStream("main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + try + { + fService.getFileInputStream(-1, "main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + fService.createFile("main:/", "foo").close(); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + + BufferedReader reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + String line = reader.readLine(); + reader.close(); + assertNull(line); + + PrintStream out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V1a"); + out.close(); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V1b"); + out.close(); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V1c"); + out.close(); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1c", line); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V2a"); + out.close(); + + assertEquals(2, fService.lookup(-1, "main:/foo").getVersionID()); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V2b"); + out.close(); + + assertEquals(2, fService.lookup(-1, "main:/foo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(2, fService.lookup(-1, "main:/foo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V2b", line); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V3"); + out.close(); + + assertEquals(3, fService.lookup(-1, "main:/foo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(3, fService.lookup(-1, "main:/foo").getVersionID()); + + try + { + fService.createLayeredFile("main:/foo", "main:/", "foo"); + fail("Unexpected"); + } + catch (AVMExistsException ee) + { + // expected + } + + fService.createLayeredFile("main:/foo", "main:/", "lfoo"); + + assertEquals(3, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + assertEquals("main:/foo", fService.lookup(-1, "main:/lfoo").getIndirection()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V3", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V3", line); + + fService.createSnapshot("main", null, null); + + assertEquals(3, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V3", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V3", line); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V4"); + out.close(); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + // TODO - review + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + assertEquals(4, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + assertEquals(4, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(4, fService.lookup(-1, "main:/foo").getVersionID()); + + // TODO - review + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V5a"); + out.close(); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V5b"); + out.close(); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V5b", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + assertEquals(5, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(5, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V5b", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V6"); + out.close(); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V6", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V4", line); + + out = new PrintStream(fService.getFileOutputStream("main:/lfoo")); + out.println("I am main:/lfoo V1"); + out.close(); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(3, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(3, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V6", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/lfoo V1", line); + + out = new PrintStream(fService.getFileOutputStream("main:/lfoo")); + out.println("I am main:/lfoo V2"); + out.close(); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(4, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(6, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(4, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V6", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/lfoo V2", line); + + fService.removeNode("main:/foo"); + + desc = fService.lookup(-1, "main:/foo"); + assertNull(desc); + + try + { + fService.getFileOutputStream("main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + try + { + fService.getFileInputStream(-1, "main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + fService.createSnapshot("main", null, null); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/lfoo V2", line); + + assertEquals(4, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.removeNode("main:/lfoo"); + + desc = fService.lookup(-1, "main:/lfoo"); + assertNull(desc); + + try + { + fService.getFileOutputStream("main:/lfoo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + try + { + fService.getFileInputStream(-1, "main:/lfoo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + fService.createSnapshot("main", null, null); + } + catch (Exception e) + { + e.printStackTrace(System.err); + throw e; + } + } + + public void testLayeredFile3() throws Exception + { + try + { + AVMNodeDescriptor desc = fService.lookup(-1, "main:/foo"); + assertNull(desc); + + try + { + fService.getFileOutputStream("main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + try + { + fService.getFileInputStream(-1, "main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + fService.createLayeredFile("main:/foo", "main:/", "lfoo"); + + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + try + { + fService.getFileOutputStream("main:/lfoo"); + fail("Unexpected"); + } + catch (AVMException e) + { + // TODO - review + } + + try + { + fService.getFileInputStream(-1, "main:/lfoo"); + fail("Unexpected"); + } + catch (AVMException e) + { + // TODO - review + } + + fService.createFile("main:/", "foo").close(); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + BufferedReader reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + String line = reader.readLine(); + reader.close(); + assertNull(line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertNull(line); + + PrintStream out = new PrintStream(fService.getFileOutputStream("main:/foo")); + out.println("I am main:/foo V1"); + out.close(); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(1, fService.lookup(-1, "main:/lfoo").getVersionID()); + + fService.createSnapshot("main", null, null); + + assertEquals(1, fService.lookup(-1, "main:/foo").getVersionID()); + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/foo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + + fService.removeNode( "main:/foo"); + + desc = fService.lookup(-1, "main:/foo"); + assertNull(desc); + + try + { + fService.getFileOutputStream("main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + try + { + fService.getFileInputStream(-1, "main:/foo"); + fail("Unexpected"); + } + catch (AVMNotFoundException nfe) + { + // expected + } + + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + + fService.createSnapshot("main", null, null); + + assertEquals(2, fService.lookup(-1, "main:/lfoo").getVersionID()); + + reader = new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, "main:/lfoo"))); + line = reader.readLine(); + reader.close(); + assertEquals("I am main:/foo V1", line); + } + catch (Exception e) + { + e.printStackTrace(System.err); + throw e; + } + } /** * Test rename. diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 199a2318f3..87e980763a 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -63,6 +63,8 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * A Repository contains a current root directory and a list of @@ -72,6 +74,7 @@ import org.alfresco.util.Pair; */ public class AVMStoreImpl implements AVMStore { + private static Log fgLogger = LogFactory.getLog(AVMStoreImpl.class); /** * The primary key. */ @@ -560,7 +563,38 @@ public class AVMStoreImpl implements AVMStore { throw new AVMExistsException("Child exists: " + name); } - // TODO Reexamine decision to not check validity of srcPath. + // TODO Reexamine decision to not check validity of srcPath. Warning for now. + String[] srcPathParts = srcPath.split(":"); + String[] dstPathParts = dstPath.split(":"); + + Lookup lPathSrc = null; + if (srcPathParts[0].equals(dstPathParts[0])) + { + lPathSrc = lookup(-1, srcPathParts[1], false, false); + } + else + { + AVMStore srcStore = AVMDAOs.Instance().fAVMStoreDAO.getByName(srcPathParts[0]); + if (srcStore != null) + { + lPathSrc = srcStore.lookup(-1, srcPathParts[1], false, false); + } + } + + AVMNode srcNode = null; + if (lPathSrc == null) + { + fgLogger.warn("CreateLayeredFile: srcPath not found: "+srcPath); + } + else + { + srcNode = (AVMNode)lPathSrc.getCurrentNode(); + if (! (srcNode instanceof FileNode)) + { + fgLogger.warn("CreateLayeredFile: srcPath is not a file: "+srcPath); + } + } + LayeredFileNodeImpl newFile = new LayeredFileNodeImpl(srcPath, this, null); @@ -573,6 +607,13 @@ public class AVMStoreImpl implements AVMStore { newFile.setAncestor(child); } + else + { + if ((srcNode != null) && (srcNode instanceof FileNode)) + { + newFile.setAncestor((FileNode)srcNode); + } + } // newFile.setVersionID(getNextVersionID()); //dir.updateModTime(); @@ -738,12 +779,24 @@ public class AVMStoreImpl implements AVMStore { throw new AVMNotFoundException("Path " + path + " not found."); } + DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); Pair temp = dir.lookupChild(lPath, name, false); AVMNode child = (temp == null) ? null : temp.getFirst(); if (child == null) { - throw new AVMNotFoundException("Does not exist: " + name); + Lookup lPathToChild = lookup(-1, path+"/"+name, true, false); + if (lPathToChild != null) + { + // ETHREEOH-2297 + child = lPathToChild.getCurrentNode(); + } + if (child == null) + { + throw new AVMNotFoundException("Does not exist: " + name); + } + + dir = lPathToChild.getCurrentNodeDirectory(); } if (!fAVMRepository.can(this, child, PermissionService.DELETE_NODE, false)) diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index b91b3c66ca..98f2c8986d 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -555,7 +555,18 @@ public class AVMSyncServiceImpl implements AVMSyncService // This is a delete. if (toLink == null) { - fAVMService.removeNode(parentPath, name); + try + { + fAVMService.removeNode(parentPath, name); + } + catch (AVMNotFoundException nfe) + { + // ignore + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("linkIn: Does not exist: "+parentPath+"/"+name); + } + } return; } mkdirs(parentPath, AVMNodeConverter.SplitBase(toLink.getPath())[0]); @@ -831,11 +842,26 @@ public class AVMSyncServiceImpl implements AVMSyncService { return AVMDifference.SAME; } - // Otherwise we know they are in conflict because they are of different type. + + AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); + if (common == null) + { + return AVMDifference.CONFLICT; + } + if (common.getId() == srcDesc.getId()) + { + return AVMDifference.OLDER; + } + if (common.getId() == dstDesc.getId()) + { + return AVMDifference.NEWER; + } + return AVMDifference.CONFLICT; } // Destination is a plain file. AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); + // Conflict case. if (common == null) { @@ -849,7 +875,23 @@ public class AVMSyncServiceImpl implements AVMSyncService { return AVMDifference.NEWER; } - // The must, finally, be in conflict. + + if (common.isLayeredFile()) + { + AVMNode dstAncNode = AVMDAOs.Instance().fAVMNodeDAO.getByID(dstDesc.getId()).getAncestor(); + if ((dstAncNode != null) && (common.getId() == dstAncNode.getId())) + { + return AVMDifference.NEWER; + } + + AVMNode srcAncNode = AVMDAOs.Instance().fAVMNodeDAO.getByID(srcDesc.getId()).getAncestor(); + if ((srcAncNode != null) && (common.getId() == srcAncNode.getId())) + { + return AVMDifference.OLDER; + } + } + + // They must, finally, be in conflict. return AVMDifference.CONFLICT; } diff --git a/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java b/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java index 1024b061d1..6f9d666453 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMDeployWebsiteAction.java @@ -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 @@ -62,6 +62,8 @@ import org.apache.commons.logging.LogFactory; /** * Deploys a website to a remote server. * + * TODO refactor and add to WCM services (when we support WCM deployment config) + * * @author gavinc */ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase @@ -75,6 +77,8 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase public static final String PARAM_CALLBACK = "callback"; public static final String ASYNC_QUEUE_NAME = "deployment"; + + public static final String LIVE_SUFFIX = "live"; private int delay = -1; private int defaultAlfRmiPort = 50500; @@ -291,7 +295,7 @@ public class AVMDeployWebsiteAction extends ActionExecuterAbstractBase if (port == null && (host.equalsIgnoreCase("localhost") || host.equalsIgnoreCase("127.0.0.1"))) { - targetPath = storePath[0] + "live:" + storePath[1]; + targetPath = storePath[0] + LIVE_SUFFIX + ":" + storePath[1]; } } else diff --git a/source/java/org/alfresco/repo/avm/actions/AVMRevertListAction.java b/source/java/org/alfresco/repo/avm/actions/AVMRevertListAction.java index d36c72035c..caa970d988 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMRevertListAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMRevertListAction.java @@ -1,5 +1,26 @@ -/** - * +/* + * 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.avm.actions; @@ -21,6 +42,8 @@ import org.apache.commons.logging.LogFactory; * This action handles reverting a selected set of nodes to a particular version. * The actionedUponNodeRef is a dummy and can be null. * @author britt + * + * @deprecated */ public class AVMRevertListAction extends ActionExecuterAbstractBase { @@ -50,6 +73,7 @@ public class AVMRevertListAction extends ActionExecuterAbstractBase /* (non-Javadoc) * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) */ + @SuppressWarnings("unchecked") @Override protected void executeImpl(Action action, NodeRef actionedUponNodeRef) { diff --git a/source/java/org/alfresco/repo/avm/actions/AVMRevertStoreAction.java b/source/java/org/alfresco/repo/avm/actions/AVMRevertStoreAction.java index 569e65eccc..2b56f5e39b 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMRevertStoreAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMRevertStoreAction.java @@ -1,5 +1,26 @@ -/** - * +/* + * 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.avm.actions; @@ -21,6 +42,8 @@ import org.apache.commons.logging.LogFactory; /** * Reverts a node and everything underneath it to a specified version. * @author britt + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxRevertSnapshotAction or org.alfresco.wcm.SandboxService.revertSnapshot */ public class AVMRevertStoreAction extends ActionExecuterAbstractBase { diff --git a/source/java/org/alfresco/repo/avm/actions/AVMRevertToVersionAction.java b/source/java/org/alfresco/repo/avm/actions/AVMRevertToVersionAction.java index df50f271af..18941a171b 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMRevertToVersionAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMRevertToVersionAction.java @@ -1,5 +1,26 @@ -/** - * +/* + * 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.avm.actions; @@ -22,7 +43,12 @@ import org.apache.commons.logging.LogFactory; * Revert a single path to a specified node. The path in head is passed * as actionedUponNodeRef. The node to revert to is passed as an AVMNodeDescriptor * parameter. + * + * TODO refactor and add to WCM services + * * @author britt + * + * @deprecated */ public class AVMRevertToVersionAction extends ActionExecuterAbstractBase { diff --git a/source/java/org/alfresco/repo/avm/actions/AVMUndoSandboxListAction.java b/source/java/org/alfresco/repo/avm/actions/AVMUndoSandboxListAction.java index 7f625be4ee..47e9eb1e2d 100644 --- a/source/java/org/alfresco/repo/avm/actions/AVMUndoSandboxListAction.java +++ b/source/java/org/alfresco/repo/avm/actions/AVMUndoSandboxListAction.java @@ -1,5 +1,26 @@ -/** - * +/* + * 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.avm.actions; @@ -27,6 +48,8 @@ import org.apache.commons.logging.LogFactory; * passed in as a packed string (Obtained by VersionPathStuffer). * The actionedUponNodeRef is a dummy and can be null. * @author britt + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxRevertListAction or org.alfresco.wcm.SandboxService.revert */ public class AVMUndoSandboxListAction extends ActionExecuterAbstractBase { @@ -59,6 +82,7 @@ public class AVMUndoSandboxListAction extends ActionExecuterAbstractBase /* (non-Javadoc) * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) */ + @SuppressWarnings("unchecked") @Override protected void executeImpl(Action action, NodeRef actionedUponNodeRef) { diff --git a/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java b/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java index 1416531f0b..5337801f0f 100644 --- a/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java +++ b/source/java/org/alfresco/repo/avm/actions/SimpleAVMPromoteAction.java @@ -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 @@ -14,13 +14,14 @@ * 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" */ + * 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.avm.actions; import java.util.List; @@ -43,6 +44,8 @@ import org.alfresco.util.Pair; * The NodeRef argument is in the source AVMStore. The 'target-store' * mandatory argument is the name of the destination store. * @author britt + * + * @deprecated */ public class SimpleAVMPromoteAction extends ActionExecuterAbstractBase { diff --git a/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java b/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java index b0deb9d7df..a22a33532c 100644 --- a/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java +++ b/source/java/org/alfresco/repo/avm/actions/SimpleAVMSubmitAction.java @@ -47,6 +47,8 @@ import org.apache.commons.logging.LogFactory; * This action submits all the newer changes in the passed in NodeRef * to its corresponding staging area. It ignores conflicts and older nodes. * @author britt + * + * @deprecated see org.alfresco.wcm.actions.WCMSandboxSubmitAction or org.alfresco.wcm.SandboxService.submit */ public class SimpleAVMSubmitAction extends ActionExecuterAbstractBase { diff --git a/source/java/org/alfresco/repo/avm/actions/StartAVMWorkflowAction.java b/source/java/org/alfresco/repo/avm/actions/StartAVMWorkflowAction.java index 009204a0a0..b1dd9f9f27 100644 --- a/source/java/org/alfresco/repo/avm/actions/StartAVMWorkflowAction.java +++ b/source/java/org/alfresco/repo/avm/actions/StartAVMWorkflowAction.java @@ -1,5 +1,26 @@ -/** - * +/* + * 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.avm.actions; @@ -26,6 +47,8 @@ import org.apache.commons.logging.LogFactory; /** * This action knows how to start an AVM specific workflow. * @author britt + * + * @deprecated */ public class StartAVMWorkflowAction extends ActionExecuterAbstractBase { diff --git a/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java b/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java index c71f2654db..f719441a83 100644 --- a/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java +++ b/source/java/org/alfresco/repo/avm/wf/AVMSubmitPackageHandler.java @@ -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 @@ -27,7 +27,6 @@ import java.io.Serializable; import java.util.List; import java.util.Map; -import org.alfresco.repo.avm.AVMDAOs; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -43,6 +42,7 @@ import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.alfresco.wcm.util.WCMUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jbpm.graph.exe.ExecutionContext; @@ -98,76 +98,87 @@ public class AVMSubmitPackageHandler extends JBPMSpringActionHandler implements final NodeRef pkg = ((JBPMNode) executionContext.getContextInstance().getVariable("bpm_package")).getNodeRef(); final Pair pkgPath = AVMNodeConverter.ToAVMVersionPath(pkg); final AVMNodeDescriptor pkgDesc = fAVMService.lookup(pkgPath.getFirst(), pkgPath.getSecond()); - final String from = (String) executionContext.getContextInstance().getVariable("wcmwf_fromPath"); - final String targetPath = pkgDesc.getIndirection(); - if (logger.isDebugEnabled()) - logger.debug("handling submit of " + pkgPath.getSecond() + " from " + from + " to " + targetPath); - - // submit the package changes - final String description = (String) executionContext.getContextInstance().getVariable("bpm_workflowDescription"); - final String tag = (String) executionContext.getContextInstance().getVariable("wcmwf_label"); - - final Map dnsProperties = this.fAVMService.queryStorePropertyKey(targetPath.split(":")[0], QName.createQName(null, ".dns%")); - String localName = dnsProperties.keySet().iterator().next().getLocalName(); - final String webProject = localName.substring(localName.lastIndexOf('.') + 1, localName.length()); - final List stagingDiffs = fAVMSyncService.compare(pkgPath.getFirst(), pkgPath.getSecond(), -1, targetPath, null); - - // Allow AVMSubmitTransactionListener to inspect the staging diffs - // so it can notify the virtualization server via JMX if when this - // submit succeeds or fails. This allows virtual webapps devoted - // to the workarea to be destroyed, and staging to be updated in - // the event that some of the files alter the behavior of the - // webapp itself (e.g.: WEB-INF/web.xml, WEB-INF/lib/*.jar), etc. - - AlfrescoTransactionSupport.bindResource("staging_diffs", stagingDiffs); - - // Workflow does this as system as the staging area has restricted access and reviewers - // may not have permission to flatten the store the workflow was submitted from - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + + if (pkgDesc == null) { - public Object doWork() throws Exception + logger.warn("Submit skipped since workflow package does not exist: "+pkgPath); + } + else + { + final String from = (String) executionContext.getContextInstance().getVariable("wcmwf_fromPath"); + final String targetPath = pkgDesc.getIndirection(); + + if (logger.isDebugEnabled()) { - fAVMSyncService.update(stagingDiffs, null, false, false, true, true, tag, description); - fAVMSyncService.flatten(pkgPath.getSecond(), targetPath); - - for (final AVMDifference diff : stagingDiffs) - { - String p = diff.getSourcePath(); - if (from != null && from.length() != 0) - { - p = from + p.substring(pkgPath.getSecond().length()); - } - recursivelyRemoveLocks(webProject, -1, p); - } - - // flatten source folder where changes were submitted from - if (from != null && from.length() > 0) - { - // first, submit changes back to sandbox forcing addition of edits in workflow (and submission - // flag removal). second, flatten sandbox, removing modified items that have been submitted - // TODO: Without locking on the sandbox, it's possible that a change to a "submitted" item - // may get lost when the item is finally approved - final List sandboxDiffs = fAVMSyncService.compare(pkgPath.getFirst(), pkgPath.getSecond(), -1, from, null); - fAVMSyncService.update(sandboxDiffs, null, true, true, false, false, tag, description); - fAVMSyncService.flatten(from, targetPath); - } - - return null; + logger.debug("handling submit of " + pkgPath.getSecond() + " from " + from + " to " + targetPath); } - }, AuthenticationUtil.getSystemUserName()); + + // submit the package changes + final String description = (String) executionContext.getContextInstance().getVariable("bpm_workflowDescription"); + final String tag = (String) executionContext.getContextInstance().getVariable("wcmwf_label"); + + final Map dnsProperties = this.fAVMService.queryStorePropertyKey(targetPath.split(":")[0], QName.createQName(null, ".dns%")); + String localName = dnsProperties.keySet().iterator().next().getLocalName(); + final String webProject = localName.substring(localName.lastIndexOf('.') + 1, localName.length()); + final List stagingDiffs = fAVMSyncService.compare(pkgPath.getFirst(), pkgPath.getSecond(), -1, targetPath, null); + + // Allow AVMSubmitTransactionListener to inspect the staging diffs + // so it can notify the virtualization server via JMX if when this + // submit succeeds or fails. This allows virtual webapps devoted + // to the workarea to be destroyed, and staging to be updated in + // the event that some of the files alter the behavior of the + // webapp itself (e.g.: WEB-INF/web.xml, WEB-INF/lib/*.jar), etc. + + AlfrescoTransactionSupport.bindResource("staging_diffs", stagingDiffs); + + // Workflow does this as system as the staging area has restricted access and reviewers + // may not have permission to flatten the store the workflow was submitted from + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + fAVMSyncService.update(stagingDiffs, null, false, false, true, true, tag, description); + fAVMSyncService.flatten(pkgPath.getSecond(), targetPath); + + for (final AVMDifference diff : stagingDiffs) + { + recursivelyRemoveLocks(webProject, -1, diff.getSourcePath()); + } + + // flatten source folder where changes were submitted from + if (from != null && from.length() > 0) + { + // first, submit changes back to sandbox forcing addition of edits in workflow (and submission + // flag removal). second, flatten sandbox, removing modified items that have been submitted + // TODO: Without locking on the sandbox, it's possible that a change to a "submitted" item + // may get lost when the item is finally approved + final List sandboxDiffs = fAVMSyncService.compare(pkgPath.getFirst(), pkgPath.getSecond(), -1, from, null); + fAVMSyncService.update(sandboxDiffs, null, true, true, false, false, tag, description); + fAVMSyncService.flatten(from, targetPath); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } } /** * Recursively remove locks from a path. Walking child folders looking for files to remove locks from. */ - private void recursivelyRemoveLocks(final String webProject, final int version, final String path) + private void recursivelyRemoveLocks(String wpStoreId, int version, String wfPath) { - if (logger.isDebugEnabled()) - logger.debug("removing lock on " + path); - AVMNodeDescriptor desc = fAVMService.lookup(version, path, true); + AVMNodeDescriptor desc = fAVMService.lookup(version, wfPath, true); if (desc.isFile() || desc.isDeletedFile()) { - fAVMLockingService.removeLock(webProject, path.substring(path.indexOf(":") + 1)); + String relativePath = WCMUtil.getStoreRelativePath(wfPath); + + if (logger.isDebugEnabled()) + { + logger.debug("removing file lock on " + relativePath + " (store id: "+wpStoreId+")"); + } + + fAVMLockingService.removeLock(wpStoreId, relativePath); } else { @@ -176,7 +187,7 @@ public class AVMSubmitPackageHandler extends JBPMSpringActionHandler implements Map list = fAVMService.getDirectoryListing(desc, true); for (AVMNodeDescriptor child : list.values()) { - recursivelyRemoveLocks(webProject, version, child.getPath()); + recursivelyRemoveLocks(wpStoreId, version, child.getPath()); } } } diff --git a/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java b/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java index 8abc80f1db..0e4d011f20 100644 --- a/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java +++ b/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java @@ -36,6 +36,8 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.PropertyMap; +import org.alfresco.wcm.asset.AssetService; +import org.alfresco.wcm.sandbox.SandboxService; import org.alfresco.wcm.util.WCMUtil; import org.alfresco.wcm.webproject.WebProjectInfo; import org.alfresco.wcm.webproject.WebProjectService; @@ -76,7 +78,7 @@ public class AbstractWCMServiceImplTest extends TestCase protected static final boolean TEST_WEBPROJ_DONT_USE_AS_TEMPLATE = false; // base web users - //protected static final String USER_ADMIN = "admin"; + protected static String USER_ADMIN; protected static final String TEST_USER = "testWebUser-"+TEST_RUN; @@ -90,22 +92,29 @@ public class AbstractWCMServiceImplTest extends TestCase // protected WebProjectService wpService; + protected SandboxService sbService; + protected AssetService assetService; + protected AuthenticationService authenticationService; protected PersonService personService; - private TransactionService transactionService; + protected TransactionService transactionService; @Override protected void setUp() throws Exception { // Get the required services wpService = (WebProjectService)ctx.getBean("WebProjectService"); + sbService = (SandboxService)ctx.getBean("SandboxService"); + assetService = (AssetService)ctx.getBean("AssetService"); + authenticationService = (AuthenticationService)ctx.getBean("AuthenticationService"); personService = (PersonService)ctx.getBean("PersonService"); transactionService = (TransactionService)ctx.getBean("TransactionService"); // By default run as Admin - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + USER_ADMIN = AuthenticationUtil.getAdminUserName(); + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); createUser(USER_ONE); createUser(USER_TWO); @@ -137,7 +146,6 @@ public class AbstractWCMServiceImplTest extends TestCase } }; transactionService.getRetryingTransactionHelper().doInTransaction(deleteWebProjectWork); - } } diff --git a/source/java/org/alfresco/wcm/WCMConcurrentTest.java b/source/java/org/alfresco/wcm/WCMConcurrentTest.java new file mode 100644 index 0000000000..6b72b672f4 --- /dev/null +++ b/source/java/org/alfresco/wcm/WCMConcurrentTest.java @@ -0,0 +1,247 @@ +/* + * 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.wcm; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; + +import javax.transaction.UserTransaction; + +import org.alfresco.config.JNDIConstants; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.wcm.asset.AssetService; +import org.alfresco.wcm.sandbox.SandboxService; + +/** + */ +public class WCMConcurrentTest extends AbstractWCMServiceImplTest +{ + private SandboxService sbService; + private AssetService assetService; + + private final static String ADMIN = "admin"; + + private final static String WP = TEST_WEBPROJ_DNS; + + private final static String SB_STG = WP; + private final static String SB_ADMIN = WP+"--"+ADMIN; + + private final static String AVM_WEBAPPS_PATH = JNDIConstants.DIR_DEFAULT_WWW+"/"+JNDIConstants.DIR_DEFAULT_APPBASE; + private final static String ROOT_PATH = AVM_WEBAPPS_PATH+"/"+"ROOT"; + + //protected static final boolean CLEAN = false; // cleanup during teardown + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + // Get the required services + sbService = (SandboxService)ctx.getBean("SandboxService"); + assetService = (AssetService)ctx.getBean("AssetService"); + } + + + /** + */ + public void test_ETWOTWO_1224() throws Exception + { + try + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN); + + wpService.createWebProject(WP, WP, "title", "description"); + + assertEquals(1, wpService.listWebApps(WP).size()); + + assertEquals(0, assetService.listAssets(SB_STG, ROOT_PATH, false).size()); + assertEquals(0, assetService.listAssets(SB_ADMIN, ROOT_PATH, false).size()); + + long start = System.currentTimeMillis(); + + System.out.println("Bulk import started ..."); + + // bulk import + + // ETWOTWO-1224 / WCM-948 - also fails with AVMNotFoundException !!! + int listingRepeat = 100; // listing web apps + String testFile = System.getProperty("user.dir") + "/source/test-resources/wcm/small-61-items.zip"; + + File zipFile = new File(testFile); + assetService.bulkImport(SB_ADMIN, ROOT_PATH, zipFile, false); + + int itemCount = assetService.listAssets(SB_ADMIN, ROOT_PATH, false).size(); + + System.out.println("Items below ROOT: "+itemCount); + + assert(itemCount > 0); + + System.out.println("... bulk import finished in "+(System.currentTimeMillis()-start)+" msecs"); + + assertEquals(0, assetService.listAssets(SB_STG, ROOT_PATH, false).size()); + assertEquals(itemCount, assetService.listAssets(SB_ADMIN, ROOT_PATH, false).size()); + + UserTransaction tx = transactionService.getUserTransaction(); + + tx.begin(); + + start = System.currentTimeMillis(); + + System.out.println("Submit initiation started ..."); + + // submit (from workflow sandbox to staging sandbox and also update user sandbox) + sbService.submitAll(SB_ADMIN , "s1", "s1"); + + tx.commit(); + + System.out.println("... submit initiation finished in "+(System.currentTimeMillis()-start)+" msecs"); + + + int threadCount = 1; + + Thread[] threads = new Thread[threadCount]; + + WCMConcurrentTestListing[] listings = new WCMConcurrentTestListing[threadCount]; + + for (int i = 0; i < threadCount; i++) + { + WCMConcurrentTestListing listing = new WCMConcurrentTestListing(i, listingRepeat); + listings[i] = listing; + threads[i] = new Thread(listing); + threads[i].start(); + } + + // join each thread so that we wait for them all to finish + for (int i = 0; i < threads.length; i++) + { + try + { + threads[i].join(); + + if (listings[i].getErrorStackTrace() != null) + { + throw new RuntimeException(listings[i].getErrorStackTrace()); + } + } + catch (InterruptedException e) + { + // not too serious - the worker threads are non-daemon + } + } + + assertEquals(1, wpService.listWebApps(WP).size()); + + assertEquals(itemCount, assetService.listAssets(SB_STG, ROOT_PATH, false).size()); + assertEquals(itemCount, assetService.listAssets(SB_ADMIN, ROOT_PATH, false).size()); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + + private class WCMConcurrentTestListing implements Runnable + { + private int id; + private int repeat; + private String errorStackTrace = null; + + public WCMConcurrentTestListing(int id, int repeat) + { + this.id = id; + this.repeat = repeat; + } + + public String getErrorStackTrace() + { + return errorStackTrace; + } + + public void run() + { + UserTransaction tx = null; + try + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN); + + long start = System.currentTimeMillis(); + + for (int i = 1; i <= repeat; i++) + { + tx = transactionService.getUserTransaction(); + + try + { + tx.begin(); + + System.out.println("Start: id: "+id+" - loop="+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"]"); + + assertEquals(1, wpService.listWebApps(WP).size()); + + Thread.sleep(500); + + System.out.println("Finish: id: "+id+" - loop="+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"]"); + + tx.commit(); + + //System.out.println("Post-commit: id: "+id+" - loop="+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"]"); + } + catch (Throwable t) + { + if (errorStackTrace == null) + { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + + errorStackTrace = sw.toString(); + } + + System.err.println("Failed: id: "+id+" - loop="+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"] "+t.getMessage()); + + if (tx != null) { try { tx.rollback(); } catch(Exception e) { }} + } + } + + System.out.println("id: "+id+" finished (repeated "+repeat+" times) in "+(System.currentTimeMillis()-start)+" msecs"); + } + catch (Throwable t) + { + System.out.println(t.getMessage()); + + if (tx != null) { try { tx.rollback(); } catch(Exception e) { }} + + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + + errorStackTrace = sw.toString(); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + } +} diff --git a/source/java/org/alfresco/wcm/WCMTestSuite.java b/source/java/org/alfresco/wcm/WCMTestSuite.java index 7997dbea09..f09788af6d 100644 --- a/source/java/org/alfresco/wcm/WCMTestSuite.java +++ b/source/java/org/alfresco/wcm/WCMTestSuite.java @@ -54,7 +54,7 @@ public class WCMTestSuite extends TestSuite suite.addTestSuite(SandboxServiceImplTest.class); suite.addTestSuite(ScriptWebProjectsTest.class); suite.addTestSuite(PreviewURIServiceImplTest.class); - + suite.addTestSuite(WCMConcurrentTest.class); return suite; } } diff --git a/source/java/org/alfresco/wcm/actions/WCMSandboxRevertListAction.java b/source/java/org/alfresco/wcm/actions/WCMSandboxRevertListAction.java new file mode 100644 index 0000000000..e79acf1e03 --- /dev/null +++ b/source/java/org/alfresco/wcm/actions/WCMSandboxRevertListAction.java @@ -0,0 +1,94 @@ +/* + * 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.wcm.actions; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.wcm.sandbox.SandboxService; + +/** + * WCM Revert List example action (supercedes AVMUndoSandboxListAction) + * + * Undo (ie. throw away) list of changed assets in a user sandbox. + * + * The actionedUponNodeRef is a dummy and can be null. + * + * @author janv + */ +public class WCMSandboxRevertListAction extends ActionExecuterAbstractBase +{ + public static final String NAME = "wcm-revert-list"; + + public static final String PARAM_PATH_LIST = "path-list"; // list of paths (relative to sandbox store) + public static final String PARAM_SANDBOX_ID = "sandbox-id"; // sandbox store id + + /** + * The WCM SandboxService + */ + private SandboxService sbService; + + public void setSandboxService(SandboxService sbService) + { + this.sbService = sbService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String sbStoreId = (String)action.getParameterValue(PARAM_SANDBOX_ID); + List relativePaths = (List)action.getParameterValue(PARAM_PATH_LIST); + + sbService.revertList(sbStoreId, relativePaths); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add( + new ParameterDefinitionImpl(PARAM_PATH_LIST, + DataTypeDefinition.ANY, + true, + getParamDisplayLabel(PARAM_PATH_LIST))); + + paramList.add( + new ParameterDefinitionImpl(PARAM_SANDBOX_ID, + DataTypeDefinition.TEXT, + true, + getParamDisplayLabel(PARAM_SANDBOX_ID))); + } +} diff --git a/source/java/org/alfresco/wcm/actions/WCMSandboxRevertSnapshotAction.java b/source/java/org/alfresco/wcm/actions/WCMSandboxRevertSnapshotAction.java new file mode 100644 index 0000000000..14f58c3076 --- /dev/null +++ b/source/java/org/alfresco/wcm/actions/WCMSandboxRevertSnapshotAction.java @@ -0,0 +1,100 @@ +/* + * 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.wcm.actions; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; +import org.alfresco.wcm.sandbox.SandboxService; +import org.alfresco.wcm.util.WCMUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * WCM Revert Snapshot example action (supercedes AVMRevertStoreAction) + * + * Reverts (staging) sandbox to a specified snapshot version. + * + * @author janv + */ +public class WCMSandboxRevertSnapshotAction extends ActionExecuterAbstractBase +{ + private static Log logger = LogFactory.getLog(WCMSandboxRevertSnapshotAction.class); + + public static final String NAME = "wcm-revert-snapshot"; + public static final String PARAM_VERSION = "version"; + + /** + * The WCM SandboxService + */ + private SandboxService sbService; + + public void setSandboxService(SandboxService sbService) + { + this.sbService = sbService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // All this does is an override submit from the older version + // to head of the store implied in the path. + Pair pathVersion = + AVMNodeConverter.ToAVMVersionPath(actionedUponNodeRef); + + if (pathVersion.getFirst() != -1) + { + logger.warn("Ignored version "+pathVersion.getFirst()+" for "+actionedUponNodeRef+" (will revert latest)"); + } + + int revertVersion = (Integer)action.getParameterValue(PARAM_VERSION); + String sbStoreId = WCMUtil.getSandboxStoreId(pathVersion.getSecond()); + + sbService.revertSnapshot(sbStoreId, revertVersion); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add( + new ParameterDefinitionImpl(PARAM_VERSION, + DataTypeDefinition.INT, + true, + getParamDisplayLabel(PARAM_VERSION))); + } +} diff --git a/source/java/org/alfresco/wcm/actions/WCMSandboxSubmitAction.java b/source/java/org/alfresco/wcm/actions/WCMSandboxSubmitAction.java new file mode 100644 index 0000000000..782935ce9a --- /dev/null +++ b/source/java/org/alfresco/wcm/actions/WCMSandboxSubmitAction.java @@ -0,0 +1,101 @@ +/* + * 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.wcm.actions; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.wcm.sandbox.SandboxService; + +/** + * WCM Submit example action (supercedes SimpleAVMSubmitAction) + * + * Submit changed assets from a user sandbox to corresponding staging sandbox - either all or a list + * + * The actionedUponNodeRef is a dummy and can be null. + * + * @author janv + */ +public class WCMSandboxSubmitAction extends ActionExecuterAbstractBase +{ + public static String NAME = "wcm-submit"; + + public static final String PARAM_PATH_LIST = "path-list"; // list of paths (relative to sandbox store) + public static final String PARAM_SANDBOX_ID = "sandbox-id"; // sandbox store id + + /** + * The WCM SandboxService + */ + private SandboxService sbService; + + public void setSandboxService(SandboxService sbService) + { + this.sbService = sbService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String sbStoreId = (String)action.getParameterValue(PARAM_SANDBOX_ID); + List relativePaths = (List)action.getParameterValue(PARAM_PATH_LIST); + + if ((relativePaths == null) || (relativePaths.size() == 0)) + { + sbService.submitAll(sbStoreId, "Submit Action", "Submit all changed items in sandbox: " + sbStoreId); + } + else + { + sbService.submitList(sbStoreId, relativePaths, "Submit Action", "Submit list of changed items in sandbox: " + sbStoreId); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add( + new ParameterDefinitionImpl(PARAM_PATH_LIST, + DataTypeDefinition.ANY, + false, + getParamDisplayLabel(PARAM_PATH_LIST))); + + paramList.add( + new ParameterDefinitionImpl(PARAM_SANDBOX_ID, + DataTypeDefinition.TEXT, + true, + getParamDisplayLabel(PARAM_SANDBOX_ID))); + } +} diff --git a/source/java/org/alfresco/wcm/actions/WCMSandboxUndoAction.java b/source/java/org/alfresco/wcm/actions/WCMSandboxUndoAction.java new file mode 100644 index 0000000000..96e67c4829 --- /dev/null +++ b/source/java/org/alfresco/wcm/actions/WCMSandboxUndoAction.java @@ -0,0 +1,101 @@ +/* + * 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.wcm.actions; + +import java.util.List; + +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.wcm.sandbox.SandboxService; + +/** + * WCM Undo example action (supercedes AVMUndoSandboxListAction) + * + * Undo (ie. throw away) changed assets in a user sandbox - either all or a list + * + * The actionedUponNodeRef is a dummy and can be null. + * + * @author janv + */ +public class WCMSandboxUndoAction extends ActionExecuterAbstractBase +{ + public static final String NAME = "wcm-undo"; + + public static final String PARAM_PATH_LIST = "path-list"; // list of paths (relative to sandbox store) + public static final String PARAM_SANDBOX_ID = "sandbox-id"; // sandbox store id + + /** + * The WCM SandboxService + */ + private SandboxService sbService; + + public void setSandboxService(SandboxService sbService) + { + this.sbService = sbService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @SuppressWarnings("unchecked") + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + String sbStoreId = (String)action.getParameterValue(PARAM_SANDBOX_ID); + List relativePaths = (List)action.getParameterValue(PARAM_PATH_LIST); + + if ((relativePaths == null) || (relativePaths.size() == 0)) + { + sbService.revertAll(sbStoreId); + } + else + { + sbService.revertList(sbStoreId, relativePaths); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add( + new ParameterDefinitionImpl(PARAM_PATH_LIST, + DataTypeDefinition.ANY, + false, + getParamDisplayLabel(PARAM_PATH_LIST))); + + paramList.add( + new ParameterDefinitionImpl(PARAM_SANDBOX_ID, + DataTypeDefinition.TEXT, + true, + getParamDisplayLabel(PARAM_SANDBOX_ID))); + } +} diff --git a/source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java b/source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java index ce4c132a19..1fd43f376f 100644 --- a/source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java +++ b/source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java @@ -28,7 +28,9 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; @@ -41,7 +43,6 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.namespace.QName; import org.alfresco.wcm.AbstractWCMServiceImplTest; import org.alfresco.wcm.sandbox.SandboxInfo; -import org.alfresco.wcm.sandbox.SandboxService; import org.alfresco.wcm.util.WCMUtil; import org.alfresco.wcm.webproject.WebProjectInfo; @@ -52,12 +53,6 @@ import org.alfresco.wcm.webproject.WebProjectInfo; */ public class AssetServiceImplTest extends AbstractWCMServiceImplTest { - // - // services - // - - private SandboxService sbService; - private AssetService assetService; // test data private static final String PREFIX = "created-by-admin-"; @@ -68,10 +63,6 @@ public class AssetServiceImplTest extends AbstractWCMServiceImplTest protected void setUp() throws Exception { super.setUp(); - - // Get the required services - sbService = (SandboxService)ctx.getBean("SandboxService"); - assetService = (AssetService)ctx.getBean("AssetService"); } @Override @@ -1136,6 +1127,67 @@ public class AssetServiceImplTest extends AbstractWCMServiceImplTest assertTrue(assetService.hasLockAccess(myFile1Asset)); } + public void testSimpleLockFile2() throws InterruptedException + { + AuthenticationUtil.setFullyAuthenticatedUser(USER_ADMIN); + + // create web project (also creates staging sandbox and admin's author sandbox) + WebProjectInfo wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-partialSubmitWithNewFolder", TEST_WEBPROJ_NAME+"-partialSubmitWithNewFolder", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, TEST_WEBPROJ_DONT_USE_AS_TEMPLATE, null); + String defaultWebApp = wpInfo.getDefaultWebApp(); + + // get admin's sandbox + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpInfo.getStoreId()); + String sbStoreId = sbInfo.getSandboxId(); + + String path = sbInfo.getSandboxRootPath() + "/" + defaultWebApp; + + // create folder + assetService.createFolderWebApp(sbStoreId, defaultWebApp, "/", "myDir1"); + AssetInfo myDir1Asset = assetService.getAssetWebApp(sbStoreId, defaultWebApp, "myDir1"); + checkAssetInfo(myDir1Asset, "myDir1", path+"/myDir1", USER_ADMIN, false, true, false, false, null); + + // note: folders do not get locked + assertNull(assetService.getLockOwner(myDir1Asset)); + assertTrue(assetService.hasLockAccess(myDir1Asset)); + + // create two files + assetService.createFileWebApp(sbStoreId, defaultWebApp, "/myDir1", "myFile1"); + assetService.createFileWebApp(sbStoreId, defaultWebApp, "/myDir1", "myFile2"); + + AssetInfo myFile1Asset = assetService.getAssetWebApp(sbStoreId, defaultWebApp, "myDir1/myFile1"); + checkAssetInfo(myFile1Asset, "myFile1", path+"/myDir1/myFile1", USER_ADMIN, true, false, false, true, USER_ADMIN); + + assertEquals(USER_ADMIN, assetService.getLockOwner(myFile1Asset)); + assertTrue(assetService.hasLockAccess(myFile1Asset)); + + AssetInfo myFile2Asset = assetService.getAssetWebApp(sbStoreId, defaultWebApp, "myDir1/myFile2"); + checkAssetInfo(myFile2Asset, "myFile2", path+"/myDir1/myFile2", USER_ADMIN, true, false, false, true, USER_ADMIN); + + assertEquals(USER_ADMIN, assetService.getLockOwner(myFile2Asset)); + assertTrue(assetService.hasLockAccess(myFile2Asset)); + + List changedAssets = sbService.listChangedWebApp(sbStoreId, defaultWebApp, false); + assertEquals(1, changedAssets.size()); + myDir1Asset = changedAssets.get(0); + checkAssetInfo(myDir1Asset, "myDir1", path+"/myDir1", USER_ADMIN, false, true, false, false, null); + + List selectedAssetsToSubmit = new ArrayList(1); + selectedAssetsToSubmit.add(myFile1Asset); + + // partial submit with new folder + sbService.submitListAssets(sbStoreId, selectedAssetsToSubmit, "submit1 label", "submit1 comment"); + Thread.sleep(SUBMIT_DELAY); + + changedAssets = sbService.listChangedWebApp(sbStoreId, defaultWebApp, false); + assertEquals(1, changedAssets.size()); + myFile2Asset = changedAssets.get(0); + + // ETWOTWO-1265 + checkAssetInfo(myFile2Asset, "myFile2", path+"/myDir1/myFile2", USER_ADMIN, true, false, false, true, USER_ADMIN); + assertEquals(USER_ADMIN, assetService.getLockOwner(myFile2Asset)); + assertTrue(assetService.hasLockAccess(myFile2Asset)); + } + public void testSimpleImport() { // create web project (also creates staging sandbox and admin's author sandbox) diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java index 16351939b8..b619117e08 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxFactory.java @@ -36,6 +36,7 @@ import org.alfresco.config.JNDIConstants; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.avm.actions.AVMDeployWebsiteAction; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; @@ -91,6 +92,8 @@ public final class SandboxFactory extends WCMUtil ZONES = Collections.unmodifiableSet(zones); } + private final static QName PROP_SANDBOX_LOCALHOST_DEPLOYED = QName.createQName(null, ".sandbox.localhost."+AVMDeployWebsiteAction.LIVE_SUFFIX); + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; @@ -239,7 +242,7 @@ public final class SandboxFactory extends WCMUtil sandboxIdProp, new PropertyValue(DataTypeDefinition.TEXT, null)); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { dumpStoreProperties(avmService, stagingStoreName); dumpStoreProperties(avmService, previewStoreName); @@ -256,10 +259,12 @@ public final class SandboxFactory extends WCMUtil */ /* package */ SandboxInfo getSandbox(String sandboxId) { - return getSandbox(sandboxId, true); + String wpStoreId = WCMUtil.getWebProjectStoreId(sandboxId); + + return getSandbox(wpStoreId, sandboxId, true); } - /* package */ SandboxInfo getSandbox(final String sandboxId, boolean withPreview) + private SandboxInfo getSandbox(String wpStoreId, String sandboxId, boolean withPreview) { AVMStoreDescriptor storeDesc = avmService.getStore(sandboxId); if (storeDesc == null) @@ -267,8 +272,6 @@ public final class SandboxFactory extends WCMUtil return null; } - String wpStoreId = WCMUtil.getWebProjectStoreId(sandboxId); - String[] storeNames = null; // Check sandbox type @@ -319,13 +322,21 @@ public final class SandboxFactory extends WCMUtil name = WCMUtil.getWorkflowId(sandboxId); storeNames = new String[] {WCMUtil.getCorrespondingMainStoreName(sandboxId), sandboxId}; } + else if (WCMUtil.isLocalhostDeployedStore(wpStoreId, sandboxId)) + { + // TODO refactor - pending explicit WCM services support for deployment config + sandboxType = PROP_SANDBOX_LOCALHOST_DEPLOYED; + name = sandboxId; + storeNames = new String[] {sandboxId}; + } if ((storeNames == null) || (storeNames.length == 0)) { throw new AlfrescoRuntimeException("Must have at least one store"); } - if ((storeNames.length == 1) && (! sandboxType.equals(SandboxConstants.PROP_SANDBOX_STAGING_MAIN))) + if ((storeNames.length == 1) && + ((! sandboxType.equals(SandboxConstants.PROP_SANDBOX_STAGING_MAIN)) && (! sandboxType.equals(PROP_SANDBOX_LOCALHOST_DEPLOYED)))) { throw new AlfrescoRuntimeException("Main store must be of type: " + SandboxConstants.PROP_SANDBOX_STAGING_MAIN); } @@ -685,7 +696,7 @@ public final class SandboxFactory extends WCMUtil sandboxIdProp, new PropertyValue(DataTypeDefinition.TEXT, null)); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { dumpStoreProperties(avmService, userStoreName); dumpStoreProperties(avmService, previewStoreName); @@ -802,7 +813,7 @@ public final class SandboxFactory extends WCMUtil sandboxIdProp, new PropertyValue(DataTypeDefinition.TEXT, null)); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { dumpStoreProperties(avmService, mainStoreName); dumpStoreProperties(avmService, previewStoreName); @@ -832,6 +843,7 @@ public final class SandboxFactory extends WCMUtil */ public SandboxInfo createReadOnlyWorkflowSandbox(final String storeId) { + String wpStoreId = WCMUtil.getWebProjectStoreId(storeId); String stagingStoreName = WCMUtil.buildStagingStoreName(storeId); // create the workflow 'main' store @@ -879,12 +891,12 @@ public final class SandboxFactory extends WCMUtil sandboxIdProp, new PropertyValue(DataTypeDefinition.TEXT, null)); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { dumpStoreProperties(avmService, mainStoreName); } - return getSandbox(mainStoreName, false); // no preview store + return getSandbox(wpStoreId, mainStoreName, false); // no preview store } @@ -1023,8 +1035,12 @@ public final class SandboxFactory extends WCMUtil return workflowStoreName; } - // list all sandboxes for a web project - public List listAllSandboxes(final String wpStoreId) + public List listAllSandboxes(String wpStoreId) + { + return listAllSandboxes(wpStoreId, false, false); + } + + public List listAllSandboxes(String wpStoreId, boolean includeWorkflowSandboxes, boolean includeLocalhostDeployed) { List stores = avmService.getStores(); @@ -1033,21 +1049,27 @@ public final class SandboxFactory extends WCMUtil { String storeName = store.getName(); - // list main stores - not preview stores or workflow stores + // list main stores - not preview stores or workflow stores or locally deployed "live" ASR servers (LIVE or TEST) if ((storeName.startsWith(wpStoreId)) && (! WCMUtil.isPreviewStore(storeName)) && - (! WCMUtil.isWorkflowStore(storeName))) + ((includeLocalhostDeployed || (! WCMUtil.isLocalhostDeployedStore(wpStoreId, storeName)))) && + ((includeWorkflowSandboxes || (! WCMUtil.isWorkflowStore(storeName)))) + ) { - sbInfos.add(getSandbox(storeName)); + sbInfos.add(getSandbox(wpStoreId, storeName, true)); } } return sbInfos; } - public void deleteSandbox(String sbStoreId) { - SandboxInfo sbInfo = getSandbox(sbStoreId); + deleteSandbox(WCMUtil.getWebProjectStoreId(sbStoreId), sbStoreId); + } + + public void deleteSandbox(String wpStoreId, String sbStoreId) + { + SandboxInfo sbInfo = getSandbox(wpStoreId, sbStoreId, true); if (sbInfo != null) { @@ -1055,7 +1077,7 @@ public final class SandboxFactory extends WCMUtil // found the sandbox to remove - remove the main store (eg. user main store, staging main store, workflow main store) String path = WCMUtil.buildSandboxRootPath(mainSandboxStore); - + // Notify virtualisation server about removing this sandbox. // // Implementation note: @@ -1094,6 +1116,11 @@ public final class SandboxFactory extends WCMUtil // remove any locks this user may have avmLockingService.removeStoreLocks(avmStoreName); } + + if (logger.isDebugEnabled()) + { + logger.debug("Deleted sandbox: " + mainSandboxStore); + } } } @@ -1294,11 +1321,11 @@ public final class SandboxFactory extends WCMUtil */ private static void dumpStoreProperties(AVMService avmService, String store) { - logger.debug("Store " + store); + logger.trace("Store " + store); Map props = avmService.getStoreProperties(store); for (QName name : props.keySet()) { - logger.debug(" " + name + ": " + props.get(name)); + logger.trace(" " + name + ": " + props.get(name)); } } diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java index 7037221007..b38d8f5dfa 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImpl.java @@ -37,8 +37,6 @@ import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.model.WCMAppModel; import org.alfresco.model.WCMWorkflowModel; import org.alfresco.repo.avm.AVMNodeConverter; -import org.alfresco.repo.avm.actions.AVMRevertStoreAction; -import org.alfresco.repo.avm.actions.AVMUndoSandboxListAction; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; @@ -47,11 +45,10 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.workflow.WorkflowModel; -import org.alfresco.service.cmr.action.Action; -import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.VersionDescriptor; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -98,10 +95,10 @@ public class SandboxServiceImpl implements SandboxService private AVMSyncService avmSyncService; private NameMatcher nameMatcher; private VirtServerRegistry virtServerRegistry; - private ActionService actionService; private WorkflowService workflowService; private AssetService assetService; private TransactionService transactionService; + private AVMLockingService avmLockingService; public void setWebProjectService(WebProjectService wpService) @@ -119,6 +116,11 @@ public class SandboxServiceImpl implements SandboxService this.avmService = avmService; } + public void setAvmLockingService(AVMLockingService avmLockingService) + { + this.avmLockingService = avmLockingService; + } + public void setAvmSyncService(AVMSyncService avmSyncService) { this.avmSyncService = avmSyncService; @@ -134,11 +136,6 @@ public class SandboxServiceImpl implements SandboxService this.virtServerRegistry = virtServerRegistry; } - public void setActionService(ActionService actionService) - { - this.actionService = actionService; - } - public void setWorkflowService(WorkflowService workflowService) { this.workflowService = workflowService; @@ -246,11 +243,12 @@ public class SandboxServiceImpl implements SandboxService { ParameterCheck.mandatoryString("wpStoreId", wpStoreId); - String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); - List sbInfos = null; - if (wpService.isContentManager(wpStoreId, currentUser)) + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + String userRole = wpService.getWebUserRole(wpStoreId, currentUser); + + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(userRole) || WCMUtil.ROLE_CONTENT_PUBLISHER.equals(userRole)) { sbInfos = sandboxFactory.listAllSandboxes(wpStoreId); } @@ -279,9 +277,12 @@ public class SandboxServiceImpl implements SandboxService ParameterCheck.mandatoryString("wpStoreId", wpStoreId); ParameterCheck.mandatoryString("userName", userName); - if (! wpService.isContentManager(wpStoreId)) + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + String userRole = wpService.getWebUserRole(wpStoreId, currentUser); + + if (WCMUtil.ROLE_CONTENT_MANAGER.equals(userRole) || WCMUtil.ROLE_CONTENT_PUBLISHER.equals(userRole)) { - throw new AccessDeniedException("Only content managers may list sandboxes for '"+userName+"' (web project id: "+wpStoreId+")"); + throw new AccessDeniedException("Only content managers or content publishers may list sandboxes for '"+userName+"' (web project id: "+wpStoreId+")"); } return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() @@ -328,9 +329,13 @@ public class SandboxServiceImpl implements SandboxService { String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); - if (! ((WCMUtil.getUserName(sbStoreId).equals(currentUser)) || (wpService.isContentManager(wpStoreId, currentUser)))) + if (! (WCMUtil.getUserName(sbStoreId).equals(currentUser))) { - throw new AccessDeniedException("Only content managers may get sandbox '"+sbStoreId+"' (web project id: "+wpStoreId+")"); + String userRole = wpService.getWebUserRole(wpStoreId, currentUser); + if (! (WCMUtil.ROLE_CONTENT_MANAGER.equals(userRole) || WCMUtil.ROLE_CONTENT_PUBLISHER.equals(userRole))) + { + throw new AccessDeniedException("Only content managers or content publishers may get sandbox '"+sbStoreId+"' (web project id: "+wpStoreId+")"); + } } } @@ -951,7 +956,9 @@ public class SandboxServiceImpl implements SandboxService // checks sandbox access (TODO review) getSandbox(sbStoreId); // ignore result - List> versionPaths = new ArrayList>(assets.size()); + String wpStoreId = WCMUtil.getWebProjectStoreId(sbStoreId); + + List assetsToRevert = new ArrayList(assets.size()); List tasks = null; for (AssetInfo asset : assets) @@ -961,27 +968,68 @@ public class SandboxServiceImpl implements SandboxService tasks = WCMWorkflowUtil.getAssociatedTasksForSandbox(workflowService, WCMUtil.getSandboxStoreId(asset.getAvmPath())); } - // TODO ... extra lookup ... either return AVMNodeDescriptor or change getAssociatedTasksForNode ... - AVMNodeDescriptor node = avmService.lookup(-1, asset.getAvmPath()); + // TODO refactor getAssociatedTasksForNode to use AssetInfo instead of AVMNodeDescriptor + AVMNodeDescriptor node = ((AssetInfoImpl)asset).getAVMNodeDescriptor(); - if (WCMWorkflowUtil.getAssociatedTasksForNode(avmService, node, tasks).size() == 0) + if (node != null) { - String revertPath = asset.getAvmPath(); - versionPaths.add(new Pair(-1, revertPath)); - - if (VirtServerUtils.requiresUpdateNotification(revertPath)) - { - // Bind the post-commit transaction listener with data required for virtualization server notification - UpdateSandboxTransactionListener tl = new UpdateSandboxTransactionListener(revertPath); - AlfrescoTransactionSupport.bindListener(tl); - } + if (WCMWorkflowUtil.getAssociatedTasksForNode(avmService, node, tasks).size() == 0) + { + assetsToRevert.add(asset); + + if (VirtServerUtils.requiresUpdateNotification(asset.getAvmPath())) + { + // Bind the post-commit transaction listener with data required for virtualization server notification + UpdateSandboxTransactionListener tl = new UpdateSandboxTransactionListener(asset.getAvmPath()); + AlfrescoTransactionSupport.bindListener(tl); + } + } } } - Map args = new HashMap(1, 1.0f); - args.put(AVMUndoSandboxListAction.PARAM_NODE_LIST, (Serializable)versionPaths); - Action action = actionService.createAction(AVMUndoSandboxListAction.NAME, args); - actionService.executeAction(action, null); // dummy action ref, list passed as action arg + for (AssetInfo asset : assetsToRevert) + { + String [] parentChild = AVMNodeConverter.SplitBase(asset.getAvmPath()); + if (parentChild.length != 2) + { + continue; + } + + AVMNodeDescriptor parent = avmService.lookup(-1, parentChild[0], true); + + if (parent.isLayeredDirectory()) + { + if (logger.isDebugEnabled()) + { + logger.debug("reverting " + parentChild[1] + " in " + parentChild[0]); + } + + avmService.makeTransparent(parentChild[0], parentChild[1]); + } + + if (asset.isFile()) + { + // is file or deleted file + String relativePath = asset.getPath(); + + if (logger.isDebugEnabled()) + { + logger.debug("unlocking file " + relativePath + " in web project " + wpStoreId); + } + + if (avmLockingService.getLock(wpStoreId, relativePath) != null) + { + avmLockingService.removeLock(wpStoreId, relativePath); + } + else + { + if (logger.isWarnEnabled()) + { + logger.warn("expected file " + relativePath + " in " + wpStoreId + " to be locked"); + } + } + } + } } /* (non-Javadoc) @@ -1039,33 +1087,31 @@ public class SandboxServiceImpl implements SandboxService /* (non-Javadoc) * @see org.alfresco.wcm.sandbox.SandboxService#revertSnapshot(java.lang.String, int) */ - public void revertSnapshot(final String sbStoreId, final int version) + public void revertSnapshot(final String sbStoreId, final int revertVersion) { ParameterCheck.mandatoryString("sbStoreId", sbStoreId); - + String wpStoreId = WCMUtil.getWebProjectStoreId(sbStoreId); if (! wpService.isContentManager(wpStoreId)) { throw new AccessDeniedException("Only content managers may revert staging sandbox '"+sbStoreId+"' (web project id: "+wpStoreId+")"); } - + // do this as system as the staging area has restricted access (and content manager may not have permission to delete children, for example) List diffs = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() { public List doWork() throws Exception { - String sandboxPath = WCMUtil.buildSandboxRootPath(sbStoreId); - - List diffs = avmSyncService.compare(-1, sandboxPath, version, sandboxPath, null); - - Map args = new HashMap(1, 1.0f); - args.put(AVMRevertStoreAction.PARAM_VERSION, version); - Action action = actionService.createAction(AVMRevertStoreAction.NAME, args); - actionService.executeAction(action, AVMNodeConverter.ToNodeRef(-1, sbStoreId + WCMUtil.AVM_STORE_SEPARATOR + "/")); + String sandboxPath = sbStoreId + WCMUtil.AVM_STORE_SEPARATOR + "/"; + List diffs = avmSyncService.compare(revertVersion, sandboxPath, -1, sandboxPath, null); + + String message = "Reverted to Version " + revertVersion + "."; + avmSyncService.update(diffs, null, false, false, true, true, message, message); + return diffs; } }, AuthenticationUtil.getSystemUserName()); - + // See if any of the files being reverted require notification of the virt server, to update the webapp for (AVMDifference diff : diffs) { diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java index bc6909d285..164094fdfb 100644 --- a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java +++ b/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java @@ -26,22 +26,32 @@ package org.alfresco.wcm.sandbox; import java.io.IOException; import java.io.InputStream; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.alfresco.config.JNDIConstants; +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.GUID; import org.alfresco.wcm.AbstractWCMServiceImplTest; +import org.alfresco.wcm.actions.WCMSandboxRevertSnapshotAction; +import org.alfresco.wcm.actions.WCMSandboxSubmitAction; +import org.alfresco.wcm.actions.WCMSandboxUndoAction; import org.alfresco.wcm.asset.AssetInfo; -import org.alfresco.wcm.asset.AssetService; import org.alfresco.wcm.util.WCMUtil; import org.alfresco.wcm.webproject.WebProjectInfo; @@ -62,9 +72,6 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // services // - private SandboxService sbService; - private AssetService assetService; - private AVMService avmService; // non-locking-aware //private AVMService avmLockingAwareService; @@ -77,9 +84,6 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest super.setUp(); // Get the required services - sbService = (SandboxService)ctx.getBean("SandboxService"); - assetService = (AssetService)ctx.getBean("AssetService"); - avmService = (AVMService)ctx.getBean("AVMService"); // WCM locking @@ -296,9 +300,9 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - // Content publisher - can only list own sandbox and staging + // Content publisher - can list all sandboxes sbInfos = sbService.listSandboxes(wpInfo.getStoreId()); - assertEquals(2, sbInfos.size()); + assertEquals(6, sbInfos.size()); // Switch to USER_THREE AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); @@ -360,21 +364,14 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; sbInfo = sbService.getSandbox(userSandboxId); checkSandboxInfo(sbInfo, userSandboxId, USER_THREE, AuthenticationUtil.getAdminUserName(), userSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); - + // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - try - { - // Content publisher - try to get another user's sandbox (-ve test) - userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; - sbInfo = sbService.getSandbox(userSandboxId); - fail("Shouldn't be able to get another author's sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } + // content publisher can get any (author) sandbox + userSandboxId = TEST_SANDBOX+"-get" + "--" + USER_THREE; + sbInfo = sbService.getSandbox(userSandboxId); + checkSandboxInfo(sbInfo, userSandboxId, USER_THREE, AuthenticationUtil.getAdminUserName(), userSandboxId, SandboxConstants.PROP_SANDBOX_AUTHOR_MAIN); // Switch to USER_THREE AuthenticationUtil.setFullyAuthenticatedUser(USER_THREE); @@ -483,7 +480,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); + assertEquals(4, sbService.listSandboxes(wpStoreId).size()); sbInfo = sbService.getAuthorSandbox(wpStoreId); assertNotNull(sbInfo); @@ -491,7 +488,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // can delete own sandbox sbService.deleteSandbox(sbInfo.getSandboxId()); - assertEquals(1, sbService.listSandboxes(wpStoreId).size()); + assertEquals(3, sbService.listSandboxes(wpStoreId).size()); sbInfo = sbService.getAuthorSandbox(wpStoreId); assertNull(sbInfo); @@ -624,22 +621,6 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest fail("The asset '" + asset.getName() + "' is not recognised"); } } - - // Switch to USER_TWO - AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - - assertEquals(2, sbService.listSandboxes(wpStoreId).size()); - - try - { - // Content Contributor should not be able to list another user's changes (-ve test) - assets = sbService.listChangedAll(userOneSandboxId, true); - fail("Shouldn't allow non-content-manager to get modified list for another sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } // test roles @@ -666,7 +647,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest try { - // Content publisher - try to list changes in another user's sandbox (-ve test) + // Content contributor - try to list changes in another user's sandbox (-ve test) assets = sbService.listChangedAll(userTwoSandboxId, true); fail("Shouldn't be able to list another author's sandbox changes"); } @@ -678,16 +659,9 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - try - { - // Content contributor - try to list changes in another user's sandbox (-ve test) - assets = sbService.listChangedAll(userOneSandboxId, true); - fail("Shouldn't be able to list another author's sandbox changes"); - } - catch (AccessDeniedException exception) - { - // Expected - } + // Content Publisher should be able to list another user's changes + assets = sbService.listChangedAll(userOneSandboxId, true); + assertEquals(2, assets.size()); // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); @@ -976,7 +950,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // content manager can submit any (author) sandbox userSandboxId = wpStoreId + "--" + USER_ONE; sbService.submitAll(userSandboxId, "my submit", null); - + // Switch to USER_ONE AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); @@ -996,17 +970,9 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - try - { - // Content publisher - try to submit another user's sandbox (-ve test) - userSandboxId = wpStoreId + "--" + USER_ONE; - sbService.submitAll(userSandboxId, "my submit", null); - fail("Shouldn't be able to submit another author's sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } + // Content publisher - can submit other sandboxes (eg. submit all) + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.submitAll(userSandboxId, "my submit", null); // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); @@ -1341,8 +1307,8 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest assertNotNull(assetService.getAssetWebApp(stagingSandboxId, webApp, "/myDir1/myFile2")); } - // revert (changed) assets in user sandbox - public void testRevert() throws IOException, InterruptedException + // revert/undo (changed) assets in user sandbox + public void testUndo() throws IOException, InterruptedException { WebProjectInfo wpInfo = wpService.createWebProject(TEST_SANDBOX+"-revertChangedAssets", TEST_WEBPROJ_NAME+" revertChangedAssets", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); @@ -1352,12 +1318,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Invite web users wpService.inviteWebUser(wpStoreId, USER_ONE, WCMUtil.ROLE_CONTENT_CONTRIBUTOR, true); - - // TODO - pending fix for ETWOTWO-981 - //wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); - - wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_MANAGER, true); - + wpService.inviteWebUser(wpStoreId, USER_TWO, WCMUtil.ROLE_CONTENT_PUBLISHER, true); wpService.inviteWebUser(wpStoreId, USER_THREE, WCMUtil.ROLE_CONTENT_MANAGER, true); wpService.inviteWebUser(wpStoreId, USER_FOUR, WCMUtil.ROLE_CONTENT_REVIEWER, true); @@ -1415,15 +1376,13 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // no changes yet assets = sbService.listChangedAll(authorSandboxId, true); assertEquals(0, assets.size()); - - //authorSandboxWebppPath = authorSandboxId + AVM_STORE_SEPARATOR + sbInfo.getSandboxRootPath() + "/" + webApp; final String MYFILE1_MODIFIED = "This is myFile1 ... modified by "+USER_TWO; writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myFile1")); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); writer.setEncoding("UTF-8"); writer.putContent(MYFILE1_MODIFIED); - + final String MYFILE2_MODIFIED = "This is myFile2 ... modified by "+USER_TWO; writer = assetService.getContentWriter(assetService.getAssetWebApp(authorSandboxId, webApp, "/myDir1/myFile2")); writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); @@ -1486,7 +1445,7 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // content manager can revert any (author) sandbox userSandboxId = wpStoreId + "--" + USER_ONE; sbService.revertAll(userSandboxId); - + // Switch to USER_ONE AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); @@ -1503,24 +1462,15 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest // Expected } - // TODO - pending fix for ETWOTWO-981 - see above - /* // Switch to USER_TWO AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); - try - { - // Content publisher - try to revert another user's sandbox (-ve test) - userSandboxId = wpStoreId + "--" + USER_ONE; - sbService.revertAll(userSandboxId); - fail("Shouldn't be able to revert another author's sandbox"); - } - catch (AccessDeniedException exception) - { - // Expected - } - */ + // TODO - requires more testing - eg. revert some changes + // Content publisher - can revert another user's sandbox + userSandboxId = wpStoreId + "--" + USER_ONE; + sbService.revertAll(userSandboxId); + // Switch to USER_FOUR AuthenticationUtil.setFullyAuthenticatedUser(USER_FOUR); @@ -1766,6 +1716,447 @@ public class SandboxServiceImplTest extends AbstractWCMServiceImplTest } } + public void testRevertSnapshot_ETWOTWO_1244() throws IOException, InterruptedException + { + Date fromDate = new Date(); + + WebProjectInfo wpInfo = wpService.createWebProject(TEST_SANDBOX+"-revertSnapshot2", TEST_WEBPROJ_NAME+" revertSnapshot2", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + + final String wpStoreId = wpInfo.getStoreId(); + final String webApp = wpInfo.getDefaultWebApp(); + final String stagingSandboxId = wpInfo.getStagingStoreName(); + + SandboxInfo sbInfo = sbService.getStagingSandbox(wpStoreId); + String stagingSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + sbInfo = sbService.getAuthorSandbox(wpStoreId); + String authorSandboxId = sbInfo.getSandboxId(); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + webApp; + + List sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + assertEquals(0, sbVersions.size()); + + assetService.createFile(authorSandboxId, authorSandboxPath, "c1.txt", null); + sbService.submitWebApp(authorSandboxId, webApp, "s1", "s1"); + Thread.sleep(SUBMIT_DELAY); + + List listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(1, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + assetService.createFile(authorSandboxId, authorSandboxPath, "c2.txt", null); + sbService.submitWebApp(authorSandboxId, webApp, "s2", "s2"); + Thread.sleep(SUBMIT_DELAY); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c2.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + int snapshotVersionId2 = sbVersions.get(0).getVersion(); + + assetService.createFile(authorSandboxId, authorSandboxPath, "c3.txt", null); + sbService.submitWebApp(authorSandboxId, webApp, "s3", "s3"); + Thread.sleep(SUBMIT_DELAY); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(3, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c2.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c3.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + sbVersions = sbService.listSnapshots(stagingSandboxId, fromDate, new Date(), false); + int snapshotVersionId3 = sbVersions.get(0).getVersion(); + + assetService.createFile(authorSandboxId, authorSandboxPath, "c4.txt", null); + sbService.submitWebApp(authorSandboxId, webApp, "s4", "s4"); + Thread.sleep(SUBMIT_DELAY); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(4, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c2.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c3.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c4.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + AssetInfo file = assetService.getAsset(authorSandboxId, authorSandboxPath+"/c2.txt"); + assetService.deleteAsset(file); + sbService.submitWebApp(authorSandboxId, webApp, "s5", "s5"); + Thread.sleep(SUBMIT_DELAY); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(3, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c3.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c4.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // revert to snapshot + sbService.revertSnapshot(stagingSandboxId, snapshotVersionId2); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(2, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c2.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + + // revert to snapshot + sbService.revertSnapshot(stagingSandboxId, snapshotVersionId3); + + listing = assetService.listAssets(stagingSandboxId, -1, stagingSandboxPath, false); + assertEquals(3, listing.size()); + for (AssetInfo asset : listing) + { + if (asset.getName().equals("c1.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c2.txt") && asset.isFile()) + { + continue; + } + else if (asset.getName().equals("c3.txt") && asset.isFile()) + { + continue; + } + else + { + fail("The asset '" + asset.getName() + "' is not recognised"); + } + } + } + + // submit sandbox + public void testSubmitAction() throws Exception + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_SANDBOX+"-submitAction", TEST_WEBPROJ_NAME+" submitAction", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + String webApp = wpInfo.getDefaultWebApp(); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + final String sbStoreId = sbInfo.getSandboxId(); + + assetService.createFolderWebApp(sbStoreId, webApp, "/", "a"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a", "b"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a/b", "c"); + + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "foo"); + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "bar"); + + List changedAssets = sbService.listChangedWebApp(sbStoreId, webApp, false); + assertEquals(1, changedAssets.size()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a", changedAssets.get(0).getPath()); + + final ActionImpl action = new ActionImpl(null, GUID.generate(), WCMSandboxSubmitAction.NAME); + + action.setParameterValue(WCMSandboxUndoAction.PARAM_SANDBOX_ID, sbStoreId); + action.setParameterValue(WCMSandboxUndoAction.PARAM_PATH_LIST, null); + + final WCMSandboxSubmitAction submit = (WCMSandboxSubmitAction)ctx.getBean(WCMSandboxSubmitAction.NAME); + + class TxnWork implements RetryingTransactionCallback + { + public Object execute() throws Exception + { + submit.execute(action, null); + return null; + } + }; + TransactionService transactionService = (TransactionService) ctx.getBean("transactionService"); + + // first submit - all (note: within /www/avm_webapps) + transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); + + Thread.sleep(SUBMIT_DELAY); + + assetService.createFile(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, "figs", null); + + AssetInfo fileAsset = assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/foo"); + assetService.getContentWriter(fileAsset).getContentOutputStream().close(); + + fileAsset = assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/bar"); + assetService.deleteAsset(fileAsset); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(3, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/bar", changedAssets.get(0).getPath()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(1).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/foo", changedAssets.get(1).getPath()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(2).getDiffCode()); + assertEquals("/"+ JNDIConstants.DIR_DEFAULT_WWW+"/figs", changedAssets.get(2).getPath()); + + List paths = new ArrayList(); + paths.add(changedAssets.get(0).getPath()); + paths.add(changedAssets.get(1).getPath()); + paths.add(changedAssets.get(2).getPath()); + + // second submit - list (note: including above /www/avm_webapps) + action.setParameterValue(WCMSandboxUndoAction.PARAM_PATH_LIST, (Serializable)paths); + action.setParameterValue(WCMSandboxUndoAction.PARAM_SANDBOX_ID, sbStoreId); + + transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); + + Thread.sleep(SUBMIT_DELAY); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(0, changedAssets.size()); + } + + // revert/undo sandbox + public void testUndoAction() throws Exception + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_SANDBOX+"-revertListAction", TEST_WEBPROJ_NAME+" revertListAction", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + String webApp = wpInfo.getDefaultWebApp(); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + final String sbStoreId = sbInfo.getSandboxId(); + + assetService.createFolderWebApp(sbStoreId, webApp, "/", "a"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a", "b"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a/b", "c"); + + List changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a", changedAssets.get(0).getPath()); + + sbService.submitWebApp(sbStoreId, webApp, "submitLabel", "submitDescription"); + + Thread.sleep(SUBMIT_DELAY); + + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "foo"); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/foo", changedAssets.get(0).getPath()); + + sbService.submitWebApp(sbStoreId, webApp, "submitLabel", "submitDescription"); + + Thread.sleep(SUBMIT_DELAY); + + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "bar"); + + assertNotNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/bar")); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/bar", changedAssets.get(0).getPath()); + + List snapshotVersions = sbService.listSnapshots(sbStoreId, false); + assertEquals(2, snapshotVersions.size()); + + final ActionImpl action = new ActionImpl(null, GUID.generate(), WCMSandboxUndoAction.NAME); + + List paths = new ArrayList(); + paths.add(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/bar"); + + action.setParameterValue(WCMSandboxUndoAction.PARAM_PATH_LIST, (Serializable)paths); + action.setParameterValue(WCMSandboxUndoAction.PARAM_SANDBOX_ID, sbStoreId); + + final WCMSandboxUndoAction revertList = (WCMSandboxUndoAction)ctx.getBean(WCMSandboxUndoAction.NAME); + + class TxnWork implements RetryingTransactionCallback + { + public Object execute() throws Exception + { + revertList.execute(action, null); + return null; + } + }; + TransactionService transactionService = (TransactionService) ctx.getBean("transactionService"); + transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); + + Thread.sleep(SUBMIT_DELAY); + + snapshotVersions = sbService.listSnapshots(sbStoreId, false); + assertEquals(2, snapshotVersions.size()); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(0, changedAssets.size()); + + assertNotNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/foo")); + assertNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/bar")); + } + + public void testRevertSnapshotAction() throws Exception + { + WebProjectInfo wpInfo = wpService.createWebProject(TEST_SANDBOX+"-revertSnapshotAction", TEST_WEBPROJ_NAME+" revertSnapshotAction", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION); + String wpStoreId = wpInfo.getStoreId(); + String webApp = wpInfo.getDefaultWebApp(); + final String stagingStoreId = wpInfo.getStagingStoreName(); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + final String sbStoreId = sbInfo.getSandboxId(); + + assetService.createFolderWebApp(sbStoreId, webApp, "/", "a"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a", "b"); + assetService.createFolderWebApp(sbStoreId, webApp, "/a/b", "c"); + + List changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a", changedAssets.get(0).getPath()); + + sbService.submitWebApp(sbStoreId, webApp, "submitLabel", "submitDescription"); + + Thread.sleep(SUBMIT_DELAY); + + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "foo"); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/foo", changedAssets.get(0).getPath()); + + sbService.submitWebApp(sbStoreId, webApp, "submitLabel", "submitDescription"); + + Thread.sleep(SUBMIT_DELAY); + + assetService.createFileWebApp(sbStoreId, webApp, "/a/b/c", "bar"); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(1, changedAssets.size()); + + assertEquals(AVMDifference.NEWER, changedAssets.get(0).getDiffCode()); + assertEquals(sbInfo.getSandboxRootPath()+"/"+webApp+"/a/b/c/bar", changedAssets.get(0).getPath()); + + sbService.submitWebApp(sbStoreId, webApp, "submitLabel", "submitDescription"); + + Thread.sleep(SUBMIT_DELAY); + + List snapshotVersions = sbService.listSnapshots(stagingStoreId, false); + assertEquals(3, snapshotVersions.size()); + + assertNotNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/foo")); + assertNotNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/bar")); + + final ActionImpl action = new ActionImpl(null, GUID.generate(), WCMSandboxRevertSnapshotAction.NAME); + action.setParameterValue(WCMSandboxRevertSnapshotAction.PARAM_VERSION, snapshotVersions.get(2).getVersion()); + + final WCMSandboxRevertSnapshotAction revertSnapshot = (WCMSandboxRevertSnapshotAction)ctx.getBean(WCMSandboxRevertSnapshotAction.NAME); + + class TxnWork implements RetryingTransactionCallback + { + public Object execute() throws Exception + { + revertSnapshot.execute(action, AVMNodeConverter.ToNodeRef(-1, stagingStoreId+":/")); + return null; + } + }; + TransactionService transactionService = (TransactionService) ctx.getBean("transactionService"); + transactionService.getRetryingTransactionHelper().doInTransaction(new TxnWork()); + + Thread.sleep(SUBMIT_DELAY); + + snapshotVersions = sbService.listSnapshots(stagingStoreId, false); + assertEquals(4, snapshotVersions.size()); + + changedAssets = sbService.listChanged(sbStoreId, JNDIConstants.DIR_DEFAULT_WWW, true); + assertEquals(0, changedAssets.size()); + + assertNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/foo")); + assertNull(assetService.getAssetWebApp(sbStoreId, webApp, "/a/b/c/bar")); + } + public void testPseudoScaleTest() { long start = System.currentTimeMillis(); diff --git a/source/java/org/alfresco/wcm/util/WCMUtil.java b/source/java/org/alfresco/wcm/util/WCMUtil.java index 089fff4013..2516ed47fe 100644 --- a/source/java/org/alfresco/wcm/util/WCMUtil.java +++ b/source/java/org/alfresco/wcm/util/WCMUtil.java @@ -33,6 +33,7 @@ import java.util.regex.Pattern; import org.alfresco.config.JNDIConstants; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.model.WCMAppModel; +import org.alfresco.repo.avm.actions.AVMDeployWebsiteAction; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMNotFoundException; @@ -122,6 +123,29 @@ public class WCMUtil { return ((storeName != null) && (storeName.endsWith(WCMUtil.STORE_SEPARATOR + WCMUtil.STORE_PREVIEW))); } + + + /** + * http://wiki.alfresco.com/wiki/WCM_Deployment_Features#Debugging_.26_Testing + * + * Examples of locally deployed store names for web project "MyWebProj" are: + * + * MyWebProjlive + * MyWebProj--adminlive + * + * Note: if web project "MyWebProjlive" is pre-created then should be possible to browse staging: + * + * http://wiki.alfresco.com/wiki/WCM_Deployment_Features#Deployed_Runtime + * + * @param storeName + * @return + */ + protected static boolean isLocalhostDeployedStore(String wpStoreId, String storeName) + { + return ((storeName.startsWith(wpStoreId)) && + (storeName.endsWith(AVMDeployWebsiteAction.LIVE_SUFFIX)) && + (! wpStoreId.endsWith(AVMDeployWebsiteAction.LIVE_SUFFIX))); + } /** * Indicates whether the store name describes a workflow store. diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java index c6b2318d92..1143b943aa 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImpl.java @@ -46,6 +46,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMNotFoundException; import org.alfresco.service.cmr.avm.AVMService; @@ -66,6 +67,7 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.DNSNameMangler; import org.alfresco.util.ParameterCheck; import org.alfresco.wcm.preview.PreviewURIServiceRegistry; @@ -101,10 +103,10 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService private AuthorityService authorityService; private PermissionService permissionService; private PersonService personService; - private SandboxFactory sandboxFactory; private VirtServerRegistry virtServerRegistry; private PreviewURIServiceRegistry previewURIProviderRegistry; + private TransactionService transactionService; public void setNodeService(NodeService nodeService) { @@ -156,6 +158,11 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService this.previewURIProviderRegistry = previewURIProviderRegistry; } + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + /* (non-Javadoc) * @see org.alfresco.wcm.WebProjectService#createWebProject(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ @@ -704,7 +711,7 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService if (wpStoreId != null) { - // Notifiy virtualization server about removing this website + // Notify virtualization server about removing this website // // Implementation note: // @@ -728,33 +735,61 @@ public class WebProjectServiceImpl extends WCMUtil implements WebProjectService String path = WCMUtil.buildStoreWebappPath(sandbox, "/ROOT"); WCMUtil.removeAllVServerWebapps(virtServerRegistry, path, true); - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - List sbInfos = sandboxFactory.listAllSandboxes(wpStoreId); - - for (SandboxInfo sbInfo : sbInfos) - { - // delete sandbox - sandboxFactory.deleteSandbox(sbInfo.getSandboxId()); - } - - // TODO delete workflow sandboxes ! - - // delete the web project node itself - nodeService.deleteNode(wpNodeRef); - - sandboxFactory.removeGroupsForStore(sandbox); - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - if (logger.isInfoEnabled()) + try { - logger.info("Deleted web project: " + wpNodeRef + " (store id: " + wpStoreId + ")"); + RetryingTransactionCallback deleteWebProjectWork = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + List sbInfos = sandboxFactory.listAllSandboxes(wpStoreId, true, true); + + for (SandboxInfo sbInfo : sbInfos) + { + String sbStoreId = sbInfo.getSandboxId(); + + if (WCMUtil.isLocalhostDeployedStore(wpStoreId, sbStoreId)) + { + if (getWebProject(WCMUtil.getWebProjectStoreId(sbStoreId)) != null) + { + continue; + } + } + + // delete sandbox (and associated preview sandbox, if it exists) + sandboxFactory.deleteSandbox(wpStoreId, sbInfo.getSandboxId()); + } + + StoreRef archiveStoreRef = nodeService.getStoreArchiveNode(wpNodeRef.getStoreRef()).getStoreRef(); + + // delete the web project node itself + nodeService.deleteNode(wpNodeRef); + nodeService.deleteNode(new NodeRef(archiveStoreRef, wpNodeRef.getId())); + + sandboxFactory.removeGroupsForStore(sandbox); + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + return null; + } + }; + + transactionService.getRetryingTransactionHelper().doInTransaction(deleteWebProjectWork); + + if (logger.isInfoEnabled()) + { + logger.info("Deleted web project: " + wpNodeRef + " (store id: " + wpStoreId + ")"); + } + } + catch (Throwable err) + { + throw new AlfrescoRuntimeException("Failed to delete web project: ", err); } } } diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java index b28c2f06b9..979d38d8fb 100644 --- a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java +++ b/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java @@ -36,16 +36,20 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.wcm.AbstractWCMServiceImplTest; +import org.alfresco.wcm.asset.AssetInfo; +import org.alfresco.wcm.sandbox.SandboxInfo; import org.alfresco.wcm.util.WCMUtil; /** @@ -74,7 +78,6 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest private static final int SCALE_WEBPROJECTS = 5; private static final int SCALE_WEBAPPS = 5; - // // services // @@ -82,6 +85,8 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest private FileFolderService fileFolderService; private AuthorityService authorityService; private PermissionService permissionService; + private AVMService avmService; + private NodeService nodeService; @Override @@ -95,6 +100,8 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest fileFolderService = (FileFolderService)ctx.getBean("FileFolderService"); authorityService = (AuthorityService)ctx.getBean("AuthorityService"); permissionService = (PermissionService)ctx.getBean("PermissionService"); + avmService = (AVMService)ctx.getBean("AVMService"); + nodeService = (NodeService)ctx.getBean("NodeService"); createUser(USER_FIVE); createUser(USER_SIX); @@ -118,16 +125,6 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest deleteUser(USER_FIVE); deleteUser(USER_SIX); - - NodeRef wpRoot = wpService.getWebProjectsRoot(); - List list = fileFolderService.list(wpRoot); - for (FileInfo fileOrFolder : list) - { - if (fileOrFolder.getName().contains(TEST_RUN)) - { - fileFolderService.delete(fileOrFolder.getNodeRef()); - } - } } super.tearDown(); @@ -521,7 +518,7 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Switch back to admin AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - + // Delete the web project wpService.deleteWebProject(wpStoreId); assertNull(wpService.getWebProject(wpStoreId)); @@ -562,6 +559,47 @@ public class WebProjectServiceImplTest extends AbstractWCMServiceImplTest // Delete the web project wpService.deleteWebProject(TEST_WEBPROJ_DNS+"-delete2"); + + // Switch back to admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // Create another test web project + wpInfo = wpService.createWebProject(TEST_WEBPROJ_DNS+"-delete3", TEST_WEBPROJ_NAME+"-delete3", TEST_WEBPROJ_TITLE, TEST_WEBPROJ_DESCRIPTION, TEST_WEBPROJ_DEFAULT_WEBAPP, true, null); + wpStoreId = wpInfo.getStoreId(); + NodeRef wpNodeRef = wpInfo.getNodeRef(); + + String defaultWebApp = wpInfo.getDefaultWebApp(); + + SandboxInfo sbInfo = sbService.getAuthorSandbox(wpStoreId); + final String authorSandboxId = sbInfo.getSandboxId(); + + // no changes yet + List assets = sbService.listChangedAll(authorSandboxId, true); + assertEquals(0, assets.size()); + + String authorSandboxPath = sbInfo.getSandboxRootPath() + "/" + defaultWebApp; + + for (int i = 1; i <= 100; i++) + { + assetService.createFile(authorSandboxId, authorSandboxPath, "myFile-"+i, null); + } + + sbService.submitAll(authorSandboxId, "s1", "s2"); + + // delete immediately - don't wait for async submit to finish - this should leave an in-flight workflow + + wpService.deleteWebProject(wpStoreId); + + List avmStores = avmService.getStores(); + for (AVMStoreDescriptor avmStore : avmStores) + { + assertFalse("Unexpected store: "+avmStore.getName(), avmStore.getName().startsWith(wpStoreId)); + } + + NodeRef wpArchiveNodeRef = new NodeRef(nodeService.getStoreArchiveNode(wpNodeRef.getStoreRef()).getStoreRef(), wpNodeRef.getId()); + assertFalse(nodeService.exists(wpArchiveNodeRef)); + + // TODO add more tests when WCM services explicitly support WCM workflows (eg. submit approval) } public void testCreateWebApp() diff --git a/source/test-resources/wcm/small-61-items.zip b/source/test-resources/wcm/small-61-items.zip new file mode 100644 index 0000000000000000000000000000000000000000..7a7225b498bebf59ef73d45794dd2692f26eb615 GIT binary patch literal 10044 zcmd^@T}&KR6vv0Hvo(GNDp{?LA8Nv?e84qQlD^hgnl|n74gDR=d+>|Fv`w_a zZ1?<|Z_KPC74Ib9wBIcC&rKC;BcjBreT0n2d!SmDu{lH=5vAOcrXKr$9uae|nR52M zHY;cDHIdNTYh6Sqy=IMz4o#wUU^K41vwOE+?&xgS&-O^p)$Me|FSuf(^H$bbHJyx2 znl?HdIK?r}%QlZvPP0?|a(j2bqx3LOkq7p63*@Ped8iwz*m48%o<4`=qVy^pSDO#J zTPb(-I_d>VuFA1?2v_YGhbHfC!L8VkM0#xHe{H|=Rb%CUY+3o{5KSP}y7Kkd>uKdP z=^Tg~T_r$7?eRgI#cEmXYzufE!6k1HU9u{;DXQ?(rwTuP zs_@gN3OIeJ(Q1W&Mq^^703tfjYL)2OsTPnTI?w{zf_S2LqVS%GSc#~~!doI>C87kA z*@$Vb;&k>jYj$EYr95K4#J*}4g7mlMEV>OVHyonjabFRdoE{K9IT+BxfoJD7Y+c4- zX;>}McpA$=g;+v{)48jeA?O3+e<5Hh%fUSmVU(mlbecoJhiyX z2mL?TyvadthNcj(5Sh}{LWELL^D!~!3CPjBLq3id4Z%l@egoE07O^)A@PTk)-$0ON_nA3~oW1z2UI?ym#|oov729UiW`akQdR2+H z*>*kLvd!Wg`zr&MvmAUN2X8Z$kKHz-`6S+ELl%zBHjAgmv&~#2f3jbZJ7!jzhw~8R zXtr1?oC(NDusWFJW;X`kW0x6++BvNA)Q5qcr{Re^e_L)$7G#RCa^f78hWTLucYZaQ zMtG00eC#?;^GV$K$1EJ1&c{>Z>DVs>Nji`0BfgIL&@Q_(A8?GfLOxOfBE5)6#w%+o zs5yjkl+~>sSfBBd%9jRE(J?jpjZ(}<+M0Hu;sp%<2?yFq*;+`cwMGTwRkQ1~MG z1fewlhYy5x%JRYLlDm>{bxnw5yx;I8g{%7zqI9c=4}^2Nf#Hn;^%?#E9}17>>kyTk z<`{Gqf}HHAwMC#fM}iMPQj#kQw{8}a9O->?F{m!N6@^pkf*{)sl}TP0B