diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index 0776ff557b..9382e98557 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -633,6 +633,9 @@
${lucene.indexer.mergerMergeFactor}
+
+ ${lucene.indexer.mergerMergeBlockingFactor}
+
${lucene.indexer.mergerMinMergeDocs}
@@ -653,6 +656,9 @@
${lucene.indexer.mergerTargetOverlayCount}
+
+ ${lucene.indexer.mergerTargetOverlaysBlockingFactor}
+
${lucene.indexer.maxDocsForInMemoryMerge}
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index 910d8fa4cf..027f41e83d 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -173,6 +173,7 @@ lucene.indexer.maxTypeCacheSize=10000
#
lucene.indexer.mergerMaxMergeDocs=1000000
lucene.indexer.mergerMergeFactor=5
+lucene.indexer.mergerMergeBlockingFactor=1
lucene.indexer.mergerMinMergeDocs=1000
#
# Properties for delta indexes (not this does not affect the final index segment which will be optimised)
@@ -186,6 +187,7 @@ lucene.indexer.writerMinMergeDocs=1000
#
lucene.indexer.mergerTargetIndexCount=5
lucene.indexer.mergerTargetOverlayCount=5
+lucene.indexer.mergerTargetOverlaysBlockingFactor=2
lucene.indexer.maxDocsForInMemoryMerge=10000
#
# Other lucene properties
diff --git a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java
index cd2bb0d459..0b945663b4 100644
--- a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java
+++ b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java
@@ -231,12 +231,23 @@ public class ConfigurationChecker extends AbstractLifecycleBean
{
if (storeRef.getProtocol().equals(StoreRef.PROTOCOL_AVM))
{
- IndexMode storeIndexMode = avmSnapShotTriggeredIndexingMethodInterceptor.getIndexMode(storeRef.getIdentifier());
- if (storeIndexMode.equals(IndexMode.UNINDEXED))
+ if (avmSnapShotTriggeredIndexingMethodInterceptor.isIndexingEnabled())
+ {
+ IndexMode storeIndexMode = avmSnapShotTriggeredIndexingMethodInterceptor.getIndexMode(storeRef.getIdentifier());
+ if (storeIndexMode.equals(IndexMode.UNINDEXED))
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Skipping index check for store: " + storeRef + " (unindexed AVM store)");
+ }
+ continue;
+ }
+ }
+ else
{
if (logger.isDebugEnabled())
{
- logger.debug("Skipping index for store: " + storeRef + " (unindexed AVM store)");
+ logger.debug("Skipping index check for store: " + storeRef + " (AVM indexing is disabled)");
}
continue;
}
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
index 723eacf110..c0efad2cf7 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
@@ -64,6 +64,7 @@ import org.alfresco.service.cmr.search.QueryParameterDefinition;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
import org.alfresco.util.SearchLanguageConversion;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -571,23 +572,18 @@ public class FileFolderServiceImpl implements FileFolderService
targetParentRef = assocRef.getParentRef();
}
+ boolean changedParent = !targetParentRef.equals(assocRef.getParentRef());
// there is nothing to do if both the name and parent folder haven't changed
- if (targetParentRef.equals(assocRef.getParentRef()))
+ if (!nameChanged && !changedParent)
{
- if (newName.equals(beforeFileInfo.getName()))
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Doing nothing - neither filename or parent has changed: \n" +
- " parent: " + targetParentRef + "\n" +
- " before: " + beforeFileInfo + "\n" +
- " new name: " + newName);
- }
- return beforeFileInfo;
- }
- else if (newName.equalsIgnoreCase(beforeFileInfo.getName()))
+ if (logger.isDebugEnabled())
{
+ logger.debug("Doing nothing - neither filename or parent has changed: \n" +
+ " parent: " + targetParentRef + "\n" +
+ " before: " + beforeFileInfo + "\n" +
+ " new name: " + newName);
}
+ return beforeFileInfo;
}
QName existingQName = assocRef.getQName();
@@ -629,13 +625,29 @@ public class FileFolderServiceImpl implements FileFolderService
// TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
if (!isSystemPath(sourceNodeRef))
{
- // move the node so that the association moves as well
- ChildAssociationRef newAssocRef = nodeService.moveNode(
- sourceNodeRef,
- targetParentRef,
- assocTypeQname,
- qname);
- targetNodeRef = newAssocRef.getChildRef();
+ // The cm:name might clash with another node in the target location.
+ if (nameChanged)
+ {
+ // The name will be changing, so we really need to set the node's name to the new
+ // name. This can't be done at the same time as the move - to avoid incorrect violations
+ // of the name constraints, the cm:name is set to something random and will be reset
+ // to the correct name later.
+ nodeService.setProperty(sourceNodeRef, ContentModel.PROP_NAME, GUID.generate());
+ }
+ try
+ {
+ // move the node so that the association moves as well
+ ChildAssociationRef newAssocRef = nodeService.moveNode(
+ sourceNodeRef,
+ targetParentRef,
+ assocTypeQname,
+ qname);
+ targetNodeRef = newAssocRef.getChildRef();
+ }
+ catch (DuplicateChildNodeNameException e)
+ {
+ throw new FileExistsException(targetParentRef, newName);
+ }
}
else
{
@@ -647,7 +659,7 @@ public class FileFolderServiceImpl implements FileFolderService
{
try
{
- // copy the node
+ // Copy the node. The cm:name will be dropped and reset later.
targetNodeRef = copyService.copy(
sourceNodeRef,
targetParentRef,
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
index 90869cdb72..d32d4c657d 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
@@ -64,13 +64,12 @@ import org.springframework.context.ApplicationContext;
/**
* @see org.alfresco.repo.model.filefolder.FileFolderServiceImpl
- *
* @author Derek Hulley
*/
public class FileFolderServiceImplTest extends TestCase
{
private static final String IMPORT_VIEW = "filefolder/filefolder-test-import.xml";
-
+
private static final String NAME_L0_FILE_A = "L0- File A";
private static final String NAME_L0_FILE_B = "L0- File B";
private static final String NAME_L0_FOLDER_A = "L0- Folder A";
@@ -83,7 +82,7 @@ public class FileFolderServiceImplTest extends TestCase
private static final String NAME_L1_FILE_C = "L1- File C (%_)";
private static final String NAME_CHECK_FILE = "CHECK_FILE";
private static final String NAME_CHECK_FOLDER = "CHECK_FOLDER";
-
+
private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
@@ -93,7 +92,7 @@ public class FileFolderServiceImplTest extends TestCase
private UserTransaction txn;
private NodeRef rootNodeRef;
private NodeRef workingRootNodeRef;
-
+
@Override
public void setUp() throws Exception
{
@@ -102,29 +101,29 @@ public class FileFolderServiceImplTest extends TestCase
nodeService = serviceRegistry.getNodeService();
fileFolderService = serviceRegistry.getFileFolderService();
dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
- AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent");
+ AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx
+ .getBean("authenticationComponent");
// start the transaction
txn = transactionService.getUserTransaction();
txn.begin();
-
+
// downgrade integrity
IntegrityChecker.setWarnInTransaction();
-
+
// authenticate
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
-
+
// create a test store
- StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + System.currentTimeMillis());
+ StoreRef storeRef = nodeService
+ .createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + System.currentTimeMillis());
rootNodeRef = nodeService.getRootNode(storeRef);
-
+
// create a folder to import into
- workingRootNodeRef = nodeService.createNode(
- rootNodeRef,
- ContentModel.ASSOC_CHILDREN,
- QName.createQName(NamespaceService.ALFRESCO_URI, "working root"),
- ContentModel.TYPE_FOLDER).getChildRef();
-
+ workingRootNodeRef = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN,
+ QName.createQName(NamespaceService.ALFRESCO_URI, "working root"), ContentModel.TYPE_FOLDER)
+ .getChildRef();
+
// import the test data
ImporterService importerService = serviceRegistry.getImporterService();
Location importLocation = new Location(workingRootNodeRef);
@@ -136,7 +135,7 @@ public class FileFolderServiceImplTest extends TestCase
Reader reader = new InputStreamReader(is);
importerService.importView(reader, importLocation, null, null);
}
-
+
public void tearDown() throws Exception
{
try
@@ -157,7 +156,11 @@ public class FileFolderServiceImplTest extends TestCase
* @param expectedFolderCount the number of uniquely named folders expected
* @param expectedNames the names of the files and folders expected
*/
- private void checkFileList(List files, int expectedFileCount, int expectedFolderCount, String[] expectedNames)
+ private void checkFileList(
+ List files,
+ int expectedFileCount,
+ int expectedFolderCount,
+ String[] expectedNames)
{
int fileCount = 0;
int folderCount = 0;
@@ -182,70 +185,61 @@ public class FileFolderServiceImplTest extends TestCase
assertEquals("Incorrect number of files", expectedFileCount, fileCount);
assertEquals("Incorrect number of folders", expectedFolderCount, folderCount);
}
-
+
public void testShallowFilesAndFoldersList() throws Exception
{
List files = fileFolderService.list(workingRootNodeRef);
// check
- String[] expectedNames = new String[] {NAME_L0_FILE_A, NAME_L0_FILE_B, NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C};
+ String[] expectedNames = new String[]
+ { NAME_L0_FILE_A, NAME_L0_FILE_B, NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C };
checkFileList(files, 2, 3, expectedNames);
}
-
+
public void testShallowFilesOnlyList() throws Exception
{
List files = fileFolderService.listFiles(workingRootNodeRef);
// check
- String[] expectedNames = new String[] {NAME_L0_FILE_A, NAME_L0_FILE_B};
+ String[] expectedNames = new String[]
+ { NAME_L0_FILE_A, NAME_L0_FILE_B };
checkFileList(files, 2, 0, expectedNames);
}
-
+
public void testShallowFoldersOnlyList() throws Exception
{
List files = fileFolderService.listFolders(workingRootNodeRef);
// check
- String[] expectedNames = new String[] {NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C};
+ String[] expectedNames = new String[]
+ { NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C };
checkFileList(files, 0, 3, expectedNames);
}
-
+
public void testShallowFileSearch() throws Exception
{
- List files = fileFolderService.search(
- workingRootNodeRef,
- NAME_L0_FILE_B,
- true,
- false,
- false);
+ List files = fileFolderService.search(workingRootNodeRef, NAME_L0_FILE_B, true, false, false);
// check
- String[] expectedNames = new String[] {NAME_L0_FILE_B};
+ String[] expectedNames = new String[]
+ { NAME_L0_FILE_B };
checkFileList(files, 1, 0, expectedNames);
}
-
+
public void testDeepFilesAndFoldersSearch() throws Exception
{
- List files = fileFolderService.search(
- workingRootNodeRef,
- "?1-*",
- true,
- true,
- true);
+ List files = fileFolderService.search(workingRootNodeRef, "?1-*", true, true, true);
// check
- String[] expectedNames = new String[] {NAME_L1_FOLDER_A, NAME_L1_FOLDER_B, NAME_L1_FILE_A, NAME_L1_FILE_B, NAME_L1_FILE_C};
+ String[] expectedNames = new String[]
+ { NAME_L1_FOLDER_A, NAME_L1_FOLDER_B, NAME_L1_FILE_A, NAME_L1_FILE_B, NAME_L1_FILE_C };
checkFileList(files, 3, 2, expectedNames);
}
-
+
public void testDeepFilesOnlySearch() throws Exception
{
- List files = fileFolderService.search(
- workingRootNodeRef,
- "?1-*",
- true,
- false,
- true);
+ List files = fileFolderService.search(workingRootNodeRef, "?1-*", true, false, true);
// check
- String[] expectedNames = new String[] {NAME_L1_FILE_A, NAME_L1_FILE_B, NAME_L1_FILE_C};
+ String[] expectedNames = new String[]
+ { NAME_L1_FILE_A, NAME_L1_FILE_B, NAME_L1_FILE_C };
checkFileList(files, 3, 0, expectedNames);
}
-
+
/**
* Helper to fetch a file or folder by name
*
@@ -258,9 +252,8 @@ public class FileFolderServiceImplTest extends TestCase
List results = fileFolderService.search(workingRootNodeRef, name, !isFolder, isFolder, true);
if (results.size() > 1)
{
- throw new AlfrescoRuntimeException("Name is not unique in hierarchy: \n" +
- " name: " + name + "\n" +
- " is folder: " + isFolder);
+ throw new AlfrescoRuntimeException("Name is not unique in hierarchy: \n" + " name: " + name + "\n"
+ + " is folder: " + isFolder);
}
else if (results.size() == 0)
{
@@ -287,7 +280,7 @@ public class FileFolderServiceImplTest extends TestCase
assertNotNull(fileInfo);
assertFalse(fileInfo.isFolder());
}
-
+
public void testRenameNormal() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
@@ -301,7 +294,7 @@ public class FileFolderServiceImplTest extends TestCase
checkInfo = getByName(newName, true);
assertNotNull("Folder info for new name is not present", checkInfo);
}
-
+
public void testRenameWithoutAssocQNameChange() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
@@ -309,33 +302,25 @@ public class FileFolderServiceImplTest extends TestCase
NodeRef folderNodeRef = folderInfo.getNodeRef();
// Create a child file
QName assocQName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "abc");
- NodeRef newFileNodeRef = fileFolderService.create(
- folderNodeRef,
- "AnotherFile.txt",
- ContentModel.TYPE_CONTENT,
+ NodeRef newFileNodeRef = fileFolderService.create(folderNodeRef, "AnotherFile.txt", ContentModel.TYPE_CONTENT,
assocQName).getNodeRef();
// Make sure that the correct association QName was used
QName checkQName = nodeService.getPrimaryParent(newFileNodeRef).getQName();
- assertEquals(
- "The given assoc QName was not used for the path",
- assocQName,
- checkQName);
+ assertEquals("The given assoc QName was not used for the path", assocQName, checkQName);
// Rename
String newName = "AnotherFile-new.txt";
folderInfo = fileFolderService.rename(newFileNodeRef, newName);
// Make sure that the association QName did not change
checkQName = nodeService.getPrimaryParent(newFileNodeRef).getQName();
- assertEquals(
- "The given assoc QName was not used for the path after a rename",
- assocQName,
- nodeService.getPrimaryParent(newFileNodeRef).getQName());
+ assertEquals("The given assoc QName was not used for the path after a rename", assocQName, nodeService
+ .getPrimaryParent(newFileNodeRef).getQName());
}
-
+
public void testRenameDuplicate() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
assertNotNull(folderInfo);
- // rename duplicate. A file with that name already exists
+ // rename duplicate. A file with that name already exists
String newName = NAME_L0_FILE_A;
try
{
@@ -347,9 +332,15 @@ public class FileFolderServiceImplTest extends TestCase
// expected
}
}
-
+
public void testMove() throws Exception
{
+ // we are testing failures as well
+ txn.commit();
+ // start a new one
+ txn = transactionService.getNonPropagatingUserTransaction();
+ txn.begin();
+
FileInfo folderToMoveInfo = getByName(NAME_L1_FOLDER_A, true);
assertNotNull(folderToMoveInfo);
NodeRef folderToMoveRef = folderToMoveInfo.getNodeRef();
@@ -372,8 +363,34 @@ public class FileFolderServiceImplTest extends TestCase
{
// expected
}
+
+ txn.rollback();
+ txn = transactionService.getNonPropagatingUserTransaction();
+ txn.begin();
+
+ // Move a file to a new location
+ FileInfo fileA = getByName(NAME_L1_FILE_A, false);
+ FileInfo folderB = getByName(NAME_L0_FOLDER_B, true);
+ fileFolderService.copy(fileA.getNodeRef(), folderB.getNodeRef(), null);
+ try
+ {
+ // Move to a target folder without a rename and expecting a name clash
+ fileFolderService.move(fileA.getNodeRef(), folderB.getNodeRef(), null);
+ fail("Duplicately-named file in target folder was not detected");
+ }
+ catch (FileExistsException e)
+ {
+ // Expected
+ }
+
+ txn.rollback();
+ txn = transactionService.getNonPropagatingUserTransaction();
+ txn.begin();
+
+ // Move to a target folder but with a rename to avoid the name clash
+ fileFolderService.move(fileA.getNodeRef(), folderB.getNodeRef(), NAME_L1_FILE_B);
}
-
+
public void testCopy() throws Exception
{
FileInfo folderToCopyInfo = getByName(NAME_L1_FOLDER_A, true);
@@ -400,7 +417,7 @@ public class FileFolderServiceImplTest extends TestCase
// expected
}
}
-
+
public void testCreateFolder() throws Exception
{
// we are testing failures as well
@@ -408,7 +425,7 @@ public class FileFolderServiceImplTest extends TestCase
// start a new one
txn = transactionService.getNonPropagatingUserTransaction();
txn.begin();
-
+
FileInfo parentFolderInfo = getByName(NAME_L0_FOLDER_A, true);
assertNotNull(parentFolderInfo);
NodeRef parentFolderRef = parentFolderInfo.getNodeRef();
@@ -461,7 +478,8 @@ public class FileFolderServiceImplTest extends TestCase
M2Type testType = testModel.createType("t111:subfolder");
testType.setParentName("cm:" + ContentModel.TYPE_FOLDER.getLocalName());
dictionaryDAO.putModel(testModel);
- fileFolderService.create(parentFolderRef, "Legal subtype of folder", QName.createQName(testNs, "subfolder"));
+ fileFolderService
+ .create(parentFolderRef, "Legal subtype of folder", QName.createQName(testNs, "subfolder"));
}
catch (Throwable e)
{
@@ -478,17 +496,17 @@ public class FileFolderServiceImplTest extends TestCase
assertTrue("Node not created", nodeService.exists(fileInfo.getNodeRef()));
assertFalse("File type expected", fileInfo.isFolder());
}
-
+
public void testCreateFile() throws Exception
{
-
+
}
-
+
public void testCreateInRoot() throws Exception
{
fileFolderService.create(rootNodeRef, "New Folder", ContentModel.TYPE_FOLDER);
}
-
+
public void testMakeFolders() throws Exception
{
// create a completely new path below the root
@@ -497,12 +515,14 @@ public class FileFolderServiceImplTest extends TestCase
namePath.add("BBB");
namePath.add("CCC");
namePath.add("DDD");
-
- FileInfo lastFileInfo = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
+
+ FileInfo lastFileInfo = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath,
+ ContentModel.TYPE_FOLDER);
assertNotNull("First makeFolder failed", lastFileInfo);
// check that a repeat works
-
- FileInfo lastFileInfoAgain = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
+
+ FileInfo lastFileInfoAgain = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath,
+ ContentModel.TYPE_FOLDER);
assertNotNull("Repeat makeFolders failed", lastFileInfoAgain);
assertEquals("Repeat created new leaf", lastFileInfo.getNodeRef(), lastFileInfoAgain.getNodeRef());
// check that it worked
@@ -518,7 +538,7 @@ public class FileFolderServiceImplTest extends TestCase
i++;
}
}
-
+
/**
* Lucene only indexes terms that are 3 characters or more
*/
@@ -530,34 +550,36 @@ public class FileFolderServiceImplTest extends TestCase
namePath.add("B");
namePath.add("C");
namePath.add("D");
-
- FileInfo lastFileInfo = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
+
+ FileInfo lastFileInfo = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath,
+ ContentModel.TYPE_FOLDER);
assertNotNull("First makeFolder failed", lastFileInfo);
// check that a repeat works
-
- FileInfo lastFileInfoAgain = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
+
+ FileInfo lastFileInfoAgain = FileFolderServiceImpl.makeFolders(fileFolderService, rootNodeRef, namePath,
+ ContentModel.TYPE_FOLDER);
assertNotNull("Repeat makeFolders failed", lastFileInfoAgain);
assertEquals("Repeat created new leaf", lastFileInfo.getNodeRef(), lastFileInfoAgain.getNodeRef());
}
-
+
public void testGetNamePath() throws Exception
{
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
assertNotNull(fileInfo);
NodeRef nodeRef = fileInfo.getNodeRef();
-
+
List infoPaths = fileFolderService.getNamePath(workingRootNodeRef, nodeRef);
assertEquals("Not enough elements", 2, infoPaths.size());
assertEquals("First level incorrent", NAME_L0_FOLDER_A, infoPaths.get(0).getName());
assertEquals("Second level incorrent", NAME_L1_FILE_A, infoPaths.get(1).getName());
-
+
// pass in a null root and make sure that it still works
infoPaths = fileFolderService.getNamePath(null, nodeRef);
assertEquals("Not enough elements", 3, infoPaths.size());
assertEquals("First level incorrent", workingRootNodeRef.getId(), infoPaths.get(0).getName());
assertEquals("Second level incorrent", NAME_L0_FOLDER_A, infoPaths.get(1).getName());
assertEquals("Third level incorrent", NAME_L1_FILE_A, infoPaths.get(2).getName());
-
+
// check that a non-aligned path is detected
NodeRef startRef = getByName(NAME_L0_FOLDER_B, true).getNodeRef();
try
@@ -570,7 +592,7 @@ public class FileFolderServiceImplTest extends TestCase
// expected
}
}
-
+
public void testSearchSimple() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
@@ -586,28 +608,28 @@ public class FileFolderServiceImplTest extends TestCase
FileInfo checkInfo = getByName(NAME_L1_FILE_A, false);
assertEquals("Incorrect node found", checkInfo.getNodeRef(), fileNodeRef);
}
-
+
public void testResolveNamePath() throws Exception
{
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
List pathElements = new ArrayList(3);
pathElements.add(NAME_L0_FOLDER_A);
pathElements.add(NAME_L1_FILE_A);
-
+
FileInfo fileInfoCheck = fileFolderService.resolveNamePath(workingRootNodeRef, pathElements);
assertNotNull("File info not found", fileInfoCheck);
assertEquals("Path not resolved to correct node", fileInfo.getNodeRef(), fileInfoCheck.getNodeRef());
}
-
+
public void testGetReaderWriter() throws Exception
{
// testing a failure
txn.commit();
txn = transactionService.getUserTransaction();
txn.begin();
-
+
FileInfo dirInfo = getByName(NAME_L0_FOLDER_A, true);
-
+
UserTransaction rollbackTxn = null;
try
{
@@ -624,9 +646,9 @@ public class FileFolderServiceImplTest extends TestCase
{
rollbackTxn.rollback();
}
-
+
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
-
+
ContentWriter writer = fileFolderService.getWriter(fileInfo.getNodeRef());
assertNotNull("Writer is null", writer);
// write some content
@@ -638,23 +660,22 @@ public class FileFolderServiceImplTest extends TestCase
String checkContent = reader.getContentString();
assertEquals("Content mismatch", content, checkContent);
}
-
+
public void testLongFileNames() throws Exception
{
- String fileName =
- "12345678901234567890123456789012345678901234567890" +
- "12345678901234567890123456789012345678901234567890" +
- "12345678901234567890123456789012345678901234567890" +
- "12345678901234567890123456789012345678901234567890" +
- "12345678901234567890123456789012345678901234567890" +
- "12345678901234567890123456789012345678901234567890";
+ String fileName = "12345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890"
+ + "12345678901234567890123456789012345678901234567890";
FileInfo fileInfo = fileFolderService.create(workingRootNodeRef, fileName, ContentModel.TYPE_CONTENT);
// see if we can get it again
NodeRef fileNodeRef = fileFolderService.searchSimple(workingRootNodeRef, fileName);
assertNotNull("Long filename not found", fileNodeRef);
assertEquals(fileInfo.getNodeRef(), fileNodeRef);
}
-
+
/**
* Validates ACT-7225
*/
diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
index 56b435dfe8..ca70194c75 100644
--- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java
@@ -184,7 +184,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
static
{
DUPLICATE_CHILD_NAME_EXCEPTIONS = new Class[] {
- ConstraintViolationException.class
+ ConstraintViolationException.class,
+ DataIntegrityViolationException.class
};
}
@@ -1956,75 +1957,97 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
}
}
+ /**
+ * Explicitly flushes the session looking out for {@link #DUPLICATE_CHILD_NAME_EXCEPTIONS exceptions}
+ * indicating that the child association name constraint has been violated.
+ *
+ * NOTE: The Hibernate session will be flushed prior to calling the callback. This is necessary
+ * to prevent legitimate other contstraint violations from being dressed up as
+ * {@link DuplicateChildNodeNameException}.
+ *
+ * @param childAssocChangingCallback the callback in which the child assoc is modified
+ * @return Returns the callback's result
+ */
+ @SuppressWarnings("unchecked")
+ private Object writeChildAssocChanges(
+ HibernateCallback childAssocChangingCallback,
+ NodeRef parentNodeRef,
+ QName assocTypeQName,
+ String childName)
+ {
+ // Make sure there are no outstanding changes to flush
+ DirtySessionMethodInterceptor.flushSession(getSession(false));
+ // Call the callback and dig into any exception
+ try
+ {
+ Object ret = getHibernateTemplate().execute(childAssocChangingCallback);
+ // Now flush. Note that we *force* it to flush as the dirty flag will not have been set.
+ DirtySessionMethodInterceptor.flushSession(getSession(false), true);
+ // No clashes
+ return ret;
+ }
+ catch (Throwable e)
+ {
+ Throwable constraintViolation = (Throwable) ExceptionStackUtil.getCause(
+ e,
+ DUPLICATE_CHILD_NAME_EXCEPTIONS);
+ if (constraintViolation == null)
+ {
+ // It was something else
+ RuntimeException ee = AlfrescoRuntimeException.makeRuntimeException(
+ e, "Exception while flushing child assoc to database");
+ throw ee;
+ }
+ else
+ {
+ if (isDebugEnabled)
+ {
+ logger.debug(
+ "Duplicate child association detected: \n" +
+ " Parent node: " + parentNodeRef + "\n" +
+ " Child node name: " + childName,
+ e);
+ }
+ throw new DuplicateChildNodeNameException(parentNodeRef, assocTypeQName, childName);
+ }
+ }
+ }
+
public Pair newChildAssoc(
Long parentNodeId,
Long childNodeId,
- boolean isPrimary,
+ final boolean isPrimary,
final QName assocTypeQName,
- QName assocQName,
+ final QName assocQName,
String newName)
{
final Node parentNode = (Node) getSession().get(NodeImpl.class, parentNodeId);
- Node childNode = (Node) getSession().get(NodeImpl.class, childNodeId);
+ final Node childNode = (Node) getSession().get(NodeImpl.class, childNodeId);
final Pair childNameUnique = getChildNameUnique(assocTypeQName, newName);
final ChildAssoc assoc = new ChildAssocImpl();
- assoc.setTypeQName(qnameDAO, assocTypeQName);
- assoc.setChildNodeName(childNameUnique.getFirst());
- assoc.setChildNodeNameCrc(childNameUnique.getSecond());
- assoc.setQName(qnameDAO, assocQName);
- assoc.setIsPrimary(isPrimary);
- assoc.setIndex(-1);
- // maintain inverse sets
- assoc.buildAssociation(parentNode, childNode);
- // Make sure that all changes to the session are persisted so that we know if any
- // failures are from the constraint or not
- DirtySessionMethodInterceptor.flushSession(getSession(false));
- Long assocId = (Long) getHibernateTemplate().execute(new HibernateCallback()
+ HibernateCallback newAssocCallback = new HibernateCallback()
{
- @SuppressWarnings("unchecked")
- public Object doInHibernate(Session session)
+ public Object doInHibernate(Session session) throws HibernateException, SQLException
{
- try
- {
- try
- {
- Object result = session.save(assoc);
- DirtySessionMethodInterceptor.flushSession(session, true);
- return result;
- }
- catch (Throwable e)
- {
- ConstraintViolationException constraintViolation = (ConstraintViolationException) ExceptionStackUtil.getCause(
- e,
- DUPLICATE_CHILD_NAME_EXCEPTIONS);
- if (constraintViolation == null)
- {
- // It was something else
- RuntimeException ee = AlfrescoRuntimeException.makeRuntimeException(
- e, "Exception while flushing child assoc to database");
- throw ee;
- }
- else
- {
- throw constraintViolation;
- }
- }
- }
- catch (ConstraintViolationException e)
- {
- // There is already an entity
- if (isDebugEnabled)
- {
- logger.debug("Duplicate child association detected: \n" + " Parent Node: "
- + parentNode.getId() + "\n" + " Child Name Used: " + childNameUnique, e);
- }
- throw new DuplicateChildNodeNameException(parentNode.getNodeRef(), assocTypeQName, childNameUnique
- .getFirst());
- }
+ assoc.setTypeQName(qnameDAO, assocTypeQName);
+ assoc.setChildNodeName(childNameUnique.getFirst());
+ assoc.setChildNodeNameCrc(childNameUnique.getSecond());
+ assoc.setQName(qnameDAO, assocQName);
+ assoc.setIsPrimary(isPrimary);
+ assoc.setIndex(-1);
+ // maintain inverse sets
+ assoc.buildAssociation(parentNode, childNode);
+ // Save it
+ return session.save(assoc);
}
- });
+ };
+ Long assocId = (Long) writeChildAssocChanges(
+ newAssocCallback,
+ parentNode.getNodeRef(),
+ assocTypeQName,
+ childNameUnique.getFirst());
// Add it to the cache
Set oldParentAssocIds = parentAssocsCache.get(childNode.getId());
@@ -2085,32 +2108,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
{
childAssoc.setChildNodeName(childNameUnique.getFirst());
childAssoc.setChildNodeNameCrc(childNameUnique.getSecond().longValue());
- // Flush again to force a DB constraint here
- try
- {
- DirtySessionMethodInterceptor.flushSession(session, true);
- // Done
- return null;
- }
- catch (ConstraintViolationException e)
- {
- // There is already an entity
- if (isDebugEnabled)
- {
- logger.debug("Duplicate child association detected: \n" + " Parent Node: "
- + parentNode.getId() + "\n" + " Child Name Used: " + childNameUnique, e);
- }
-
- throw new DuplicateChildNodeNameException(parentNode.getNodeRef(), childAssoc
- .getTypeQName(qnameDAO), childNameUnique.getFirst());
- }
+ return null;
}
};
-
- // Make sure that all changes to the session are persisted so that we know if any
- // failures are from the constraint or not
- DirtySessionMethodInterceptor.flushSession(getSession(false));
- getHibernateTemplate().execute(callback);
+ writeChildAssocChanges(
+ callback,
+ parentNode.getNodeRef(),
+ childAssoc.getTypeQName(qnameDAO),
+ childName);
// Done
if (isDebugEnabled)
@@ -2169,9 +2174,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
Long childAssocId,
Long parentNodeId,
Long childNodeId,
- QName assocTypeQName,
- QName assocQName,
- int index,
+ final QName assocTypeQName,
+ final QName assocQName,
+ final int index,
String childName)
{
final ChildAssoc childAssoc = getChildAssocNotNull(childAssocId);
@@ -2182,19 +2187,31 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
final Node newParentNode = getNodeNotNull(parentNodeId);
final Node newChildNode = getNodeNotNull(childNodeId);
final NodeRef newChildNodeRef = newChildNode.getNodeRef();
+ final Pair childNameUnique = getChildNameUnique(assocTypeQName, childName);
// Reset the cm:name duplicate handling. This has to be redone, if required.
- Pair childNameUnique = getChildNameUnique(assocTypeQName, childName);
- childAssoc.setChildNodeName(childNameUnique.getFirst());
- childAssoc.setChildNodeNameCrc(childNameUnique.getSecond());
-
- childAssoc.buildAssociation(newParentNode, newChildNode);
- childAssoc.setTypeQName(qnameDAO, assocTypeQName);
- childAssoc.setQName(qnameDAO, assocQName);
- if (index >= 0)
+ HibernateCallback updateChildAssocCallback = new HibernateCallback()
{
- childAssoc.setIndex(index);
- }
+ public Object doInHibernate(Session session) throws HibernateException, SQLException
+ {
+ childAssoc.setChildNodeName(childNameUnique.getFirst());
+ childAssoc.setChildNodeNameCrc(childNameUnique.getSecond());
+
+ childAssoc.buildAssociation(newParentNode, newChildNode);
+ childAssoc.setTypeQName(qnameDAO, assocTypeQName);
+ childAssoc.setQName(qnameDAO, assocQName);
+ if (index >= 0)
+ {
+ childAssoc.setIndex(index);
+ }
+ return null;
+ }
+ };
+ writeChildAssocChanges(
+ updateChildAssocCallback,
+ newParentNode.getNodeRef(),
+ assocTypeQName,
+ childNameUnique.getFirst());
// Record change ID
if (oldChildNodeRef.equals(newChildNodeRef))
@@ -3446,6 +3463,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements
}
};
NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback);
+ if (result == null)
+ {
+ return null;
+ }
Pair ret = new Pair(result.getId(), result.getNodeAssocRef(qnameDAO));
return ret;
}
diff --git a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java
index 181ad4419a..9234ead3bb 100644
--- a/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java
+++ b/source/java/org/alfresco/repo/search/AVMSnapShotTriggeredIndexingMethodInterceptor.java
@@ -219,8 +219,18 @@ public class AVMSnapShotTriggeredIndexingMethodInterceptor implements MethodInte
{
this.defaultMode = defaultMode;
}
-
+
/**
+ * Is snapshot triggered indexing enabled
+ *
+ * @return true if indexing is enabled for AVM
+ */
+ public boolean isIndexingEnabled()
+ {
+ return enableIndexing;
+ }
+
+ /**
* @param store
* @param before
* @param after
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java
index 457bf74581..fdcd8dc270 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java
@@ -171,12 +171,16 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI
private int mergerMergeFactor = 5;
+ private int mergerMergeBlockingFactor = 1;
+
private int mergerMinMergeDocs = 1000;
private int mergerTargetIndexCount = 5;
private int mergerTargetOverlayCount = 5;
+ private int mergerTargetOverlaysBlockingFactor = 2;
+
private int termIndexInterval =IndexWriter.DEFAULT_TERM_INDEX_INTERVAL;
private boolean useNioMemoryMapping = true;
@@ -1686,6 +1690,16 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI
{
this.mergerMergeFactor = mergerMergeFactor;
}
+
+ public int getMergerMergeBlockingFactor()
+ {
+ return mergerMergeBlockingFactor;
+ }
+
+ public void setMergerMergeBlockingFactor(int mergerMergeBlockingFactor)
+ {
+ this.mergerMergeBlockingFactor = mergerMergeBlockingFactor;
+ }
public int getMergerMinMergeDocs()
{
@@ -1716,6 +1730,16 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI
{
this.mergerTargetOverlayCount = mergerTargetOverlayCount;
}
+
+ public int getMergerTargetOverlaysBlockingFactor()
+ {
+ return mergerTargetOverlaysBlockingFactor;
+ }
+
+ public void setMergerTargetOverlaysBlockingFactor(int mergerTargetOverlaysBlockingFactor)
+ {
+ this.mergerTargetOverlaysBlockingFactor = mergerTargetOverlaysBlockingFactor;
+ }
public int getTermIndexInterval()
{
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java
index fe896b4a22..4e92ccd51b 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java
@@ -138,6 +138,14 @@ public interface LuceneConfig
*/
public int getMergerMergeFactor();
+ /**
+ * The factor by which the merge factor is multiplied to determine the allowable number of indexes before blocking.
+ *
+ * @return the factor by which the merge factor is multiplied to determine the allowable number of indexes before
+ * blocking
+ */
+ public int getMergerMergeBlockingFactor();
+
/**
* Lucene merger config
* @return
@@ -150,6 +158,15 @@ public interface LuceneConfig
*/
public int getMergerTargetOverlayCount();
+ /**
+ * The factor by which the target overlay count is multiplied to determine the allowable number of overlays before
+ * blocking.
+ *
+ * @return the factor by which the target overlay count is multiplied to determine the allowable number of overlays
+ * before blocking
+ */
+ public int getMergerTargetOverlaysBlockingFactor();
+
/**
* Target index count. Over this indexes will be merged together.
* @return
@@ -228,4 +245,5 @@ public interface LuceneConfig
* @return
*/
public ConfigurableApplicationContext getApplicationContext();
+
}
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
index 8d8c8b946c..cc3c8a05c2 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
@@ -228,7 +228,7 @@ public class IndexInfo implements IndexMonitor
* Lock for the index entries
*/
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
-
+
/**
* Read only index readers that also do reference counting.
*/
@@ -315,12 +315,16 @@ public class IndexInfo implements IndexMonitor
private int mergerMergeFactor = 5;
+ private int mergerMergeBlockingFactor = 1;
+
private int mergerMaxMergeDocs = 1000000;
private boolean mergerUseCompoundFile = true;
private int mergerTargetOverlays = 5;
+ private int mergerTargetOverlaysBlockingFactor = 2;
+
// Common properties for indexers
private long writeLockTimeout = IndexWriter.WRITE_LOCK_TIMEOUT;
@@ -404,9 +408,11 @@ public class IndexInfo implements IndexMonitor
this.writerMaxMergeDocs = config.getWriterMaxMergeDocs();
this.mergerMinMergeDocs = config.getMergerMinMergeDocs();
this.mergerMergeFactor = config.getMergerMergeFactor();
+ this.mergerMergeBlockingFactor = config.getMergerMergeBlockingFactor();
this.mergerMaxMergeDocs = config.getMergerMaxMergeDocs();
this.termIndexInterval = config.getTermIndexInterval();
this.mergerTargetOverlays = config.getMergerTargetOverlayCount();
+ this.mergerTargetOverlaysBlockingFactor = config.getMergerTargetOverlaysBlockingFactor();
// Work out the relative path of the index
try
{
@@ -1261,7 +1267,38 @@ public class IndexInfo implements IndexMonitor
getReadLock();
try
{
- transition.beforeWithReadLock(id, toDelete, read);
+ // beforeWithReadLock may indicate that we need to block for the merger to do some work
+ while (!transition.beforeWithReadLock(id, toDelete, read))
+ {
+ synchronized (merger)
+ {
+ // If the merger is scheduled, let's wait for it...
+ int count = merger.getScheduledCount();
+ if (count <= 0)
+ {
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("CAN'T THROTTLE: " + indexEntries.size());
+ }
+ break;
+ }
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug("THROTTLING: " + indexEntries.size());
+ }
+ releaseReadLock();
+ try
+ {
+ merger.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ getReadLock();
+ }
+
+
releaseReadLock();
getWriteLock();
try
@@ -1361,7 +1398,7 @@ public class IndexInfo implements IndexMonitor
*/
private interface Transition
{
- void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException;
+ boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException;
void transition(String id, Set toDelete, Set read) throws IOException;
@@ -1375,9 +1412,9 @@ public class IndexInfo implements IndexMonitor
*/
private class PreparingTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
- // Nothing to do
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1411,9 +1448,10 @@ public class IndexInfo implements IndexMonitor
*/
private class PreparedTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
-
+ // We want to block until the merger has executed if we have more than a certain number of indexes
+ return indexEntries.size() <= mergerMergeBlockingFactor * mergerMergeFactor + mergerTargetOverlaysBlockingFactor * mergerTargetOverlays;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1479,9 +1517,9 @@ public class IndexInfo implements IndexMonitor
private class CommittingTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
-
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1513,13 +1551,14 @@ public class IndexInfo implements IndexMonitor
ThreadLocal tl = new ThreadLocal();
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
// Make sure we have set up the reader for the data
// ... and close it so we do not up the ref count
closeDelta(id);
IndexEntry entry = indexEntries.get(id);
tl.set(buildReferenceCountingIndexReader(id, entry.getDocumentCount()));
+ return true;
}
/**
@@ -1594,9 +1633,9 @@ public class IndexInfo implements IndexMonitor
private class RollingBackTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
-
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1628,11 +1667,12 @@ public class IndexInfo implements IndexMonitor
{
ThreadLocal tl = new ThreadLocal();
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
closeDelta(id);
IndexEntry entry = indexEntries.get(id);
tl.set(buildReferenceCountingIndexReader(id, entry.getDocumentCount()));
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1682,9 +1722,9 @@ public class IndexInfo implements IndexMonitor
private class DeletableTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
-
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -1727,9 +1767,9 @@ public class IndexInfo implements IndexMonitor
private class ActiveTransition implements Transition
{
- public void beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
+ public boolean beforeWithReadLock(String id, Set toDelete, Set read) throws IOException
{
-
+ return true;
}
public void transition(String id, Set toDelete, Set read) throws IOException
@@ -2906,6 +2946,13 @@ public class IndexInfo implements IndexMonitor
private abstract class AbstractSchedulable implements Schedulable, Runnable
{
ScheduledState scheduledState = ScheduledState.UN_SCHEDULED;
+
+ private int scheduledCount;
+
+ public synchronized int getScheduledCount()
+ {
+ return scheduledCount;
+ }
public synchronized void schedule()
{
@@ -2917,6 +2964,7 @@ public class IndexInfo implements IndexMonitor
break;
case UN_SCHEDULED:
scheduledState = ScheduledState.SCHEDULED;
+ scheduledCount++;
threadPoolExecutor.execute(this);
break;
case RECOVERY_SCHEDULED:
@@ -2931,8 +2979,10 @@ public class IndexInfo implements IndexMonitor
{
switch (scheduledState)
{
- case RECOVERY_SCHEDULED:
case SCHEDULED:
+ scheduledCount--;
+ notifyAll();
+ case RECOVERY_SCHEDULED:
scheduledState = ScheduledState.UN_SCHEDULED;
break;
case FAILED:
@@ -2977,8 +3027,10 @@ public class IndexInfo implements IndexMonitor
{
switch (scheduledState)
{
- case RECOVERY_SCHEDULED:
case SCHEDULED:
+ scheduledCount--;
+ notifyAll();
+ case RECOVERY_SCHEDULED:
scheduledState = ScheduledState.FAILED;
break;
case FAILED:
diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java
index af9ac01c2f..5d13c7abfb 100644
--- a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java
+++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java
@@ -418,7 +418,9 @@ public class RetryingTransactionHelper
{
// Sleep a random amount of time before retrying.
// The sleep interval increases with the number of retries.
- int sleepIntervalRandom = count > 0 ? random.nextInt(count * retryWaitIncrementMs) : minRetryWaitMs;
+ int sleepIntervalRandom = (count > 0 && retryWaitIncrementMs > 0)
+ ? random.nextInt(count * retryWaitIncrementMs)
+ : minRetryWaitMs;
int sleepInterval = Math.min(maxRetryWaitMs, sleepIntervalRandom);
sleepInterval = Math.max(sleepInterval, minRetryWaitMs);
if (logger.isInfoEnabled() && !logger.isDebugEnabled())