diff --git a/source/java/org/alfresco/jcr/item/ItemImpl.java b/source/java/org/alfresco/jcr/item/ItemImpl.java index 2c87bea30b..edf6e807e9 100644 --- a/source/java/org/alfresco/jcr/item/ItemImpl.java +++ b/source/java/org/alfresco/jcr/item/ItemImpl.java @@ -38,7 +38,6 @@ import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.version.VersionException; import org.alfresco.jcr.session.SessionImpl; -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; /** @@ -107,7 +106,6 @@ public abstract class ItemImpl implements Item */ public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException { - AlfrescoTransactionSupport.flush(); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/avm/AVMCrawlTestP.java b/source/java/org/alfresco/repo/avm/AVMCrawlTestP.java index 8d1df80d8c..7d421918b1 100644 --- a/source/java/org/alfresco/repo/avm/AVMCrawlTestP.java +++ b/source/java/org/alfresco/repo/avm/AVMCrawlTestP.java @@ -1,127 +1,127 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.List; - -import org.alfresco.repo.avm.util.BulkLoader; - - -/** - * Another performance test that runs simultaneous crawlers that - * do operations with locality of reference. - * @author britt - */ -public class AVMCrawlTestP extends AVMServiceTestBase -{ - /** - * Do the crawl test. - */ - public void testCrawl() - { - int n = 4; // Number of Threads. - int m = 2; // How many multiples of content to start with. - long runTime = 3600000; // 1 Hour. . - fService.purgeStore("main"); - BulkLoader loader = new BulkLoader(); - loader.setAvmService(fService); - for (int i = 0; i < m; i++) - { - fService.createStore("d" + i); - loader.recursiveLoad("source", "d" + i + ":/"); - fService.createSnapshot("d" + i, null, null); - } - long startTime = System.currentTimeMillis(); - List crawlers = new ArrayList(); - List threads = new ArrayList(); - for (int i = 0; i < n; i++) - { - crawlers.add(new AVMCrawler(fService)); - threads.add(new Thread(crawlers.get(i))); - threads.get(i).start(); - } - while (true) - { - try - { - Thread.sleep(5000); - // Check that none of the crawlers has errored out. - for (AVMCrawler crawler : crawlers) - { - if (crawler.getError()) - { - for (AVMCrawler craw : crawlers) - { - craw.setDone(); - } - for (Thread thread : threads) - { - try - { - thread.join(); - } - catch (InterruptedException ie) - { - // Do nothing. - } - } - fail(); - } - } - } - catch (InterruptedException ie) - { - // Do nothing. - } - long now = System.currentTimeMillis(); - if (now - startTime > runTime) - { - break; - } - } - for (AVMCrawler crawler : crawlers) - { - crawler.setDone(); - } - for (Thread thread : threads) - { - try - { - thread.join(); - } - catch (InterruptedException ie) - { - // Do nothing. - } - } - long ops = 0L; - for (AVMCrawler crawler : crawlers) - { - ops += crawler.getOpCount(); - } - long time = System.currentTimeMillis() - startTime; - System.out.println("Ops/Sec: " + (ops * 1000L / time)); - } -} +/* + * 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; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.avm.util.BulkLoader; + + +/** + * Another performance test that runs simultaneous crawlers that + * do operations with locality of reference. + * @author britt + */ +public class AVMCrawlTestP extends AVMServiceTestBase +{ + /** + * Do the crawl test. + */ + public void testCrawl() + { + int n = 8; // Number of Threads. + int m = 2; // How many multiples of content to start with. + long runTime = 28800000; // 8 Hours. . + fService.purgeStore("main"); + BulkLoader loader = new BulkLoader(); + loader.setAvmService(fService); + for (int i = 0; i < m; i++) + { + fService.createStore("d" + i); + loader.recursiveLoad("source", "d" + i + ":/"); + fService.createSnapshot("d" + i, null, null); + } + long startTime = System.currentTimeMillis(); + List crawlers = new ArrayList(); + List threads = new ArrayList(); + for (int i = 0; i < n; i++) + { + crawlers.add(new AVMCrawler(fService)); + threads.add(new Thread(crawlers.get(i))); + threads.get(i).start(); + } + while (true) + { + try + { + Thread.sleep(5000); + // Check that none of the crawlers has errored out. + for (AVMCrawler crawler : crawlers) + { + if (crawler.getError()) + { + for (AVMCrawler craw : crawlers) + { + craw.setDone(); + } + for (Thread thread : threads) + { + try + { + thread.join(); + } + catch (InterruptedException ie) + { + // Do nothing. + } + } + fail(); + } + } + } + catch (InterruptedException ie) + { + // Do nothing. + } + long now = System.currentTimeMillis(); + if (now - startTime > runTime) + { + break; + } + } + for (AVMCrawler crawler : crawlers) + { + crawler.setDone(); + } + for (Thread thread : threads) + { + try + { + thread.join(); + } + catch (InterruptedException ie) + { + // Do nothing. + } + } + long ops = 0L; + for (AVMCrawler crawler : crawlers) + { + ops += crawler.getOpCount(); + } + long time = System.currentTimeMillis() - startTime; + System.out.println("Ops/Sec: " + (ops * 1000L / time)); + } +} diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java index 1f7b2da59b..2bf6c98db0 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeService.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java @@ -46,6 +46,7 @@ import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.avm.AVMStoreDescriptor; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.InvalidAspectException; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.cmr.dictionary.PropertyDefinition; @@ -1194,7 +1195,12 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi } } } - values.put(qName, new PropertyValue(null, properties.get(qName))); + DataTypeDefinition def = dictionaryService.getDataType(qName); + if (def == null) + { + def = dictionaryService.getDataType(properties.get(qName).getClass()); + } + values.put(qName, new PropertyValue(def.getName(), properties.get(qName))); } fAVMService.setNodeProperties(avmVersionPath.getSecond(), values); // Invoke policy behaviors. @@ -1287,7 +1293,12 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi try { // Map propsBefore = getProperties(nodeRef); - fAVMService.setNodeProperty(avmVersionPath.getSecond(), qname, new PropertyValue(null, value)); + DataTypeDefinition def = dictionaryService.getDataType(qname); + if (def == null) + { + def = dictionaryService.getDataType(value.getClass()); + } + fAVMService.setNodeProperty(avmVersionPath.getSecond(), qname, new PropertyValue(def.getName(), value)); // Map propsAfter = getProperties(nodeRef); // Invoke policy behaviors. // invokeOnUpdateNode(nodeRef); diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index b51e183dae..314baba190 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -639,7 +639,8 @@ public class AVMRepository throw new AVMNotFoundException("Path not found."); } srcDir = (DirectoryNode)sPath.getCurrentNode(); - srcNode = srcDir.lookupChild(sPath, srcName, false); + Pair temp = srcDir.lookupChild(sPath, srcName, false); + srcNode = (temp == null) ? null : temp.getFirst(); if (srcNode == null) { throw new AVMNotFoundException("Not found: " + srcName); @@ -665,7 +666,8 @@ public class AVMRepository throw new AVMNotFoundException("Path not found."); } DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode(); - AVMNode child = dstDir.lookupChild(dPath, dstName, true); + Pair temp = dstDir.lookupChild(dPath, dstName, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Node exists: " + dstName); diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index f69fa66117..239275f744 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -254,13 +254,13 @@ public class AVMServiceTest extends AVMServiceTestBase props = new HashMap(); QName n1 = QName.createQName("silly.uri", "Prop1"); - PropertyValue p1 = new PropertyValue(null, new Date(System.currentTimeMillis())); + PropertyValue p1 = new PropertyValue(DataTypeDefinition.DATETIME, new Date(System.currentTimeMillis())); props.put(n1, p1); QName n2 = QName.createQName("silly.uri", "Prop2"); - PropertyValue p2 = new PropertyValue(null, "A String Property."); + PropertyValue p2 = new PropertyValue(DataTypeDefinition.TEXT, "A String Property."); props.put(n2, p2); QName n3 = QName.createQName("silly.uri", "Prop3"); - PropertyValue p3 = new PropertyValue(null, 42); + PropertyValue p3 = new PropertyValue(DataTypeDefinition.INT, 42); props.put(n3, p3); fService.setNodeProperties("main:/a/b/c/bar", props); fService.createSnapshot("main", null, null); @@ -5187,11 +5187,11 @@ public class AVMServiceTest extends AVMServiceTestBase fService.addAspect("main:/a/b/c/foo", ContentModel.ASPECT_TITLED); fService.addAspect("main:/a/b/c/foo", ContentModel.ASPECT_AUDITABLE); Map properties = new HashMap(); - properties.put(ContentModel.PROP_ACCESSED, new PropertyValue(null, new Date(System.currentTimeMillis()))); - properties.put(ContentModel.PROP_CREATED, new PropertyValue(null, new Date(System.currentTimeMillis()))); - properties.put(ContentModel.PROP_MODIFIED, new PropertyValue(null, new Date(System.currentTimeMillis()))); - properties.put(ContentModel.PROP_CREATOR, new PropertyValue(null, "Giles")); - properties.put(ContentModel.PROP_MODIFIER, new PropertyValue(null, "Quentin")); + properties.put(ContentModel.PROP_ACCESSED, new PropertyValue(DataTypeDefinition.DATETIME, new Date(System.currentTimeMillis()))); + properties.put(ContentModel.PROP_CREATED, new PropertyValue(DataTypeDefinition.DATETIME, new Date(System.currentTimeMillis()))); + properties.put(ContentModel.PROP_MODIFIED, new PropertyValue(DataTypeDefinition.DATETIME, new Date(System.currentTimeMillis()))); + properties.put(ContentModel.PROP_CREATOR, new PropertyValue(DataTypeDefinition.TEXT, "Giles")); + properties.put(ContentModel.PROP_MODIFIER, new PropertyValue(DataTypeDefinition.TEXT, "Quentin")); fService.setNodeProperties("main:/a/b/c/foo", properties); fService.createSnapshot("main", null, null); diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 956b86e879..5cadfa3ce6 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -59,6 +59,7 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; +import org.alfresco.util.Pair; /** * A Repository contains a current root directory and a list of @@ -326,7 +327,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + path + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Child exists: " + name); @@ -368,7 +370,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + dstPath + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Child exists: " + name); @@ -411,7 +414,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + path + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Child exists: " + name); @@ -446,7 +450,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + path + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Child exists: " + name); @@ -483,7 +488,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + dstPath + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { throw new AVMExistsException("Child exists: " + name); @@ -1431,7 +1437,8 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AVMNotFoundException("Path " + path + " not found."); } DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode(); - AVMNode child = dir.lookupChild(lPath, name, true); + Pair temp = dir.lookupChild(lPath, name, true); + AVMNode child = (temp == null) ? null : temp.getFirst(); if (child == null) { throw new AVMNotFoundException("Node not found: " + name); diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index 29e87c960f..a98badf5f6 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -1,818 +1,826 @@ -/* - * 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; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.service.cmr.avm.AVMBadArgumentException; -import org.alfresco.service.cmr.avm.AVMNodeDescriptor; -import org.alfresco.service.cmr.avm.AVMNotFoundException; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.avmsync.AVMDifference; -import org.alfresco.service.cmr.avmsync.AVMSyncException; -import org.alfresco.service.cmr.avmsync.AVMSyncService; -import org.alfresco.util.NameMatcher; -import org.apache.log4j.Logger; - -/** - * This implements APIs that allow comparison and synchronization - * of node trees as well as cumulative operations on layers to - * support various content production models. - * @author britt - */ -public class AVMSyncServiceImpl implements AVMSyncService -{ - private static Logger fgLogger = Logger.getLogger(AVMSyncServiceImpl.class); - - /** - * The AVMService. - */ - private AVMService fAVMService; - - /** - * The AVMRepository. - */ - private AVMRepository fAVMRepository; - - /** - * Do nothing constructor. - */ - public AVMSyncServiceImpl() - { - } - - /** - * Set the AVM Service. For Spring. - * @param avmService The AVMService reference. - */ - public void setAvmService(AVMService avmService) - { - fAVMService = avmService; - } - - public void setAvmRepository(AVMRepository avmRepository) - { - fAVMRepository = avmRepository; - } - - /** - * Get a difference list between two corresponding node trees. - * @param srcVersion The version id for the source tree. - * @param srcPath The avm path to the source tree. - * @param dstVersion The version id for the destination tree. - * @param dstPath The avm path to the destination tree. - * @param excluder A NameMatcher used to exclude files from consideration. - * @return A List of AVMDifference structs which can be used for - * the update operation. - */ - public List compare(int srcVersion, String srcPath, - int dstVersion, String dstPath, - NameMatcher excluder) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug(srcPath + " : " + dstPath); - try - { - throw new Exception(); - } - catch (Exception e) - { - fgLogger.debug("Stack Trace: ", e); - } - } - if (srcPath == null || dstPath == null) - { - throw new AVMBadArgumentException("Illegal null path."); - } - List result = new ArrayList(); - AVMNodeDescriptor srcDesc = fAVMService.lookup(srcVersion, srcPath, true); - if (srcDesc == null) - { - throw new AVMSyncException("Source not found: " + srcPath); - } - AVMNodeDescriptor dstDesc = fAVMService.lookup(dstVersion, dstPath, true); - if (dstDesc == null) - { - // Special case: no pre-existing version in the destination. - result.add(new AVMDifference(srcVersion, srcPath, - dstVersion, dstPath, - AVMDifference.NEWER)); - } - else - { - // Invoke the recursive implementation. - compare(srcVersion, srcDesc, dstVersion, dstDesc, result, excluder); - } - return result; - } - - /** - * Internal recursive implementation of compare. - * @param srcVersion The version of the source tree. - * @param srcDesc The current source descriptor. - * @param dstVersion The version of the destination tree. - * @param dstDesc The current dstDesc - */ - private void compare(int srcVersion, AVMNodeDescriptor srcDesc, - int dstVersion, AVMNodeDescriptor dstDesc, - List result, NameMatcher excluder) - { - // Determine how the source and destination nodes differ. - if (excluder != null && (excluder.matches(srcDesc.getPath()) || - excluder.matches(dstDesc.getPath()))) - { - return; - } - int diffCode = compareOne(srcDesc, dstDesc); - switch (diffCode) - { - case AVMDifference.SAME : - { - // A big short circuit. - return; - } - // The trivial to handle cases. - case AVMDifference.NEWER : - case AVMDifference.OLDER : - case AVMDifference.CONFLICT : - { - result.add(new AVMDifference(srcVersion, srcDesc.getPath(), - dstVersion, dstDesc.getPath(), - diffCode)); - return; - } - case AVMDifference.DIRECTORY : - { - // First special case: source is a layered directory which points to - // the destinations path, and we are comparing 'head' versions. - if (srcDesc.isLayeredDirectory() && - srcDesc.getIndirection().equals(dstDesc.getPath()) && srcVersion < 0 && dstVersion < 0) - { - // Get only a direct listing, since that's all that can be different. - Map srcList = - fAVMService.getDirectoryListingDirect(srcDesc, true); - // The biggest shortcut: if the source directory is directly empty - // then we're done. - if (srcList.size() == 0) - { - return; - } - // We grab a complete listing of the destination. - Map dstList = - fAVMService.getDirectoryListing(dstDesc, true); - for (String name : srcList.keySet()) - { - AVMNodeDescriptor srcChild = srcList.get(name); - AVMNodeDescriptor dstChild = dstList.get(name); - String dstPath = AVMNodeConverter.ExtendAVMPath(dstDesc.getPath(), name); - if (excluder != null && (excluder.matches(srcChild.getPath()) || - excluder.matches(dstPath))) - { - continue; - } - if (dstChild == null) - { - // A missing destination child means the source is NEWER. - result.add(new AVMDifference(srcVersion, srcChild.getPath(), - dstVersion, - dstPath, - AVMDifference.NEWER)); - continue; - } - // Otherwise recursively invoke. - compare(srcVersion, srcChild, - dstVersion, dstChild, - result, excluder); - } - return; - } - // Second special case. Just as above but reversed. - if (dstDesc.isLayeredDirectory() && - dstDesc.getIndirection().equals(srcDesc.getPath()) && srcVersion < 0 && dstVersion < 0) - { - // Get direct content of destination. - Map dstList = - fAVMService.getDirectoryListingDirect(dstDesc, true); - // Big short circuit. - if (dstList.size() == 0) - { - return; - } - // Get the source listing. - Map srcList = - fAVMService.getDirectoryListing(srcDesc, true); - for (String name : dstList.keySet()) - { - AVMNodeDescriptor dstChild = dstList.get(name); - AVMNodeDescriptor srcChild = srcList.get(name); - String srcPath = AVMNodeConverter.ExtendAVMPath(srcDesc.getPath(), name); - if (excluder != null && (excluder.matches(srcPath) || - excluder.matches(dstChild.getPath()))) - { - continue; - } - if (srcChild == null) - { - // Missing means the source is older. - result.add(new AVMDifference(srcVersion, - srcPath, - dstVersion, dstChild.getPath(), - AVMDifference.OLDER)); - continue; - } - // Otherwise, recursively invoke. - compare(srcVersion, srcChild, - dstVersion, dstChild, - result, excluder); - } - return; - } - // Neither of the special cases apply, so brute force is the only answer. - Map srcList = - fAVMService.getDirectoryListing(srcDesc, true); - Map dstList = - fAVMService.getDirectoryListing(dstDesc, true); - // Iterate over the source. - for (String name : srcList.keySet()) - { - AVMNodeDescriptor srcChild = srcList.get(name); - AVMNodeDescriptor dstChild = dstList.get(name); - String dstPath = AVMNodeConverter.ExtendAVMPath(dstDesc.getPath(), name); - if (excluder != null && (excluder.matches(srcChild.getPath()) || - excluder.matches(dstPath))) - { - continue; - } - if (dstChild == null) - { - // Not found in the destination means NEWER. - result.add(new AVMDifference(srcVersion, srcChild.getPath(), - dstVersion, - dstPath, - AVMDifference.NEWER)); - continue; - } - // Otherwise recursive invocation. - compare(srcVersion, srcChild, - dstVersion, dstChild, - result, excluder); - } - // Iterate over the destination. - for (String name : dstList.keySet()) - { - if (srcList.containsKey(name)) - { - continue; - } - AVMNodeDescriptor dstChild = dstList.get(name); - String srcPath = AVMNodeConverter.ExtendAVMPath(srcDesc.getPath(), name); - if (excluder != null && (excluder.matches(srcPath) || - excluder.matches(dstChild.getPath()))) - { - continue; - } - // An entry not found in the source is OLDER. - result.add(new AVMDifference(srcVersion, - srcPath, - dstVersion, dstChild.getPath(), - AVMDifference.OLDER)); - } - break; - } - default : - { - throw new AVMSyncException("Invalid Difference Code, Internal Error."); - } - } - } - - /** - * Updates the destination nodes in the AVMDifferences - * with the source nodes. Normally any conflicts or cases in - * which the source of an AVMDifference is older than the destination - * will cause the transaction to roll back. - * @param diffList A List of AVMDifference structs. - * @param excluder A possibly null name matcher to exclude unwanted updates. - * @param ignoreConflicts If this is true the update will skip those - * AVMDifferences which are in conflict with - * the destination. - * @param ignoreOlder If this is true the update will skip those - * AVMDifferences which have the source older than the destination. - * @param overrideConflicts If this is true the update will override conflicting - * AVMDifferences and replace the destination with the conflicting source. - * @param overrideOlder If this is true the update will override AVMDifferences - * @param tag Short update blurb. - * @param description Full update blurb. - * in which the source is older than the destination and overwrite the destination. - */ - public void update(List diffList, - NameMatcher excluder, boolean ignoreConflicts, boolean ignoreOlder, - boolean overrideConflicts, boolean overrideOlder, String tag, String description) - { - if (fgLogger.isDebugEnabled()) - { - try - { - throw new Exception("Stack Trace."); - } - catch (Exception e) - { - fgLogger.debug("Stack trace: ", e); - } - } - Map storeVersions = new HashMap(); - Set destStores = new HashSet(); - for (AVMDifference diff : diffList) - { - if (excluder != null && (excluder.matches(diff.getSourcePath()) || - excluder.matches(diff.getDestinationPath()))) - { - continue; - } - if (!diff.isValid()) - { - throw new AVMSyncException("Malformed AVMDifference."); - } - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("update: " + diff); - } - // Snapshot the source if needed. - int version = diff.getSourceVersion(); - if (version < 0) - { - int colonOff = diff.getSourcePath().indexOf(':'); - if (colonOff == -1) - { - throw new AVMBadArgumentException("Invalid path."); - } - String storeName = diff.getSourcePath().substring(0, colonOff); - if (storeVersions.containsKey(storeName)) - { - // We've already snapshotted this store. - version = storeVersions.get(storeName); - } - else - { - version = fAVMService.createSnapshot(storeName, "Snapshotted for submit.", null).get(storeName); - storeVersions.put(storeName, version); - } - } - AVMNodeDescriptor srcDesc = fAVMService.lookup(version, - diff.getSourcePath(), true); - String [] dstParts = AVMNodeConverter.SplitBase(diff.getDestinationPath()); - if (dstParts[0] == null || diff.getDestinationVersion() >= 0) - { - // You can't have a root node as a destination. - throw new AVMSyncException("Invalid destination node: " + diff.getDestinationPath()); - } - AVMNodeDescriptor dstDesc = fAVMService.lookup(-1, diff.getDestinationPath(), true); - // The default is that the source is newer in the case where - // the destination doesn't exist. - int diffCode = AVMDifference.NEWER; - if (dstDesc != null) - { - diffCode = compareOne(srcDesc, dstDesc); - } - // Keep track of stores updated so that they can all be snapshotted - // at end of update. - String dstPath = diff.getDestinationPath(); - destStores.add(dstPath.substring(0, dstPath.indexOf(':'))); - // Dispatch. - switch (diffCode) - { - case AVMDifference.SAME : - { - // Nada to do. - continue; - } - case AVMDifference.NEWER : - { - // You can't delete what isn't there. - linkIn(dstParts[0], dstParts[1], srcDesc, excluder, dstDesc != null && !dstDesc.isDeleted()); - continue; - } - case AVMDifference.OLDER : - { - // You can force it. - if (overrideOlder) - { - linkIn(dstParts[0], dstParts[1], srcDesc, excluder, !dstDesc.isDeleted()); - continue; - } - // You can ignore it. - if (ignoreOlder) - { - continue; - } - // Or it's an error. - throw new AVMSyncException("Older version prevents update."); - } - case AVMDifference.CONFLICT : - { - // You can force it. - if (overrideConflicts) - { - linkIn(dstParts[0], dstParts[1], srcDesc, excluder, true); - continue; - } - // You can ignore it. - if (ignoreConflicts) - { - continue; - } - // Or it's an error. - throw new AVMSyncException("Conflict prevents update."); - } - case AVMDifference.DIRECTORY : - { - // You can only ignore this. - if (ignoreConflicts) - { - continue; - } - // Otherwise it's an error. - throw new AVMSyncException("Directory conflict prevents update."); - } - default : - { - throw new AVMSyncException("Invalid Difference Code: Internal Error."); - } - } - } - for (String storeName : destStores) - { - fAVMService.createSnapshot(storeName, tag, description); - } - } - - /** - * Do the actual work of connecting nodes to the destination tree. - * @param parentPath The parent path the node will go in. - * @param name The name it will have. - * @param toLink The node descriptor. - * @param removeFirst Whether to do a removeNode before linking in. - */ - private void linkIn(String parentPath, String name, AVMNodeDescriptor toLink, NameMatcher excluder, boolean removeFirst) - { - // This is a delete. - if (toLink == null) - { - fAVMService.removeNode(parentPath, name); - return; - } - mkdirs(parentPath, AVMNodeConverter.SplitBase(toLink.getPath())[0]); - if (removeFirst) - { - fAVMService.removeNode(parentPath, name); - } - if (toLink.isLayeredDirectory() && !toLink.isPrimary()) - { - recursiveCopy(parentPath, name, toLink, excluder); - return; - } - fAVMService.link(parentPath, name, toLink); - } - - /** - * Recursively copy a node into the given position. - * @param parentPath The place to put it. - * @param name The name to give it. - * @param toCopy The it to put. - */ - private void recursiveCopy(String parentPath, String name, AVMNodeDescriptor toCopy, NameMatcher excluder) - { - fAVMService.createDirectory(parentPath, name); - String newParentPath = AVMNodeConverter.ExtendAVMPath(parentPath, name); - fAVMService.setMetaDataFrom(newParentPath, toCopy); - AVMNodeDescriptor parentDesc = fAVMService.lookup(-1, newParentPath, true); - Map children = - fAVMService.getDirectoryListing(toCopy, true); - for (Map.Entry entry : children.entrySet()) - { - recursiveCopy(parentDesc, entry.getKey(), entry.getValue(), excluder); - } - } - - /** - * Shortcutting helper that uses an AVMNodeDescriptor parent. - * @param parent The parent we are linking into. - * @param name The name to link in. - * @param toCopy The node to link in. - */ - private void recursiveCopy(AVMNodeDescriptor parent, String name, AVMNodeDescriptor toCopy, NameMatcher excluder) - { - String newPath = AVMNodeConverter.ExtendAVMPath(parent.getPath(), name); - if (excluder != null && (excluder.matches(newPath) || - excluder.matches(toCopy.getPath()))) - { - return; - } - // If it's a file or deleted simply link it in. - if (toCopy.isFile() || toCopy.isDeleted() || toCopy.isPlainDirectory()) - { - fAVMRepository.link(parent, name, toCopy); - return; - } - // Otherwise make a directory in the target parent, and recursiveCopy all the source - // children into it. - AVMNodeDescriptor newParentDesc = fAVMRepository.createDirectory(parent, name); - fAVMService.setMetaDataFrom(newParentDesc.getPath(), toCopy); - Map children = - fAVMService.getDirectoryListing(toCopy, true); - for (Map.Entry entry : children.entrySet()) - { - recursiveCopy(newParentDesc, entry.getKey(), entry.getValue(), excluder); - } - } - - /** - * The workhorse of comparison and updating. Determine the versioning relationship - * of two nodes. - * @param srcDesc Descriptor for the source node. - * @param dstDesc Descriptor for the destination node. - * @return One of SAME, OLDER, NEWER, CONFLICT, DIRECTORY - */ - private int compareOne(AVMNodeDescriptor srcDesc, AVMNodeDescriptor dstDesc) - { - if (srcDesc == null) - { - return AVMDifference.OLDER; - } - if (srcDesc.getId() == dstDesc.getId()) - { - return AVMDifference.SAME; - } - // Matched directories that are not identical are nominally in conflict - // but get their own special difference code for comparison logic. The DIRECTORY - // difference code never gets returned to callers of compare. - if (srcDesc.isDirectory() && dstDesc.isDirectory()) - { - return AVMDifference.DIRECTORY; - } - // Check for mismatched fundamental types. - if ((srcDesc.isDirectory() && dstDesc.isFile()) || - (srcDesc.isFile() && dstDesc.isDirectory())) - { - return AVMDifference.CONFLICT; - } - // A deleted node on either side means uniform handling because - // a deleted node can be the descendent of any other type of node. - if (srcDesc.isDeleted() || dstDesc.isDeleted()) - { - AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); - if (common == null) - { - return AVMDifference.CONFLICT; - } - if (common.getId() == srcDesc.getId()) - { - return AVMDifference.OLDER; - } - if (common.getId() == dstDesc.getId()) - { - return AVMDifference.NEWER; - } - // Must be a conflict. - return AVMDifference.CONFLICT; - } - // At this point both source and destination are both some kind of file. - if (srcDesc.isLayeredFile()) - { - // Handle the layered file source case. - if (dstDesc.isPlainFile()) - { - // We consider a layered source file that points at the destination - // file SAME. - if (srcDesc.getIndirection().equals(dstDesc.getPath())) - { - return AVMDifference.SAME; - } - // We know that they are in conflict since they are of different types. - return AVMDifference.CONFLICT; - } - // Destination is a layered file also. - AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); - if (common == null) - { - return AVMDifference.CONFLICT; - } - if (common.getId() == srcDesc.getId()) - { - return AVMDifference.OLDER; - } - if (common.getId() == dstDesc.getId()) - { - return AVMDifference.NEWER; - } - // Finally we know they are in conflict. - return AVMDifference.CONFLICT; - } - // Source is a plain file. - if (dstDesc.isLayeredFile()) - { - // We consider a source file that is the target of a layered destination file to be - // SAME. - if (dstDesc.getIndirection().equals(srcDesc.getPath())) - { - return AVMDifference.SAME; - } - // Otherwise we know they are in conflict because they are of different type. - return AVMDifference.CONFLICT; - } - // Destination is a plain file. - AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); - // Conflict case. - if (common == null) - { - return AVMDifference.CONFLICT; - } - if (common.getId() == srcDesc.getId()) - { - return AVMDifference.OLDER; - } - if (common.getId() == dstDesc.getId()) - { - return AVMDifference.NEWER; - } - // The must, finally, be in conflict. - return AVMDifference.CONFLICT; - } - - /** - * Flattens a layer so that all all nodes under and including - * layerPath become translucent to any nodes in the - * corresponding location under and including underlyingPath - * that are the same version. - * @param layerPath The overlying layer path. - * @param underlyingPath The underlying path. - */ - public void flatten(String layerPath, String underlyingPath) - { - if (layerPath == null || underlyingPath == null) - { - throw new AVMBadArgumentException("Illegal null path."); - } - AVMNodeDescriptor layerNode = fAVMService.lookup(-1, layerPath, true); - if (layerNode == null) - { - throw new AVMNotFoundException("Not found: " + layerPath); - } - AVMNodeDescriptor underlyingNode = fAVMService.lookup(-1, underlyingPath, true); - if (underlyingNode == null) - { - throw new AVMNotFoundException("Not found: " + underlyingPath); - } - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("flatten: " + layerNode + " " + underlyingNode); - try - { - throw new Exception("Stack Trace:"); - } - catch (Exception e) - { - fgLogger.debug("Stack Trace: ", e); - } - } - flatten(layerNode, underlyingNode); - } - - /** - * This is the implementation of flatten. - * @param layer The on top node. - * @param underlying The underlying node. - */ - private boolean flatten(AVMNodeDescriptor layer, AVMNodeDescriptor underlying) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("flatten: " + layer + " " + underlying); - } - if (!layer.isLayeredDirectory()) - { - return false; - } - // layer and underlying must match for flattening to be useful. - if (!layer.getIndirection().equals(underlying.getPath())) - { - return false; - } - // The underlying thing must be a directory. - if (!underlying.isDirectory()) - { - return false; - } - Map layerListing = - fAVMService.getDirectoryListingDirect(-1, layer.getPath(), true); - // If the layer is empty (directly, that is) we're done. - if (layerListing.size() == 0) - { - return true; - } -// layer = fAVMService.forceCopy(layer.getPath()); - // Grab the listing - Map underListing = - fAVMService.getDirectoryListing(underlying, true); - boolean flattened = true; - for (String name : layerListing.keySet()) - { - AVMNodeDescriptor topNode = layerListing.get(name); - AVMNodeDescriptor bottomNode = underListing.get(name); -// fgLogger.error("Trying to flatten out: " + name); - if (bottomNode == null) - { - flattened = false; - continue; - } - // We've found an identity so flatten it. - if (topNode.getId() == bottomNode.getId()) - { - fAVMRepository.flatten(layer.getPath(), name); -// fgLogger.error("Identity flattened: " + name); - } - else - { - // Otherwise recursively flatten the children. - if (flatten(topNode, bottomNode)) - { - fAVMRepository.flatten(layer.getPath(), name); -// fgLogger.error("Recursively flattened: " + name); - } - else - { - flattened = false; - } - } - } - return flattened; - } - - /** - * Takes a layer, deletes it and recreates it pointing at the same underlying - * node. Any changes in the layer are lost (except to history if the layer has been - * snapshotted.) - * @param layerPath - */ - public void resetLayer(String layerPath) - { - AVMNodeDescriptor desc = fAVMService.lookup(-1, layerPath); - if (desc == null) - { - throw new AVMNotFoundException("Not Found: " + layerPath); - } - String [] parts = AVMNodeConverter.SplitBase(layerPath); - fAVMService.removeNode(parts[0], parts[1]); - fAVMService.createLayeredDirectory(desc.getIndirection(), parts[0], parts[1]); - } - - /** - * Make sure this entire directory path exists. - * @param path - * @param sourcePath - */ - private void mkdirs(String path, String sourcePath) - { - if (fAVMService.lookup(-1, path) != null) - { - return; - } - String [] pathParts = AVMNodeConverter.SplitBase(path); - if (pathParts[0] == null) - { - // This is a root path and as such has to exist. - // Something else is going on. - throw new AVMSyncException("No corresponding destination path: " + path); - } - mkdirs(pathParts[0], AVMNodeConverter.SplitBase(sourcePath)[0]); - fAVMService.createDirectory(pathParts[0], pathParts[1]); - fAVMService.setMetaDataFrom(path, fAVMService.lookup(-1, sourcePath)); - } -} +/* + * 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; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.avm.AVMBadArgumentException; +import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avmsync.AVMDifference; +import org.alfresco.service.cmr.avmsync.AVMSyncException; +import org.alfresco.service.cmr.avmsync.AVMSyncService; +import org.alfresco.util.NameMatcher; +import org.apache.log4j.Logger; + +/** + * This implements APIs that allow comparison and synchronization + * of node trees as well as cumulative operations on layers to + * support various content production models. + * @author britt + */ +public class AVMSyncServiceImpl implements AVMSyncService +{ + private static Logger fgLogger = Logger.getLogger(AVMSyncServiceImpl.class); + + /** + * The AVMService. + */ + private AVMService fAVMService; + + /** + * The AVMRepository. + */ + private AVMRepository fAVMRepository; + + /** + * Do nothing constructor. + */ + public AVMSyncServiceImpl() + { + } + + /** + * Set the AVM Service. For Spring. + * @param avmService The AVMService reference. + */ + public void setAvmService(AVMService avmService) + { + fAVMService = avmService; + } + + public void setAvmRepository(AVMRepository avmRepository) + { + fAVMRepository = avmRepository; + } + + /** + * Get a difference list between two corresponding node trees. + * @param srcVersion The version id for the source tree. + * @param srcPath The avm path to the source tree. + * @param dstVersion The version id for the destination tree. + * @param dstPath The avm path to the destination tree. + * @param excluder A NameMatcher used to exclude files from consideration. + * @return A List of AVMDifference structs which can be used for + * the update operation. + */ + public List compare(int srcVersion, String srcPath, + int dstVersion, String dstPath, + NameMatcher excluder) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug(srcPath + " : " + dstPath); + try + { + throw new Exception(); + } + catch (Exception e) + { + fgLogger.debug("Stack Trace: ", e); + } + } + if (srcPath == null || dstPath == null) + { + throw new AVMBadArgumentException("Illegal null path."); + } + List result = new ArrayList(); + AVMNodeDescriptor srcDesc = fAVMService.lookup(srcVersion, srcPath, true); + if (srcDesc == null) + { + throw new AVMSyncException("Source not found: " + srcPath); + } + AVMNodeDescriptor dstDesc = fAVMService.lookup(dstVersion, dstPath, true); + if (dstDesc == null) + { + // Special case: no pre-existing version in the destination. + result.add(new AVMDifference(srcVersion, srcPath, + dstVersion, dstPath, + AVMDifference.NEWER)); + } + else + { + // Invoke the recursive implementation. + compare(srcVersion, srcDesc, dstVersion, dstDesc, result, excluder); + } + return result; + } + + /** + * Internal recursive implementation of compare. + * @param srcVersion The version of the source tree. + * @param srcDesc The current source descriptor. + * @param dstVersion The version of the destination tree. + * @param dstDesc The current dstDesc + */ + private void compare(int srcVersion, AVMNodeDescriptor srcDesc, + int dstVersion, AVMNodeDescriptor dstDesc, + List result, NameMatcher excluder) + { + // Determine how the source and destination nodes differ. + if (excluder != null && (excluder.matches(srcDesc.getPath()) || + excluder.matches(dstDesc.getPath()))) + { + return; + } + int diffCode = compareOne(srcDesc, dstDesc); + switch (diffCode) + { + case AVMDifference.SAME : + { + // A big short circuit. + return; + } + // The trivial to handle cases. + case AVMDifference.NEWER : + case AVMDifference.OLDER : + case AVMDifference.CONFLICT : + { + result.add(new AVMDifference(srcVersion, srcDesc.getPath(), + dstVersion, dstDesc.getPath(), + diffCode)); + return; + } + case AVMDifference.DIRECTORY : + { + // First special case: source is a layered directory which points to + // the destinations path, and we are comparing 'head' versions. + if (srcDesc.isLayeredDirectory() && + srcDesc.getIndirection().equals(dstDesc.getPath()) && srcVersion < 0 && dstVersion < 0) + { + // Get only a direct listing, since that's all that can be different. + Map srcList = + fAVMService.getDirectoryListingDirect(srcDesc, true); + // The biggest shortcut: if the source directory is directly empty + // then we're done. + if (srcList.size() == 0) + { + return; + } + // We grab a complete listing of the destination. + Map dstList = + fAVMService.getDirectoryListing(dstDesc, true); + for (String name : srcList.keySet()) + { + AVMNodeDescriptor srcChild = srcList.get(name); + AVMNodeDescriptor dstChild = dstList.get(name); + String dstPath = AVMNodeConverter.ExtendAVMPath(dstDesc.getPath(), name); + if (excluder != null && (excluder.matches(srcChild.getPath()) || + excluder.matches(dstPath))) + { + continue; + } + if (dstChild == null) + { + // A missing destination child means the source is NEWER. + result.add(new AVMDifference(srcVersion, srcChild.getPath(), + dstVersion, + dstPath, + AVMDifference.NEWER)); + continue; + } + // Otherwise recursively invoke. + compare(srcVersion, srcChild, + dstVersion, dstChild, + result, excluder); + } + return; + } + // Second special case. Just as above but reversed. + if (dstDesc.isLayeredDirectory() && + dstDesc.getIndirection().equals(srcDesc.getPath()) && srcVersion < 0 && dstVersion < 0) + { + // Get direct content of destination. + Map dstList = + fAVMService.getDirectoryListingDirect(dstDesc, true); + // Big short circuit. + if (dstList.size() == 0) + { + return; + } + // Get the source listing. + Map srcList = + fAVMService.getDirectoryListing(srcDesc, true); + for (String name : dstList.keySet()) + { + AVMNodeDescriptor dstChild = dstList.get(name); + AVMNodeDescriptor srcChild = srcList.get(name); + String srcPath = AVMNodeConverter.ExtendAVMPath(srcDesc.getPath(), name); + if (excluder != null && (excluder.matches(srcPath) || + excluder.matches(dstChild.getPath()))) + { + continue; + } + if (srcChild == null) + { + // Missing means the source is older. + result.add(new AVMDifference(srcVersion, + srcPath, + dstVersion, dstChild.getPath(), + AVMDifference.OLDER)); + continue; + } + // Otherwise, recursively invoke. + compare(srcVersion, srcChild, + dstVersion, dstChild, + result, excluder); + } + return; + } + // Neither of the special cases apply, so brute force is the only answer. + Map srcList = + fAVMService.getDirectoryListing(srcDesc, true); + Map dstList = + fAVMService.getDirectoryListing(dstDesc, true); + // Iterate over the source. + for (String name : srcList.keySet()) + { + AVMNodeDescriptor srcChild = srcList.get(name); + AVMNodeDescriptor dstChild = dstList.get(name); + String dstPath = AVMNodeConverter.ExtendAVMPath(dstDesc.getPath(), name); + if (excluder != null && (excluder.matches(srcChild.getPath()) || + excluder.matches(dstPath))) + { + continue; + } + if (dstChild == null) + { + // Not found in the destination means NEWER. + result.add(new AVMDifference(srcVersion, srcChild.getPath(), + dstVersion, + dstPath, + AVMDifference.NEWER)); + continue; + } + // Otherwise recursive invocation. + compare(srcVersion, srcChild, + dstVersion, dstChild, + result, excluder); + } + // Iterate over the destination. + for (String name : dstList.keySet()) + { + if (srcList.containsKey(name)) + { + continue; + } + AVMNodeDescriptor dstChild = dstList.get(name); + String srcPath = AVMNodeConverter.ExtendAVMPath(srcDesc.getPath(), name); + if (excluder != null && (excluder.matches(srcPath) || + excluder.matches(dstChild.getPath()))) + { + continue; + } + // An entry not found in the source is OLDER. + result.add(new AVMDifference(srcVersion, + srcPath, + dstVersion, dstChild.getPath(), + AVMDifference.OLDER)); + } + break; + } + default : + { + throw new AVMSyncException("Invalid Difference Code, Internal Error."); + } + } + } + + /** + * Updates the destination nodes in the AVMDifferences + * with the source nodes. Normally any conflicts or cases in + * which the source of an AVMDifference is older than the destination + * will cause the transaction to roll back. + * @param diffList A List of AVMDifference structs. + * @param excluder A possibly null name matcher to exclude unwanted updates. + * @param ignoreConflicts If this is true the update will skip those + * AVMDifferences which are in conflict with + * the destination. + * @param ignoreOlder If this is true the update will skip those + * AVMDifferences which have the source older than the destination. + * @param overrideConflicts If this is true the update will override conflicting + * AVMDifferences and replace the destination with the conflicting source. + * @param overrideOlder If this is true the update will override AVMDifferences + * @param tag Short update blurb. + * @param description Full update blurb. + * in which the source is older than the destination and overwrite the destination. + */ + public void update(List diffList, + NameMatcher excluder, boolean ignoreConflicts, boolean ignoreOlder, + boolean overrideConflicts, boolean overrideOlder, String tag, String description) + { + if (fgLogger.isDebugEnabled()) + { + try + { + throw new Exception("Stack Trace."); + } + catch (Exception e) + { + fgLogger.debug("Stack trace: ", e); + } + } + Map storeVersions = new HashMap(); + Set destStores = new HashSet(); + for (AVMDifference diff : diffList) + { + if (excluder != null && (excluder.matches(diff.getSourcePath()) || + excluder.matches(diff.getDestinationPath()))) + { + continue; + } + if (!diff.isValid()) + { + throw new AVMSyncException("Malformed AVMDifference."); + } + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("update: " + diff); + } + // Snapshot the source if needed. + int version = diff.getSourceVersion(); + if (version < 0) + { + int colonOff = diff.getSourcePath().indexOf(':'); + if (colonOff == -1) + { + throw new AVMBadArgumentException("Invalid path."); + } + String storeName = diff.getSourcePath().substring(0, colonOff); + if (storeVersions.containsKey(storeName)) + { + // We've already snapshotted this store. + version = storeVersions.get(storeName); + } + else + { + version = fAVMService.createSnapshot(storeName, "Snapshotted for submit.", null).get(storeName); + storeVersions.put(storeName, version); + } + } + AVMNodeDescriptor srcDesc = fAVMService.lookup(version, + diff.getSourcePath(), true); + String [] dstParts = AVMNodeConverter.SplitBase(diff.getDestinationPath()); + if (dstParts[0] == null || diff.getDestinationVersion() >= 0) + { + // You can't have a root node as a destination. + throw new AVMSyncException("Invalid destination node: " + diff.getDestinationPath()); + } + AVMNodeDescriptor dstDesc = fAVMService.lookup(-1, diff.getDestinationPath(), true); + // The default is that the source is newer in the case where + // the destination doesn't exist. + int diffCode = AVMDifference.NEWER; + if (dstDesc != null) + { + diffCode = compareOne(srcDesc, dstDesc); + } + // Keep track of stores updated so that they can all be snapshotted + // at end of update. + String dstPath = diff.getDestinationPath(); + destStores.add(dstPath.substring(0, dstPath.indexOf(':'))); + // Dispatch. + switch (diffCode) + { + case AVMDifference.SAME : + { + // Nada to do. + continue; + } + case AVMDifference.NEWER : + { + // You can't delete what isn't there. + linkIn(dstParts[0], dstParts[1], srcDesc, excluder, dstDesc != null && !dstDesc.isDeleted()); + continue; + } + case AVMDifference.OLDER : + { + // You can force it. + if (overrideOlder) + { + linkIn(dstParts[0], dstParts[1], srcDesc, excluder, !dstDesc.isDeleted()); + continue; + } + // You can ignore it. + if (ignoreOlder) + { + continue; + } + // Or it's an error. + throw new AVMSyncException("Older version prevents update."); + } + case AVMDifference.CONFLICT : + { + // You can force it. + if (overrideConflicts) + { + linkIn(dstParts[0], dstParts[1], srcDesc, excluder, true); + continue; + } + // You can ignore it. + if (ignoreConflicts) + { + continue; + } + // Or it's an error. + throw new AVMSyncException("Conflict prevents update."); + } + case AVMDifference.DIRECTORY : + { + // You can only ignore this. + if (ignoreConflicts) + { + continue; + } + // Otherwise it's an error. + throw new AVMSyncException("Directory conflict prevents update."); + } + default : + { + throw new AVMSyncException("Invalid Difference Code: Internal Error."); + } + } + } + for (String storeName : destStores) + { + fAVMService.createSnapshot(storeName, tag, description); + } + } + + /** + * Do the actual work of connecting nodes to the destination tree. + * @param parentPath The parent path the node will go in. + * @param name The name it will have. + * @param toLink The node descriptor. + * @param removeFirst Whether to do a removeNode before linking in. + */ + private void linkIn(String parentPath, String name, AVMNodeDescriptor toLink, NameMatcher excluder, boolean removeFirst) + { + // This is a delete. + if (toLink == null) + { + fAVMService.removeNode(parentPath, name); + return; + } + mkdirs(parentPath, AVMNodeConverter.SplitBase(toLink.getPath())[0]); + if (removeFirst) + { + fAVMService.removeNode(parentPath, name); + } + if (toLink.isLayeredDirectory() && !toLink.isPrimary()) + { + recursiveCopy(parentPath, name, toLink, excluder); + return; + } + fAVMService.link(parentPath, name, toLink); + } + + /** + * Recursively copy a node into the given position. + * @param parentPath The place to put it. + * @param name The name to give it. + * @param toCopy The it to put. + */ + private void recursiveCopy(String parentPath, String name, AVMNodeDescriptor toCopy, NameMatcher excluder) + { + fAVMService.createDirectory(parentPath, name); + String newParentPath = AVMNodeConverter.ExtendAVMPath(parentPath, name); + fAVMService.setMetaDataFrom(newParentPath, toCopy); + AVMNodeDescriptor parentDesc = fAVMService.lookup(-1, newParentPath, true); + Map children = + fAVMService.getDirectoryListing(toCopy, true); + for (Map.Entry entry : children.entrySet()) + { + recursiveCopy(parentDesc, entry.getKey(), entry.getValue(), excluder); + } + } + + /** + * Shortcutting helper that uses an AVMNodeDescriptor parent. + * @param parent The parent we are linking into. + * @param name The name to link in. + * @param toCopy The node to link in. + */ + private void recursiveCopy(AVMNodeDescriptor parent, String name, AVMNodeDescriptor toCopy, NameMatcher excluder) + { + String newPath = AVMNodeConverter.ExtendAVMPath(parent.getPath(), name); + if (excluder != null && (excluder.matches(newPath) || + excluder.matches(toCopy.getPath()))) + { + return; + } + // If it's a file or deleted simply link it in. + if (toCopy.isFile() || toCopy.isDeleted() || toCopy.isPlainDirectory()) + { + fAVMRepository.link(parent, name, toCopy); + return; + } + // Otherwise make a directory in the target parent, and recursiveCopy all the source + // children into it. + AVMNodeDescriptor newParentDesc = fAVMRepository.createDirectory(parent, name); + fAVMService.setMetaDataFrom(newParentDesc.getPath(), toCopy); + Map children = + fAVMService.getDirectoryListing(toCopy, true); + for (Map.Entry entry : children.entrySet()) + { + recursiveCopy(newParentDesc, entry.getKey(), entry.getValue(), excluder); + } + } + + /** + * The workhorse of comparison and updating. Determine the versioning relationship + * of two nodes. + * @param srcDesc Descriptor for the source node. + * @param dstDesc Descriptor for the destination node. + * @return One of SAME, OLDER, NEWER, CONFLICT, DIRECTORY + */ + private int compareOne(AVMNodeDescriptor srcDesc, AVMNodeDescriptor dstDesc) + { + if (srcDesc == null) + { + return AVMDifference.OLDER; + } + if (srcDesc.getId() == dstDesc.getId()) + { + return AVMDifference.SAME; + } + // Matched directories that are not identical are nominally in conflict + // but get their own special difference code for comparison logic. The DIRECTORY + // difference code never gets returned to callers of compare. + if (srcDesc.isDirectory() && dstDesc.isDirectory()) + { + return AVMDifference.DIRECTORY; + } + // Check for mismatched fundamental types. + if ((srcDesc.isDirectory() && dstDesc.isFile()) || + (srcDesc.isFile() && dstDesc.isDirectory())) + { + return AVMDifference.CONFLICT; + } + // A deleted node on either side means uniform handling because + // a deleted node can be the descendent of any other type of node. + if (srcDesc.isDeleted() || dstDesc.isDeleted()) + { + AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); + if (common == null) + { + return AVMDifference.CONFLICT; + } + if (common.getId() == srcDesc.getId()) + { + return AVMDifference.OLDER; + } + if (common.getId() == dstDesc.getId()) + { + return AVMDifference.NEWER; + } + // Must be a conflict. + return AVMDifference.CONFLICT; + } + // At this point both source and destination are both some kind of file. + if (srcDesc.isLayeredFile()) + { + // Handle the layered file source case. + if (dstDesc.isPlainFile()) + { + // We consider a layered source file that points at the destination + // file SAME. + if (srcDesc.getIndirection().equals(dstDesc.getPath())) + { + return AVMDifference.SAME; + } + // We know that they are in conflict since they are of different types. + return AVMDifference.CONFLICT; + } + // Destination is a layered file also. + AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); + if (common == null) + { + return AVMDifference.CONFLICT; + } + if (common.getId() == srcDesc.getId()) + { + return AVMDifference.OLDER; + } + if (common.getId() == dstDesc.getId()) + { + return AVMDifference.NEWER; + } + // Finally we know they are in conflict. + return AVMDifference.CONFLICT; + } + // Source is a plain file. + if (dstDesc.isLayeredFile()) + { + // We consider a source file that is the target of a layered destination file to be + // SAME. + if (dstDesc.getIndirection().equals(srcDesc.getPath())) + { + return AVMDifference.SAME; + } + // Otherwise we know they are in conflict because they are of different type. + return AVMDifference.CONFLICT; + } + // Destination is a plain file. + AVMNodeDescriptor common = fAVMService.getCommonAncestor(srcDesc, dstDesc); + // Conflict case. + if (common == null) + { + return AVMDifference.CONFLICT; + } + if (common.getId() == srcDesc.getId()) + { + return AVMDifference.OLDER; + } + if (common.getId() == dstDesc.getId()) + { + return AVMDifference.NEWER; + } + // The must, finally, be in conflict. + return AVMDifference.CONFLICT; + } + + /** + * Flattens a layer so that all all nodes under and including + * layerPath become translucent to any nodes in the + * corresponding location under and including underlyingPath + * that are the same version. + * @param layerPath The overlying layer path. + * @param underlyingPath The underlying path. + */ + public void flatten(String layerPath, String underlyingPath) + { + if (layerPath == null || underlyingPath == null) + { + throw new AVMBadArgumentException("Illegal null path."); + } + AVMNodeDescriptor layerNode = fAVMService.lookup(-1, layerPath, true); + if (layerNode == null) + { + throw new AVMNotFoundException("Not found: " + layerPath); + } + AVMNodeDescriptor underlyingNode = fAVMService.lookup(-1, underlyingPath, true); + if (underlyingNode == null) + { + throw new AVMNotFoundException("Not found: " + underlyingPath); + } + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("flatten: " + layerNode + " " + underlyingNode); + try + { + throw new Exception("Stack Trace:"); + } + catch (Exception e) + { + fgLogger.debug("Stack Trace: ", e); + } + } + flatten(layerNode, underlyingNode); + } + + /** + * This is the implementation of flatten. + * @param layer The on top node. + * @param underlying The underlying node. + */ + private boolean flatten(AVMNodeDescriptor layer, AVMNodeDescriptor underlying) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("flatten: " + layer + " " + underlying); + } + if (!layer.isLayeredDirectory()) + { + return false; + } + // layer and underlying must match for flattening to be useful. + if (!layer.getIndirection().equals(underlying.getPath())) + { + return false; + } + // The underlying thing must be a directory. + if (!underlying.isDirectory()) + { + return false; + } + Map layerListing = + fAVMService.getDirectoryListingDirect(-1, layer.getPath(), true); + // If the layer is empty (directly, that is) we're done. + if (layerListing.size() == 0) + { + return true; + } + List diffs = compare(-1, layer.getPath(), -1, underlying.getPath(), null); + if (diffs.size() == 0) + { + for (String name : layerListing.keySet()) + { + fAVMRepository.flatten(layer.getPath(), name); + } + return true; + } + // Grab the listing + Map underListing = + fAVMService.getDirectoryListing(underlying, true); + boolean flattened = true; + for (String name : layerListing.keySet()) + { + AVMNodeDescriptor topNode = layerListing.get(name); + AVMNodeDescriptor bottomNode = underListing.get(name); +// fgLogger.error("Trying to flatten out: " + name); + if (bottomNode == null) + { + flattened = false; + continue; + } + // We've found an identity so flatten it. + if (topNode.getId() == bottomNode.getId()) + { + fAVMRepository.flatten(layer.getPath(), name); +// fgLogger.error("Identity flattened: " + name); + } + else + { + // Otherwise recursively flatten the children. + if (flatten(topNode, bottomNode)) + { + fAVMRepository.flatten(layer.getPath(), name); +// fgLogger.error("Recursively flattened: " + name); + } + else + { + flattened = false; + } + } + } + return flattened; + } + + /** + * Takes a layer, deletes it and recreates it pointing at the same underlying + * node. Any changes in the layer are lost (except to history if the layer has been + * snapshotted.) + * @param layerPath + */ + public void resetLayer(String layerPath) + { + AVMNodeDescriptor desc = fAVMService.lookup(-1, layerPath); + if (desc == null) + { + throw new AVMNotFoundException("Not Found: " + layerPath); + } + String [] parts = AVMNodeConverter.SplitBase(layerPath); + fAVMService.removeNode(parts[0], parts[1]); + fAVMService.createLayeredDirectory(desc.getIndirection(), parts[0], parts[1]); + } + + /** + * Make sure this entire directory path exists. + * @param path + * @param sourcePath + */ + private void mkdirs(String path, String sourcePath) + { + if (fAVMService.lookup(-1, path) != null) + { + return; + } + String [] pathParts = AVMNodeConverter.SplitBase(path); + if (pathParts[0] == null) + { + // This is a root path and as such has to exist. + // Something else is going on. + throw new AVMSyncException("No corresponding destination path: " + path); + } + mkdirs(pathParts[0], AVMNodeConverter.SplitBase(sourcePath)[0]); + fAVMService.createDirectory(pathParts[0], pathParts[1]); + fAVMService.setMetaDataFrom(path, fAVMService.lookup(-1, sourcePath)); + } +} diff --git a/source/java/org/alfresco/repo/avm/DirectoryNode.java b/source/java/org/alfresco/repo/avm/DirectoryNode.java index acb4d3d4b3..9cea6e277b 100644 --- a/source/java/org/alfresco/repo/avm/DirectoryNode.java +++ b/source/java/org/alfresco/repo/avm/DirectoryNode.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.SortedMap; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; +import org.alfresco.util.Pair; /** * The interface for Directory Nodes. @@ -54,7 +55,7 @@ public interface DirectoryNode extends AVMNode * @param name The name of the child to lookup. * @param includeDeleted Include deleted nodes or not. */ - public AVMNode lookupChild(Lookup lPath, String name, boolean includeDeleted); + public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted); /** * Lookup a child node using an AVMNodeDescriptor as context. diff --git a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java index 4bf73fa845..4fdc7cc258 100644 --- a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java @@ -35,6 +35,7 @@ import org.alfresco.service.cmr.avm.AVMException; import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.util.Pair; /** * A layered directory node. A layered directory node points at @@ -507,7 +508,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec * @return The child or null if not found. */ @SuppressWarnings("unchecked") - public AVMNode lookupChild(Lookup lPath, String name, boolean includeDeleted) + public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted) { ChildKey key = new ChildKey(this, name); ChildEntry entry = AVMDAOs.Instance().fChildEntryDAO.get(key); @@ -517,7 +518,7 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec { return null; } - return AVMNodeUnwrapper.Unwrap(entry.getChild()); + return new Pair(AVMNodeUnwrapper.Unwrap(entry.getChild()), true); } // Don't check our underlying directory if we are opaque. if (fOpacity) @@ -529,7 +530,11 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec if (lookup != null) { DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode(); - AVMNode retVal = dir.lookupChild(lookup, name, includeDeleted); + Pair retVal = dir.lookupChild(lookup, name, includeDeleted); + if (retVal != null) + { + retVal.setSecond(false); + } lPath.setFinalStore(lookup.getFinalStore()); return retVal; } @@ -573,12 +578,12 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec if (lookup != null) { DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode(); - AVMNode child = dir.lookupChild(lookup, name, includeDeleted); + Pair child = dir.lookupChild(lookup, name, includeDeleted); if (child == null) { return null; } - return child.getDescriptor(lookup); + return child.getFirst().getDescriptor(lookup); } else { @@ -613,7 +618,15 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec } else { - child = lookupChild(lPath, name, false); + Pair temp = lookupChild(lPath, name, false); + if (temp == null) + { + child = null; + } + else + { + child = temp.getFirst(); + } indirect = true; } if (child != null && (indirect || child.getStoreNew() == null || child.getAncestor() != null)) @@ -893,7 +906,8 @@ class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirec throw new AVMBadArgumentException("Non primary layered directories cannot be linked."); } // Look for an existing child of that name. - AVMNode existing = lookupChild(lPath, name, true); + Pair temp = lookupChild(lPath, name, true); + AVMNode existing = (temp == null) ? null : temp.getFirst(); ChildKey key = new ChildKey(this, name); if (existing != null) { diff --git a/source/java/org/alfresco/repo/avm/Lookup.java b/source/java/org/alfresco/repo/avm/Lookup.java index e7320f55d0..c97b2fd19b 100644 --- a/source/java/org/alfresco/repo/avm/Lookup.java +++ b/source/java/org/alfresco/repo/avm/Lookup.java @@ -218,7 +218,7 @@ class Lookup implements Serializable * @param write Whether this is in the context of * a write operation. */ - public void add(AVMNode node, String name, boolean write) + public void add(AVMNode node, String name, boolean directlyContained, boolean write) { LookupComponent comp = new LookupComponent(); comp.setName(name); @@ -226,7 +226,11 @@ class Lookup implements Serializable if (fPosition >= 0 && fDirectlyContained && fComponents.get(fPosition).getNode().getType() == AVMNodeType.LAYERED_DIRECTORY) { - fDirectlyContained = ((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node); +// if (directlyContained != ((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node)) +// { +// System.err.println("Bloody Murder!"); +// } + fDirectlyContained = directlyContained; } if (!write) { diff --git a/source/java/org/alfresco/repo/avm/LookupCache.java b/source/java/org/alfresco/repo/avm/LookupCache.java index bca5443827..c486cc90ba 100644 --- a/source/java/org/alfresco/repo/avm/LookupCache.java +++ b/source/java/org/alfresco/repo/avm/LookupCache.java @@ -1,287 +1,290 @@ -/** - * - */ -package org.alfresco.repo.avm; - -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; - -/** - * All lookup traffic goes through here. - * @author britt - */ -public class LookupCache -{ - private static Logger fgLogger = Logger.getLogger(LookupCache.class); - - /** - * The Map of of keys to lookups. - */ - private SimpleCache fCache; - - /** - * Reference to the Node DAO. - */ - private AVMNodeDAO fAVMNodeDAO; - - /** - * Reference to the Store DAO. - */ - private AVMStoreDAO fAVMStoreDAO; - - /** - * Make one up. - */ - public LookupCache() - { - } - - /** - * Set up the node dao. - * @param dao The dao to set. - */ - public void setAvmNodeDAO(AVMNodeDAO dao) - { - fAVMNodeDAO = dao; - } - - /** - * Set the store dao. - * @param dao The dao to set. - */ - public void setAvmStoreDAO(AVMStoreDAO dao) - { - fAVMStoreDAO = dao; - } - - public void setTransactionalCache(SimpleCache cache) - { - fCache = cache; - } - - /** - * Lookup a path. Try to fulfill the request from the cache. - * @param store The AVMStore. - * @param version The versions. - * @param path The path we are looking up. - * @param write Whether this is a write lookup. - * @param includeDeleted - * @return - */ - public Lookup lookup(AVMStore store, int version, SimplePath path, - boolean write, boolean includeDeleted) - { - // Create a key object. - 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. - if (path.size() == 0) - { - return null; - } - Lookup result = new Lookup(store, store.getName(), version); - // Grab the root node to start the lookup. - DirectoryNode dir = null; - // Versions less than 0 mean get current. - if (version < 0) - { - dir = store.getRoot(); - } - else - { - dir = fAVMNodeDAO.getAVMStoreRoot(store, version); - } - if (dir == null) - { - return null; - } - // Add an entry for the root. - result.add(dir, "", write); - dir = (DirectoryNode)result.getCurrentNode(); - if (path.size() == 1 && path.get(0).equals("")) - { - fCache.put(key, result); - return result; - } - // Now look up each path element in sequence up to one - // before the end. - for (int i = 0; i < path.size() - 1; i++) - { - AVMNode child = dir.lookupChild(result, path.get(i), includeDeleted); - if (child == null) - { - return null; - } - // Every element that is not the last needs to be a directory. - if (child.getType() != AVMNodeType.PLAIN_DIRECTORY && - child.getType() != AVMNodeType.LAYERED_DIRECTORY) - { - return null; - } - result.add(child, path.get(i), write); - dir = (DirectoryNode)result.getCurrentNode(); - } - // Now look up the last element. - AVMNode child = dir.lookupChild(result, path.get(path.size() - 1), - includeDeleted); - if (child == null) - { - return null; - } - result.add(child, path.get(path.size() - 1), write); - fCache.put(key, result); - return result; - } - - /** - * Try to find a match in the cache. - * @param key The lookup key. - * @return A valid for this session Lookup or null if not found. - */ - private synchronized Lookup findInCache(LookupKey key) - { - Lookup found = fCache.get(key); - if (found != null) - { - // Get a freshened Lookup. - Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); - // Check that nothing horrible is wrong. This should - // be assertible, but I'll leave the check in for now. - if (!result.isValid()) - { - fgLogger.error("Invalid entry in cache: " + key); - return null; - } - return result; - } - // Alternatively for a read lookup a write can match. - if (!key.isWrite()) - { - // Make a copy of the key and set it to 'write' - LookupKey newKey = new LookupKey(key); - newKey.setWrite(true); - found = fCache.get(newKey); - if (found != null) - { - // We found it. Make a freshened copy of the Lookup. - Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); - // Check for badness. This should be assertible but I'll - // leave the check in for now. - if (!result.isValid()) - { - fgLogger.error("Invalid entry in cache: " + newKey); - return null; - } - return result; - } - } - return null; - } - - // Following are the cache invalidation calls. - - /** - * Called when a simple write operation occurs. This - * invalidates all read lookups and all layered lookups. - */ - public synchronized void onWrite(String storeName) - { - // Invalidate if it's a read lookup in the store, or - // any read lookup is it's layered. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if ((key.getStoreName().equals(storeName) && - !key.isWrite()) || value == null || - (!key.isWrite() && value.isLayered())) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - /** - * Called when a delete has occurred in a store. This invalidates both - * reads and write lookups in that store. - */ - public synchronized void onDelete(String storeName) - { - // Invalidate any entries that are in the store or are layered lookups. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if (key.getStoreName().equals(storeName) || - value == null || value.isLayered()) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - /** - * Called when a snapshot occurs in a store. This invalidates write - * lookups. Read lookups stay untouched. - */ - public synchronized void onSnapshot(String storeName) - { - // Invalidate any entries that in the store and writes or - // any layered lookups. - List keys = new ArrayList(); - for (LookupKey key : fCache.getKeys()) - { - keys.add(key); - } - for (LookupKey key : keys) - { - Lookup value = fCache.get(key); - if ((key.getStoreName().equals(storeName) && - key.isWrite()) || - value == null || value.isLayered()) - { - if (fgLogger.isDebugEnabled()) - { - fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); - } - fCache.remove(key); - } - } - } - - public synchronized void reset() - { - fCache.clear(); - } -} +/** + * + */ +package org.alfresco.repo.avm; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.avm.util.SimplePath; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.util.Pair; +import org.apache.log4j.Logger; + +/** + * All lookup traffic goes through here. + * @author britt + */ +public class LookupCache +{ + private static Logger fgLogger = Logger.getLogger(LookupCache.class); + + /** + * The Map of of keys to lookups. + */ + private SimpleCache fCache; + + /** + * Reference to the Node DAO. + */ + private AVMNodeDAO fAVMNodeDAO; + + /** + * Reference to the Store DAO. + */ + private AVMStoreDAO fAVMStoreDAO; + + /** + * Make one up. + */ + public LookupCache() + { + } + + /** + * Set up the node dao. + * @param dao The dao to set. + */ + public void setAvmNodeDAO(AVMNodeDAO dao) + { + fAVMNodeDAO = dao; + } + + /** + * Set the store dao. + * @param dao The dao to set. + */ + public void setAvmStoreDAO(AVMStoreDAO dao) + { + fAVMStoreDAO = dao; + } + + public void setTransactionalCache(SimpleCache cache) + { + fCache = cache; + } + + /** + * Lookup a path. Try to fulfill the request from the cache. + * @param store The AVMStore. + * @param version The versions. + * @param path The path we are looking up. + * @param write Whether this is a write lookup. + * @param includeDeleted + * @return + */ + public Lookup lookup(AVMStore store, int version, SimplePath path, + boolean write, boolean includeDeleted) + { + // Create a key object. + 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. + if (path.size() == 0) + { + return null; + } + Lookup result = new Lookup(store, store.getName(), version); + // Grab the root node to start the lookup. + DirectoryNode dir = null; + // Versions less than 0 mean get current. + if (version < 0) + { + dir = store.getRoot(); + } + else + { + VersionRoot vRoot = AVMDAOs.Instance().fVersionRootDAO.getByVersionID(store, version); + dir = vRoot.getRoot(); +// dir = fAVMNodeDAO.getAVMStoreRoot(store, version); + } + if (dir == null) + { + return null; + } + // Add an entry for the root. + result.add(dir, "", true, write); + dir = (DirectoryNode)result.getCurrentNode(); + if (path.size() == 1 && path.get(0).equals("")) + { + fCache.put(key, result); + return result; + } + // Now look up each path element in sequence up to one + // before the end. + for (int i = 0; i < path.size() - 1; i++) + { + Pair child = dir.lookupChild(result, path.get(i), includeDeleted); + if (child == null) + { + return null; + } + // Every element that is not the last needs to be a directory. + if (child.getFirst().getType() != AVMNodeType.PLAIN_DIRECTORY && + child.getFirst().getType() != AVMNodeType.LAYERED_DIRECTORY) + { + return null; + } + result.add(child.getFirst(), path.get(i), child.getSecond(), write); + dir = (DirectoryNode)result.getCurrentNode(); + } + // Now look up the last element. + Pair child = dir.lookupChild(result, path.get(path.size() - 1), + includeDeleted); + if (child == null) + { + return null; + } + result.add(child.getFirst(), path.get(path.size() - 1), child.getSecond(), write); + fCache.put(key, result); + return result; + } + + /** + * Try to find a match in the cache. + * @param key The lookup key. + * @return A valid for this session Lookup or null if not found. + */ + private synchronized Lookup findInCache(LookupKey key) + { + Lookup found = fCache.get(key); + if (found != null) + { + // Get a freshened Lookup. + Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); + // Check that nothing horrible is wrong. This should + // be assertible, but I'll leave the check in for now. + if (!result.isValid()) + { + fgLogger.error("Invalid entry in cache: " + key); + return null; + } + return result; + } + // Alternatively for a read lookup a write can match. + if (!key.isWrite()) + { + // Make a copy of the key and set it to 'write' + LookupKey newKey = new LookupKey(key); + newKey.setWrite(true); + found = fCache.get(newKey); + if (found != null) + { + // We found it. Make a freshened copy of the Lookup. + Lookup result = new Lookup(found, fAVMNodeDAO, fAVMStoreDAO); + // Check for badness. This should be assertible but I'll + // leave the check in for now. + if (!result.isValid()) + { + fgLogger.error("Invalid entry in cache: " + newKey); + return null; + } + return result; + } + } + return null; + } + + // Following are the cache invalidation calls. + + /** + * Called when a simple write operation occurs. This + * invalidates all read lookups and all layered lookups. + */ + public synchronized void onWrite(String storeName) + { + // Invalidate if it's a read lookup in the store, or + // any read lookup is it's layered. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + !key.isWrite()) || value == null || + (!key.isWrite() && value.isLayered())) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + /** + * Called when a delete has occurred in a store. This invalidates both + * reads and write lookups in that store. + */ + public synchronized void onDelete(String storeName) + { + // Invalidate any entries that are in the store or are layered lookups. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if (key.getStoreName().equals(storeName) || + value == null || value.isLayered()) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + /** + * Called when a snapshot occurs in a store. This invalidates write + * lookups. Read lookups stay untouched. + */ + public synchronized void onSnapshot(String storeName) + { + // Invalidate any entries that in the store and writes or + // any layered lookups. + List keys = new ArrayList(); + for (LookupKey key : fCache.getKeys()) + { + keys.add(key); + } + for (LookupKey key : keys) + { + Lookup value = fCache.get(key); + if ((key.getStoreName().equals(storeName) && + key.isWrite()) || + value == null || value.isLayered()) + { + if (fgLogger.isDebugEnabled()) + { + fgLogger.debug("Invalidating: " + key + ", " + (value != null ? value.getCurrentNode().getId() : -2)); + } + fCache.remove(key); + } + } + } + + public synchronized void reset() + { + fCache.clear(); + } +} diff --git a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java index e60ed57918..d007471211 100644 --- a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java @@ -34,6 +34,7 @@ import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMExistsException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.util.Pair; /** * A plain directory. No monkey tricks except for possiblyCopy. @@ -185,7 +186,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory * @return The child or null. */ @SuppressWarnings("unchecked") - public AVMNode lookupChild(Lookup lPath, String name, boolean includeDeleted) + public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted) { ChildKey key = new ChildKey(this, name); ChildEntry entry = AVMDAOs.Instance().fChildEntryDAO.get(key); @@ -196,7 +197,7 @@ class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectory } // We're doing the hand unrolling of the proxy because // Hibernate/CGLIB proxies are broken. - return AVMNodeUnwrapper.Unwrap(entry.getChild()); + return new Pair(AVMNodeUnwrapper.Unwrap(entry.getChild()), true); } /** diff --git a/source/java/org/alfresco/repo/avm/VersionRoot.java b/source/java/org/alfresco/repo/avm/VersionRoot.java index 4b7654da38..e2cfa23420 100644 --- a/source/java/org/alfresco/repo/avm/VersionRoot.java +++ b/source/java/org/alfresco/repo/avm/VersionRoot.java @@ -1,116 +1,116 @@ -/* - * 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; - -/** - * Represents a single version root. - * @author britt - */ -public interface VersionRoot -{ - /** - * @return the createDate - */ - public long getCreateDate(); - - /** - * @param createDate the createDate to set - */ - public void setCreateDate(long createDate); - - /** - * @return the creator - */ - public String getCreator(); - - /** - * @param creator the creator to set - */ - public void setCreator(String creator); - - /** - * @return the id - */ - public long getId(); - - /** - * @param id the id to set - */ - public void setId(long id); - - /** - * @return the AVMStore - */ - public AVMStore getAvmStore(); - - /** - * @param store the store to set - */ - public void setAvmStore(AVMStore store); - - /** - * @return the root - */ - public DirectoryNode getRoot(); - - /** - * @param root the root to set - */ - public void setRoot(DirectoryNode root); - - /** - * Set the version id. - * @param versionID - */ - public void setVersionID(int versionID); - - /** - * Get the version id. - * @return The version id. - */ - public int getVersionID(); - - /** - * Get the tag (short description). - * @return The tag. - */ - public String getTag(); - - /** - * Get the thick description. - * @return The thick description. - */ - public String getDescription(); - - /** - * Set the tag. - * @param tag - */ - public void setTag(String tag); - - /** - * Set the description. - * @param description - */ - public void setDescription(String description); +/* + * 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; + +/** + * Represents a single version root. + * @author britt + */ +public interface VersionRoot +{ + /** + * @return the createDate + */ + public long getCreateDate(); + + /** + * @param createDate the createDate to set + */ + public void setCreateDate(long createDate); + + /** + * @return the creator + */ + public String getCreator(); + + /** + * @param creator the creator to set + */ + public void setCreator(String creator); + + /** + * @return the id + */ + public Long getId(); + + /** + * @param id the id to set + */ + public void setId(long id); + + /** + * @return the AVMStore + */ + public AVMStore getAvmStore(); + + /** + * @param store the store to set + */ + public void setAvmStore(AVMStore store); + + /** + * @return the root + */ + public DirectoryNode getRoot(); + + /** + * @param root the root to set + */ + public void setRoot(DirectoryNode root); + + /** + * Set the version id. + * @param versionID + */ + public void setVersionID(int versionID); + + /** + * Get the version id. + * @return The version id. + */ + public int getVersionID(); + + /** + * Get the tag (short description). + * @return The tag. + */ + public String getTag(); + + /** + * Get the thick description. + * @return The thick description. + */ + public String getDescription(); + + /** + * Set the tag. + * @param tag + */ + public void setTag(String tag); + + /** + * Set the description. + * @param description + */ + public void setDescription(String description); } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/avm/VersionRootImpl.java b/source/java/org/alfresco/repo/avm/VersionRootImpl.java index 8ed3d00bca..98e998a6ce 100644 --- a/source/java/org/alfresco/repo/avm/VersionRootImpl.java +++ b/source/java/org/alfresco/repo/avm/VersionRootImpl.java @@ -1,259 +1,259 @@ -/* - * 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; - -import java.io.Serializable; - - -/** - * Hold a single version root. - * @author britt - */ -class VersionRootImpl implements VersionRoot, Serializable -{ - static final long serialVersionUID = 8826954538210455917L; - - /** - * The object id - */ - private Long fID; - - /** - * The version id. - */ - private int fVersionID; - - /** - * The creation date. - */ - private long fCreateDate; - - /** - * The creator. - */ - private String fCreator; - - /** - * The AVMStore. - */ - private AVMStore fAVMStore; - - /** - * The root node. - */ - private DirectoryNode fRoot; - - /** - * The short description. - */ - private String fTag; - - /** - * The thick description. - */ - private String fDescription; - - /** - * A default constructor. - */ - public VersionRootImpl() - { - } - - /** - * Rich constructor. - * @param store - * @param root - * @param versionID - * @param createDate - * @param creator - */ - public VersionRootImpl(AVMStore store, - DirectoryNode root, - int versionID, - long createDate, - String creator, - String tag, - String description) - { - fAVMStore = store; - fRoot = root; - fVersionID = versionID; - fCreateDate = createDate; - fCreator = creator; - fTag = tag; - fDescription = description; - } - - public long getCreateDate() - { - return fCreateDate; - } - - public void setCreateDate(long createDate) - { - fCreateDate = createDate; - } - - public String getCreator() - { - return fCreator; - } - - public void setCreator(String creator) - { - fCreator = creator; - } - - public long getId() - { - return fID; - } - - public void setId(long id) - { - fID = id; - } - - public AVMStore getAvmStore() - { - return fAVMStore; - } - - public void setAvmStore(AVMStore store) - { - fAVMStore = store; - } - - public DirectoryNode getRoot() - { - return fRoot; - } - - public void setRoot(DirectoryNode root) - { - fRoot = root; - } - - /** - * Set the versionID. - * @param versionID - */ - public void setVersionID(int versionID) - { - fVersionID = versionID; - } - - /** - * Get the version id. - * @return The version id. - */ - public int getVersionID() - { - return fVersionID; - } - - /** - * Check equality. Based on AVMStore equality and version id equality. - * @param obj - * @return Equality. - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - if (!(obj instanceof VersionRoot)) - { - return false; - } - VersionRoot other = (VersionRoot)obj; - return fAVMStore.equals(other.getAvmStore()) - && fVersionID == other.getVersionID(); - } - - /** - * Generate a hash code. - * @return The hash code. - */ - @Override - public int hashCode() - { - return fAVMStore.hashCode() + fVersionID; - } - - /** - * Get the tag (short description). - * @return The tag. - */ - public String getTag() - { - return fTag; - } - - /** - * Set the tag (short description). - * @param tag The short description. - */ - public void setTag(String tag) - { - fTag = tag; - } - - /** - * Get the thick description. - * @return The thick description. - */ - public String getDescription() - { - return fDescription; - } - - /** - * Set the thick description. - * @param description The thick discription. - */ - public void setDescription(String description) - { - fDescription = description; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() - { - StringBuilder builder = new StringBuilder(); - builder.append("[VersionRoot:"); - builder.append(fAVMStore.getName()); - builder.append(':'); - builder.append(fVersionID); - builder.append(']'); - return builder.toString(); - } -} - +/* + * 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; + +import java.io.Serializable; + + +/** + * Hold a single version root. + * @author britt + */ +public class VersionRootImpl implements VersionRoot, Serializable +{ + static final long serialVersionUID = 8826954538210455917L; + + /** + * The object id + */ + private Long fID; + + /** + * The version id. + */ + private int fVersionID; + + /** + * The creation date. + */ + private long fCreateDate; + + /** + * The creator. + */ + private String fCreator; + + /** + * The AVMStore. + */ + private AVMStore fAVMStore; + + /** + * The root node. + */ + private DirectoryNode fRoot; + + /** + * The short description. + */ + private String fTag; + + /** + * The thick description. + */ + private String fDescription; + + /** + * A default constructor. + */ + public VersionRootImpl() + { + } + + /** + * Rich constructor. + * @param store + * @param root + * @param versionID + * @param createDate + * @param creator + */ + public VersionRootImpl(AVMStore store, + DirectoryNode root, + int versionID, + long createDate, + String creator, + String tag, + String description) + { + fAVMStore = store; + fRoot = root; + fVersionID = versionID; + fCreateDate = createDate; + fCreator = creator; + fTag = tag; + fDescription = description; + } + + public long getCreateDate() + { + return fCreateDate; + } + + public void setCreateDate(long createDate) + { + fCreateDate = createDate; + } + + public String getCreator() + { + return fCreator; + } + + public void setCreator(String creator) + { + fCreator = creator; + } + + public Long getId() + { + return fID; + } + + public void setId(long id) + { + fID = id; + } + + public AVMStore getAvmStore() + { + return fAVMStore; + } + + public void setAvmStore(AVMStore store) + { + fAVMStore = store; + } + + public DirectoryNode getRoot() + { + return fRoot; + } + + public void setRoot(DirectoryNode root) + { + fRoot = root; + } + + /** + * Set the versionID. + * @param versionID + */ + public void setVersionID(int versionID) + { + fVersionID = versionID; + } + + /** + * Get the version id. + * @return The version id. + */ + public int getVersionID() + { + return fVersionID; + } + + /** + * Check equality. Based on AVMStore equality and version id equality. + * @param obj + * @return Equality. + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof VersionRoot)) + { + return false; + } + VersionRoot other = (VersionRoot)obj; + return fAVMStore.equals(other.getAvmStore()) + && fVersionID == other.getVersionID(); + } + + /** + * Generate a hash code. + * @return The hash code. + */ + @Override + public int hashCode() + { + return fAVMStore.hashCode() + fVersionID; + } + + /** + * Get the tag (short description). + * @return The tag. + */ + public String getTag() + { + return fTag; + } + + /** + * Set the tag (short description). + * @param tag The short description. + */ + public void setTag(String tag) + { + fTag = tag; + } + + /** + * Get the thick description. + * @return The thick description. + */ + public String getDescription() + { + return fDescription; + } + + /** + * Set the thick description. + * @param description The thick discription. + */ + public void setDescription(String description) + { + fDescription = description; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + builder.append("[VersionRoot:"); + builder.append(fAVMStore.getName()); + builder.append(':'); + builder.append(fVersionID); + builder.append(']'); + return builder.toString(); + } +} + diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml b/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml index 9211b87e6c..aa57cdbef4 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml +++ b/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml @@ -1,315 +1,315 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/avm/hibernate/VersionRootDAOHibernate.java b/source/java/org/alfresco/repo/avm/hibernate/VersionRootDAOHibernate.java index 2b72dcc5c9..2b5fbeecc3 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/VersionRootDAOHibernate.java +++ b/source/java/org/alfresco/repo/avm/hibernate/VersionRootDAOHibernate.java @@ -1,178 +1,210 @@ -/* - * 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.hibernate; - -import java.util.Date; -import java.util.List; - -import org.alfresco.repo.avm.AVMNode; -import org.alfresco.repo.avm.AVMStore; -import org.alfresco.repo.avm.VersionRoot; -import org.alfresco.repo.avm.VersionRootDAO; -import org.hibernate.Query; -import org.springframework.orm.hibernate3.support.HibernateDaoSupport; - -/** - * This is the Hibernate version of the DAO for version roots. - * @author britt - */ -class VersionRootDAOHibernate extends HibernateDaoSupport implements - VersionRootDAO -{ - /** - * Do nothing constructor. - */ - public VersionRootDAOHibernate() - { - super(); - } - - /** - * Save an unsaved VersionRoot. - * @param vr The VersionRoot to save. - */ - public void save(VersionRoot vr) - { - getSession().save(vr); - } - - /** - * Delete a VersionRoot. - * @param vr The VersionRoot to delete. - */ - public void delete(VersionRoot vr) - { - getSession().delete(vr); - getSession().flush(); - } - - /** - * Get all the version roots in a given store. - * @param store The store. - * @return A List of VersionRoots. In id order. - */ - @SuppressWarnings("unchecked") - public List getAllInAVMStore(AVMStore store) - { - Query query = getSession().createQuery("from VersionRootImpl v where v.avmStore = :store order by v.versionID"); - query.setEntity("store", store); - return (List)query.list(); - } - - /** - * Get the version of a store by dates. - * @param store The store. - * @param from The starting date. May be null but not with to null also. - * @param to The ending date. May be null but not with from null also. - * @return A List of VersionRoots. - */ - @SuppressWarnings("unchecked") - public List getByDates(AVMStore store, Date from, Date to) - { - Query query; - if (from == null) - { - query = - getSession().createQuery("from VersionRootImpl vr where vr.createDate <= :to " + - "and vr.avmStore = :store " + - "order by vr.versionID"); - query.setLong("to", to.getTime()); - } - else if (to == null) - { - query = - getSession().createQuery("from VersionRootImpl vr " + - "where vr.createDate >= :from " + - "and vr.avmStore = :store " + - "order by vr.versionID"); - query.setLong("from", from.getTime()); - } - else - { - query = - getSession().createQuery("from VersionRootImpl vr "+ - "where vr.createDate between :from and :to " + - "and vr.avmStore = :store " + - "order by vr.versionID"); - query.setLong("from", from.getTime()); - query.setLong("to", to.getTime()); - } - query.setEntity("store", store); - return (List)query.list(); - } - - /** - * Get the VersionRoot corresponding to the given id. - * @param store The store - * @param id The version id. - * @return The VersionRoot or null if not found. - */ - public VersionRoot getByVersionID(AVMStore store, int id) - { - Query query = getSession().getNamedQuery("VersionRoot.VersionByID"); - query.setEntity("store", store); - query.setInteger("version", id); - return (VersionRoot)query.uniqueResult(); - } - - /** - * Get one from its root. - * @param root The root to match. - * @return The version root or null. - */ - public VersionRoot getByRoot(AVMNode root) - { - Query query = getSession().createQuery("from VersionRootImpl vr " + - "where vr.root = :root"); - query.setEntity("root", root); - return (VersionRoot)query.uniqueResult(); - } - - /** - * Get the highest numbered version in a repository. - * @param rep The repository. - * @return The highest numbered version. - */ - public VersionRoot getMaxVersion(AVMStore rep) - { - Query query = getSession().createQuery("from VersionRootImpl vr " + - "where vr.avmStore = :store and vr.versionID = " + - "(select max(v.versionID) from VersionRootImpl v where v.avmStore = :store)"); - query.setEntity("store", rep); - return (VersionRoot)query.uniqueResult(); - } - - /** - * Get the highest numbered id from all the versions in a store. - * @param store The store. - * @return The highest numbered id. - */ - public Integer getMaxVersionID(AVMStore store) - { - Query query = getSession().createQuery("select max(vr.versionID) from VersionRootImpl vr " + - "where vr.avmStore = :store"); - query.setEntity("store", store); - return (Integer)query.uniqueResult(); - } -} +/* + * 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.hibernate; + +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; + +import org.alfresco.repo.avm.AVMNode; +import org.alfresco.repo.avm.AVMStore; +import org.alfresco.repo.avm.VersionRoot; +import org.alfresco.repo.avm.VersionRootImpl; +import org.alfresco.repo.avm.VersionRootDAO; +import org.alfresco.util.Pair; +import org.hibernate.Query; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * This is the Hibernate version of the DAO for version roots. + * @author britt + */ +class VersionRootDAOHibernate extends HibernateDaoSupport implements + VersionRootDAO +{ + private LinkedHashMap, Long> fCache; + + /** + * Do nothing constructor. + */ + public VersionRootDAOHibernate() + { + super(); + fCache = new LinkedHashMap, Long>(); + } + + /** + * Save an unsaved VersionRoot. + * @param vr The VersionRoot to save. + */ + public void save(VersionRoot vr) + { + getSession().save(vr); + } + + /** + * Delete a VersionRoot. + * @param vr The VersionRoot to delete. + */ + public void delete(VersionRoot vr) + { + getSession().delete(vr); + getSession().flush(); + } + + /** + * Get all the version roots in a given store. + * @param store The store. + * @return A List of VersionRoots. In id order. + */ + @SuppressWarnings("unchecked") + public List getAllInAVMStore(AVMStore store) + { + Query query = getSession().createQuery("from VersionRootImpl v where v.avmStore = :store order by v.versionID"); + query.setEntity("store", store); + return (List)query.list(); + } + + /** + * Get the version of a store by dates. + * @param store The store. + * @param from The starting date. May be null but not with to null also. + * @param to The ending date. May be null but not with from null also. + * @return A List of VersionRoots. + */ + @SuppressWarnings("unchecked") + public List getByDates(AVMStore store, Date from, Date to) + { + Query query; + if (from == null) + { + query = + getSession().createQuery("from VersionRootImpl vr where vr.createDate <= :to " + + "and vr.avmStore = :store " + + "order by vr.versionID"); + query.setLong("to", to.getTime()); + } + else if (to == null) + { + query = + getSession().createQuery("from VersionRootImpl vr " + + "where vr.createDate >= :from " + + "and vr.avmStore = :store " + + "order by vr.versionID"); + query.setLong("from", from.getTime()); + } + else + { + query = + getSession().createQuery("from VersionRootImpl vr "+ + "where vr.createDate between :from and :to " + + "and vr.avmStore = :store " + + "order by vr.versionID"); + query.setLong("from", from.getTime()); + query.setLong("to", to.getTime()); + } + query.setEntity("store", store); + return (List)query.list(); + } + + /** + * Get the VersionRoot corresponding to the given id. + * @param store The store + * @param id The version id. + * @return The VersionRoot or null if not found. + */ + public synchronized VersionRoot getByVersionID(AVMStore store, int id) + { + Long vID = fCache.get(new Pair(store.getId(), id)); + if (vID != null) + { + VersionRoot root = (VersionRoot)getSession().get(VersionRootImpl.class, vID); + if (root != null) + { + return root; + } + fCache.remove(new Pair(store.getId(), id)); + } + Query query = getSession().getNamedQuery("VersionRoot.VersionByID"); + query.setEntity("store", store); + query.setInteger("version", id); + VersionRoot root = (VersionRoot)query.uniqueResult(); + if (root != null) + { + vID = root.getId(); + if (vID != null) + { + if (fCache.size() >= 10) + { + Iterator> iter = fCache.keySet().iterator(); + iter.next(); + iter.remove(); + } + fCache.put(new Pair(store.getId(), id), vID); + } + } + return root; + } + + /** + * Get one from its root. + * @param root The root to match. + * @return The version root or null. + */ + public VersionRoot getByRoot(AVMNode root) + { + Query query = getSession().createQuery("from VersionRootImpl vr " + + "where vr.root = :root"); + query.setEntity("root", root); + return (VersionRoot)query.uniqueResult(); + } + + /** + * Get the highest numbered version in a repository. + * @param rep The repository. + * @return The highest numbered version. + */ + public VersionRoot getMaxVersion(AVMStore rep) + { + Query query = getSession().createQuery("from VersionRootImpl vr " + + "where vr.avmStore = :store and vr.versionID = " + + "(select max(v.versionID) from VersionRootImpl v where v.avmStore = :store)"); + query.setEntity("store", rep); + return (VersionRoot)query.uniqueResult(); + } + + /** + * Get the highest numbered id from all the versions in a store. + * @param store The store. + * @return The highest numbered id. + */ + public Integer getMaxVersionID(AVMStore store) + { + Query query = getSession().createQuery("select max(vr.versionID) from VersionRootImpl vr " + + "where vr.avmStore = :store"); + query.setEntity("store", store); + return (Integer)query.uniqueResult(); + } +} diff --git a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java index 03878c8a26..e142bc50a6 100644 --- a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceImpl.java @@ -264,7 +264,7 @@ public class AVMLockingServiceImpl implements AVMLockingService keys.add(lock.getWebProject()); String digest = MD5.Digest(lock.getPath().getBytes()); keys.add(digest); - if (fAttributeService.getAttribute(keys) != null) + if (fAttributeService.exists(keys)) { throw new AVMExistsException("Lock Exists: " + keys); } @@ -316,8 +316,7 @@ public class AVMLockingServiceImpl implements AVMLockingService keys.add(WEB_PROJECTS); keys.add(webProject); keys.add(pathKey); - Attribute lockData = fAttributeService.getAttribute(keys); - if (lockData == null) + if (!fAttributeService.exists(keys)) { return; } diff --git a/source/java/org/alfresco/repo/avm/util/BulkLoader.java b/source/java/org/alfresco/repo/avm/util/BulkLoader.java index 15514f1099..bd0ba26557 100644 --- a/source/java/org/alfresco/repo/avm/util/BulkLoader.java +++ b/source/java/org/alfresco/repo/avm/util/BulkLoader.java @@ -34,6 +34,7 @@ import java.util.Map; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.cmr.avm.AVMException; import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.namespace.QName; /** @@ -78,7 +79,7 @@ public class BulkLoader Map props = new HashMap(); for (int i = 0; i < fPropertyCount; i++) { - props.put(QName.createQName("silly", "prop" + i), new PropertyValue(null, "I am property " + i)); + props.put(QName.createQName("silly", "prop" + i), new PropertyValue(DataTypeDefinition.TEXT, "I am property " + i)); } File file = new File(fsPath); String name = file.getName(); diff --git a/source/java/org/alfresco/repo/content/metadata/xml/XPathMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/xml/XPathMetadataExtracter.java index bb59db11a9..94067c0bc9 100644 --- a/source/java/org/alfresco/repo/content/metadata/xml/XPathMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/xml/XPathMetadataExtracter.java @@ -249,30 +249,16 @@ public class XPathMetadataExtracter extends AbstractMappingMetadataExtracter imp { String documentProperty = element.getKey(); XPathExpression xpathExpression = element.getValue(); - // Execute it - NodeList nodeList = (NodeList) xpathExpression.evaluate(document, XPathConstants.NODESET); - // Convert the value + // Get the value, assuming it is a nodeset Serializable value = null; - int nodeCount = nodeList.getLength(); - if (nodeCount == 0) + try { - // No result + value = getNodeSetValue(document, xpathExpression); } - else if (nodeCount == 1) + catch (XPathExpressionException e) { - Node node = nodeList.item(0); - // Get the string value - value = node.getTextContent(); - } - else - { - // Make a collection of the values - ArrayList stringValues = new ArrayList(5); - for (int i = 0; i < nodeCount; i++) - { - stringValues.add(nodeList.item(i).getTextContent()); - } - value = stringValues; + // That didn't work, so give it a try as a STRING + value = getStringValue(document, xpathExpression); } // Put the value rawProperties.put(documentProperty, value); @@ -281,6 +267,57 @@ public class XPathMetadataExtracter extends AbstractMappingMetadataExtracter imp return rawProperties; } + private Serializable getStringValue(Document document, XPathExpression xpathExpression) throws XPathExpressionException + { + String value = (String) xpathExpression.evaluate(document, XPathConstants.STRING); + // Done + return value; + } + + private Serializable getNodeSetValue(Document document, XPathExpression xpathExpression) throws XPathExpressionException + { + // Execute it + NodeList nodeList = null; + try + { + nodeList = (NodeList) xpathExpression.evaluate(document, XPathConstants.NODESET); + } + catch (XPathExpressionException e) + { + // Expression didn't evaluate to a nodelist + if (logger.isDebugEnabled()) + { + logger.debug("Unable to evaluate expression and return a NODESET: " + xpathExpression); + } + throw e; + } + // Convert the value + Serializable value = null; + int nodeCount = nodeList.getLength(); + if (nodeCount == 0) + { + // No result + } + else if (nodeCount == 1) + { + Node node = nodeList.item(0); + // Get the string value + value = node.getTextContent(); + } + else + { + // Make a collection of the values + ArrayList stringValues = new ArrayList(5); + for (int i = 0; i < nodeCount; i++) + { + stringValues.add(nodeList.item(i).getTextContent()); + } + value = stringValues; + } + // Done + return value; + } + /** * A utility method to convert mapping properties to the Map form. * diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 6969faa2bd..14833d5647 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -600,6 +600,23 @@ public class FileFolderServiceImpl implements FileFolderService NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(newName)); + QName targetParentType = nodeService.getType(targetParentRef); + + // Fix AWC-1517 + QName assocTypeQname = null; + if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_FOLDER)) + { + assocTypeQname = ContentModel.ASSOC_CONTAINS; // cm:folder -> cm:contains + } + else if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_CONTAINER)) + { + assocTypeQname = ContentModel.ASSOC_CHILDREN; // sys:container -> sys:children + } + else + { + throw new InvalidTypeException("Unexpected type (" + targetParentType + ") for target parent: " + targetParentRef); + } + // move or copy NodeRef targetNodeRef = null; if (move) @@ -611,7 +628,7 @@ public class FileFolderServiceImpl implements FileFolderService ChildAssociationRef newAssocRef = nodeService.moveNode( sourceNodeRef, targetParentRef, - assocRef.getTypeQName(), + assocTypeQname, qname); targetNodeRef = newAssocRef.getChildRef(); } @@ -629,7 +646,7 @@ public class FileFolderServiceImpl implements FileFolderService targetNodeRef = copyService.copy( sourceNodeRef, targetParentRef, - assocRef.getTypeQName(), + assocTypeQname, qname, true); } diff --git a/source/java/org/alfresco/repo/model/ml/MLContentInterceptor.java b/source/java/org/alfresco/repo/model/ml/MLContentInterceptor.java index 959a137a28..cb9f3f9361 100644 --- a/source/java/org/alfresco/repo/model/ml/MLContentInterceptor.java +++ b/source/java/org/alfresco/repo/model/ml/MLContentInterceptor.java @@ -31,6 +31,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; @@ -84,7 +85,8 @@ public class MLContentInterceptor implements MethodInterceptor NodeRef nodeRef = (NodeRef) args[0]; // Shortcut it if the node is not an empty translation - if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM) || + !nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) { return invocation.proceed(); } diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index 67e8a51998..55470448b4 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -40,6 +40,7 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.MLText; 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.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; @@ -251,7 +252,7 @@ public class MLPropertyInterceptor implements MethodInterceptor */ private NodeRef getPivotNodeRef(NodeRef nodeRef) { - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) + if (!nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM) && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION)) { return multilingualContentService.getPivotTranslation(nodeRef); } diff --git a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java index 5cc67c97d5..48337d38f3 100644 --- a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java +++ b/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java @@ -355,7 +355,7 @@ public class ArchiveAndRestoreTest extends TestCase verifyNodeExistence(bb_, true); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // check that the required properties are present and correct Map bb_Properties = nodeService.getProperties(bb_); @@ -383,7 +383,7 @@ public class ArchiveAndRestoreTest extends TestCase verifyChildAssocExistence(childAssocBtoBB_, true); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // restore the node nodeService.restoreNode(b_, null, null, null); @@ -398,7 +398,7 @@ public class ArchiveAndRestoreTest extends TestCase nodeService.deleteNode(a); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // restore in reverse order nodeService.restoreNode(a_, null, null, null); @@ -414,7 +414,7 @@ public class ArchiveAndRestoreTest extends TestCase nodeService.deleteNode(b); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // restore in reverse order nodeService.restoreNode(b_, null, null, null); @@ -430,7 +430,7 @@ public class ArchiveAndRestoreTest extends TestCase nodeService.deleteNode(b); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // in restoring 'a' first, there will be some associations that won't be recreated nodeService.restoreNode(a_, null, null, null); @@ -486,7 +486,7 @@ public class ArchiveAndRestoreTest extends TestCase cumulatedArchiveTimeNs += (end - start); // flush - AlfrescoTransactionSupport.flush(); + //AlfrescoTransactionSupport.flush(); // now restore start = System.nanoTime(); 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 8fe2f1bbd5..52d7d8ac7e 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,6 @@ 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.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionResourceInterceptor; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; @@ -53,7 +52,7 @@ public class SessionSizeManagementTest extends BaseNodeServiceTest { try { - Class clazz = SessionSizeManagementTest.class; + Class clazz = SessionSizeManagementTest.class; createNodesMethod = clazz.getMethod( "createNodes", new Class[] {NodeService.class, Integer.TYPE, Boolean.TYPE}); @@ -118,9 +117,6 @@ public class SessionSizeManagementTest extends BaseNodeServiceTest NodeService nodeService = getNodeService(); createNodes(nodeService, LOAD_COUNT, false); // We can't check the session size as this is dependent on machine speed - - // Now flush integrity to be sure things are not broken - AlfrescoTransactionSupport.flush(); } /** @@ -136,8 +132,5 @@ public class SessionSizeManagementTest extends BaseNodeServiceTest } createNodes(nodeService, LOAD_COUNT, true); - - // Now flush integrity to be sure things are not broken - AlfrescoTransactionSupport.flush(); } } diff --git a/source/java/org/alfresco/repo/node/index/AVMFullIndexRecoveryComponent.java b/source/java/org/alfresco/repo/node/index/AVMFullIndexRecoveryComponent.java index 145bf1183e..535d2155ac 100644 --- a/source/java/org/alfresco/repo/node/index/AVMFullIndexRecoveryComponent.java +++ b/source/java/org/alfresco/repo/node/index/AVMFullIndexRecoveryComponent.java @@ -1,5 +1,6 @@ package org.alfresco.repo.node.index; +import java.util.LinkedHashMap; import java.util.List; import org.alfresco.repo.node.index.FullIndexRecoveryComponent.RecoveryMode; @@ -50,15 +51,13 @@ public class AVMFullIndexRecoveryComponent extends AbstractReindexComponent { this.lockServer = lockServer; } - public void setAvmService(AVMService avmService) { this.avmService = avmService; } - public void setAvmSnapShotTriggeredIndexingMethodInterceptor( - AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) + public void setAvmSnapShotTriggeredIndexingMethodInterceptor(AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) { this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; } @@ -72,77 +71,180 @@ public class AVMFullIndexRecoveryComponent extends AbstractReindexComponent private void processStores() { List stores = avmService.getStores(); - if(stores.size() == 0) + LinkedHashMap actions = new LinkedHashMap(); + if (stores.size() == 0) { return; } - int count = 0; - int tracker = -1; - logger.info("Checking indexes for AVM Stores"); - for (AVMStoreDescriptor store : stores) + switch (recoveryMode) { - if (isShuttingDown()) + case AUTO: + case VALIDATE: + int count = 0; + int tracker = -1; + if (logger.isDebugEnabled()) { - return; + logger.debug("Checking indexes for AVM Stores: " + recoveryMode); } - processStore(store.getName()); - count++; - if (count*10l/stores.size() > tracker) - { - tracker = (int)(count*10l/stores.size()); - logger.info(" Stores "+(tracker*10)+"% complete"); + for (AVMStoreDescriptor store : stores) + { + if (isShuttingDown()) + { + return; + } + actions.put(store.getName(), checkStore(store.getName())); + count++; + if (count * 10l / stores.size() > tracker) + { + tracker = (int) (count * 10l / stores.size()); + if (logger.isDebugEnabled()) + { + logger.debug(" Store check " + (tracker * 10) + "% complete"); + } + } + } + if (logger.isDebugEnabled()) + { + logger.debug("Finished checking indexes for AVM Stores"); + } + break; + case FULL: + case NONE: + for (AVMStoreDescriptor store : stores) + { + if (isShuttingDown()) + { + return; + } + actions.put(store.getName(), checkStore(store.getName())); + } + break; + default: + } + + int full = 0; + int auto = 0; + int invalid = 0; + for (String store : actions.keySet()) + { + RecoveryMode mode = actions.get(store); + switch (mode) + { + case AUTO: + auto++; + break; + case FULL: + full++; + break; + case VALIDATE: + invalid++; + break; + case NONE: + default: } } - logger.info("Finished checking indexes for AVM Stores"); + + if (recoveryMode != RecoveryMode.NONE) + { + if (logger.isDebugEnabled()) + { + logger.debug("Invalid indexes: " + invalid); + logger.debug("Indexes for full rebuild: " + full); + logger.debug("Indexes for auto update: " + auto); + } + } + + int count = 0; + int tracker = -1; + int total = full + auto; + if (total > 0) + { + logger.info("Rebuilding indexes for " + total + " AVM Stores"); + for (String store : actions.keySet()) + { + RecoveryMode mode = actions.get(store); + if (isShuttingDown()) + { + return; + } + if ((mode == RecoveryMode.FULL) || (mode == RecoveryMode.AUTO)) + { + processStore(store, mode); + count++; + } + if (count * 10l / total > tracker) + { + tracker = (int) (count * 10l / total); + logger.info(" Reindex " + (tracker * 10) + "% complete"); + } + } + logger.info("Finished rebuilding indexes for AVM Stores"); + } + } - private void processStore(String store) + private RecoveryMode checkStore(String store) { if (logger.isDebugEnabled()) { - logger.debug("Performing AVM index recovery for type: " + recoveryMode + " on store " + store); + logger.debug("Checking AVM store for index recovery: " + recoveryMode + " on store " + store); } // do we just ignore if (recoveryMode == RecoveryMode.NONE) { - return; + return RecoveryMode.NONE; } - // check the level of cover required - boolean fullRecoveryRequired = false; + + // Nothing to do for unindexed stores + if (avmSnapShotTriggeredIndexingMethodInterceptor.getIndexMode(store) == IndexMode.UNINDEXED) + { + + if (!avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated(store)) + { + logger.warn(" Index for avm store " + store + " is out of date"); + return recoveryMode; + } + else + { + return RecoveryMode.NONE; + } + } + if (recoveryMode == RecoveryMode.FULL) // no validate required { - fullRecoveryRequired = true; + return RecoveryMode.FULL; } else // validate first { - if(avmSnapShotTriggeredIndexingMethodInterceptor.getIndexMode(store) == IndexMode.UNINDEXED) + if (!avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated(store)) { - return; + logger.warn(" Index for avm store " + store + " is out of date"); + return recoveryMode; } - int lastActualSnapshotId = avmService.getLatestSnapshotID(store); if (lastActualSnapshotId <= 0) { - return; + return RecoveryMode.NONE; } int lastIndexedSnapshotId = avmSnapShotTriggeredIndexingMethodInterceptor.getLastIndexedSnapshot(store); if (lastActualSnapshotId != lastIndexedSnapshotId) { - logger.warn("Index for avm store " + store + " is out of date"); - // this store isn't up to date - if (recoveryMode == RecoveryMode.VALIDATE) - { - // the store is out of date - validation failed - } - else if (recoveryMode == RecoveryMode.AUTO) - { - fullRecoveryRequired = true; - } + logger.warn(" Index for avm store " + store + " is out of date"); + return recoveryMode; + } + else + { + return recoveryMode.NONE; } } + } + + private void processStore(String store, RecoveryMode mode) + { + // put the server into read-only mode for the duration boolean allowWrite = !transactionService.isReadOnly(); try @@ -153,11 +255,8 @@ public class AVMFullIndexRecoveryComponent extends AbstractReindexComponent transactionService.setAllowWrite(false); } - // do we need to perform a full recovery - if (fullRecoveryRequired) - { - recoverStore(store); - } + recoverStore(store, mode); + } finally { @@ -167,56 +266,97 @@ public class AVMFullIndexRecoveryComponent extends AbstractReindexComponent } - private void recoverStore(String store) + private void recoverStore(String store, RecoveryMode mode) { int tracker = -1; - int latest = avmService.getLatestSnapshotID(store); - if(latest <= 0) + + if (mode == RecoveryMode.AUTO) { - return; + logger.info(" Auto recovering index for " + store); } - logger.info("Recovery for "+store); - - if(!avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated(store)) + else if (mode == RecoveryMode.FULL) + { + logger.info(" Rebuilding index for " + store); + } + + if (!avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated(store)) { avmSnapShotTriggeredIndexingMethodInterceptor.createIndex(store); } - for (int i = 0; i <= latest; i++) + + int latest = avmService.getLatestSnapshotID(store); + if (latest <= 0) { - if (isShuttingDown()) + return; + } + + boolean wasRecovered = false; + + if (avmSnapShotTriggeredIndexingMethodInterceptor.getIndexMode(store) != IndexMode.UNINDEXED) + { + for (int i = 0; i <= latest; i++) { - return; - } - recoverSnapShot(store, i); - if (i*10l/latest > tracker) - { - tracker = (int)(i*10l/latest); - logger.info(" Store "+store +" "+(tracker*10)+"% complete"); + if (isShuttingDown()) + { + return; + } + wasRecovered = recoverSnapShot(store, i, mode, wasRecovered); + if (i * 10l / latest > tracker) + { + tracker = (int) (i * 10l / latest); + if (logger.isDebugEnabled()) + { + logger.debug(" Store " + store + " " + (tracker * 10) + "% complete"); + } + } } } - logger.info("Recovery for "+store+" done"); + if (logger.isDebugEnabled()) + { + logger.debug(" Index updated for " + store); + } } - private void recoverSnapShot(final String store, final int id) + private boolean recoverSnapShot(final String store, final int id, final RecoveryMode mode, final boolean wasRecovered) { if (logger.isDebugEnabled()) { - logger.debug("Reindexing avm store: " + store + " snapshot id " + id); + logger.debug(" Reindexing avm store: " + store + " snapshot id " + id); } - RetryingTransactionCallback reindexWork = new RetryingTransactionCallback() + RetryingTransactionCallback reindexWork = new RetryingTransactionCallback() { - public Object execute() throws Exception + public Boolean execute() throws Exception { - if (!avmSnapShotTriggeredIndexingMethodInterceptor.isSnapshotIndexed(store, id)) + if (wasRecovered) { avmSnapShotTriggeredIndexingMethodInterceptor.indexSnapshot(store, id - 1, id); + return true; } - // done - return null; + else + { + if (mode == RecoveryMode.AUTO) + { + if (!avmSnapShotTriggeredIndexingMethodInterceptor.isSnapshotIndexed(store, id)) + { + avmSnapShotTriggeredIndexingMethodInterceptor.indexSnapshot(store, id - 1, id); + return true; + } + else + { + return wasRecovered; + } + } + else + { + avmSnapShotTriggeredIndexingMethodInterceptor.indexSnapshot(store, id - 1, id); + return true; + } + } + } }; - transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork, true, true); + return transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork, true, true); // done } diff --git a/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java new file mode 100644 index 0000000000..7b8e02ee5a --- /dev/null +++ b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTracker.java @@ -0,0 +1,129 @@ +/* + * 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.index; + +import java.util.List; + +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.avm.AVMStoreDescriptor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Track and update when snapshots are created and indexed in a cluster + */ +public class AVMRemoteSnapshotTracker extends AbstractReindexComponent +{ + private static Log logger = LogFactory.getLog(AVMRemoteSnapshotTracker.class); + + private AVMService avmService; + + private AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor; + + public void setAvmService(AVMService avmService) + { + this.avmService = avmService; + } + + public void setAvmSnapShotTriggeredIndexingMethodInterceptor(AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor) + { + this.avmSnapShotTriggeredIndexingMethodInterceptor = avmSnapShotTriggeredIndexingMethodInterceptor; + } + + @Override + protected void reindexImpl() + { + processStores(); + } + + /** + * Loop throught the avm stores and compare the latest snapshot to that in the index. + * Update the index if it has fallen behind. + * + */ + private void processStores() + { + List stores = avmService.getStores(); + if (stores.size() == 0) + { + return; + } + + boolean upToDate; + do + { + upToDate = true; + for (AVMStoreDescriptor store : stores) + { + if (isShuttingDown()) + { + break; + } + int current = avmService.getLatestSnapshotID(store.getName()); + int lastIndexed = avmSnapShotTriggeredIndexingMethodInterceptor.getLastIndexedSnapshot(store.getName()); + + if (lastIndexed < current) + { + if(logger.isDebugEnabled()) + { + logger.debug("Updating index for store "+store.getName()+" from snapshot "+lastIndexed+ " to "+current); + } + recoverSnapShot(store.getName(), lastIndexed, current); + upToDate = false; + } + } + } + while(!upToDate); + + } + + /** + * Do the index update in one lump (not individual snapshots) + * + * @param store + * @param lastIndexed + * @param current + */ + private void recoverSnapShot(final String store, final int lastIndexed, final int current) + { + + RetryingTransactionCallback reindexWork = new RetryingTransactionCallback() + { + public Object execute() throws Exception + { + if(lastIndexed == -1) + { + avmSnapShotTriggeredIndexingMethodInterceptor.createIndex(store); + } + avmSnapShotTriggeredIndexingMethodInterceptor.indexSnapshot(store, lastIndexed, current); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork, true); + } + +} diff --git a/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java new file mode 100644 index 0000000000..c369c7d124 --- /dev/null +++ b/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java @@ -0,0 +1,206 @@ +/* + * 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.index; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.node.db.NodeDaoService; +import org.alfresco.repo.search.AVMSnapShotTriggeredIndexingMethodInterceptor; +import org.alfresco.repo.search.Indexer; +import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.TransactionServiceImpl; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.BaseSpringTest; + +/** + * Test that the index tracker catches up + * + * @author andyh + */ +public class AVMRemoteSnapshotTrackerTest extends BaseSpringTest +{ + + private AuthenticationComponent authenticationComponent; + + private AVMService avmService; + + private AVMSnapShotTriggeredIndexingMethodInterceptor avmSnapShotTriggeredIndexingMethodInterceptor; + + private TransactionService transactionService; + + private UserTransaction testTX; + + private SearchService searchService; + + private NodeService nodeService; + + private FullTextSearchIndexer ftsIndexer; + + private Indexer indexer; + + private NodeDaoService nodeDaoService; + + public AVMRemoteSnapshotTrackerTest() + { + // TODO Auto-generated constructor stub + } + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + ServiceRegistry serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); + + avmService = (AVMService) applicationContext.getBean("AVMService"); + avmSnapShotTriggeredIndexingMethodInterceptor = (AVMSnapShotTriggeredIndexingMethodInterceptor) applicationContext.getBean("avmSnapShotTriggeredIndexingMethodInterceptor"); + transactionService = (TransactionService) applicationContext.getBean("transactionComponent"); + + searchService = serviceRegistry.getSearchService(); + nodeService = serviceRegistry.getNodeService(); + ftsIndexer = (FullTextSearchIndexer) applicationContext.getBean("LuceneFullTextSearchIndexer"); + indexer = (Indexer) applicationContext.getBean("indexerComponent"); + nodeDaoService = (NodeDaoService) applicationContext.getBean("nodeDaoService"); + + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent"); + authenticationComponent.setSystemUserAsCurrentUser(); + + } + + @Override + protected void onTearDownInTransaction() throws Exception + { + if (testTX.getStatus() == Status.STATUS_ACTIVE) + { + testTX.rollback(); + } + try + + { + authenticationComponent.clearCurrentSecurityContext(); + } + catch (Throwable e) + { + // do nothing + } + super.onTearDownInTransaction(); + } + + public void testCatchUp() + { + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("one")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("two")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("three")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("four")); + + // initial state + avmService.createStore("one"); + //assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("one")); + avmService.createDirectory("one:/", "a"); + avmService.createSnapshot("one", null, null); + + avmService.createStore("two"); + avmService.createDirectory("two:/", "a"); + + avmService.createStore("three"); + + // Check state + + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("two")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("two")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("three")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("three")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("four")); + + // Disable the indexer and do updates + + avmSnapShotTriggeredIndexingMethodInterceptor.setEnableIndexing(false); + + // one unchanged + + // two snap shot + avmService.createSnapshot("two", null, null); + + // three update and snapshot + + avmService.createDirectory("three:/", "a"); + avmService.createSnapshot("three", null, null); + + // four create + + avmService.createStore("four"); + avmService.createDirectory("four:/", "a"); + avmService.createSnapshot("four", null, null); + avmService.createDirectory("four:/", "b"); + avmService.createSnapshot("four", null, null); + + // + + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("two")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("two")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("three")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("three")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("four")); + assertFalse(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("four")); + + avmSnapShotTriggeredIndexingMethodInterceptor.setEnableIndexing(true); + + AVMRemoteSnapshotTracker tracker = new AVMRemoteSnapshotTracker(); + tracker.setAuthenticationComponent(authenticationComponent); + tracker.setAvmService(avmService); + tracker.setAvmSnapShotTriggeredIndexingMethodInterceptor(avmSnapShotTriggeredIndexingMethodInterceptor); + tracker.setTransactionService((TransactionServiceImpl) transactionService); + tracker.setFtsIndexer(ftsIndexer); + tracker.setIndexer(indexer); + tracker.setNodeDaoService(nodeDaoService); + tracker.setNodeService(nodeService); + tracker.setSearcher(searchService); + + tracker.reindex(); + + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("one")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("two")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("two")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("three")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("three")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.hasIndexBeenCreated("four")); + assertTrue(avmSnapShotTriggeredIndexingMethodInterceptor.isIndexUpToDate("four")); + } + +} diff --git a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java index 7fc067a77d..841086f4bb 100644 --- a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java +++ b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java @@ -469,6 +469,7 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte if (indexer instanceof AVMLuceneIndexer) { AVMLuceneIndexer avmIndexer = (AVMLuceneIndexer) indexer; + avmIndexer.flushPending(); return avmIndexer.hasIndexBeenCreated(store); } return false; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java index 7c55933a7f..e5eee939c9 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java @@ -1545,16 +1545,30 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl } } + public boolean hasIndexBeenCreated(String store) { - IndexReader mainReader = null; + return hasIndexBeenCreatedimpl(store, IndexChannel.MAIN) || hasIndexBeenCreatedimpl(store, IndexChannel.DELTA); + } + + public boolean hasIndexBeenCreatedimpl(String store, IndexChannel channel) + { + IndexReader reader = null; try { - mainReader = getReader(); + if (channel == IndexChannel.DELTA) + { + flushPending(); + reader = getDeltaReader(); + } + else + { + reader = getReader(); + } TermDocs termDocs = null; try { - termDocs = mainReader.termDocs(new Term("ISROOT", "T")); + termDocs = reader.termDocs(new Term("ISROOT", "T")); return termDocs.next(); } finally @@ -1573,9 +1587,17 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl { try { - if (mainReader != null) + + if (reader != null) { - mainReader.close(); + if (channel == IndexChannel.DELTA) + { + closeDeltaReader(); + } + else + { + reader.close(); + } } } catch (IOException e) diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java index 5f77fcb471..b2ae5e745e 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java @@ -35,7 +35,6 @@ import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcher; -import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -352,22 +351,13 @@ public abstract class AlfrescoTransactionSupport } /** - * Flush in-transaction resources. A transaction must be active. - *

- * The flush may include: - *

    - *
  • {@link TransactionalDao#flush()}
  • - *
  • {@link RuleService#executePendingRules()}
  • - *
  • {@link IntegrityChecker#checkIntegrity()}
  • - *
- * + * No-op + * + * @deprecated No longer does anything */ public static void flush() { - // get transaction-local synchronization - TransactionSynchronizationImpl synch = getSynchronization(); - // flush - synch.flush(); + // No-op } /** @@ -548,15 +538,6 @@ public abstract class AlfrescoTransactionSupport return sb.toString(); } - /** - * Performs the in-transaction flushing. Typically done during a transaction or - * before commit. - */ - public void flush() - { - throw new UnsupportedOperationException("Manual flush no longer supported."); - } - /** * @see AlfrescoTransactionSupport#SESSION_SYNCHRONIZATION_ORDER */ @@ -589,7 +570,7 @@ public abstract class AlfrescoTransactionSupport /** * Pre-commit cleanup. *

- * Ensures that the session resources are {@link #flush() flushed}. + * Ensures that the session transaction listeners are property executed. * The Lucene indexes are then prepared. */ @Override diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java index 667a90922e..adf84cb4b1 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java @@ -157,10 +157,6 @@ public class AlfrescoTransactionSupportTest extends TestCase // register it AlfrescoTransactionSupport.bindListener(listener); - // test flush - AlfrescoTransactionSupport.flush(); - assertTrue("flush not called on listener", strings.contains("flush")); - // test commit txn.commit(); assertTrue("beforeCommit not called on listener", strings.contains("beforeCommit")); diff --git a/source/java/org/alfresco/repo/transaction/TransactionListener.java b/source/java/org/alfresco/repo/transaction/TransactionListener.java index 52805c7c9c..11741fbfb6 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionListener.java +++ b/source/java/org/alfresco/repo/transaction/TransactionListener.java @@ -34,11 +34,7 @@ package org.alfresco.repo.transaction; public interface TransactionListener { /** - * Allows the listener to flush any consuming resources. This mechanism is - * used primarily during long-lived transactions to ensure that system resources - * are not used up. - *

- * This method must not be used for implementing business logic. + * @deprecated No longer supported */ void flush(); diff --git a/source/test-resources/xml-metadata/eclipse-project-mappings.properties b/source/test-resources/xml-metadata/eclipse-project-mappings.properties index 23ca69a138..f154f389f3 100644 --- a/source/test-resources/xml-metadata/eclipse-project-mappings.properties +++ b/source/test-resources/xml-metadata/eclipse-project-mappings.properties @@ -13,3 +13,4 @@ description=cm:description multi-value-text=test:multi-value-text multi-value-node=test:multi-value-node complex-node=test:complex-node +element-name=test:element-name diff --git a/source/test-resources/xml-metadata/eclipse-project-xpath-mappings.properties b/source/test-resources/xml-metadata/eclipse-project-xpath-mappings.properties index 15f384d335..5481605d19 100644 --- a/source/test-resources/xml-metadata/eclipse-project-xpath-mappings.properties +++ b/source/test-resources/xml-metadata/eclipse-project-xpath-mappings.properties @@ -10,4 +10,5 @@ name=/projectDescription/name/text() description=/projectDescription/comment/text() multi-value-text=/projectDescription/natures/nature/text() multi-value-node=/projectDescription/natures/nature -complex-node=/projectDescription/natures \ No newline at end of file +complex-node=/projectDescription/natures +element-name=name(/projectDescription) \ No newline at end of file