From 1b272d6766faee687631fe9a4e45beaf8341955c Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Mon, 10 Sep 2007 23:15:37 +0000 Subject: [PATCH] Merged V2.1 to HEAD 6486: Fix for AWC-1134 (Made minimum length for username and password configurable) 6487: Added "copy.verbose" property to build script to list all files being copied in the 'deploy-tomcat-exploded' and 'deploy-webclient-changes' targets 6488: Changed default CIFS server to remove underscore as it can cause problems with OpenOffice. 6489: Fix for WCM-498 (Expired items that are deleted are not represented as such in the change request dialog or submit dialog) 6490: Fix for WCM-446 and WCM-624. Multi-select and All operations in Modified Files list respect items in workflow and ignore them. 6491: Fixes profoundly brain dead behavior in AVMLockingService.init(). 6492: Refactoring of LookupCache to be clustering compatible 6493: Staging and locking changes 6494: WCM Revert action moved to end of all action lists to avoid accidently clicking it (as it has no confirmation screen!) 6496: Fixes for WCM-746 and WCM-747 - Edit File Properties and Edit Folder Properties working correctly again for AVM objects. 6497: Enable virtualization server to be (re)started at any point prior or during the startup of the alfresco webapp (fixes WCM-750). 6498: WCM-742 - moving locks from the preview sandbox to the main user sandbox. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6739 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/avm-services-context.xml | 13 +- config/alfresco/cache-context.xml | 31 +- config/alfresco/file-servers.xml | 2 +- config/alfresco/public-services-context.xml | 3 - .../repo/avm/AVMExpiredContentProcessor.java | 20 +- .../repo/avm/AVMLookupCacheListener.java | 53 - .../org/alfresco/repo/avm/AVMRepository.java | 37 +- .../org/alfresco/repo/avm/AVMScaleTestP.java | 4 +- .../org/alfresco/repo/avm/AVMServiceImpl.java | 47 - .../org/alfresco/repo/avm/AVMServiceTest.java | 269 ++-- .../alfresco/repo/avm/AVMServiceTestBase.java | 4 +- .../org/alfresco/repo/avm/AVMStoreImpl.java | 10 + source/java/org/alfresco/repo/avm/Lookup.java | 5 +- .../org/alfresco/repo/avm/LookupCache.java | 313 +--- .../java/org/alfresco/repo/avm/LookupKey.java | 6 +- .../avm/locking/AVMLockingServiceImpl.java | 1412 +++++++++-------- .../alfresco/repo/avm/util/SimplePath.java | 6 +- 17 files changed, 1011 insertions(+), 1224 deletions(-) delete mode 100644 source/java/org/alfresco/repo/avm/AVMLookupCacheListener.java diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index 5cdc9ddc91..2eed041490 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -146,14 +146,8 @@ - - 50 - - - - - - + + @@ -290,6 +284,9 @@ + + + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 180340b0c2..1f8f04b81e 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -244,7 +244,36 @@ 10 - + + + + + + + + + + + org.alfresco.repo.avm.lookupSharedCache + + + + + + + + + + + + + + org.alfresco.repo.avm.lookupTransactionalCache + + + 50 + + diff --git a/config/alfresco/file-servers.xml b/config/alfresco/file-servers.xml index 94d80de702..1966844b43 100644 --- a/config/alfresco/file-servers.xml +++ b/config/alfresco/file-servers.xml @@ -2,7 +2,7 @@ - + Alfresco CIFS Server diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index eef4296cf1..27efcd4982 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -795,9 +795,6 @@ - - - diff --git a/source/java/org/alfresco/repo/avm/AVMExpiredContentProcessor.java b/source/java/org/alfresco/repo/avm/AVMExpiredContentProcessor.java index 33860c3567..e99217ff7d 100644 --- a/source/java/org/alfresco/repo/avm/AVMExpiredContentProcessor.java +++ b/source/java/org/alfresco/repo/avm/AVMExpiredContentProcessor.java @@ -48,6 +48,7 @@ import org.alfresco.sandbox.SandboxConstants; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMSyncService; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; @@ -86,6 +87,7 @@ public class AVMExpiredContentProcessor protected Map>> expiredContent; protected AVMService avmService; protected AVMSyncService avmSyncService; + protected AVMService avmLockingAwareService; protected NodeService nodeService; protected WorkflowService workflowService; protected PersonService personService; @@ -157,6 +159,11 @@ public class AVMExpiredContentProcessor { this.searchService = searchService; } + + public void setAvmLockingAwareService(AVMService avmLockingAwareService) + { + this.avmLockingAwareService = avmLockingAwareService; + } /** * Executes the expired content processor. @@ -305,7 +312,7 @@ public class AVMExpiredContentProcessor if (logger.isDebugEnabled()) logger.debug("Examining expiration date for '" + nodePath + "': " + - expirationDateProp.getStringValue()); + expirationDateProp); if (expirationDateProp != null) { @@ -465,10 +472,10 @@ public class AVMExpiredContentProcessor String path = workflowStoreName + ":/" + JNDIConstants.DIR_DEFAULT_WWW + "/" + JNDIConstants.DIR_DEFAULT_APPBASE; // DNS name mangle the property name - can only contain value DNS characters! - String dnsProp = SandboxConstants.PROP_DNS + DNSNameMangler.MakeDNSName(userStore, packageName); + String dnsProp = SandboxConstants.PROP_DNS + DNSNameMangler.MakeDNSName(stagingStore, packageName); this.avmService.setStoreProperty(workflowStoreName, QName.createQName(null, dnsProp), new PropertyValue(DataTypeDefinition.TEXT, path)); - + // the main workflow store depends on the main user store (dist=1) String prop_key = SandboxConstants.PROP_BACKGROUND_LAYER + userStore; this.avmService.setStoreProperty(workflowStoreName, QName.createQName(null, prop_key), @@ -560,11 +567,10 @@ public class AVMExpiredContentProcessor String pathInWorkflowStore = workflowStoreName + ":" + relPath; // call forceCopy to make sure the path appears modified in the workflow - // sandbox, if the item is already modified or deleted this call has no - // effect. - this.avmService.forceCopy(pathInWorkflowStore); + // sandbox, if the item is already modified or deleted this call has no effect. + this.avmLockingAwareService.forceCopy(pathInWorkflowStore); } - + // convert package to workflow package AVMNodeDescriptor packageDesc = avmService.lookup(-1, packagesPath); NodeRef packageNodeRef = workflowService.createPackage( diff --git a/source/java/org/alfresco/repo/avm/AVMLookupCacheListener.java b/source/java/org/alfresco/repo/avm/AVMLookupCacheListener.java deleted file mode 100644 index 8b1aa5f4ef..0000000000 --- a/source/java/org/alfresco/repo/avm/AVMLookupCacheListener.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * - */ -package org.alfresco.repo.avm; - -import org.alfresco.repo.transaction.TransactionListenerAdapter; - -/** - * This is the listener that cleans up the lookup cache on transaction - * rollback. - * @author britt - */ -public class AVMLookupCacheListener extends TransactionListenerAdapter -{ - /** - * The lookup cache. - */ - private LookupCache fLookupCache; - - /** - * A default constructor. - */ - public AVMLookupCacheListener() - { - } - - /** - * Set the Lookup Cache. - * @param lookupCache - */ - public void setLookupCache(LookupCache lookupCache) - { - fLookupCache = lookupCache; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListenerAdapter#afterRollback() - */ - @Override - public void afterRollback() - { - fLookupCache.onRollback(); - } - - /* (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListenerAdapter#afterCommit() - */ - @Override - public void afterCommit() - { - fLookupCache.onCommit(); - } -} diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 5e89be0eab..b51e183dae 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -633,7 +633,6 @@ public class AVMRepository { throw new AVMNotFoundException("Store not found."); } - fLookupCache.onDelete(pathParts[0]); sPath = srcRepo.lookupDirectory(-1, pathParts[1], true); if (sPath == null) { @@ -645,6 +644,7 @@ public class AVMRepository { throw new AVMNotFoundException("Not found: " + srcName); } + fLookupCache.onDelete(pathParts[0]); } finally { @@ -659,7 +659,6 @@ public class AVMRepository { throw new AVMNotFoundException("Store not found."); } - fLookupCache.onWrite(pathParts[0]); Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true); if (dPath == null) { @@ -761,6 +760,7 @@ public class AVMRepository { dstNode.setAncestor(srcNode); } + fLookupCache.onWrite(pathParts[0]); } finally { @@ -808,10 +808,10 @@ public class AVMRepository { throw new AVMNotFoundException("Store not found."); } - fLookupCache.onSnapshot(storeName); Map result = store.createSnapshot(tag, description, new HashMap()); for (Map.Entry entry : result.entrySet()) { + fLookupCache.onSnapshot(entry.getKey()); fCreateVersionTxnListener.versionCreated(entry.getKey(), entry.getValue()); } return result; @@ -1639,18 +1639,33 @@ public class AVMRepository */ public Lookup lookupDirectory(int version, String path) { - fLookupCount.set(fLookupCount.get() + 1); - if (fLookupCount.get() > 50) + Integer count = fLookupCount.get(); + try { - throw new AVMCycleException("Cycle in lookup."); + if (count == null) + { + fLookupCount.set(1); + } + fLookupCount.set(fLookupCount.get() + 1); + if (fLookupCount.get() > 50) + { + throw new AVMCycleException("Cycle in lookup."); + } + String [] pathParts = SplitPath(path); + AVMStore store = getAVMStoreByName(pathParts[0]); + if (store == null) + { + return null; + } + return store.lookupDirectory(version, pathParts[1], false); } - String [] pathParts = SplitPath(path); - AVMStore store = getAVMStoreByName(pathParts[0]); - if (store == null) + finally { - return null; + if (count == null) + { + fLookupCount.set(null); + } } - return store.lookupDirectory(version, pathParts[1], false); } /** diff --git a/source/java/org/alfresco/repo/avm/AVMScaleTestP.java b/source/java/org/alfresco/repo/avm/AVMScaleTestP.java index 3dfb0a2035..ea0cf9ec6b 100644 --- a/source/java/org/alfresco/repo/avm/AVMScaleTestP.java +++ b/source/java/org/alfresco/repo/avm/AVMScaleTestP.java @@ -36,12 +36,12 @@ public class AVMScaleTestP extends AVMServiceTestBase { public void testScaling() { - int n = 4; // The number of BulkLoads to do. + int n = 250; // The number of BulkLoads to do. int futzCount = 10; // The number of post snapshot modifications to make after each load. String load = "/Users/britt/hibernate-3.1"; // The tree of stuff to load. BulkLoader loader = new BulkLoader(); loader.setAvmService(fService); - loader.setPropertyCount(5); + loader.setPropertyCount(50); BulkReader reader = new BulkReader(); reader.setAvmService(fService); long lastTime = System.currentTimeMillis(); diff --git a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java index d2d6046457..a67ab64d15 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java @@ -36,8 +36,6 @@ import java.util.SortedMap; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMException; import org.alfresco.service.cmr.avm.AVMExistsException; @@ -72,8 +70,6 @@ public class AVMServiceImpl implements AVMService */ private AVMRepository fAVMRepository; - private TransactionListener fTransactionListener; - /** * Basic constructor for the service. */ @@ -90,15 +86,6 @@ public class AVMServiceImpl implements AVMService fAVMRepository = avmRepository; } - /** - * Set the transaction listener. - * @param transactionListener - */ - public void setTransactionListener(TransactionListener transactionListener) - { - fTransactionListener = transactionListener; - } - /** * Get an InputStream from a file. * @param version The version to look under. @@ -139,7 +126,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.getOutputStream(path); } @@ -169,7 +155,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.createContentWriter(path); } @@ -361,7 +346,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.createFile(path, name); } @@ -378,7 +362,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); // Save the contents to temp space. File temp; try @@ -419,7 +402,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.createDirectory(path, name); } @@ -436,7 +418,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.createLayeredFile(srcPath, parent, name); } @@ -453,7 +434,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.createLayeredDirectory(srcPath, parent, name); } @@ -485,7 +465,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.createBranch(version, srcPath, dstPath, name); } @@ -501,7 +480,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.remove(parent, name); } @@ -520,7 +498,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Cannot remove root node: " + path); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.remove(basePath[0], basePath[1]); } @@ -539,7 +516,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.rename(srcParent, srcName, dstParent, dstName); } @@ -554,7 +530,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.uncover(dirPath, name); } @@ -570,7 +545,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.flatten(dirPath, name); } @@ -616,7 +590,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Store is null."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.createSnapshot(store, tag, description); } @@ -861,7 +834,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.retargetLayeredDirectory(path, target); } @@ -875,7 +847,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Path is null."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.makePrimary(path); } @@ -967,7 +938,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setOpacity(path, opacity); } @@ -1018,7 +988,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setNodeProperty(path, name, value); } @@ -1033,7 +1002,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setNodeProperties(path, properties); } @@ -1091,7 +1059,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.deleteNodeProperty(path, name); } @@ -1105,7 +1072,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.deleteNodeProperties(path); } @@ -1208,7 +1174,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Invalid null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.deleteStoreProperty(store, name); } @@ -1250,7 +1215,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null Path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.getContentDataForWrite(path); } @@ -1268,7 +1232,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null Path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setContentData(path, data); } @@ -1283,7 +1246,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal null argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setMetaDataFrom(path, from); } @@ -1300,7 +1262,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal Null Argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.addAspect(path, aspectName); } @@ -1342,7 +1303,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.removeAspect(path, aspectName); } @@ -1374,7 +1334,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal Null Argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.link(parentPath, name, toLink); } @@ -1388,7 +1347,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Null Path."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); return fAVMRepository.forceCopy(path); } @@ -1427,7 +1385,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal name."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); AVMNodeDescriptor srcDesc = lookup(srcVersion, srcPath); recursiveCopy(srcVersion, srcDesc, dstPath, name); } @@ -1508,7 +1465,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Cannot revert store root: " + path); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.revert(baseName[0], baseName[1], toRevertTo); } @@ -1521,7 +1477,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal Null Argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setGuid(path, guid); } @@ -1534,7 +1489,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal Null Argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setEncoding(path, encoding); } @@ -1547,7 +1501,6 @@ public class AVMServiceImpl implements AVMService { throw new AVMBadArgumentException("Illegal Null Argument."); } - AlfrescoTransactionSupport.bindListener(fTransactionListener); fAVMRepository.setMimeType(path, mimeType); } } diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index ffd2ae9c40..f788bb9fbb 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -347,136 +347,6 @@ public class AVMServiceTest extends AVMServiceTestBase } - /** - * Test async indexing. - * @throws Exception - */ - public void testAsyncIndex() throws Exception - { - // Make sure the slate is clean ... - UserTransaction tx = fTransactionService.getUserTransaction(); - tx.begin(); - if(fService.getStore("avmAsynchronousTest") != null) - { - assertTrue(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); - fService.purgeStore("avmAsynchronousTest"); - assertTrue(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); - assertFalse(fIndexingInterceptor.hasIndexBeenCreated("bananaStoreWoof")); - } - else - { - assertFalse(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); - } - StoreRef storeRef = AVMNodeConverter.ToStoreRef("avmAsynchronousTest"); - Indexer indexer = fIndexerAndSearcher.getIndexer(storeRef); - if(indexer instanceof AVMLuceneIndexer) - { - AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer)indexer; - avmIndexer.deleteIndex("avmAsynchronousTest", IndexMode.SYNCHRONOUS); - } - tx.commit(); - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(-1, fIndexingInterceptor.getLastIndexedSnapshot("bananaStoreWoof")); - assertEquals(-1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - tx.commit(); - - // TODO: Suspend and resume indexing in case we are really unlucky and hit an index before we expect it. - - SearchService searchService = fIndexerAndSearcher.getSearcher(storeRef, true); - ResultSet results; - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(0, results.length()); - results.close(); - - fService.createStore("avmAsynchronousTest"); - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - tx.commit(); - - fService.createSnapshot("avmAsynchronousTest", null, null); - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - tx.commit(); - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(1, results.length()); - results.close(); - - fService.createDirectory("avmAsynchronousTest:/", "a"); - fService.createDirectory("avmAsynchronousTest:/a", "b"); - fService.createDirectory("avmAsynchronousTest:/a/b", "c"); - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); - tx.commit(); - - fService.createSnapshot("avmAsynchronousTest", null, null); - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); - assertFalse(fIndexingInterceptor.isIndexUpToDateAndSearchable("avmAsynchronousTest")); - assertEquals(IndexMode.ASYNCHRONOUS, fIndexingInterceptor.getIndexMode("avmAsynchronousTest")); - assertEquals(IndexMode.SYNCHRONOUS, fIndexingInterceptor.getIndexMode("main")); - assertTrue(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 0)); - assertTrue(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 1)); - assertFalse(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 2)); - tx.commit(); - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(1, results.length()); - results.close(); - - Thread.sleep(180000); - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(4, results.length()); - results.close(); - - - tx = fTransactionService.getUserTransaction(); - tx.begin(); - assertEquals(1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); - assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); - assertTrue(fIndexingInterceptor.isIndexUpToDateAndSearchable("avmAsynchronousTest")); - tx.commit(); - - fService.purgeStore("avmAsynchronousTest"); - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(0, results.length()); - results.close(); - - fService.createStore("avmAsynchronousTest"); - fService.createSnapshot("avmAsynchronousTest", null, null); - fService.createDirectory("avmAsynchronousTest:/", "a"); - fService.createDirectory("avmAsynchronousTest:/a", "b"); - fService.createDirectory("avmAsynchronousTest:/a/b", "c"); - fService.createSnapshot("avmAsynchronousTest", null, null); - fService.purgeStore("avmAsynchronousTest"); - fService.createStore("avmAsynchronousTest"); - fService.createSnapshot("avmAsynchronousTest", null, null); - fService.createDirectory("avmAsynchronousTest:/", "a"); - fService.createDirectory("avmAsynchronousTest:/a", "b"); - fService.createDirectory("avmAsynchronousTest:/a/b", "c"); - fService.createSnapshot("avmAsynchronousTest", null, null); - - Thread.sleep(180000); - - results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); - assertEquals(4, results.length()); - results.close(); - } public void testForceCopyDeleted() { @@ -537,8 +407,16 @@ public class AVMServiceTest extends AVMServiceTestBase fService.createBranch(1, "layer:/root", "branch:/", "branch"); fService.createSnapshot("branch", null, null); fService.getFileOutputStream("main:/a/b/c/foo").close(); + System.out.println("main 1"); + System.out.println(recursiveList("main", 1, true)); + System.out.println("branch 1"); + System.out.println(recursiveList("branch", 1, true)); assertEquals(fService.lookup(1, "main:/a/b/c/foo").getId(), fService.lookup(1, "branch:/branch/layer/b/c/foo").getId()); + System.out.println("main -1"); + System.out.println(recursiveList("main", -1, true)); + System.out.println("branch -1"); + System.out.println(recursiveList("branch", -1, true)); assertEquals(fService.lookup(-1, "main:/a/b/c/foo").getId(), fService.lookup(-1, "branch:/branch/layer/b/c/foo").getId()); } @@ -5641,4 +5519,135 @@ public class AVMServiceTest extends AVMServiceTestBase fail(); } } + + /** + * Test async indexing. + * @throws Exception + */ + public void testAsyncIndex() throws Exception + { + // Make sure the slate is clean ... + UserTransaction tx = fTransactionService.getUserTransaction(); + tx.begin(); + if(fService.getStore("avmAsynchronousTest") != null) + { + assertTrue(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); + fService.purgeStore("avmAsynchronousTest"); + assertTrue(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); + assertFalse(fIndexingInterceptor.hasIndexBeenCreated("bananaStoreWoof")); + } + else + { + assertFalse(fIndexingInterceptor.hasIndexBeenCreated("avmAsynchronousTest")); + } + StoreRef storeRef = AVMNodeConverter.ToStoreRef("avmAsynchronousTest"); + Indexer indexer = fIndexerAndSearcher.getIndexer(storeRef); + if(indexer instanceof AVMLuceneIndexer) + { + AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer)indexer; + avmIndexer.deleteIndex("avmAsynchronousTest", IndexMode.SYNCHRONOUS); + } + tx.commit(); + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(-1, fIndexingInterceptor.getLastIndexedSnapshot("bananaStoreWoof")); + assertEquals(-1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + tx.commit(); + + // TODO: Suspend and resume indexing in case we are really unlucky and hit an index before we expect it. + + SearchService searchService = fIndexerAndSearcher.getSearcher(storeRef, true); + ResultSet results; + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(0, results.length()); + results.close(); + + fService.createStore("avmAsynchronousTest"); + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + tx.commit(); + + fService.createSnapshot("avmAsynchronousTest", null, null); + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + tx.commit(); + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(1, results.length()); + results.close(); + + fService.createDirectory("avmAsynchronousTest:/", "a"); + fService.createDirectory("avmAsynchronousTest:/a", "b"); + fService.createDirectory("avmAsynchronousTest:/a/b", "c"); + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(0, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); + tx.commit(); + + fService.createSnapshot("avmAsynchronousTest", null, null); + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); + assertFalse(fIndexingInterceptor.isIndexUpToDateAndSearchable("avmAsynchronousTest")); + assertEquals(IndexMode.ASYNCHRONOUS, fIndexingInterceptor.getIndexMode("avmAsynchronousTest")); + assertEquals(IndexMode.SYNCHRONOUS, fIndexingInterceptor.getIndexMode("main")); + assertTrue(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 0)); + assertTrue(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 1)); + assertFalse(fIndexingInterceptor.isSnapshotIndexed("avmAsynchronousTest", 2)); + tx.commit(); + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(1, results.length()); + results.close(); + + Thread.sleep(180000); + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(4, results.length()); + results.close(); + + + tx = fTransactionService.getUserTransaction(); + tx.begin(); + assertEquals(1, fIndexingInterceptor.getLastIndexedSnapshot("avmAsynchronousTest")); + assertTrue(fIndexingInterceptor.isIndexUpToDate("avmAsynchronousTest")); + assertTrue(fIndexingInterceptor.isIndexUpToDateAndSearchable("avmAsynchronousTest")); + tx.commit(); + + fService.purgeStore("avmAsynchronousTest"); + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(0, results.length()); + results.close(); + + fService.createStore("avmAsynchronousTest"); + fService.createSnapshot("avmAsynchronousTest", null, null); + fService.createDirectory("avmAsynchronousTest:/", "a"); + fService.createDirectory("avmAsynchronousTest:/a", "b"); + fService.createDirectory("avmAsynchronousTest:/a/b", "c"); + fService.createSnapshot("avmAsynchronousTest", null, null); + fService.purgeStore("avmAsynchronousTest"); + fService.createStore("avmAsynchronousTest"); + fService.createSnapshot("avmAsynchronousTest", null, null); + fService.createDirectory("avmAsynchronousTest:/", "a"); + fService.createDirectory("avmAsynchronousTest:/a", "b"); + fService.createDirectory("avmAsynchronousTest:/a/b", "c"); + fService.createSnapshot("avmAsynchronousTest", null, null); + + Thread.sleep(180000); + + results = searchService.query(storeRef, "lucene", "PATH:\"//.\""); + assertEquals(4, results.length()); + results.close(); + } } diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java b/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java index 6449c72d35..e5c9512296 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java @@ -24,7 +24,6 @@ package org.alfresco.repo.avm; import java.io.IOException; -import java.io.PrintStream; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -39,9 +38,7 @@ import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.service.cmr.avmsync.AVMSyncService; -import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; @@ -225,6 +222,7 @@ public class AVMServiceTestBase extends TestCase Map listing = fService.getDirectoryListing(version, path, true); for (String name : listing.keySet()) { + System.err.println(name); builder.append(recursiveList(basename + name, version, indent + 2, followLinks)); } } diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index ee0dca4b43..e6ab1900b6 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -214,11 +214,20 @@ public class AVMStoreImpl implements AVMStore, Serializable { continue; } + fAVMRepository.forceCopy(entry.getPath()); + // TODO This leaves the behavior of LayeredFiles not quite + // right. + /* String parentName[] = AVMNodeConverter.SplitBase(entry.getPath()); parentName[0] = parentName[0].substring(parentName[0].indexOf(':') + 1); lookup = lookupDirectory(-1, parentName[0], true); DirectoryNode parent = (DirectoryNode)lookup.getCurrentNode(); AVMNode child = parent.lookupChild(lookup, parentName[1], false); + // TODO For debugging. + if (child == null) + { + System.err.println("Yoiks!"); + } // TODO This is funky. Need to look carefully to see that this call // does exactly what's needed. lookup.add(child, parentName[1], false); @@ -232,6 +241,7 @@ public class AVMStoreImpl implements AVMStore, Serializable newChild = ((LayeredFileNode)child).copyLiterally(lookup); } parent.putChild(parentName[1], newChild); + */ } // Clear out the new nodes. List newInRep = AVMDAOs.Instance().fAVMNodeDAO.getNewInStore(this); diff --git a/source/java/org/alfresco/repo/avm/Lookup.java b/source/java/org/alfresco/repo/avm/Lookup.java index 9a50a3b9c5..e7320f55d0 100644 --- a/source/java/org/alfresco/repo/avm/Lookup.java +++ b/source/java/org/alfresco/repo/avm/Lookup.java @@ -23,6 +23,7 @@ package org.alfresco.repo.avm; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -34,8 +35,10 @@ import org.alfresco.util.Pair; * from the root directory of a repository. * @author britt */ -class Lookup +class Lookup implements Serializable { + private static final long serialVersionUID = -2844833688622561L; + /** * Is this lookup valid? */ diff --git a/source/java/org/alfresco/repo/avm/LookupCache.java b/source/java/org/alfresco/repo/avm/LookupCache.java index b966e14cc6..bca5443827 100644 --- a/source/java/org/alfresco/repo/avm/LookupCache.java +++ b/source/java/org/alfresco/repo/avm/LookupCache.java @@ -3,14 +3,11 @@ */ package org.alfresco.repo.avm; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.ArrayList; +import java.util.List; import org.alfresco.repo.avm.util.SimplePath; +import org.alfresco.repo.cache.SimpleCache; import org.apache.log4j.Logger; /** @@ -21,42 +18,10 @@ public class LookupCache { private static Logger fgLogger = Logger.getLogger(LookupCache.class); - - /** - * Per transaction lookup results to be added to the cache on successful - * commit. - */ - private ThreadLocal> fToBeAdded; - - /** - * Per transaction set of invalidated lookup keys. - */ - private ThreadLocal> fToBePurged; - /** * The Map of of keys to lookups. */ - private Map fCache; - - /** - * The Map of time stamps to keys. - */ - private SortedMap fTimeStamps; - - /** - * The inverse map of keys to timestamps. - */ - private Map fInverseTimeStamps; - - /** - * The timestamp to next issue. - */ - private long fTimeStamp; - - /** - * The maximum number of lines to have in the cache. - */ - private int fMaxSize; + private SimpleCache fCache; /** * Reference to the Node DAO. @@ -73,13 +38,6 @@ public class LookupCache */ public LookupCache() { - fCache = new HashMap(); - fTimeStamps = new TreeMap(); - fInverseTimeStamps = new HashMap(); - fToBeAdded = new ThreadLocal>(); - fToBePurged = new ThreadLocal>(); - fTimeStamp = 0L; - fMaxSize = 100; } /** @@ -100,13 +58,9 @@ public class LookupCache fAVMStoreDAO = dao; } - /** - * Set the maximum cache size. - * @param maxSize - */ - public void setMaxSize(int maxSize) + public void setTransactionalCache(SimpleCache cache) { - fMaxSize = maxSize; + fCache = cache; } /** @@ -125,8 +79,14 @@ public class LookupCache LookupKey key = new LookupKey(version, path, store.getName(), write, includeDeleted); // Is it in the cache? Lookup found = findInCache(key); + // TODO Testing. + // found = null; if (found != null) { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Cache Hit: " + key + ", " + found.getCurrentNode().getId()); + } return found; } // Make up a Lookup to hold the results. @@ -155,7 +115,7 @@ public class LookupCache dir = (DirectoryNode)result.getCurrentNode(); if (path.size() == 1 && path.get(0).equals("")) { - updateCache(key, result); + fCache.put(key, result); return result; } // Now look up each path element in sequence up to one @@ -184,7 +144,7 @@ public class LookupCache return null; } result.add(child, path.get(path.size() - 1), write); - updateCache(key, result); + fCache.put(key, result); return result; } @@ -195,24 +155,7 @@ public class LookupCache */ private synchronized Lookup findInCache(LookupKey key) { - // Get the current transaction's purged set. - Set purged = fToBePurged.get(); - // Get the current transaction's added map. - Map added = fToBeAdded.get(); - // See if it's cached in the transaction. - Lookup found = (added != null) ? added.get(key) : null; - // It's not. - if (found == null) - { - // If it's been purged in the transaction it is - // a miss. - if (purged != null && purged.contains(key)) - { - return null; - } - found = fCache.get(key); - } - // Despite the odds, we found a hit. + Lookup found = fCache.get(key); if (found != null) { // Get a freshened Lookup. @@ -224,8 +167,6 @@ public class LookupCache fgLogger.error("Invalid entry in cache: " + key); return null; } - // Prepare the cache for a timestamp update on commit. - updateCache(key, found); return result; } // Alternatively for a read lookup a write can match. @@ -234,18 +175,7 @@ public class LookupCache // Make a copy of the key and set it to 'write' LookupKey newKey = new LookupKey(key); newKey.setWrite(true); - // Is it in the transaction's cache? - found = (added != null) ? added.get(newKey) : null; - // If not. - if (found == null) - { - // If it's been purged it's a miss. - if (purged != null && purged.contains(newKey)) - { - return null; - } - found = fCache.get(newKey); - } + found = fCache.get(newKey); if (found != null) { // We found it. Make a freshened copy of the Lookup. @@ -257,103 +187,12 @@ public class LookupCache fgLogger.error("Invalid entry in cache: " + newKey); return null; } - // Prepare the cache to update time stamp. - updateCache(newKey, found); return result; } } return null; } - /** - * Add or update an entry in the cache. - * @param key - * @param lookup - */ - private void updateCache(LookupKey key, Lookup lookup) - { - // First, put it in the transaction scoped cache. - Map map = fToBeAdded.get(); - if (map == null) - { - map = new HashMap(); - } - map.put(key, lookup); - // Remove any corresponding entry from the purge list. - Set purged = fToBePurged.get(); - if (purged == null) - { - return; - } - purged.remove(key); - } - - /** - * Called when a transaction has successfully committed, - * to make lookups from the transaction available to other transactions. - */ - public synchronized void onCommit() - { - // First get rid of all entries purged by the transaction. - Set purged = fToBePurged.get(); - if (purged != null) - { - purgeEntries(purged); - } - // Null out the thread local. - fToBePurged.set(null); - // Get and go through the transaction's added list. - Map added = fToBeAdded.get(); - if (added == null) - { - return; - } - for (Map.Entry entry : added.entrySet()) - { - LookupKey key = entry.getKey(); - Lookup lookup = entry.getValue(); - // If the cache already has the key, remove it. - if (fCache.containsKey(key)) - { - fCache.remove(key); - Long oldTime = fInverseTimeStamps.get(key); - fInverseTimeStamps.remove(key); - fTimeStamps.remove(oldTime); - } - // Add the entry. - long timeStamp = fTimeStamp++; - fTimeStamps.put(timeStamp, key); - fInverseTimeStamps.put(key, timeStamp); - fCache.put(key, lookup); - // Check if we're over the limit and purge the - // LRU entry if we are. - if (fCache.size() > fMaxSize) - { - // Get rid of the oldest entry. - Long oldTime = fTimeStamps.firstKey(); - LookupKey old = fTimeStamps.remove(oldTime); - fInverseTimeStamps.remove(old); - fCache.remove(old); - } - } - // Null out the ThreadLocal. - fToBeAdded.set(null); - } - - /** - * Remove a Set of entries. - * @param keys The Set of entries. - */ - private void purgeEntries(Set keys) - { - for (LookupKey key : keys) - { - fCache.remove(key); - Long time = fInverseTimeStamps.remove(key); - fTimeStamps.remove(time); - } - } - // Following are the cache invalidation calls. /** @@ -362,37 +201,25 @@ public class LookupCache */ public synchronized void onWrite(String storeName) { - // Get or make up the purged Set for this transaction. - Set purged = fToBePurged.get(); - if (purged == null) - { - purged = new HashSet(); - fToBePurged.set(purged); - } // Invalidate if it's a read lookup in the store, or // any read lookup is it's layered. - for (Map.Entry entry : fCache.entrySet()) + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) { - if ((entry.getKey().getStoreName().equals(storeName) && - !entry.getKey().isWrite()) || - (!entry.getKey().isWrite() && entry.getValue().isLayered())) - { - purged.add(entry.getKey()); - } + keys.add(key); } - // Remove entries from the added set using the same criteria. - Map added = fToBeAdded.get(); - if (added == null) + for (LookupKey key : keys) { - return; - } - for (Map.Entry entry : added.entrySet()) - { - if ((entry.getKey().getStoreName().equals(storeName) && - !entry.getKey().isWrite()) || - (!entry.getKey().isWrite() && entry.getValue().isLayered())) + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + !key.isWrite()) || value == null || + (!key.isWrite() && value.isLayered())) { - added.remove(entry.getKey()); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); } } } @@ -403,34 +230,23 @@ public class LookupCache */ public synchronized void onDelete(String storeName) { - // Get or make up a fresh purged Set. - Set purged = fToBePurged.get(); - if (purged == null) - { - purged = new HashSet(); - fToBePurged.set(purged); - } // Invalidate any entries that are in the store or are layered lookups. - for (Map.Entry entry : fCache.entrySet()) + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) { - if (entry.getKey().getStoreName().equals(storeName) || - entry.getValue().isLayered()) - { - purged.add(entry.getKey()); - } + keys.add(key); } - // Get rid of any similarly matching elements in the added list. - Map added = fToBeAdded.get(); - if (added == null) + for (LookupKey key : keys) { - return; - } - for (Map.Entry entry : added.entrySet()) - { - if (entry.getKey().getStoreName().equals(storeName) || - entry.getValue().isLayered()) + Lookup value = fCache.get(key); + if (key.getStoreName().equals(storeName) || + value == null || value.isLayered()) { - added.remove(entry.getKey()); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); } } } @@ -441,48 +257,31 @@ public class LookupCache */ public synchronized void onSnapshot(String storeName) { - // Get or make up a purged set. - Set purged = fToBePurged.get(); - if (purged == null) - { - purged = new HashSet(); - fToBePurged.set(purged); - } // Invalidate any entries that in the store and writes or // any layered lookups. - for (Map.Entry entry : fCache.entrySet()) + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) { - if ((entry.getKey().getStoreName().equals(storeName) && - entry.getKey().isWrite()) || - entry.getValue().isLayered()) - { - purged.add(entry.getKey()); - } + keys.add(key); } - // Remove from the added list by the same criteria. - Map added = fToBeAdded.get(); - if (added == null) + for (LookupKey key : keys) { - return; - } - for (Map.Entry entry : added.entrySet()) - { - if ((entry.getKey().getStoreName().equals(storeName) && - entry.getKey().isWrite()) || - entry.getValue().isLayered()) + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + key.isWrite()) || + value == null || value.isLayered()) { - added.remove(entry.getKey()); + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); } } } - /** - * Called when a rollback has occurred. - */ - public synchronized void onRollback() + public synchronized void reset() { - // Just toss the transaction level changes. - fToBeAdded.set(null); - fToBePurged.set(null); + fCache.clear(); } } diff --git a/source/java/org/alfresco/repo/avm/LookupKey.java b/source/java/org/alfresco/repo/avm/LookupKey.java index 6e06c55db2..8e425ec192 100644 --- a/source/java/org/alfresco/repo/avm/LookupKey.java +++ b/source/java/org/alfresco/repo/avm/LookupKey.java @@ -3,14 +3,18 @@ */ package org.alfresco.repo.avm; +import java.io.Serializable; + import org.alfresco.repo.avm.util.SimplePath; /** * This is the key by which Lookup's are retrieved from the cache. * @author britt */ -public class LookupKey +public class LookupKey implements Serializable { + private static final long serialVersionUID = 8471482833953423406L; + /** * The name of the store. */ diff --git a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java index eb48b1ebbd..03878c8a26 100644 --- a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java @@ -1,698 +1,714 @@ -/* - * 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.avm.locking; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.model.WCMAppModel; -import org.alfresco.repo.attributes.Attribute; -import org.alfresco.repo.attributes.MapAttributeValue; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.cmr.attributes.AttrQueryEquals; -import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.avm.AVMBadArgumentException; -import org.alfresco.service.cmr.avm.AVMExistsException; -import org.alfresco.service.cmr.avm.AVMNotFoundException; -import org.alfresco.service.cmr.avm.locking.AVMLock; -import org.alfresco.service.cmr.avm.locking.AVMLockingService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.namespace.RegexQNamePattern; -import org.alfresco.util.MD5; -import org.alfresco.util.Pair; - -/** - * Implementation of the lock service. - * @author britt - */ -public class AVMLockingServiceImpl implements AVMLockingService -{ - public static final String LOCK_TABLE = ".avm_lock_table"; - public static final String WEB_PROJECTS = "web_projects"; - public static final String USERS = "users"; - public static final String STORES = "stores"; - - private static final String ROLE_CONTENT_MANAGER = "ContentManager"; - - /** - * Store name containing the web project nodes. - */ - private String webProjectStore; - - /** - * SearchService for access to web project properties. - */ - private SearchService fSearchService; - - /** - * AttributeService reference. - */ - private AttributeService fAttributeService; - - /** - * AuthorityService reference. - */ - private AuthorityService fAuthorityService; - - /** - * PersonService reference. - */ - private PersonService fPersonService; - - /** - * The NodeService. - */ - private NodeService fNodeService; - - /** - * Transaction Helper reference. - */ - private RetryingTransactionHelper fRetryingTransactionHelper; - - - /** - * @param webProjectStore The webProjectStore to set - */ - public void setWebProjectStore(String webProjectStore) - { - this.webProjectStore = webProjectStore; - } - - /** - * Setter for AttributeService reference. - * @param service - */ - public void setAttributeService(AttributeService service) - { - fAttributeService = service; - } - - /** - * Set the authority service reference. - * @param service - */ - public void setAuthorityService(AuthorityService service) - { - fAuthorityService = service; - } - - /** - * Set the person service reference. - * @param service - */ - public void setPersonService(PersonService service) - { - fPersonService = service; - } - - /** - * Setter for RetryingTransactionHelper reference. - * @param helper - */ - public void setRetryingTransactionHelper(RetryingTransactionHelper helper) - { - fRetryingTransactionHelper = helper; - } - - public void setSearchService(SearchService service) - { - fSearchService = service; - } - - public void setNodeService(NodeService service) - { - fNodeService = service; - } - - public void init() - { - RetryingTransactionHelper.RetryingTransactionCallback callback = - new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() - { - Attribute table = fAttributeService.getAttribute(LOCK_TABLE); - if (table != null) - { -/* - Attribute stores = fAttributeService.getAttribute(LOCK_TABLE + '/' + STORES); - if (stores == null) - { - fAttributeService.setAttribute(LOCK_TABLE, STORES, new MapAttributeValue()); - } - Attribute users = fAttributeService.getAttribute(LOCK_TABLE + '/' + USERS); - if (users == null) - { - fAttributeService.setAttribute(LOCK_TABLE, USERS, new MapAttributeValue()); - } -*/ - Attribute webProjects = fAttributeService.getAttribute(LOCK_TABLE + '/' + WEB_PROJECTS); - if (webProjects == null) - { - fAttributeService.setAttribute(LOCK_TABLE, WEB_PROJECTS, new MapAttributeValue()); - } - return null; - } - fAttributeService.setAttribute("", LOCK_TABLE, new MapAttributeValue()); - fAttributeService.setAttribute(LOCK_TABLE, WEB_PROJECTS, new MapAttributeValue()); -/* - fAttributeService.setAttribute(LOCK_TABLE, USERS, new MapAttributeValue()); - fAttributeService.setAttribute(LOCK_TABLE, STORES, new MapAttributeValue()); -*/ - return null; - } - }; - fRetryingTransactionHelper.doInTransaction(callback, false); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getLock(java.lang.String, java.lang.String) - */ - public AVMLock getLock(String webProject, String path) - { - path = normalizePath(path); - List keys = new ArrayList(3); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(webProject); - List> attrs = - fAttributeService.query(keys, new AttrQueryEquals(MD5.Digest(path.getBytes()))); - if (attrs.size() == 0) - { - return null; - } - return new AVMLock(attrs.get(0).getSecond()); - } - - /** - * Utility to get relative paths into canonical form. - * @param path The incoming path. - * @return The normalized path. - */ - private String normalizePath(String path) - { - while (path.startsWith("/")) - { - path = path.substring(1); - } - while (path.endsWith("/")) - { - path = path.substring(0, path.length() - 1); - } - return path.replaceAll("/+", "/"); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getUsersLocks(java.lang.String) - */ - public List getUsersLocks(String user) - { -// List keys = new ArrayList(3); -// keys.add(LOCK_TABLE); -// keys.add(USERS); -// keys.add(user); -// Attribute userLocks = fAttributeService.getAttribute(keys); -// List locks = new ArrayList(); -// if (userLocks == null) -// { -// return locks; -// } -// for (Attribute entry : userLocks) -// { -// String webProject = entry.get("web_project").getStringValue(); -// String path = entry.get("path").getStringValue(); -// locks.add(getLock(webProject, path)); -// } -// return locks; - return null; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#lockPath(org.alfresco.service.cmr.avm.locking.AVMLock) - */ - public void lockPath(AVMLock lock) - { - for (String authority : lock.getOwners()) - { - if (!fAuthorityService.authorityExists(authority) && - !fPersonService.personExists(authority)) - { - throw new AVMBadArgumentException("Not an Authority: " + authority); - } - } - List keys = new ArrayList(); - Attribute lockData = lock.getAttribute(); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(lock.getWebProject()); - String digest = MD5.Digest(lock.getPath().getBytes()); - keys.add(digest); - if (fAttributeService.getAttribute(keys) != null) - { - throw new AVMExistsException("Lock Exists: " + keys); - } - keys.remove(3); - fAttributeService.setAttribute(keys, digest, lockData); -// Attribute reverseEntry = new MapAttributeValue(); -// reverseEntry.put("web_project", new StringAttributeValue(lock.getWebProject())); -// reverseEntry.put("path", new StringAttributeValue(lock.getPath())); -// keys.clear(); -// keys.add(LOCK_TABLE); -// keys.add(USERS); -// for (String user : lock.getOwners()) -// { -// keys.add(user); -// Attribute userEntry = fAttributeService.getAttribute(keys); -// keys.remove(2); -// if (userEntry == null) -// { -// fAttributeService.setAttribute(keys, user, new ListAttributeValue()); -// } -// keys.add(user); -// fAttributeService.addAttribute(keys, reverseEntry); -// keys.remove(2); -// } -// String store = lock.getStore(); -// keys.clear(); -// keys.add(LOCK_TABLE); -// keys.add(STORES); -// keys.add(store); -// Attribute storeEntry = fAttributeService.getAttribute(keys); -// keys.remove(2); -// if (storeEntry == null) -// { -// fAttributeService.setAttribute(keys, store, new ListAttributeValue()); -// } -// keys.add(store); -// fAttributeService.addAttribute(keys, reverseEntry); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeLock(java.lang.String, java.lang.String) - */ - public void removeLock(String webProject, String path) - { - path = normalizePath(path); - String pathKey = MD5.Digest(path.getBytes()); - List keys = new ArrayList(4); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(webProject); - keys.add(pathKey); - Attribute lockData = fAttributeService.getAttribute(keys); - if (lockData == null) - { - return; - } - keys.remove(3); - fAttributeService.removeAttribute(keys, pathKey); -// AVMLock lock = new AVMLock(lockData); -// List userKeys = new ArrayList(); -// userKeys.add(LOCK_TABLE); -// userKeys.add(USERS); -// for (String user : lock.getOwners()) -// { -// userKeys.add(user); -// Attribute userLocks = fAttributeService.getAttribute(userKeys); -// for (int i = userLocks.size() - 1; i >= 0; i--) -// { -// Attribute lockInfo = userLocks.get(i); -// if (lockInfo.get("web_project").getStringValue().equals(lock.getWebProject()) -// && lockInfo.get("path").getStringValue().equals(lock.getPath())) -// { -// fAttributeService.removeAttribute(userKeys, i); -// break; -// } -// } -// userKeys.remove(2); -// } -// List storeKeys = new ArrayList(3); -// storeKeys.add(LOCK_TABLE); -// storeKeys.add(STORES); -// String store = lock.getStore(); -// storeKeys.add(store); -// Attribute storeLocks = fAttributeService.getAttribute(storeKeys); -// for (int i = storeLocks.size() - 1; i >= 0; i--) -// { -// Attribute lockInfo = storeLocks.get(i); -// if (lockInfo.get("web_project").getStringValue().equals(lock.getWebProject()) && -// lockInfo.get("path").getStringValue().equals(lock.getPath())) -// { -// fAttributeService.removeAttribute(storeKeys, i); -// break; -// } -// } - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#addWebProject(java.lang.String) - */ - public void addWebProject(String webProject) - { - List keys = new ArrayList(3); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(webProject); - if (fAttributeService.exists(keys)) - { - return; - } - keys.remove(2); - fAttributeService.setAttribute(keys, webProject, new MapAttributeValue()); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getWebProjectLocks(java.lang.String) - */ - public List getWebProjectLocks(String webProject) - { - List keys = new ArrayList(3); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(webProject); - Attribute locksMap = fAttributeService.getAttribute(keys); - List result = new ArrayList(); - if (locksMap != null) - { - for (Attribute lockData : locksMap.values()) - { - result.add(new AVMLock(lockData)); - } - } - return result; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeWebProject(java.lang.String) - */ - public void removeWebProject(String webProject) - { -// List userKeys = new ArrayList(2); -// userKeys.add(LOCK_TABLE); -// userKeys.add(USERS); -// List users = fAttributeService.getKeys(userKeys); -// // TODO This works incredibly slowly. AttributeService has to support -// // extended querying on values. -// for (String user : users) -// { -// userKeys.add(user); -// Attribute userLocks = fAttributeService.getAttribute(userKeys); -// Iterator iter = userLocks.iterator(); -// while (iter.hasNext()) -// { -// Attribute lockInfo = iter.next(); -// if (lockInfo.get("web_project").getStringValue().equals(webProject)) -// { -// iter.remove(); -// } -// } -// userKeys.remove(2); -// fAttributeService.setAttribute(userKeys, user, userLocks); -// } -// List storeKeys = new ArrayList(); -// storeKeys.add(LOCK_TABLE); -// storeKeys.add(STORES); -// List stores = fAttributeService.getKeys(storeKeys); -// // TODO Ditto. -// for (String store : stores) -// { -// storeKeys.add(store); -// Attribute storeLocks = fAttributeService.getAttribute(storeKeys); -// Iterator iter = storeLocks.iterator(); -// while (iter.hasNext()) -// { -// Attribute lockInfo = iter.next(); -// if (lockInfo.get("web_project").getStringValue().equals(webProject)) -// { -// iter.remove(); -// } -// } -// storeKeys.remove(2); -// fAttributeService.setAttribute(storeKeys, store, storeLocks); -// } - List keys = new ArrayList(2); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - fAttributeService.removeAttribute(keys, webProject); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getStoreLocks(java.lang.String) - */ - public List getStoreLocks(String store) - { - return null; -// List locks = new ArrayList(3); -// List keys = new ArrayList(); -// keys.add(LOCK_TABLE); -// keys.add(STORES); -// keys.add(store); -// List lockKeys = new ArrayList(); -// lockKeys.add(LOCK_TABLE); -// lockKeys.add(WEB_PROJECTS); -// Attribute storeLocks = fAttributeService.getAttribute(keys); -// for (Attribute lockInfo : storeLocks) -// { -// String webProject = lockInfo.get("web_project").getStringValue(); -// String path = lockInfo.get("path").getStringValue(); -// lockKeys.add(webProject); -// lockKeys.add(MD5.Digest(path.getBytes())); -// Attribute lockData = fAttributeService.getAttribute(lockKeys); -// locks.add(new AVMLock(lockData)); -// lockKeys.remove(3); -// lockKeys.remove(2); -// } -// return locks; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#modifyLock(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.List, java.util.List) - */ - public void modifyLock(String webProject, String path, String newPath, String newStore, List usersToRemove, List usersToAdd) - { - AVMLock lock = getLock(webProject, path); - if (lock == null) - { - throw new AVMNotFoundException("Lock not found for " + webProject + ":" + path); - } - removeLock(webProject, path); - if (newPath != null) - { - lock.setPath(newPath); - } - if (newStore != null) - { - lock.setStore(newStore); - } - if (usersToRemove != null) - { - for (String user : usersToRemove) - { - lock.getOwners().remove(user); - } - } - if (usersToAdd != null) - { - for (String user : usersToAdd) - { - if (!fAuthorityService.authorityExists(user) && - !fPersonService.personExists(user)) - { - throw new AVMBadArgumentException("Not an authority: " + user); - } - if (lock.getOwners().contains(user)) - { - continue; - } - lock.getOwners().add(user); - } - } - lockPath(lock); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeStoreLocks(java.lang.String) - */ - public void removeStoreLocks(String store) - { - String webProject = store; - int index = store.indexOf("--"); - if (index >= 0) - { - webProject = store.substring(0, index); - } - List keys = new ArrayList(3); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - keys.add(webProject); - Attribute project = fAttributeService.getAttribute(keys); - if (project == null) - { - return; - } - for (Map.Entry entry: project.entrySet()) - { - AVMLock lock = new AVMLock(entry.getValue()); - if (lock.getStore().equals(store)) - { - project.remove(entry.getKey()); - } - } - keys.remove(2); - fAttributeService.setAttribute(keys, webProject, project); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#hasAccess(java.lang.String, java.lang.String) - */ - public boolean hasAccess(String webProject, String avmPath, String user) - { - if (fPersonService.getPerson(user) == null && - !fAuthorityService.authorityExists(user)) - { - return false; - } - if (fAuthorityService.isAdminAuthority(user)) - { - return true; - } - StoreRef storeRef = new StoreRef(this.webProjectStore); - ResultSet results = fSearchService.query( - storeRef, - SearchService.LANGUAGE_LUCENE, - "@wca\\:avmstore:\"" + webProject + '"'); - if (results.getNodeRefs().size() == 1) - { - return hasAccess(webProject, results.getNodeRefs().get(0), avmPath, user); - } - return false; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#hasAccess(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) - */ - public boolean hasAccess(NodeRef webProjectRef, String avmPath, String user) - { - if (fPersonService.getPerson(user) == null && - !fAuthorityService.authorityExists(user)) - { - return false; - } - if (fAuthorityService.isAdminAuthority(user)) - { - return true; - } - String webProject = (String)fNodeService.getProperty(webProjectRef, WCMAppModel.PROP_AVMSTORE); - return hasAccess(webProject, webProjectRef, avmPath, user); - } - - private boolean hasAccess(String webProject, NodeRef webProjectRef, String avmPath, String user) - { - List children = fNodeService.getChildAssocs( - webProjectRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef child : children) - { - NodeRef childRef = child.getChildRef(); - if (fNodeService.getProperty(childRef, WCMAppModel.PROP_WEBUSERNAME).equals(user) && - fNodeService.getProperty(childRef, WCMAppModel.PROP_WEBUSERROLE).equals(ROLE_CONTENT_MANAGER)) - { - return true; - } - } - String[] storePath = avmPath.split(":"); - if (storePath.length != 2) - { - throw new AVMBadArgumentException("Malformed AVM Path : " + avmPath); - } - String path = normalizePath(storePath[1]); - AVMLock lock = getLock(webProject, path); - if (lock == null) - { - return true; - } - if (!lock.getStore().equals(storePath[0])) - { - return false; - } - List owners = lock.getOwners(); - for (String owner : owners) - { - if (AuthorityType.getAuthorityType(owner) == AuthorityType.EVERYONE) - { - return true; - } - if (checkAgainstAuthority(user, owner)) - { - return true; - } - } - return false; - } - - /** - * Helper function that checks the transitive closure of authorities for user. - * @param user - * @param authority - * @return - */ - private boolean checkAgainstAuthority(String user, String authority) - { - if (user.equalsIgnoreCase(authority)) - { - return true; - } - Set containing = fAuthorityService.getContainingAuthorities(null, user, false); - for (String parent : containing) - { - if (parent.equalsIgnoreCase(authority)) - { - return true; - } - } - return false; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getWebProjects() - */ - public List getWebProjects() - { - List keys = new ArrayList(2); - keys.add(LOCK_TABLE); - keys.add(WEB_PROJECTS); - return fAttributeService.getKeys(keys); - } -} +/* + * 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.avm.locking; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.WCMAppModel; +import org.alfresco.repo.attributes.Attribute; +import org.alfresco.repo.attributes.MapAttributeValue; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.attributes.AttrQueryEquals; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.avm.AVMBadArgumentException; +import org.alfresco.service.cmr.avm.AVMExistsException; +import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.service.cmr.avm.locking.AVMLock; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.MD5; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation of the lock service. + * @author britt + */ +public class AVMLockingServiceImpl implements AVMLockingService +{ + public static final String LOCK_TABLE = ".avm_lock_table"; + public static final String WEB_PROJECTS = "web_projects"; + public static final String USERS = "users"; + public static final String STORES = "stores"; + + private static final String ROLE_CONTENT_MANAGER = "ContentManager"; + + private static final Log logger = LogFactory.getLog(AVMLockingServiceImpl.class); + + /** + * Store name containing the web project nodes. + */ + private String webProjectStore; + + /** + * SearchService for access to web project properties. + */ + private SearchService fSearchService; + + /** + * AttributeService reference. + */ + private AttributeService fAttributeService; + + /** + * AuthorityService reference. + */ + private AuthorityService fAuthorityService; + + /** + * PersonService reference. + */ + private PersonService fPersonService; + + /** + * The NodeService. + */ + private NodeService fNodeService; + + /** + * Transaction Helper reference. + */ + private RetryingTransactionHelper fRetryingTransactionHelper; + + + /** + * @param webProjectStore The webProjectStore to set + */ + public void setWebProjectStore(String webProjectStore) + { + this.webProjectStore = webProjectStore; + } + + /** + * Setter for AttributeService reference. + * @param service + */ + public void setAttributeService(AttributeService service) + { + fAttributeService = service; + } + + /** + * Set the authority service reference. + * @param service + */ + public void setAuthorityService(AuthorityService service) + { + fAuthorityService = service; + } + + /** + * Set the person service reference. + * @param service + */ + public void setPersonService(PersonService service) + { + fPersonService = service; + } + + /** + * Setter for RetryingTransactionHelper reference. + * @param helper + */ + public void setRetryingTransactionHelper(RetryingTransactionHelper helper) + { + fRetryingTransactionHelper = helper; + } + + public void setSearchService(SearchService service) + { + fSearchService = service; + } + + public void setNodeService(NodeService service) + { + fNodeService = service; + } + + public void init() + { + RetryingTransactionHelper.RetryingTransactionCallback callback = + new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() + { + if (!fAttributeService.exists(LOCK_TABLE)) + { + fAttributeService.setAttribute("", LOCK_TABLE, new MapAttributeValue()); + } + if (!fAttributeService.exists(LOCK_TABLE + '/' + WEB_PROJECTS)) + { + fAttributeService.setAttribute(LOCK_TABLE, WEB_PROJECTS, new MapAttributeValue()); + } + return null; + } + }; + fRetryingTransactionHelper.doInTransaction(callback, false); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getLock(java.lang.String, java.lang.String) + */ + public AVMLock getLock(String webProject, String path) + { + path = normalizePath(path); + List keys = new ArrayList(3); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(webProject); + List> attrs = + fAttributeService.query(keys, new AttrQueryEquals(MD5.Digest(path.getBytes()))); + if (attrs.size() == 0) + { + return null; + } + return new AVMLock(attrs.get(0).getSecond()); + } + + /** + * Utility to get relative paths into canonical form. + * @param path The incoming path. + * @return The normalized path. + */ + private String normalizePath(String path) + { + while (path.startsWith("/")) + { + path = path.substring(1); + } + while (path.endsWith("/")) + { + path = path.substring(0, path.length() - 1); + } + return path.replaceAll("/+", "/"); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getUsersLocks(java.lang.String) + */ + public List getUsersLocks(String user) + { +// List keys = new ArrayList(3); +// keys.add(LOCK_TABLE); +// keys.add(USERS); +// keys.add(user); +// Attribute userLocks = fAttributeService.getAttribute(keys); +// List locks = new ArrayList(); +// if (userLocks == null) +// { +// return locks; +// } +// for (Attribute entry : userLocks) +// { +// String webProject = entry.get("web_project").getStringValue(); +// String path = entry.get("path").getStringValue(); +// locks.add(getLock(webProject, path)); +// } +// return locks; + return null; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#lockPath(org.alfresco.service.cmr.avm.locking.AVMLock) + */ + public void lockPath(AVMLock lock) + { + for (String authority : lock.getOwners()) + { + if (!fAuthorityService.authorityExists(authority) && + !fPersonService.personExists(authority)) + { + throw new AVMBadArgumentException("Not an Authority: " + authority); + } + } + List keys = new ArrayList(); + Attribute lockData = lock.getAttribute(); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(lock.getWebProject()); + String digest = MD5.Digest(lock.getPath().getBytes()); + keys.add(digest); + if (fAttributeService.getAttribute(keys) != null) + { + throw new AVMExistsException("Lock Exists: " + keys); + } + keys.remove(3); + fAttributeService.setAttribute(keys, digest, lockData); +// Attribute reverseEntry = new MapAttributeValue(); +// reverseEntry.put("web_project", new StringAttributeValue(lock.getWebProject())); +// reverseEntry.put("path", new StringAttributeValue(lock.getPath())); +// keys.clear(); +// keys.add(LOCK_TABLE); +// keys.add(USERS); +// for (String user : lock.getOwners()) +// { +// keys.add(user); +// Attribute userEntry = fAttributeService.getAttribute(keys); +// keys.remove(2); +// if (userEntry == null) +// { +// fAttributeService.setAttribute(keys, user, new ListAttributeValue()); +// } +// keys.add(user); +// fAttributeService.addAttribute(keys, reverseEntry); +// keys.remove(2); +// } +// String store = lock.getStore(); +// keys.clear(); +// keys.add(LOCK_TABLE); +// keys.add(STORES); +// keys.add(store); +// Attribute storeEntry = fAttributeService.getAttribute(keys); +// keys.remove(2); +// if (storeEntry == null) +// { +// fAttributeService.setAttribute(keys, store, new ListAttributeValue()); +// } +// keys.add(store); +// fAttributeService.addAttribute(keys, reverseEntry); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeLock(java.lang.String, java.lang.String) + */ + public void removeLock(String webProject, String path) + { + path = normalizePath(path); + String pathKey = MD5.Digest(path.getBytes()); + List keys = new ArrayList(4); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(webProject); + keys.add(pathKey); + Attribute lockData = fAttributeService.getAttribute(keys); + if (lockData == null) + { + return; + } + keys.remove(3); + fAttributeService.removeAttribute(keys, pathKey); +// AVMLock lock = new AVMLock(lockData); +// List userKeys = new ArrayList(); +// userKeys.add(LOCK_TABLE); +// userKeys.add(USERS); +// for (String user : lock.getOwners()) +// { +// userKeys.add(user); +// Attribute userLocks = fAttributeService.getAttribute(userKeys); +// for (int i = userLocks.size() - 1; i >= 0; i--) +// { +// Attribute lockInfo = userLocks.get(i); +// if (lockInfo.get("web_project").getStringValue().equals(lock.getWebProject()) +// && lockInfo.get("path").getStringValue().equals(lock.getPath())) +// { +// fAttributeService.removeAttribute(userKeys, i); +// break; +// } +// } +// userKeys.remove(2); +// } +// List storeKeys = new ArrayList(3); +// storeKeys.add(LOCK_TABLE); +// storeKeys.add(STORES); +// String store = lock.getStore(); +// storeKeys.add(store); +// Attribute storeLocks = fAttributeService.getAttribute(storeKeys); +// for (int i = storeLocks.size() - 1; i >= 0; i--) +// { +// Attribute lockInfo = storeLocks.get(i); +// if (lockInfo.get("web_project").getStringValue().equals(lock.getWebProject()) && +// lockInfo.get("path").getStringValue().equals(lock.getPath())) +// { +// fAttributeService.removeAttribute(storeKeys, i); +// break; +// } +// } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#addWebProject(java.lang.String) + */ + public void addWebProject(String webProject) + { + List keys = new ArrayList(3); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(webProject); + if (fAttributeService.exists(keys)) + { + return; + } + keys.remove(2); + fAttributeService.setAttribute(keys, webProject, new MapAttributeValue()); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getWebProjectLocks(java.lang.String) + */ + public List getWebProjectLocks(String webProject) + { + List keys = new ArrayList(3); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(webProject); + Attribute locksMap = fAttributeService.getAttribute(keys); + List result = new ArrayList(); + if (locksMap != null) + { + for (Attribute lockData : locksMap.values()) + { + result.add(new AVMLock(lockData)); + } + } + return result; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeWebProject(java.lang.String) + */ + public void removeWebProject(String webProject) + { +// List userKeys = new ArrayList(2); +// userKeys.add(LOCK_TABLE); +// userKeys.add(USERS); +// List users = fAttributeService.getKeys(userKeys); +// // TODO This works incredibly slowly. AttributeService has to support +// // extended querying on values. +// for (String user : users) +// { +// userKeys.add(user); +// Attribute userLocks = fAttributeService.getAttribute(userKeys); +// Iterator iter = userLocks.iterator(); +// while (iter.hasNext()) +// { +// Attribute lockInfo = iter.next(); +// if (lockInfo.get("web_project").getStringValue().equals(webProject)) +// { +// iter.remove(); +// } +// } +// userKeys.remove(2); +// fAttributeService.setAttribute(userKeys, user, userLocks); +// } +// List storeKeys = new ArrayList(); +// storeKeys.add(LOCK_TABLE); +// storeKeys.add(STORES); +// List stores = fAttributeService.getKeys(storeKeys); +// // TODO Ditto. +// for (String store : stores) +// { +// storeKeys.add(store); +// Attribute storeLocks = fAttributeService.getAttribute(storeKeys); +// Iterator iter = storeLocks.iterator(); +// while (iter.hasNext()) +// { +// Attribute lockInfo = iter.next(); +// if (lockInfo.get("web_project").getStringValue().equals(webProject)) +// { +// iter.remove(); +// } +// } +// storeKeys.remove(2); +// fAttributeService.setAttribute(storeKeys, store, storeLocks); +// } + List keys = new ArrayList(2); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + fAttributeService.removeAttribute(keys, webProject); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getStoreLocks(java.lang.String) + */ + public List getStoreLocks(String store) + { + return null; +// List locks = new ArrayList(3); +// List keys = new ArrayList(); +// keys.add(LOCK_TABLE); +// keys.add(STORES); +// keys.add(store); +// List lockKeys = new ArrayList(); +// lockKeys.add(LOCK_TABLE); +// lockKeys.add(WEB_PROJECTS); +// Attribute storeLocks = fAttributeService.getAttribute(keys); +// for (Attribute lockInfo : storeLocks) +// { +// String webProject = lockInfo.get("web_project").getStringValue(); +// String path = lockInfo.get("path").getStringValue(); +// lockKeys.add(webProject); +// lockKeys.add(MD5.Digest(path.getBytes())); +// Attribute lockData = fAttributeService.getAttribute(lockKeys); +// locks.add(new AVMLock(lockData)); +// lockKeys.remove(3); +// lockKeys.remove(2); +// } +// return locks; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#modifyLock(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.List, java.util.List) + */ + public void modifyLock(String webProject, String path, String newPath, String newStore, List usersToRemove, List usersToAdd) + { + AVMLock lock = getLock(webProject, path); + if (lock == null) + { + throw new AVMNotFoundException("Lock not found for " + webProject + ":" + path); + } + removeLock(webProject, path); + if (newPath != null) + { + lock.setPath(newPath); + } + if (newStore != null) + { + lock.setStore(newStore); + } + if (usersToRemove != null) + { + for (String user : usersToRemove) + { + lock.getOwners().remove(user); + } + } + if (usersToAdd != null) + { + for (String user : usersToAdd) + { + if (!fAuthorityService.authorityExists(user) && + !fPersonService.personExists(user)) + { + throw new AVMBadArgumentException("Not an authority: " + user); + } + if (lock.getOwners().contains(user)) + { + continue; + } + lock.getOwners().add(user); + } + } + lockPath(lock); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#removeStoreLocks(java.lang.String) + */ + public void removeStoreLocks(String store) + { + String webProject = store; + int index = store.indexOf("--"); + if (index >= 0) + { + webProject = store.substring(0, index); + } + List keys = new ArrayList(3); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + keys.add(webProject); + Attribute project = fAttributeService.getAttribute(keys); + if (project == null) + { + return; + } + for (Map.Entry entry: project.entrySet()) + { + AVMLock lock = new AVMLock(entry.getValue()); + if (lock.getStore().equals(store)) + { + project.remove(entry.getKey()); + } + } + keys.remove(2); + fAttributeService.setAttribute(keys, webProject, project); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#hasAccess(java.lang.String, java.lang.String) + */ + public boolean hasAccess(String webProject, String avmPath, String user) + { + if (fPersonService.getPerson(user) == null && + !fAuthorityService.authorityExists(user)) + { + return false; + } + if (fAuthorityService.isAdminAuthority(user)) + { + return true; + } + StoreRef storeRef = new StoreRef(this.webProjectStore); + ResultSet results = fSearchService.query( + storeRef, + SearchService.LANGUAGE_LUCENE, + "@wca\\:avmstore:\"" + webProject + '"'); + if (results.getNodeRefs().size() == 1) + { + return hasAccess(webProject, results.getNodeRefs().get(0), avmPath, user); + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#hasAccess(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String) + */ + public boolean hasAccess(NodeRef webProjectRef, String avmPath, String user) + { + if (fPersonService.getPerson(user) == null && + !fAuthorityService.authorityExists(user)) + { + return false; + } + if (fAuthorityService.isAdminAuthority(user)) + { + return true; + } + String webProject = (String)fNodeService.getProperty(webProjectRef, WCMAppModel.PROP_AVMSTORE); + return hasAccess(webProject, webProjectRef, avmPath, user); + } + + private boolean hasAccess(String webProject, NodeRef webProjectRef, String avmPath, String user) + { + String[] storePath = avmPath.split(":"); + if (storePath.length != 2) + { + throw new AVMBadArgumentException("Malformed AVM Path : " + avmPath); + } + + if (logger.isDebugEnabled()) + logger.debug("Testing lock access on path: " + avmPath + " for user: " + user + " in webproject: " + webProject); + + // check if a lock exists at all for this path in the specified webproject id + String path = normalizePath(storePath[1]); + AVMLock lock = getLock(webProject, path); + if (lock == null) + { + if (logger.isDebugEnabled()) + logger.debug(" GRANTED: No lock found."); + return true; + } + + // locks are ignored in a workflow store + if (storePath[0].contains("--workflow")) + { + if (logger.isDebugEnabled()) + logger.debug(" GRANTED: Workflow store path."); + return true; + } + + // locks are specific to a store - no access if the stores are different + if (!lock.getStore().equals(storePath[0])) + { + if (logger.isDebugEnabled()) + logger.debug(" DENIED: Store on path and lock (" + lock.getStore().toString() + ") do not match."); + return false; + } + + // check for content manager role - we allow access to all managers within the same store + List children = fNodeService.getChildAssocs( + webProjectRef, WCMAppModel.ASSOC_WEBUSER, RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef child : children) + { + NodeRef childRef = child.getChildRef(); + if (fNodeService.getProperty(childRef, WCMAppModel.PROP_WEBUSERNAME).equals(user) && + fNodeService.getProperty(childRef, WCMAppModel.PROP_WEBUSERROLE).equals(ROLE_CONTENT_MANAGER)) + { + if (logger.isDebugEnabled()) + logger.debug(" GRANTED: Store match and user is ContentManager role in webproject."); + return true; + } + } + + // finally check the owners of the lock against the specified authority + List owners = lock.getOwners(); + for (String owner : owners) + { + if (AuthorityType.getAuthorityType(owner) == AuthorityType.EVERYONE) + { + if (logger.isDebugEnabled()) + logger.debug(" GRANTED: Authority EVERYONE matched lock owner."); + return true; + } + if (checkAgainstAuthority(user, owner)) + { + if (logger.isDebugEnabled()) + logger.debug(" GRANTED: User matched as lock owner."); + return true; + } + } + + if (logger.isDebugEnabled()) + logger.debug(" DENIED: User did not match as lock owner."); + return false; + } + + /** + * Helper function that checks the transitive closure of authorities for user. + * @param user + * @param authority + * @return + */ + private boolean checkAgainstAuthority(String user, String authority) + { + if (user.equalsIgnoreCase(authority)) + { + return true; + } + Set containing = fAuthorityService.getContainingAuthorities(null, user, false); + for (String parent : containing) + { + if (parent.equalsIgnoreCase(authority)) + { + return true; + } + } + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.avm.locking.AVMLockingService#getWebProjects() + */ + public List getWebProjects() + { + List keys = new ArrayList(2); + keys.add(LOCK_TABLE); + keys.add(WEB_PROJECTS); + return fAttributeService.getKeys(keys); + } +} diff --git a/source/java/org/alfresco/repo/avm/util/SimplePath.java b/source/java/org/alfresco/repo/avm/util/SimplePath.java index 42a8231e34..21cbeb9c20 100644 --- a/source/java/org/alfresco/repo/avm/util/SimplePath.java +++ b/source/java/org/alfresco/repo/avm/util/SimplePath.java @@ -3,12 +3,16 @@ */ package org.alfresco.repo.avm.util; +import java.io.Serializable; + /** * Holds a simple path. * @author britt */ -public class SimplePath +public class SimplePath implements Serializable { + private static final long serialVersionUID = 2696828491008988470L; + /** * The names of the path's components. */