From 0f0d7a2fe0863112c545eb402d4c34efd68d47d4 Mon Sep 17 00:00:00 2001 From: Jan Vonka Date: Wed, 17 Mar 2010 11:26:46 +0000 Subject: [PATCH] Merged V3.2 to HEAD 19246: ALF-1940 - case-insensitive AVM path lookups fail on a DB that is (configured by default to be) case-sensitive 19280: AVM - preserve case on child lookup, add unit tests 19316: AVM - fix rename (change in 'case') & add unit tests (ALF-1725 & ALF-1767) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19337 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../avm-common-SqlMap.xml | 67 +++++- .../org/alfresco/repo/avm/AVMRepository.java | 203 +++++++++++------- .../repo/avm/AVMServiceLocalTest.java | 140 +++++++++++- .../org/alfresco/repo/avm/AVMServiceTest.java | 68 +++++- .../org/alfresco/repo/avm/AVMStoreImpl.java | 2 +- .../alfresco/repo/avm/AVMSyncServiceImpl.java | 99 ++++++--- .../org/alfresco/repo/avm/ChildEntryDAO.java | 6 +- .../org/alfresco/repo/avm/DirectoryNode.java | 8 + .../alfresco/repo/avm/DirectoryNodeImpl.java | 18 ++ .../repo/avm/LayeredDirectoryNodeImpl.java | 66 ++++-- .../alfresco/repo/avm/MultiTAVMService.java | 1 + .../alfresco/repo/avm/NOOPLookupCache.java | 26 +-- .../repo/avm/PlainDirectoryNodeImpl.java | 10 +- .../repo/avm/ibatis/ChildEntryDAOIbatis.java | 17 +- .../repo/domain/avm/AVMNodeLinksDAO.java | 5 + .../avm/AbstractAVMNodeLinksDAOImpl.java | 23 +- .../avm/ibatis/AVMNodeLinksDAOImpl.java | 50 ++++- .../service/cmr/avm/AVMStoreDescriptor.java | 51 ++++- 18 files changed, 680 insertions(+), 180 deletions(-) diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml index 8c3406ef9b..d6ef9380b9 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml @@ -838,6 +838,17 @@ child_id = #childNodeId# + + + + + + + + + delete + from + avm_child_entries + where + parent_id = #parentNodeId# and + lower(name) = lower(#name#) and + child_id = #childNodeId# + + --> + + + + update + avm_child_entries + set + name = #name# + where + parent_id = #parentNodeId# and + child_id = #childNodeId# + + + delete from @@ -912,7 +966,16 @@ name = #name# - + + delete + from + avm_child_entries + where + parent_id = #parentNodeId# and + lower(name) = lower(#name#) + + + delete from diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 2ba414f7b0..790d94375d 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -721,7 +721,7 @@ public class AVMRepository throw new AVMNotFoundException("Path not found."); } srcDir = (DirectoryNode) sPath.getCurrentNode(); - + Pair temp = srcDir.lookupChild(sPath, srcName, false); srcNode = (temp == null) ? null : temp.getFirst(); if (srcNode == null) @@ -760,108 +760,147 @@ public class AVMRepository } Pair temp = dstDir.lookupChild(dPath, dstName, true); AVMNode child = (temp == null) ? null : temp.getFirst(); + + boolean renameCase = false; if (child != null && child.getType() != AVMNodeType.DELETED_NODE) { - throw new AVMExistsException("Node exists: " + dstName); - } - - Long parentAcl = dstDir.getAcl() == null ? null : dstDir.getAcl().getId(); - - AVMNode dstNode = null; - // We've passed the check, so we can go ahead and do the rename. - if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY) - { - // If the source is layered then the renamed thing needs to be layered also. - if (sPath.isLayered()) + String avmSrcPath = AVMUtil.extendAVMPath(srcPath, srcName); + String avmDstPath = AVMUtil.extendAVMPath(dstPath, dstName); + + if ((avmSrcPath.equalsIgnoreCase(avmDstPath)) && (! srcName.equals(dstName))) { - // If this is a rename happening in the same layer we make a new - // OverlayedDirectoryNode that is not a primary indirection layer. - // Otherwise we do make the new OverlayedDirectoryNode a primary - // Indirection layer. This complexity begs the question of whether - // we should allow renames from within one layer to within another - // layer. Allowing it makes the logic absurdly complex. - if (dPath.isLayered() && dPath.getTopLayer().equals(sPath.getTopLayer())) + // specific rename 'case' only (within a store) + if (fgLogger.isDebugEnabled()) { - dstNode = new LayeredDirectoryNodeImpl((PlainDirectoryNode) srcNode, dstRepo, sPath, true, parentAcl, ACLCopyMode.COPY); - ((LayeredDirectoryNode) dstNode).setLayerID(sPath.getTopLayer().getLayerID()); + fgLogger.debug("rename: only change case: from "+avmSrcPath+" to "+avmDstPath); + } + renameCase = true; + } + else + { + throw new AVMExistsException("Node exists: " + dstName); + } + } + + if (! renameCase) + { + // general rename/move + + Long parentAcl = dstDir.getAcl() == null ? null : dstDir.getAcl().getId(); + + AVMNode dstNode = null; + // We've passed the check, so we can go ahead and do the rename. + if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY) + { + // If the source is layered then the renamed thing needs to be layered also. + if (sPath.isLayered()) + { + // If this is a rename happening in the same layer we make a new + // OverlayedDirectoryNode that is not a primary indirection layer. + // Otherwise we do make the new OverlayedDirectoryNode a primary + // Indirection layer. This complexity begs the question of whether + // we should allow renames from within one layer to within another + // layer. Allowing it makes the logic absurdly complex. + if (dPath.isLayered() && dPath.getTopLayer().equals(sPath.getTopLayer())) + { + dstNode = new LayeredDirectoryNodeImpl((PlainDirectoryNode) srcNode, dstRepo, sPath, true, parentAcl, ACLCopyMode.COPY); + ((LayeredDirectoryNode) dstNode).setLayerID(sPath.getTopLayer().getLayerID()); + } + else + { + dstNode = new LayeredDirectoryNodeImpl((DirectoryNode) srcNode, dstRepo, sPath, srcName, parentAcl, ACLCopyMode.COPY); + + // note: re-use generated node id as a layer id + ((LayeredDirectoryNode) dstNode).setLayerID(dstNode.getId()); + } + + AVMDAOs.Instance().fAVMNodeDAO.update(dstNode); } else { - dstNode = new LayeredDirectoryNodeImpl((DirectoryNode) srcNode, dstRepo, sPath, srcName, parentAcl, ACLCopyMode.COPY); - - // note: re-use generated node id as a layer id - ((LayeredDirectoryNode) dstNode).setLayerID(dstNode.getId()); + dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); + } + } + else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY) + { + if (!sPath.isLayered() || (sPath.isInThisLayer() && srcDir.getType() == AVMNodeType.LAYERED_DIRECTORY && ((LayeredDirectoryNode) srcDir).directlyContains(srcNode))) + { + Lookup srcLookup = lookup(-1, srcPath + "/" + srcName, true); + // Use the simple 'copy' constructor. + dstNode = new LayeredDirectoryNodeImpl((LayeredDirectoryNode) srcNode, dstRepo, srcLookup, true, parentAcl, ACLCopyMode.COPY); + ((LayeredDirectoryNode) dstNode).setLayerID(((LayeredDirectoryNode) srcNode).getLayerID()); + } + else + { + // If the source node is a primary indirection, then the 'copy' constructor + // is used. Otherwise the alternate constructor is called and its + // indirection is calculated from it's source context. + if (((LayeredDirectoryNode) srcNode).getPrimaryIndirection()) + { + Lookup srcLookup = lookup(-1, srcPath + "/" + srcName, true); + dstNode = new LayeredDirectoryNodeImpl((LayeredDirectoryNode) srcNode, dstRepo, srcLookup, true, parentAcl, ACLCopyMode.COPY); + } + else + { + dstNode = new LayeredDirectoryNodeImpl((DirectoryNode) srcNode, dstRepo, sPath, srcName, parentAcl, ACLCopyMode.COPY); + } + // What needs to be done here is dependent on whether the + // rename is to a layered context. If so then it should get the layer id + // of its destination parent. Otherwise it should get a new layer + // id. + if (dPath.isLayered()) + { + ((LayeredDirectoryNode) dstNode).setLayerID(dPath.getTopLayer().getLayerID()); + } + else + { + // note: re-use generated node id as a layer id + ((LayeredDirectoryNode) dstNode).setLayerID(dstNode.getId()); + } } AVMDAOs.Instance().fAVMNodeDAO.update(dstNode); } - else + else if (srcNode.getType() == AVMNodeType.LAYERED_FILE) { - dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); - } - } - else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY) - { - if (!sPath.isLayered() || (sPath.isInThisLayer() && srcDir.getType() == AVMNodeType.LAYERED_DIRECTORY && ((LayeredDirectoryNode) srcDir).directlyContains(srcNode))) - { - Lookup srcLookup = lookup(-1, srcPath + "/" + srcName, true); - // Use the simple 'copy' constructor. - dstNode = new LayeredDirectoryNodeImpl((LayeredDirectoryNode) srcNode, dstRepo, srcLookup, true, parentAcl, ACLCopyMode.COPY); - ((LayeredDirectoryNode) dstNode).setLayerID(((LayeredDirectoryNode) srcNode).getLayerID()); + dstNode = new LayeredFileNodeImpl((LayeredFileNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); } else + // This is a plain file node. { - // If the source node is a primary indirection, then the 'copy' constructor - // is used. Otherwise the alternate constructor is called and its - // indirection is calculated from it's source context. - if (((LayeredDirectoryNode) srcNode).getPrimaryIndirection()) - { - Lookup srcLookup = lookup(-1, srcPath + "/" + srcName, true); - dstNode = new LayeredDirectoryNodeImpl((LayeredDirectoryNode) srcNode, dstRepo, srcLookup, true, parentAcl, ACLCopyMode.COPY); - } - else - { - dstNode = new LayeredDirectoryNodeImpl((DirectoryNode) srcNode, dstRepo, sPath, srcName, parentAcl, ACLCopyMode.COPY); - } - // What needs to be done here is dependent on whether the - // rename is to a layered context. If so then it should get the layer id - // of its destination parent. Otherwise it should get a new layer - // id. - if (dPath.isLayered()) - { - ((LayeredDirectoryNode) dstNode).setLayerID(dPath.getTopLayer().getLayerID()); - } - else - { - // note: re-use generated node id as a layer id - ((LayeredDirectoryNode) dstNode).setLayerID(dstNode.getId()); - } + dstNode = new PlainFileNodeImpl((PlainFileNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); } - AVMDAOs.Instance().fAVMNodeDAO.update(dstNode); - } - else if (srcNode.getType() == AVMNodeType.LAYERED_FILE) - { - dstNode = new LayeredFileNodeImpl((LayeredFileNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); + srcDir.removeChild(sPath, srcName); + // srcDir.updateModTime(); + // dstNode.setVersionID(dstRepo.getNextVersionID()); + if (child != null) + { + dstNode.setAncestor(child); + } + + //dstDir.updateModTime(); + dstDir.putChild(dstName, dstNode); + if (child == null) + { + dstNode.setAncestor(srcNode); + } } else - // This is a plain file node. { - dstNode = new PlainFileNodeImpl((PlainFileNode) srcNode, dstRepo, parentAcl, ACLCopyMode.COPY); - } - srcDir.removeChild(sPath, srcName); - // srcDir.updateModTime(); - // dstNode.setVersionID(dstRepo.getNextVersionID()); - if (child != null) - { - dstNode.setAncestor(child); - } - //dstDir.updateModTime(); - dstDir.putChild(dstName, dstNode); - if (child == null) - { - dstNode.setAncestor(srcNode); + // specific rename 'case' only (within a store) + + forceCopy(AVMUtil.extendAVMPath(srcPath, srcName)); + + Pair result = srcDir.lookupChildEntry(sPath, srcName, false); + if (result != null) + { + ChildKey key = result.getFirst().getKey(); + key.setName(srcName); + AVMDAOs.Instance().fChildEntryDAO.rename(key, dstName); + } } + fLookupCache.onWrite(pathParts[0]); } finally diff --git a/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java b/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java index 45f43091c3..2d65096610 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java @@ -1214,8 +1214,7 @@ public class AVMServiceLocalTest extends TestCase } } - - public void testRename6() throws Exception + public void testRename1() throws Exception { try { @@ -1252,6 +1251,143 @@ public class AVMServiceLocalTest extends TestCase } } + public void testRename2() throws Exception + { + String fileLower = "foo"; + String fileUpper = "FOO"; + + try + { + logger.debug("created 2 stores: main, layer"); + + fService.createDirectory("main:/", "a"); + fService.createFile("main:/a", fileLower); + + logger.debug("created: main:/a/"+fileLower); + + AVMNodeDescriptor desc = fService.lookup(-1, "main:/a/"+fileLower); + assertNotNull(desc); + assertEquals("main:/a/"+fileLower, desc.getPath()); + + fService.createLayeredDirectory("main:/a", "layer:/", "a"); + + logger.debug("created: layer:/a/"+fileLower+" -> main:/a/"+fileLower); + + assertNotNull(fService.lookup(-1, "layer:/a/"+fileLower)); + + List diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + fService.rename("layer:/a/", fileLower, "layer:/a", fileUpper); + + logger.debug("rename: layer:/a/"+fileLower+" -> layer:/a/"+fileUpper); + + diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals("[layer:/a/"+fileUpper+"[-1] > main:/a/"+fileUpper+"[-1]]", diffs.toString()); + + fSyncService.update(diffs, null, false, false, false, false, null, null); + + logger.debug("update: layer:/a/"+fileUpper+" -> main:/a/"+fileUpper); + + diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + fSyncService.flatten("layer:/a", "main:/a"); + + logger.debug("flatten: layer:/a -> main:/a"); + + desc = fService.lookup(-1, "main:/a/"+fileLower); + assertNotNull(desc); + assertEquals("main:/a/"+fileUpper, desc.getPath()); + + desc = fService.lookup(-1, "main:/a/"+fileUpper); + assertNotNull(desc); + assertEquals("main:/a/"+fileUpper, desc.getPath()); + } + catch (Exception e) + { + e.printStackTrace(); + throw e; + } + } + + public void testRename3() throws Exception + { + try + { + logger.debug("created 2 stores: main, layer"); + + fService.createDirectory("main:/", "a"); + fService.createDirectory("main:/a", "b"); + fService.createDirectory("main:/a/b", "c"); + + logger.debug("created: main:/a/b/c"); + + fService.createLayeredDirectory("main:/a", "layer:/", "a"); + + logger.debug("created: layer:/a -> main:/a"); + + List diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + AVMNodeDescriptor desc = fService.lookup(-1, "main:/a/b"); + assertNotNull(desc); + assertEquals("main:/a/b", desc.getPath()); + + desc = fService.lookup(-1, "main:/a/B"); + assertNotNull(desc); + assertEquals("main:/a/b", desc.getPath()); + + desc = fService.lookup(-1, "layer:/a/b"); + assertNotNull(desc); + assertEquals("layer:/a/b", desc.getPath()); + + fService.rename("layer:/a/", "b", "layer:/a", "B"); + + logger.debug("rename: layer:/a/b -> layer:/a/B"); + + desc = fService.lookup(-1, "main:/a/b"); + assertNotNull(desc); + assertEquals("main:/a/b", desc.getPath()); + + desc = fService.lookup(-1, "layer:/a/B"); + assertNotNull(desc); + assertEquals("layer:/a/B", desc.getPath()); + + desc = fService.lookup(-1, "layer:/a/b"); + assertNotNull(desc); + assertEquals("layer:/a/B", desc.getPath()); + + diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals("[layer:/a/B[-1] > main:/a/B[-1]]", diffs.toString()); + + fSyncService.update(diffs, null, false, false, false, false, null, null); + + logger.debug("update: layer:/a/B -> main:/a/B"); + + diffs = fSyncService.compare(-1, "layer:/a", -1, "main:/a", null); + assertEquals(0, diffs.size()); + + fSyncService.flatten("layer:/a", "main:/a"); + + logger.debug("flatten: layer:/a -> main:/a"); + + desc = fService.lookup(-1, "main:/a/b"); + assertNotNull(desc); + assertEquals("main:/a/B", desc.getPath()); + + desc = fService.lookup(-1, "main:/a/B"); + assertNotNull(desc); + assertEquals("main:/a/B", desc.getPath()); + } + catch (Exception e) + { + e.printStackTrace(); + throw e; + } + } + + /** * Test file properties update ... */ diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index 3598ec47f4..6262473817 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -5976,24 +5976,88 @@ public class AVMServiceTest extends AVMServiceTestBase */ public void testCaseInsensitive() throws Exception { + String storeName = "caseIn"; + String storeNameUpper = storeName.toUpperCase(); + String storeNameLower = storeName.toLowerCase(); + try { - setupBasicTree(); + fService.createStore(storeName); + try { - fService.createFile("main:/a/b/c", "Foo").close(); + fService.createStore(storeNameUpper); fail(); } catch (AVMExistsException e) { // Do nothing. } + + AVMStoreDescriptor storeDesc1 = fService.getStore(storeNameUpper); + assertNotNull(storeDesc1); + assertEquals(storeName, storeDesc1.getName()); + + AVMStoreDescriptor storeDesc2 = fService.getStore(storeNameLower); + assertNotNull(storeDesc2); + assertEquals(storeName, storeDesc2.getName()); + + assertEquals(storeDesc1, storeDesc2); // same id + + fService.createDirectory(storeName+":/", "a"); + fService.createDirectory(storeName+":/a", "B"); + fService.createDirectory(storeName+":/a/B", "c"); + fService.createFile(storeName+":/a/B/c", "Foo").close(); + + AVMNodeDescriptor desc1 = fService.lookup(-1, storeNameUpper+":/A/B/C/FOO"); + assertNotNull(desc1); + assertEquals(storeName+":/a/B/c/Foo", desc1.getPath()); + + try + { + fService.createFile(storeName+":/a/B/c", "FoO").close(); + fail(); + } + catch (AVMExistsException e) + { + // Do nothing. + } + + AVMNodeDescriptor desc2 = fService.lookup(-1, storeNameLower+":/a/b/c/foo"); + assertNotNull(desc2); + assertEquals(storeName+":/a/B/c/Foo", desc2.getPath()); + + assertEquals(desc1, desc2); // same id + + desc1 = fService.lookup(-1, storeNameUpper+":/A/B/C"); + assertNotNull(desc1); + assertEquals(storeName+":/a/B/c", desc1.getPath()); + + try + { + fService.createFile(storeName+":/a/b", "C").close(); + fail(); + } + catch (AVMExistsException e) + { + // Do nothing. + } + + desc2 = fService.lookup(-1, storeNameLower+":/a/b/c"); + assertNotNull(desc2); + assertEquals(storeName+":/a/B/c", desc2.getPath()); + + assertEquals(desc1, desc2); // same id } catch (Exception e) { e.printStackTrace(System.err); throw e; } + finally + { + fService.purgeStore(storeName); + } } /** diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 25e0c96872..abd0b68e0e 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -1286,7 +1286,7 @@ public class AVMStoreImpl implements AVMStore PropertyValue createdValue = getProperty(ContentModel.PROP_CREATED); Date created = createdValue == null ? (new Date()) : (Date) createdValue.getValue(DataTypeDefinition.DATE); created = (created == null) ? (new Date()) : created; - return new AVMStoreDescriptor(getName(), creator, created.getTime()); + return new AVMStoreDescriptor(getId(), getName(), creator, created.getTime()); } /** diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java index 0b215df363..a9883b2e24 100644 --- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java @@ -161,9 +161,29 @@ public class AVMSyncServiceImpl implements AVMSyncService int dstVersion, AVMNodeDescriptor dstDesc, List result, NameMatcher excluder, boolean firstLevel) { + String srcPath = srcDesc.getPath(); + String dstPath = dstDesc.getPath(); + + String srcParts[] = AVMUtil.splitBase(srcPath); + String srcChildName = srcParts[1]; + + String dstParts[] = AVMUtil.splitBase(dstPath); + String dstChildName = dstParts[1]; + + if ((dstChildName.equalsIgnoreCase(srcChildName)) && (! dstChildName.equals(srcChildName))) + { + // specific rename 'case' + String dstParentPath = dstParts[0]; + if (dstParentPath == null) + { + dstParentPath = AVMUtil.buildAVMPath(AVMUtil.getStoreName(dstPath), ""); + } + dstPath = AVMUtil.extendAVMPath(dstParentPath, srcChildName); + } + // Determine how the source and destination nodes differ. - if (excluder != null && (excluder.matches(srcDesc.getPath()) || - excluder.matches(dstDesc.getPath()))) + if (excluder != null && (excluder.matches(srcPath) || + excluder.matches(dstPath))) { return; } @@ -180,8 +200,8 @@ public class AVMSyncServiceImpl implements AVMSyncService case AVMDifference.OLDER : case AVMDifference.CONFLICT : { - result.add(new AVMDifference(srcVersion, srcDesc.getPath(), - dstVersion, dstDesc.getPath(), + result.add(new AVMDifference(srcVersion, srcPath, + dstVersion, dstPath, diffCode)); return; } @@ -190,7 +210,7 @@ public class AVMSyncServiceImpl implements AVMSyncService // 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) + srcDesc.getIndirection().equalsIgnoreCase(dstPath) && srcVersion < 0 && dstVersion < 0) { // skip firstLevel (root) if (! firstLevel) @@ -203,8 +223,8 @@ public class AVMSyncServiceImpl implements AVMSyncService case AVMDifference.NEWER : case AVMDifference.CONFLICT : { - result.add(new AVMDifference(srcVersion, srcDesc.getPath(), - dstVersion, dstDesc.getPath(), + result.add(new AVMDifference(srcVersion, srcPath, + dstVersion, dstPath, dirDiffCode)); return; // short circuit } @@ -235,19 +255,21 @@ public class AVMSyncServiceImpl implements AVMSyncService { 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))) + + String srcChildPath = srcChild.getPath(); + String dstChildPath = AVMNodeConverter.ExtendAVMPath(dstPath, name); + + if (excluder != null && (excluder.matches(srcChildPath) || + excluder.matches(dstChildPath))) { continue; } if (dstChild == null) { // A missing destination child means the source is NEWER. - result.add(new AVMDifference(srcVersion, srcChild.getPath(), - dstVersion, - dstPath, - AVMDifference.NEWER)); + result.add(new AVMDifference(srcVersion, srcChildPath, + dstVersion, dstChildPath, + AVMDifference.NEWER)); continue; } // Otherwise recursively invoke. @@ -259,7 +281,7 @@ public class AVMSyncServiceImpl implements AVMSyncService } // Second special case. Just as above but reversed. if (dstDesc.isLayeredDirectory() && - dstDesc.getIndirection().equals(srcDesc.getPath()) && srcVersion < 0 && dstVersion < 0) + dstDesc.getIndirection().equalsIgnoreCase(srcPath) && srcVersion < 0 && dstVersion < 0) { // skip firstLevel (root) if (! firstLevel) @@ -272,8 +294,8 @@ public class AVMSyncServiceImpl implements AVMSyncService case AVMDifference.NEWER : case AVMDifference.CONFLICT : { - result.add(new AVMDifference(srcVersion, srcDesc.getPath(), - dstVersion, dstDesc.getPath(), + result.add(new AVMDifference(srcVersion, srcPath, + dstVersion, dstPath, dirDiffCode)); return; // short circuit } @@ -303,18 +325,20 @@ public class AVMSyncServiceImpl implements AVMSyncService { 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()))) + + String srcChildPath = AVMNodeConverter.ExtendAVMPath(srcPath, name); + String dstChildPath = dstChild.getPath(); + + if (excluder != null && (excluder.matches(srcChildPath) || + excluder.matches(dstChildPath))) { continue; } if (srcChild == null) { // Missing means the source is older. - result.add(new AVMDifference(srcVersion, - srcPath, - dstVersion, dstChild.getPath(), + result.add(new AVMDifference(srcVersion, srcChildPath, + dstVersion, dstChildPath, AVMDifference.OLDER)); continue; } @@ -335,18 +359,20 @@ public class AVMSyncServiceImpl implements AVMSyncService { 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))) + + String srcChildPath = srcChild.getPath(); + String dstChildPath = AVMNodeConverter.ExtendAVMPath(dstPath, name); + + if (excluder != null && (excluder.matches(srcChildPath) || + excluder.matches(dstChildPath))) { continue; } if (dstChild == null) { // Not found in the destination means NEWER. - result.add(new AVMDifference(srcVersion, srcChild.getPath(), - dstVersion, - dstPath, + result.add(new AVMDifference(srcVersion, srcChildPath, + dstVersion, dstChildPath, AVMDifference.NEWER)); continue; } @@ -362,17 +388,20 @@ public class AVMSyncServiceImpl implements AVMSyncService { continue; } + AVMNodeDescriptor dstChild = dstList.get(name); - String srcPath = AVMNodeConverter.ExtendAVMPath(srcDesc.getPath(), name); - if (excluder != null && (excluder.matches(srcPath) || - excluder.matches(dstChild.getPath()))) + + String srcChildPath = AVMNodeConverter.ExtendAVMPath(srcPath, name); + String dstChildPath = dstChild.getPath(); + + if (excluder != null && (excluder.matches(srcChildPath) || + excluder.matches(dstChildPath))) { continue; } // An entry not found in the source is OLDER. - result.add(new AVMDifference(srcVersion, - srcPath, - dstVersion, dstChild.getPath(), + result.add(new AVMDifference(srcVersion, srcChildPath, + dstVersion, dstChildPath, AVMDifference.OLDER)); } break; diff --git a/source/java/org/alfresco/repo/avm/ChildEntryDAO.java b/source/java/org/alfresco/repo/avm/ChildEntryDAO.java index 3948c8a434..7d0de0aef7 100644 --- a/source/java/org/alfresco/repo/avm/ChildEntryDAO.java +++ b/source/java/org/alfresco/repo/avm/ChildEntryDAO.java @@ -64,10 +64,10 @@ public interface ChildEntryDAO public List getByChild(AVMNode child); /** - * Update a dirty ChildEntry. - * @param child The dirty entry. + * Rename a child entry (specific rename 'case' only) + * @param child The one to rename. */ - public void update(ChildEntry child); + public void rename(ChildKey key, String newName); /** * Delete one. diff --git a/source/java/org/alfresco/repo/avm/DirectoryNode.java b/source/java/org/alfresco/repo/avm/DirectoryNode.java index 736605e0d5..2d51d7be3d 100644 --- a/source/java/org/alfresco/repo/avm/DirectoryNode.java +++ b/source/java/org/alfresco/repo/avm/DirectoryNode.java @@ -52,6 +52,14 @@ public interface DirectoryNode extends AVMNode */ public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted); + /** + * Lookup a child entry. + * @param lPath The Lookup so far. + * @param name The name of the child to lookup. + * @param includeDeleted Include deleted nodes or not. + */ + public Pair lookupChildEntry(Lookup lPath, String name, boolean includeDeleted); + /** * Lookup a child node using an AVMNodeDescriptor as context. * @param mine The node descriptor for this. diff --git a/source/java/org/alfresco/repo/avm/DirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/DirectoryNodeImpl.java index 9eef8019bd..416aa04d9b 100644 --- a/source/java/org/alfresco/repo/avm/DirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/DirectoryNodeImpl.java @@ -21,6 +21,7 @@ package org.alfresco.repo.avm; import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avm.AVMNotFoundException; +import org.alfresco.util.Pair; /** * Base class for Directories. @@ -82,4 +83,21 @@ public abstract class DirectoryNodeImpl extends AVMNodeImpl implements Directory { return AVMDAOs.Instance().fChildEntryDAO.existsParentChild(this, node); } + + /** + * Lookup a child node by name. + * @param lPath The lookup path so far. + * @param name The name to lookup. + * @param includeDeleted Whether to lookup deleted nodes. + * @return The child node or null. + */ + public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted) + { + Pair result = lookupChildEntry(lPath, name, includeDeleted); + if (result == null) + { + return null; + } + return new Pair(result.getFirst().getChild(), result.getSecond()); + } } diff --git a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java index a3db9b6ba4..b846086a54 100644 --- a/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/LayeredDirectoryNodeImpl.java @@ -444,10 +444,12 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer */ public Map getListing(Lookup lPath, String childNamePattern, boolean includeDeleted) { - // Get the base listing from the thing we indirect to. Map listing = new HashMap(); + Map baseLowerKeyName = new HashMap(); + if (!getOpacity()) { + // If we are not opaque, get the underlying base listing (from the thing we indirect to) Lookup lookup = AVMRepository.GetInstance().lookupDirectory(getUnderlyingVersion(lPath), getUnderlying(lPath)); if (lookup != null) { @@ -464,6 +466,7 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer } } listing.put(entry.getKey(), entry.getValue()); + baseLowerKeyName.put(entry.getKey().toLowerCase(), entry.getKey()); } } } @@ -483,6 +486,13 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer } else { + String keyName = baseLowerKeyName.get(entry.getKey().getName().toLowerCase()); + if (keyName != null) + { + // specific rename 'case' only + listing.remove(keyName); + } + listing.put(entry.getKey().getName(), entry.getChild()); } } @@ -584,16 +594,18 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer { throw new AVMBadArgumentException("Illegal null argument."); } - SortedMap baseListing = new TreeMap(String.CASE_INSENSITIVE_ORDER); - // If we are not opaque, get the underlying base listing. + SortedMap listing = new TreeMap(String.CASE_INSENSITIVE_ORDER); + Map baseLowerKeyName = new HashMap(); + if (!getOpacity()) { + // If we are not opaque, get the underlying base listing (from the thing we indirect to) Lookup lookup = AVMRepository.GetInstance().lookupDirectory(dir.getIndirectionVersion(), dir.getIndirection()); if (lookup != null) { DirectoryNode dirNode = (DirectoryNode) lookup.getCurrentNode(); - Map listing = dirNode.getListing(lookup, childNamePattern, includeDeleted); - for (Map.Entry entry : listing.entrySet()) + Map underListing = dirNode.getListing(lookup, childNamePattern, includeDeleted); + for (Map.Entry entry : underListing.entrySet()) { if (entry.getValue().getType() == AVMNodeType.LAYERED_DIRECTORY || entry.getValue().getType() == AVMNodeType.PLAIN_DIRECTORY) @@ -603,37 +615,45 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer continue; } } - baseListing.put(entry.getKey(), + listing.put(entry.getKey(), entry.getValue().getDescriptor(dir.getPath(), entry.getKey(), lookup.getCurrentIndirection(), lookup.getCurrentIndirectionVersion())); + + baseLowerKeyName.put(entry.getKey().toLowerCase(), entry.getKey()); } } } - List children = AVMDAOs.Instance().fChildEntryDAO.getByParent(this, childNamePattern); - for (ChildEntry child : children) + for (ChildEntry entry : AVMDAOs.Instance().fChildEntryDAO.getByParent(this, childNamePattern)) { - if (child.getChild().getType() == AVMNodeType.LAYERED_DIRECTORY || - child.getChild().getType() == AVMNodeType.PLAIN_DIRECTORY) + if (entry.getChild().getType() == AVMNodeType.LAYERED_DIRECTORY || + entry.getChild().getType() == AVMNodeType.PLAIN_DIRECTORY) { - if (!AVMRepository.GetInstance().can(null, child.getChild(), PermissionService.READ_CHILDREN, false)) + if (!AVMRepository.GetInstance().can(null, entry.getChild(), PermissionService.READ_CHILDREN, false)) { continue; } } - if (!includeDeleted && child.getChild().getType() == AVMNodeType.DELETED_NODE) + if (!includeDeleted && entry.getChild().getType() == AVMNodeType.DELETED_NODE) { - baseListing.remove(child.getKey().getName()); + listing.remove(entry.getKey().getName()); } else { - baseListing.put(child.getKey().getName(), child.getChild() - .getDescriptor(dir.getPath(), child.getKey().getName(), dir.getIndirection(), dir.getIndirectionVersion())); + String keyName = baseLowerKeyName.get(entry.getKey().getName().toLowerCase()); + if (keyName != null) + { + // specific rename 'case' only + listing.remove(keyName); + } + + listing.put(entry.getKey().getName(), entry.getChild() + .getDescriptor(dir.getPath(), entry.getKey().getName(), dir.getIndirection(), dir.getIndirectionVersion())); } } - return baseListing; + return listing; } - + /** * Get the names of nodes deleted in this directory. * @@ -652,9 +672,9 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer } return listing; } - + /** - * Lookup a child by name. + * Lookup a child entry by name. * * @param lPath * The Lookup. @@ -664,9 +684,9 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer * The version in which we are looking. * @param write * Whether this lookup is occurring in a write context. - * @return The child or null if not found. + * @return The child entry or null if not found. */ - public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted) + public Pair lookupChildEntry(Lookup lPath, String name, boolean includeDeleted) { ChildKey key = new ChildKey(this, name); ChildEntry entry = AVMDAOs.Instance().fChildEntryDAO.get(key); @@ -676,7 +696,7 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer { return null; } - Pair result = new Pair(entry.getChild(), true); + Pair result = new Pair(entry, true); return result; } // Don't check our underlying directory if we are opaque. @@ -689,7 +709,7 @@ public class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements Layer if (lookup != null) { DirectoryNode dir = (DirectoryNode) lookup.getCurrentNode(); - Pair retVal = dir.lookupChild(lookup, name, includeDeleted); + Pair retVal = dir.lookupChildEntry(lookup, name, includeDeleted); if (retVal != null) { retVal.setSecond(false); diff --git a/source/java/org/alfresco/repo/avm/MultiTAVMService.java b/source/java/org/alfresco/repo/avm/MultiTAVMService.java index cce9ce6087..b8937b63fa 100644 --- a/source/java/org/alfresco/repo/avm/MultiTAVMService.java +++ b/source/java/org/alfresco/repo/avm/MultiTAVMService.java @@ -982,6 +982,7 @@ public class MultiTAVMService implements AVMService } return new AVMStoreDescriptor( + store.getId(), getBaseStoreName(store.getName()), store.getCreator(), store.getCreateDate()); diff --git a/source/java/org/alfresco/repo/avm/NOOPLookupCache.java b/source/java/org/alfresco/repo/avm/NOOPLookupCache.java index 03478b7004..1326ff2375 100644 --- a/source/java/org/alfresco/repo/avm/NOOPLookupCache.java +++ b/source/java/org/alfresco/repo/avm/NOOPLookupCache.java @@ -18,7 +18,6 @@ */ package org.alfresco.repo.avm; -import org.alfresco.repo.avm.util.AVMUtil; import org.alfresco.repo.avm.util.SimplePath; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.security.PermissionService; @@ -91,20 +90,22 @@ public class NOOPLookupCache implements LookupCache { throw new AccessDeniedException("Not allowed to read children: " + path.get(i) + " ("+store.getName()+")"); } - Pair child = dir.lookupChild(result, path.get(i), includeDeleted); - if (child == null) + Pair childEntryResult = dir.lookupChildEntry(result, path.get(i), includeDeleted); + if (childEntryResult == null) { return null; } + AVMNode child = childEntryResult.getFirst().getChild(); // 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) + if (child.getType() != AVMNodeType.PLAIN_DIRECTORY && + child.getType() != AVMNodeType.LAYERED_DIRECTORY) { return null; } - prevDir = (DirectoryNode)child.getFirst(); - result.add(child.getFirst(), path.get(i), child.getSecond(), write); + prevDir = (DirectoryNode)child; + String lookupPathElementName = childEntryResult.getFirst().getKey().getName(); + result.add(child, lookupPathElementName, childEntryResult.getSecond(), write); dir = (DirectoryNode)result.getCurrentNode(); } // Now look up the last element. @@ -112,9 +113,8 @@ public class NOOPLookupCache implements LookupCache { throw new AccessDeniedException("Not allowed to read children: " + path.get(path.size() - 1) + " ("+store.getName()+")"); } - Pair child = dir.lookupChild(result, path.get(path.size() - 1), - includeDeleted); - if (child == null) + Pair childEntryResult = dir.lookupChildEntry(result, path.get(path.size() - 1), includeDeleted); + if (childEntryResult == null) { if (write && (dir.getType() == AVMNodeType.LAYERED_DIRECTORY)) { @@ -127,7 +127,7 @@ public class NOOPLookupCache implements LookupCache { return null; } - child = new Pair(AVMNodeUnwrapper.Unwrap(entry.getChild()), true); + childEntryResult = new Pair(entry, true); } else { @@ -139,7 +139,9 @@ public class NOOPLookupCache implements LookupCache return null; } } - result.add(child.getFirst(), path.get(path.size() - 1), child.getSecond(), write); + AVMNode child = childEntryResult.getFirst().getChild(); + String lookupPathElementName = childEntryResult.getFirst().getKey().getName(); + result.add(child, lookupPathElementName, childEntryResult.getSecond(), write); return result; } diff --git a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java index b1cfde3b5f..d9079cceb4 100644 --- a/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/PlainDirectoryNodeImpl.java @@ -203,15 +203,15 @@ public class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDi { return new ArrayList(); } - + /** - * Lookup a child by name. + * Lookup a child entry by name. * @param lPath The lookup path so far. * @param name The name to lookup. * @param includeDeleted Whether to lookup deleted nodes. - * @return The child or null. + * @return The child entry or null. */ - public Pair lookupChild(Lookup lPath, String name, boolean includeDeleted) + public Pair lookupChildEntry(Lookup lPath, String name, boolean includeDeleted) { ChildKey key = new ChildKey(this, name); ChildEntry entry = AVMDAOs.Instance().fChildEntryDAO.get(key); @@ -223,7 +223,7 @@ public class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDi { return null; } - Pair result = new Pair(entry.getChild(), true); + Pair result = new Pair(entry, true); return result; } diff --git a/source/java/org/alfresco/repo/avm/ibatis/ChildEntryDAOIbatis.java b/source/java/org/alfresco/repo/avm/ibatis/ChildEntryDAOIbatis.java index 753917e758..5a2a61296d 100644 --- a/source/java/org/alfresco/repo/avm/ibatis/ChildEntryDAOIbatis.java +++ b/source/java/org/alfresco/repo/avm/ibatis/ChildEntryDAOIbatis.java @@ -21,6 +21,7 @@ package org.alfresco.repo.avm.ibatis; import java.util.ArrayList; import java.util.List; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.avm.AVMDAOs; import org.alfresco.repo.avm.AVMNode; import org.alfresco.repo.avm.ChildEntry; @@ -98,11 +99,21 @@ class ChildEntryDAOIbatis implements ChildEntryDAO } /* (non-Javadoc) - * @see org.alfresco.repo.avm.ChildEntryDAO#update(org.alfresco.repo.avm.ChildEntry) + * @see org.alfresco.repo.avm.ChildEntryDAO#rename(org.alfresco.repo.avm.ChildKey, String) */ - public void update(ChildEntry child) + public void rename(ChildKey key, String newName) { - // NOOP + // direct rename should only be used if changing case + if (! key.getName().equalsIgnoreCase(newName)) + { + throw new AlfrescoRuntimeException("Invalid rename (can only change case"); + } + + AVMChildEntryEntity childEntryEntity = AVMDAOs.Instance().newAVMNodeLinksDAO.getChildEntry(key.getParent().getId(), key.getName()); + + childEntryEntity.setName(newName); + + AVMDAOs.Instance().newAVMNodeLinksDAO.updateChildEntry(childEntryEntity); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/domain/avm/AVMNodeLinksDAO.java b/source/java/org/alfresco/repo/domain/avm/AVMNodeLinksDAO.java index 5d48ae8313..be21fcdab9 100644 --- a/source/java/org/alfresco/repo/domain/avm/AVMNodeLinksDAO.java +++ b/source/java/org/alfresco/repo/domain/avm/AVMNodeLinksDAO.java @@ -68,6 +68,11 @@ public interface AVMNodeLinksDAO */ public AVMChildEntryEntity getChildEntry(long parentNodeId, long childNodeId); + /** + * Specific rename 'case' only + */ + public void updateChildEntry(AVMChildEntryEntity childEntryEntity); + /** * Delete one */ diff --git a/source/java/org/alfresco/repo/domain/avm/AbstractAVMNodeLinksDAOImpl.java b/source/java/org/alfresco/repo/domain/avm/AbstractAVMNodeLinksDAOImpl.java index c7c1ae8fb7..cd7cfa1c6d 100644 --- a/source/java/org/alfresco/repo/domain/avm/AbstractAVMNodeLinksDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/avm/AbstractAVMNodeLinksDAOImpl.java @@ -191,6 +191,24 @@ public abstract class AbstractAVMNodeLinksDAOImpl implements AVMNodeLinksDAO return result; } + /** + * {@inheritDoc} + */ + public void updateChildEntry(AVMChildEntryEntity childEntryEntity) + { + ParameterCheck.mandatory("childEntryEntity", childEntryEntity); + ParameterCheck.mandatory("childEntryEntity.getParentNodeId()", childEntryEntity.getParentNodeId()); + ParameterCheck.mandatory("childEntryEntity.getChildId()", childEntryEntity.getChildId()); + ParameterCheck.mandatory("childEntryEntity.getName()", childEntryEntity.getName()); + + ChildKey key = new ChildKey(childEntryEntity.getParentNodeId(), childEntryEntity.getName()); + int updated = avmChildEntryCache.updateValue(key, childEntryEntity); + if (updated < 1) + { + throw new ConcurrencyFailureException("AVMChildEntry for parent/name (" + key.getParentNodeId() + ", " + key.getName() + ") no longer exists"); + } + } + /** * {@inheritDoc} */ @@ -344,7 +362,7 @@ public abstract class AbstractAVMNodeLinksDAOImpl implements AVMNodeLinksDAO public int updateValue(ChildKey key, AVMChildEntryEntity value) { - throw new UnsupportedOperationException("updateValue(Long, AVMChildEntryEntity"); + return updateChildEntryEntity(value); } public int deleteByKey(ChildKey key) @@ -367,6 +385,9 @@ public abstract class AbstractAVMNodeLinksDAOImpl implements AVMNodeLinksDAO protected abstract AVMChildEntryEntity getChildEntryEntity(AVMChildEntryEntity childEntryEntity); protected abstract void createChildEntryEntity(AVMChildEntryEntity childEntryEntity); + + protected abstract int updateChildEntryEntity(AVMChildEntryEntity childEntryEntity); // specific rename 'case' only + protected abstract int deleteChildEntryEntity(long parentNodeId, String name); protected abstract int deleteChildEntryEntity(long parentNodeId, long childNodeId); protected abstract int deleteChildEntryEntities(long parentNodeId); diff --git a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMNodeLinksDAOImpl.java b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMNodeLinksDAOImpl.java index e81f94d634..6638cbc2f3 100644 --- a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMNodeLinksDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMNodeLinksDAOImpl.java @@ -22,9 +22,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.alfresco.repo.domain.avm.AVMChildEntryEntity; import org.alfresco.repo.domain.avm.AVMHistoryLinkEntity; import org.alfresco.repo.domain.avm.AVMMergeLinkEntity; -import org.alfresco.repo.domain.avm.AVMChildEntryEntity; import org.alfresco.repo.domain.avm.AbstractAVMNodeLinksDAOImpl; import org.springframework.orm.ibatis.SqlMapClientTemplate; @@ -37,15 +37,26 @@ import org.springframework.orm.ibatis.SqlMapClientTemplate; public class AVMNodeLinksDAOImpl extends AbstractAVMNodeLinksDAOImpl { private static final String SELECT_AVM_NODE_CHILD_ENTRY ="alfresco.avm.select_AVMChildEntry"; // parent + name + child + private static final String SELECT_AVM_NODE_CHILD_ENTRY_L ="alfresco.avm.select_AVMChildEntryL"; // parent + lower(name) + child + private static final String SELECT_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME ="alfresco.avm.select_AVMChildEntryByParentAndName"; // parent + name + private static final String SELECT_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME_L ="alfresco.avm.select_AVMChildEntryByParentAndNameL"; // parent + lower(name) + private static final String SELECT_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_CHILD ="alfresco.avm.select_AVMChildEntryByParentAndChild"; // parent + child private static final String SELECT_AVM_NODE_CHILD_ENTRIES_BY_PARENT ="alfresco.avm.select_AVMNodeChildEntriesByParent"; // parent + private static final String SELECT_AVM_NODE_CHILD_ENTRIES_BY_PARENT_AND_NAME_PATTERN ="alfresco.avm.select_AVMNodeChildEntriesByParentAndNamePattern"; // parent + name pattern + private static final String SELECT_AVM_NODE_CHILD_ENTRIES_BY_PARENT_AND_NAME_PATTERN_L ="alfresco.avm.select_AVMNodeChildEntriesByParentAndNamePatternL"; // parent + lower(name pattern) + private static final String SELECT_AVM_NODE_CHILD_ENTRIES_BY_CHILD ="alfresco.avm.select_AVMNodeChildEntriesByChild"; // child private static final String INSERT_AVM_NODE_CHILD_ENTRY ="alfresco.avm.insert_AVMChildEntry"; // parent + name + child + private static final String UPDATE_AVM_NODE_CHILD_ENTRY ="alfresco.avm.update_AVMChildEntry"; // parent + child (update name) + private static final String DELETE_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME ="alfresco.avm.delete_AVMChildEntryByParentAndName"; // parent + name + private static final String DELETE_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME_L ="alfresco.avm.delete_AVMChildEntryByParentAndNameL"; // parent + lower(name) + private static final String DELETE_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_CHILD ="alfresco.avm.delete_AVMChildEntryByParentAndChild"; // parent + child private static final String DELETE_AVM_NODE_CHILD_ENTRIES_BY_PARENT ="alfresco.avm.delete_AVMNodeChildEntriesByParent"; // parent @@ -64,14 +75,27 @@ public class AVMNodeLinksDAOImpl extends AbstractAVMNodeLinksDAOImpl private SqlMapClientTemplate template; + // Initial generic fix for ALF-1940 (pending SAIL-349) + // Note: in order to override to false DB must be setup to be case-insensitive (at least on column avm_child_entries.name) + private boolean toLower = true; + public void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) { this.template = sqlMapClientTemplate; } + public void setToLower(boolean toLower) + { + this.toLower = toLower; + } + @Override protected AVMChildEntryEntity getChildEntryEntity(AVMChildEntryEntity childEntryEntity) { + if (toLower) + { + return (AVMChildEntryEntity) template.queryForObject(SELECT_AVM_NODE_CHILD_ENTRY_L, childEntryEntity); + } return (AVMChildEntryEntity) template.queryForObject(SELECT_AVM_NODE_CHILD_ENTRY, childEntryEntity); } @@ -79,6 +103,11 @@ public class AVMNodeLinksDAOImpl extends AbstractAVMNodeLinksDAOImpl protected AVMChildEntryEntity getChildEntryEntity(long parentNodeId, String name) { AVMChildEntryEntity childEntryEntity = new AVMChildEntryEntity(parentNodeId, name); + + if (toLower) + { + return (AVMChildEntryEntity) template.queryForObject(SELECT_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME_L, childEntryEntity); + } return (AVMChildEntryEntity) template.queryForObject(SELECT_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME, childEntryEntity); } @@ -105,6 +134,11 @@ public class AVMNodeLinksDAOImpl extends AbstractAVMNodeLinksDAOImpl Map params = new HashMap(1); params.put("id", parentNodeId); params.put("pattern", childNamePattern); + + if (toLower) + { + return (List) template.queryForList(SELECT_AVM_NODE_CHILD_ENTRIES_BY_PARENT_AND_NAME_PATTERN_L, params); + } return (List) template.queryForList(SELECT_AVM_NODE_CHILD_ENTRIES_BY_PARENT_AND_NAME_PATTERN, params); } @@ -123,10 +157,24 @@ public class AVMNodeLinksDAOImpl extends AbstractAVMNodeLinksDAOImpl template.insert(INSERT_AVM_NODE_CHILD_ENTRY, childEntryEntity); } + @Override + protected int updateChildEntryEntity(AVMChildEntryEntity childEntryEntity) + { + // TODO: concurrency control - note: specific rename 'case' only + //childEntryEntity.incrementVers(); + + return template.update(UPDATE_AVM_NODE_CHILD_ENTRY, childEntryEntity); + } + @Override protected int deleteChildEntryEntity(long parentNodeId, String name) { AVMChildEntryEntity childEntryEntity = new AVMChildEntryEntity(parentNodeId, name); + + if (toLower) + { + return template.delete(DELETE_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME_L, childEntryEntity); + } return template.delete(DELETE_AVM_NODE_CHILD_ENTRY_BY_PARENT_AND_NAME, childEntryEntity); } diff --git a/source/java/org/alfresco/service/cmr/avm/AVMStoreDescriptor.java b/source/java/org/alfresco/service/cmr/avm/AVMStoreDescriptor.java index 391e7127f8..61f27426d4 100644 --- a/source/java/org/alfresco/service/cmr/avm/AVMStoreDescriptor.java +++ b/source/java/org/alfresco/service/cmr/avm/AVMStoreDescriptor.java @@ -30,7 +30,12 @@ import org.springframework.extensions.surf.util.ISO8601DateFormat; public class AVMStoreDescriptor implements Serializable { private static final long serialVersionUID = -4401863082685362175L; - + + /** + * The object id. + */ + private long fID; + /** * The name. */ @@ -46,15 +51,16 @@ public class AVMStoreDescriptor implements Serializable */ private long fCreateDate; - public AVMStoreDescriptor(String name, - String creator, - long createDate) + public AVMStoreDescriptor(long id, + String name, + String creator, + long createDate) { fName = name; fCreator = creator; fCreateDate = createDate; } - + /** * @return the fCreateDate */ @@ -62,7 +68,7 @@ public class AVMStoreDescriptor implements Serializable { return fCreateDate; } - + /** * @return the fCreator */ @@ -70,7 +76,7 @@ public class AVMStoreDescriptor implements Serializable { return fCreator; } - + /** * @return the fName */ @@ -79,8 +85,37 @@ public class AVMStoreDescriptor implements Serializable return fName; } + /** + * Get the object id. + * @return The object id. + */ + public long getId() + { + return fID; + } + public String toString() { - return "[" + fName + ":" + fCreator + ":" + ISO8601DateFormat.format(new Date(fCreateDate)) + "]"; + return "[" + fID + ":"+fName + ":" + fCreator + ":" + ISO8601DateFormat.format(new Date(fCreateDate)) + "]"; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof AVMStoreDescriptor)) + { + return false; + } + return fID == ((AVMStoreDescriptor)obj).fID; + } + + @Override + public int hashCode() + { + return (int)fID; } }