From cecd138ed6fc5fa776233ccfe7f4652e6053fcb3 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Mon, 19 May 2008 16:09:48 +0000 Subject: [PATCH] Merged V2.2 to HEAD 8514: Updated clusterd cach example - ACL changes, attributes, AVM and minor fix ups 8522: Fix for AR-1694 8587: Merged V2.1 to V2.2 8575: Fix for AR-2166 "Display of tasks in MyAlfresco dashboard broken when using oracle" 8617: AVMTestRemote - fix test/data 8632: Merged V2.1 to V2.2 8623: Fixed AR-2122: Code re-entry paths through transaction resource interceptor cause data loss 8624: Fixed test associated with session resource management fixes git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@9169 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/avm-services-context.xml | 2 +- config/alfresco/core-services-context.xml | 2 +- .../ehcache-custom.xml.sample.cluster | 348 +++++++++++++++--- config/alfresco/node-services-context.xml | 2 +- config/alfresco/repository.properties | 3 + .../org/alfresco/repo/avm/AVMTestRemote.java | 2 +- .../archive/LargeArchiveAndRestoreTest.java | 318 ++++++++++++++++ .../hibernate/SessionSizeManagementTest.java | 6 +- .../search/impl/lucene/ADMLuceneTest.java | 7 +- ...stractLuceneIndexerAndSearcherFactory.java | 230 +++++++++++- ...eEntryTransactionResourceInterceptor.java} | 33 +- .../jbpm/JBPMTransactionTemplate.java | 16 +- .../session-size-test-context.xml | 2 +- 13 files changed, 882 insertions(+), 89 deletions(-) create mode 100644 source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java rename source/java/org/alfresco/repo/transaction/{TransactionResourceInterceptor.java => SingleEntryTransactionResourceInterceptor.java} (88%) diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index 64257cb32f..e1ef29c112 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -378,7 +378,7 @@ - + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index a9a96ecfc6..f03fb5c945 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -961,7 +961,7 @@ - ${dir.indexes}/backup-lucene-indexes + ${dir.indexes.backup} diff --git a/config/alfresco/extension/ehcache-custom.xml.sample.cluster b/config/alfresco/extension/ehcache-custom.xml.sample.cluster index 77fc3984a3..4161b24fee 100644 --- a/config/alfresco/extension/ehcache-custom.xml.sample.cluster +++ b/config/alfresco/extension/ehcache-custom.xml.sample.cluster @@ -180,43 +180,9 @@ replicateAsynchronously = false"/> - - - - - - - - - - + + + + + + + + + + + + + + + @@ -311,25 +322,10 @@ replicateUpdatesViaCopy = false, replicateAsynchronously = false"/> - + - - - - - @@ -356,9 +352,69 @@ replicateUpdatesViaCopy = false, replicateAsynchronously = false"/> + + + + + + + + + + + + + + + + + + + + @@ -371,6 +427,186 @@ replicateUpdatesViaCopy = false, replicateAsynchronously = false"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index acec6563b0..77bdac87ff 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -184,7 +184,7 @@ - + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 7690195da7..71ed3bc488 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -10,6 +10,9 @@ dir.auditcontentstore=${dir.root}/audit.contentstore # The location for lucene index files dir.indexes=${dir.root}/lucene-indexes +# The location for index backups +dir.indexes.backup=${dir.root}/backup-lucene-indexes + # The location for lucene index locks dir.indexes.lock=${dir.indexes}/locks diff --git a/source/java/org/alfresco/repo/avm/AVMTestRemote.java b/source/java/org/alfresco/repo/avm/AVMTestRemote.java index fba607fb66..32a08768ae 100644 --- a/source/java/org/alfresco/repo/avm/AVMTestRemote.java +++ b/source/java/org/alfresco/repo/avm/AVMTestRemote.java @@ -98,7 +98,7 @@ public class AVMTestRemote extends TestCase AVMNodeDescriptor found = fAVMRemote.lookup(-1, "test2932:/a/foo.txt"); Pair path = fAVMRemote.getAPath(found); assertEquals(path.getSecond(), "test2932:/a/foo.txt"); - explorePaths("foo--admin:/"); + explorePaths("test2932:/"); } /** diff --git a/source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java b/source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java new file mode 100644 index 0000000000..e175caacdc --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2005-2007 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.node.archive; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; + +/** + * Test large datasets' archival and restore + * + * @author Derek Hulley + */ +public class LargeArchiveAndRestoreTest extends TestCase +{ + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private static Log logger = LogFactory.getLog(LargeArchiveAndRestoreTest.class); + + private NodeService nodeService; + private NodeArchiveService nodeArchiveService; + private FileFolderService fileFolderService; + private AuthenticationComponent authenticationComponent; + private TransactionService transactionService; + + private NodeRef rootNodeRef; + + @Override + public void setUp() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); + nodeService = serviceRegistry.getNodeService(); + fileFolderService = serviceRegistry.getFileFolderService(); + nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService"); + authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + transactionService = serviceRegistry.getTransactionService(); + + try + { + authenticationComponent.setSystemUserAsCurrentUser(); + + // create a test store + StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + rootNodeRef = nodeService.getRootNode(storeRef); + } + finally + { + authenticationComponent.clearCurrentSecurityContext(); + } + } + + @Override + public void tearDown() throws Exception + { + } + + public void testSetUp() throws Exception + { + } + + private static final int NUM_FOLDERS = 5; + private static final int MAX_DEPTH = 3; + private static final int NUM_FILES = 10; + private class CreateDataCallback implements RetryingTransactionCallback + { + private NodeRef parentNodeRef; + private List rollbackMessages; + + public CreateDataCallback(NodeRef parentNodeRef) + { + this.parentNodeRef = parentNodeRef; + this.rollbackMessages = new ArrayList(20); + } + + public NodeRef execute() throws Throwable + { + // Make the root of the tree + String foldername = String.format("FOLDER-%04d-%04d", 0, 0); + System.out.println("Creating folder " + foldername + " in parent " + parentNodeRef); + PropertyMap props = new PropertyMap(); + props.put(ContentModel.PROP_NAME, "foldername"); + NodeRef folderNodeRef = nodeService.createNode( + parentNodeRef, + ContentModel.ASSOC_CHILDREN, + ContentModel.ASSOC_CHILDREN, + ContentModel.TYPE_FOLDER, + props).getChildRef(); + // Now add children + makeFolders(folderNodeRef, 1); + + // Rollback if necessary + if (rollbackMessages.size() > 0) + { + StringBuilder sb = new StringBuilder(1024); + sb.append("Errors during create: \n"); + for (String msg : rollbackMessages) + { + sb.append(" ").append(msg); + } + throw new RuntimeException(sb.toString()); + } + + return folderNodeRef; + } + + private void makeFolders(NodeRef parentNodeRef, int depth) + { + if (depth == MAX_DEPTH) + { + // We're deep enough, so add the files + for (int i = 0; i < NUM_FILES; i++) + { + String filename = String.format("FILE-%04d-%04d", depth, i); + addFile(parentNodeRef, filename, depth); + } + } + else + { + int nextDepth = depth + 1; + // Not deep enough yet, so add directories + for (int i = 0; i < NUM_FOLDERS; i++) + { + // Create the directory + String foldername = String.format("FOLDER-%04d-%04d", depth, i); + NodeRef folderNodeRef = addFolder(parentNodeRef, foldername, depth); + // Recurse + makeFolders(folderNodeRef, nextDepth); + } + } + } + + private NodeRef addFolder(NodeRef parentNodeRef, String foldername, int depth) + { + System.out.println(makeTabs(depth) + "Creating folder " + foldername + " in parent " + parentNodeRef); + FileInfo info = fileFolderService.create(parentNodeRef, foldername, ContentModel.TYPE_FOLDER); + String name = info.getName(); + if (!name.equals(foldername)) + { + String msg = "A foldername '" + foldername + "' was not persisted: " + info; + logger.error(msg); + rollbackMessages.add(msg); + } + return info.getNodeRef(); + } + + private NodeRef addFile(NodeRef parentNodeRef, String filename, int depth) + { + System.out.println(makeTabs(depth) + "Creating file " + filename + " in parent " + parentNodeRef); + FileInfo info = fileFolderService.create(parentNodeRef, filename, ContentModel.TYPE_CONTENT); + String name = info.getName(); + if (!name.equals(filename)) + { + String msg = "A filename '" + filename + "' was not persisted: " + info; + logger.error(msg); + rollbackMessages.add(msg); + } + return info.getNodeRef(); + } + + private String makeTabs(int count) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) + { + sb.append(" "); + } + return sb.toString(); + } + } + + public void testCreateAndRestore() throws Exception + { + RunAsWork createHierarchyWork = new RunAsWork() + { + public NodeRef doWork() throws Exception + { + CreateDataCallback callback = new CreateDataCallback(rootNodeRef); + return transactionService.getRetryingTransactionHelper().doInTransaction(callback); + } + }; + // Create the hierarchy + final NodeRef parentNodeRef = AuthenticationUtil.runAs(createHierarchyWork, AuthenticationUtil.SYSTEM_USER_NAME); + // Delete it + final RetryingTransactionCallback deleteHierarchyCallback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Delete it + fileFolderService.delete(parentNodeRef); + // Now try to find the archived node + NodeRef archivedParentNodeRef = new NodeRef("archive", parentNodeRef.getStoreRef().getIdentifier(), parentNodeRef.getId()); + // Check it + if (!nodeService.exists(archivedParentNodeRef)) + { + throw new InvalidNodeRefException("Archived node not found after delete: " + archivedParentNodeRef, archivedParentNodeRef); + } + if (nodeService.exists(parentNodeRef)) + { + throw new InvalidNodeRefException("Original node was found after delete: " + parentNodeRef, parentNodeRef); + } + return archivedParentNodeRef; + } + }; + RunAsWork deleteHierarchyWork = new RunAsWork() + { + public NodeRef doWork() throws Exception + { + return transactionService.getRetryingTransactionHelper().doInTransaction(deleteHierarchyCallback); + } + }; + final NodeRef archivedParentNodeRef = AuthenticationUtil.runAs(deleteHierarchyWork, AuthenticationUtil.SYSTEM_USER_NAME); + // Restore it + final RetryingTransactionCallback restoreHierarchyCallback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Delete it + List report = nodeArchiveService.restoreArchivedNodes(Collections.singletonList(archivedParentNodeRef)); + // Dump the report + System.out.println("Restore report: \n" + report); + // Check it + if (nodeService.exists(archivedParentNodeRef)) + { + throw new InvalidNodeRefException("Archived node was found after restore: " + archivedParentNodeRef, archivedParentNodeRef); + } + if (!nodeService.exists(parentNodeRef)) + { + throw new InvalidNodeRefException("Original node was not found after restore: " + parentNodeRef, parentNodeRef); + } + return parentNodeRef; + } + }; + RunAsWork restoreHierarchyWork = new RunAsWork() + { + public NodeRef doWork() throws Exception + { + return transactionService.getRetryingTransactionHelper().doInTransaction(restoreHierarchyCallback); + } + }; + final NodeRef checkParentNodeRef = AuthenticationUtil.runAs(restoreHierarchyWork, AuthenticationUtil.SYSTEM_USER_NAME); + assertEquals("Restored node reference doesn't match original", parentNodeRef, checkParentNodeRef); + + // Purge it + final RetryingTransactionCallback purgeHierarchyCallback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Delete it + nodeService.addAspect(parentNodeRef, ContentModel.ASPECT_TEMPORARY, null); + fileFolderService.delete(parentNodeRef); + // Now try to find the archived node + NodeRef archivedParentNodeRef = new NodeRef("archive", parentNodeRef.getStoreRef().getIdentifier(), parentNodeRef.getId()); + // Check it + if (nodeService.exists(archivedParentNodeRef)) + { + throw new InvalidNodeRefException("Node not purged by delete: " + archivedParentNodeRef, archivedParentNodeRef); + } + if (nodeService.exists(parentNodeRef)) + { + throw new InvalidNodeRefException("Original node was found after purge: " + parentNodeRef, parentNodeRef); + } + return null; + } + }; + RunAsWork purgeHierarchyWork = new RunAsWork() + { + public Object doWork() throws Exception + { + transactionService.getRetryingTransactionHelper().doInTransaction(purgeHierarchyCallback); + return null; + } + }; + AuthenticationUtil.runAs(purgeHierarchyWork, AuthenticationUtil.SYSTEM_USER_NAME); + } +} diff --git a/source/java/org/alfresco/repo/node/db/hibernate/SessionSizeManagementTest.java b/source/java/org/alfresco/repo/node/db/hibernate/SessionSizeManagementTest.java index 52d7d8ac7e..93985112ef 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/SessionSizeManagementTest.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/SessionSizeManagementTest.java @@ -29,7 +29,7 @@ import java.lang.reflect.Method; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.BaseNodeServiceTest; import org.alfresco.repo.node.db.DbNodeServiceImpl; -import org.alfresco.repo.transaction.TransactionResourceInterceptor; +import org.alfresco.repo.transaction.SingleEntryTransactionResourceInterceptor; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -45,7 +45,7 @@ import org.alfresco.service.namespace.QName; */ public class SessionSizeManagementTest extends BaseNodeServiceTest { - private TransactionResourceInterceptor interceptor; + private SingleEntryTransactionResourceInterceptor interceptor; private Method createNodesMethod; public SessionSizeManagementTest() @@ -85,7 +85,7 @@ public class SessionSizeManagementTest extends BaseNodeServiceTest { super.onSetUpInTransaction(); // Get the interceptor for manual testing - interceptor = (TransactionResourceInterceptor) applicationContext.getBean("testSessionSizeResourceInterceptor"); + interceptor = (SingleEntryTransactionResourceInterceptor) applicationContext.getBean("testSessionSizeResourceInterceptor"); } /** Helper to create a given number of nodes using the provided service */ diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java index 567c260ec7..927467bc3d 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java @@ -40,11 +40,7 @@ import java.util.Locale; import java.util.Map; import java.util.Random; -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.RollbackException; import javax.transaction.Status; -import javax.transaction.SystemException; import javax.transaction.UserTransaction; import junit.framework.TestCase; @@ -68,7 +64,6 @@ import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.TransactionResourceInterceptor; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -303,7 +298,7 @@ public class ADMLuceneTest extends TestCase testProperties.put(QName.createQName(TEST_NAMESPACE, "path-ista"), nodeService.getPath(n3)); testProperties.put(QName.createQName(TEST_NAMESPACE, "locale-ista"), Locale.UK); testProperties.put(QName.createQName(TEST_NAMESPACE, "null"), null); - testProperties.put(QName.createQName(TEST_NAMESPACE, "list"), new ArrayList()); + testProperties.put(QName.createQName(TEST_NAMESPACE, "list"), new ArrayList()); MLText mlText = new MLText(); mlText.addValue(Locale.ENGLISH, "banana"); mlText.addValue(Locale.FRENCH, "banane"); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java index d6041ec7b1..a48acedc4d 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java @@ -25,6 +25,12 @@ package org.alfresco.repo.search.impl.lucene; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -53,7 +59,6 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.GUID; -import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.search.BooleanQuery; @@ -913,7 +918,7 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI { return this.threadPoolExecutor; } - + public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) { this.threadPoolExecutor = threadPoolExecutor; @@ -930,6 +935,8 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI public static class LuceneIndexBackupComponent { + private static String BACKUP_TEMP_NAME = ".indexbackup_temp"; + private TransactionService transactionService; private Set factories; @@ -1018,7 +1025,7 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI { throw new AlfrescoRuntimeException("Target location may not be a root directory: " + targetDir); } - File tempDir = new File(targetParentDir, "indexbackup_temp"); + File tempDir = new File(targetParentDir, BACKUP_TEMP_NAME); for (LuceneIndexerAndSearcher factory : factories) { @@ -1066,7 +1073,7 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI /** * Makes a backup of the source directory via a temporary folder */ - private static void backupDirectory(File sourceDir, File tempDir, File targetDir) throws Exception + private void backupDirectory(File sourceDir, File tempDir, File targetDir) throws Exception { if (!sourceDir.exists()) { @@ -1076,21 +1083,21 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI // delete the files from the temp directory if (tempDir.exists()) { - FileUtils.deleteDirectory(tempDir); + deleteDirectory(tempDir); if (tempDir.exists()) { throw new AlfrescoRuntimeException("Temp directory exists and cannot be deleted: " + tempDir); } } // copy to the temp directory - FileUtils.copyDirectory(sourceDir, tempDir, true); + copyDirectory(sourceDir, tempDir, true); // check that the temp directory was created if (!tempDir.exists()) { throw new AlfrescoRuntimeException("Copy to temp location failed"); } // delete the target directory - FileUtils.deleteDirectory(targetDir); + deleteDirectory(targetDir); if (targetDir.exists()) { throw new AlfrescoRuntimeException("Failed to delete older files from target location"); @@ -1103,6 +1110,163 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI throw new AlfrescoRuntimeException("Failed to rename temporary directory to target backup directory"); } } + + private void copyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException + { + if (destDir.exists()) + { + throw new IOException("Destination should be created from clean"); + } + else + { + if (!destDir.mkdirs()) + { + throw new IOException("Destination '" + destDir + "' directory cannot be created"); + } + if (preserveFileDate) + { + destDir.setLastModified(srcDir.lastModified()); + } + } + if (!destDir.canWrite()) + { + throw new IOException("No acces to destination directory" + destDir); + } + + File[] files = srcDir.listFiles(); + if (files == null) + { + throw new IOException(" No Access to " + srcDir); + } + for (int i = 0; i < files.length; i++) + { + File currentCopyTarget = new File(destDir, files[i].getName()); + if (files[i].isDirectory()) + { + // Skip any temp index file + if (files[i].getName().equals(tempDir.getName())) + { + // skip any temp back up directories + } + else if (files[i].getName().equals(targetDir.getName())) + { + // skip any back up directories + } + else + { + copyDirectory(files[i], currentCopyTarget, preserveFileDate); + } + } + else + { + copyFile(files[i], currentCopyTarget, preserveFileDate); + } + } + } + + private void copyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException + { + if (destFile.exists()) + { + throw new IOException("File shoud not exist " + destFile); + } + + FileInputStream input = new FileInputStream(srcFile); + try + { + FileOutputStream output = new FileOutputStream(destFile); + try + { + copy(input, output); + } + finally + { + try + { + output.close(); + } + catch (IOException io) + { + + } + } + } + finally + { + try + { + input.close(); + } + catch (IOException io) + { + + } + } + + // check copy + if (srcFile.length() != destFile.length()) + { + throw new IOException("Failed to copy full from '" + srcFile + "' to '" + destFile + "'"); + } + if (preserveFileDate) + { + destFile.setLastModified(srcFile.lastModified()); + } + } + + public int copy(InputStream input, OutputStream output) throws IOException + { + byte[] buffer = new byte[2048 * 4]; + int count = 0; + int n = 0; + while ((n = input.read(buffer)) != -1) + { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + public void deleteDirectory(File directory) throws IOException + { + if (!directory.exists()) + { + return; + } + if (!directory.isDirectory()) + { + throw new IllegalArgumentException("Not a directory " + directory); + } + + File[] files = directory.listFiles(); + if (files == null) + { + throw new IOException("Failed to delete director - no access" + directory); + } + + for (int i = 0; i < files.length; i++) + { + File file = files[i]; + + if (file.isDirectory()) + { + deleteDirectory(file); + } + else + { + if (!file.delete()) + { + throw new IOException("Unable to delete file: " + file); + } + } + } + + if (!directory.delete()) + { + throw new IOException("Unable to delete directory " + directory); + } + } + } } @@ -1270,4 +1434,56 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI } } + public static void main(String[] args) throws IOException + { + // delete a directory .... + if(args.length != 1) + { + return; + } + File file = new File(args[0]); + deleteDirectory(file); + } + + public static void deleteDirectory(File directory) throws IOException + { + if (!directory.exists()) + { + return; + } + if (!directory.isDirectory()) + { + throw new IllegalArgumentException("Not a directory " + directory); + } + + File[] files = directory.listFiles(); + if (files == null) + { + throw new IOException("Failed to delete director - no access" + directory); + } + + for (int i = 0; i < files.length; i++) + { + File file = files[i]; + + System.out.println("."); + //System.out.println("Deleting "+file.getCanonicalPath()); + if (file.isDirectory()) + { + deleteDirectory(file); + } + else + { + if (!file.delete()) + { + throw new IOException("Unable to delete file: " + file); + } + } + } + + if (!directory.delete()) + { + throw new IOException("Unable to delete directory " + directory); + } + } } diff --git a/source/java/org/alfresco/repo/transaction/TransactionResourceInterceptor.java b/source/java/org/alfresco/repo/transaction/SingleEntryTransactionResourceInterceptor.java similarity index 88% rename from source/java/org/alfresco/repo/transaction/TransactionResourceInterceptor.java rename to source/java/org/alfresco/repo/transaction/SingleEntryTransactionResourceInterceptor.java index b1a2af12cd..f32e7620af 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionResourceInterceptor.java +++ b/source/java/org/alfresco/repo/transaction/SingleEntryTransactionResourceInterceptor.java @@ -51,13 +51,18 @@ import org.aopalliance.intercept.MethodInvocation; * This class supports both interceptor-based calling as well as manual calling. * Long-running processes can call an this manually on every iteration and * get the same behaviour of regular calls to the resouce managers. + *

+ * The current thread is marked on first entry and all subsequent nested re-entries + * will just get passed down to the underlying delegate invocation method. * * @see org.alfresco.util.resource.MethodResourceManager * * @author Derek Hulley + * @since 2.1.3 */ -public class TransactionResourceInterceptor implements MethodInterceptor +public class SingleEntryTransactionResourceInterceptor implements MethodInterceptor { + private ThreadLocal threadLocalReentryCheck; private List methodResourceManagers; /** Default 10000ms (10s) */ private long elapsedTimeBeforeActivationMillis = 10000L; @@ -72,9 +77,10 @@ public class TransactionResourceInterceptor implements MethodInterceptor */ private String resourceKey; - public TransactionResourceInterceptor() + public SingleEntryTransactionResourceInterceptor() { resourceKey = "MethodStats" + super.toString(); + threadLocalReentryCheck = new ThreadLocal(); } /** @@ -115,12 +121,31 @@ public class TransactionResourceInterceptor implements MethodInterceptor public Object invoke(MethodInvocation invocation) throws Throwable { - if (methodResourceManagers == null || methodResourceManagers.size() == 0) + if (threadLocalReentryCheck.get() == Boolean.TRUE) + { + // We're already in a wrapped resource, so avoid doing anything + return invocation.proceed(); + } + else if (methodResourceManagers == null || methodResourceManagers.size() == 0) { // We just ignore everything return invocation.proceed(); } - + try + { + // Mark this thread + threadLocalReentryCheck.set(Boolean.TRUE); + return invokeInternal(invocation); + } + finally + { + // Unmark this thread + threadLocalReentryCheck.set(null); + } + } + + private Object invokeInternal(MethodInvocation invocation) throws Throwable + { // Get the txn start time long txnStartTime = AlfrescoTransactionSupport.getTransactionStartTime(); if (txnStartTime < 0) diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMTransactionTemplate.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMTransactionTemplate.java index 33cc619818..2b0ea2c8ae 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMTransactionTemplate.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMTransactionTemplate.java @@ -157,14 +157,6 @@ public class JBPMTransactionTemplate extends JbpmTemplate */ public void beforeCommit(boolean readOnly) { - JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY); - if (context != null) - { - super.releaseContext(context); - - if (logger.isDebugEnabled()) - logger.debug("Detached (commit) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId()); - } } @@ -181,6 +173,14 @@ public class JBPMTransactionTemplate extends JbpmTemplate */ public void afterCommit() { + JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY); + if (context != null) + { + super.releaseContext(context); + + if (logger.isDebugEnabled()) + logger.debug("Detached (commit) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId()); + } } diff --git a/source/test-resources/session-size-test-context.xml b/source/test-resources/session-size-test-context.xml index 135e81e6e6..eb808c8fa3 100644 --- a/source/test-resources/session-size-test-context.xml +++ b/source/test-resources/session-size-test-context.xml @@ -6,7 +6,7 @@ - +