Merged V1.3 to HEAD (3180:3203, 3204:3217)

svn merge svn://www.alfresco.org:3691/alfresco/BRANCHES/V1.3@3180 svn://www.alfresco.org:3691/alfresco/BRANCHES/V1.3@3203 .
   svn merge svn://www.alfresco.org:3691/alfresco/BRANCHES/V1.3@3204 svn://www.alfresco.org:3691/alfresco/BRANCHES/V1.3@3217 .


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@3407 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-07-26 11:00:06 +00:00
parent 595556f3c5
commit f4e00169ab
40 changed files with 1155 additions and 643 deletions

View File

@@ -31,6 +31,7 @@ import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -236,10 +237,80 @@ public class FileFolderPerformanceTester extends TestCase
}
}
public void test_2_ordered_1_10() throws Exception
private void readStructure(
final NodeRef parentNodeRef,
final int threadCount,
final int repetitions,
final double[] dumpPoints)
{
buildStructure(rootFolderRef, 2, false, 1, 10, null);
final List<ChildAssociationRef> children = nodeService.getChildAssocs(parentNodeRef);
Runnable runnable = new Runnable()
{
public void run()
{
// authenticate
authenticationComponent.setSystemUserAsCurrentUser();
for (int i = 0; i < repetitions; i++)
{
// read the contents of each folder
for (ChildAssociationRef childAssociationRef : children)
{
final NodeRef folderRef = childAssociationRef.getChildRef();
TransactionWork<Object> readWork = new TransactionWork<Object>()
{
public Object doWork() throws Exception
{
// read the child associations of the folder
nodeService.getChildAssocs(folderRef);
// get the type
nodeService.getType(folderRef);
// done
return null;
};
};
TransactionUtil.executeInUserTransaction(transactionService, readWork, true);
}
}
}
};
// kick off the required number of threads
logger.debug("\n" +
"Starting " + threadCount +
" threads reading properties and children of " + children.size() +
" folder " + repetitions +
" times.");
ThreadGroup threadGroup = new ThreadGroup(getName());
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
threads[i] = new Thread(threadGroup, runnable, String.format("FileReader-%02d", i));
threads[i].start();
}
// join each thread so that we wait for them all to finish
for (int i = 0; i < threads.length; i++)
{
try
{
threads[i].join();
}
catch (InterruptedException e)
{
// not too serious - the worker threads are non-daemon
}
}
}
public void test_1_ordered_1_10() throws Exception
{
buildStructure(rootFolderRef, 1, false, 1, 10, null);
}
// public void test_1_ordered_1_10_read() throws Exception
// {
// buildStructure(rootFolderRef, 1, false, 50, 1, null);
// readStructure(rootFolderRef, 50, 1000, null);
// }
//
// public void test_4_ordered_10_100() throws Exception
// {
@@ -250,24 +321,34 @@ public class FileFolderPerformanceTester extends TestCase
// {
// buildStructure(rootFolderRef, 4, true, 10, 100, new double[] {0.25, 0.50, 0.75});
// }
public void test_4_shuffled_100_100() throws Exception
// public void test_1_ordered_100_100() throws Exception
// {
// buildStructure(
// rootFolderRef,
// 1,
// false,
// 100,
// 100,
// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
// }
public void test_1_shuffled_10_400() throws Exception
{
buildStructure(
rootFolderRef,
1,
true,
10,
400,
new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
}
public void test_4_shuffled_10_100() throws Exception
{
buildStructure(
rootFolderRef,
4,
true,
10,
100,
100,
new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
new double[] {0.05, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
}
// public void test_4_shuffled_1000_1000() throws Exception
// {
// buildStructure(
// rootFolderRef,
// 4,
// true,
// 1000,
// 1000,
// new double[] {0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90});
// }
}

View File

@@ -45,6 +45,9 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.search.QueryParameterDefinition;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -59,17 +62,6 @@ import org.apache.commons.logging.LogFactory;
*/
public class FileFolderServiceImpl implements FileFolderService
{
/** Shallow search for all files */
private static final String XPATH_QUERY_SHALLOW_FILES =
"./*" +
"[(subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
/** Shallow search for all folder */
private static final String XPATH_QUERY_SHALLOW_FOLDERS =
"./*" +
"[not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "'))]";
/** Shallow search for files and folders with a name pattern */
private static final String XPATH_QUERY_SHALLOW_ALL =
"./*" +
@@ -77,6 +69,23 @@ public class FileFolderServiceImpl implements FileFolderService
" and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
/** Shallow search for all files and folders */
private static final String LUCENE_QUERY_SHALLOW_ALL =
"+PARENT:\"${cm:parent}\"" +
"-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" ";
/** Shallow search for all files and folders */
private static final String LUCENE_QUERY_SHALLOW_FOLDERS =
"+PARENT:\"${cm:parent}\"" +
"-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " +
"+TYPE:\"" + ContentModel.TYPE_FOLDER + "\" ";
/** Shallow search for all files and folders */
private static final String LUCENE_QUERY_SHALLOW_FILES =
"+PARENT:\"${cm:parent}\"" +
"-TYPE:\"" + ContentModel.TYPE_SYSTEM_FOLDER + "\" " +
"+TYPE:\"" + ContentModel.TYPE_CONTENT + "\" ";
/** Deep search for files and folders with a name pattern */
private static final String XPATH_QUERY_DEEP_ALL =
".//*" +
@@ -85,8 +94,8 @@ public class FileFolderServiceImpl implements FileFolderService
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
/** empty parameters */
private static final QueryParameterDefinition[] PARAMS_EMPTY = new QueryParameterDefinition[0];
private static final QueryParameterDefinition[] PARAMS_ANY_NAME = new QueryParameterDefinition[1];
private static final QName PARAM_QNAME_PARENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "parent");
private static Log logger = LogFactory.getLog(FileFolderServiceImpl.class);
@@ -100,6 +109,7 @@ public class FileFolderServiceImpl implements FileFolderService
// TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
private List systemPaths;
private DataTypeDefinition dataTypeNodeRef;
/**
* Default constructor
@@ -157,6 +167,7 @@ public class FileFolderServiceImpl implements FileFolderService
dictionaryService.getDataType(DataTypeDefinition.TEXT),
true,
"%");
dataTypeNodeRef = dictionaryService.getDataType(DataTypeDefinition.NODE_REF);
}
/**
@@ -202,12 +213,17 @@ public class FileFolderServiceImpl implements FileFolderService
private void checkExists(NodeRef parentFolderRef, String name)
throws FileExistsException
{
// check for existing file or folder
List<FileInfo> existingFileInfos = this.search(parentFolderRef, name, true, true, false);
if (existingFileInfos.size() > 0)
// the name is never a wildcard, so we can perform an exact search
List<NodeRef> nodeRefs = searchInternal(parentFolderRef, name, true, true, false);
if (nodeRefs.size() == 0)
{
throw new FileExistsException(existingFileInfos.get(0));
// no match
return;
}
// we found a match, so raise the exception
NodeRef duplicateNodeRef = nodeRefs.get(0);
FileInfo duplicateFileInfo = toFileInfo(duplicateNodeRef);
throw new FileExistsException(duplicateFileInfo);
}
/**
@@ -258,18 +274,10 @@ public class FileFolderServiceImpl implements FileFolderService
}
}
/**
* TODO: Use Lucene search to get file attributes without having to visit the node service
*/
public List<FileInfo> list(NodeRef contextNodeRef)
{
// execute the query
List<NodeRef> nodeRefs = searchService.selectNodes(
contextNodeRef,
XPATH_QUERY_SHALLOW_ALL,
PARAMS_ANY_NAME,
namespaceService,
false);
List<NodeRef> nodeRefs = luceneSearch(contextNodeRef, true, true);
// convert the noderefs
List<FileInfo> results = toFileInfo(nodeRefs);
// done
@@ -282,18 +290,10 @@ public class FileFolderServiceImpl implements FileFolderService
return results;
}
/**
* TODO: Use Lucene search to get file attributes without having to visit the node service
*/
public List<FileInfo> listFiles(NodeRef contextNodeRef)
{
// execute the query
List<NodeRef> nodeRefs = searchService.selectNodes(
contextNodeRef,
XPATH_QUERY_SHALLOW_FILES,
PARAMS_EMPTY,
namespaceService,
false);
List<NodeRef> nodeRefs = luceneSearch(contextNodeRef, false, true);
// convert the noderefs
List<FileInfo> results = toFileInfo(nodeRefs);
// done
@@ -306,18 +306,10 @@ public class FileFolderServiceImpl implements FileFolderService
return results;
}
/**
* TODO: Use Lucene search to get file attributes without having to visit the node service
*/
public List<FileInfo> listFolders(NodeRef contextNodeRef)
{
// execute the query
List<NodeRef> nodeRefs = searchService.selectNodes(
contextNodeRef,
XPATH_QUERY_SHALLOW_FOLDERS,
PARAMS_EMPTY,
namespaceService,
false);
List<NodeRef> nodeRefs = luceneSearch(contextNodeRef, true, false);
// convert the noderefs
List<FileInfo> results = toFileInfo(nodeRefs);
// done
@@ -338,6 +330,7 @@ public class FileFolderServiceImpl implements FileFolderService
return search(contextNodeRef, namePattern, true, true, includeSubFolders);
}
private static final String LUCENE_MULTI_CHAR_WILDCARD = "*";
/**
* Full search with all options
*/
@@ -348,50 +341,9 @@ public class FileFolderServiceImpl implements FileFolderService
boolean folderSearch,
boolean includeSubFolders)
{
// shortcut if the search is requesting nothing
if (!fileSearch && !folderSearch)
{
return Collections.emptyList();
}
// get the raw nodeRefs
List<NodeRef> nodeRefs = searchInternal(contextNodeRef, namePattern, fileSearch, folderSearch, includeSubFolders);
// if the name pattern is null, then we use the ANY pattern
QueryParameterDefinition[] params = null;
if (namePattern != null)
{
// the interface specifies the Lucene syntax, so perform a conversion
namePattern = SearchLanguageConversion.convert(
SearchLanguageConversion.DEF_LUCENE,
SearchLanguageConversion.DEF_XPATH_LIKE,
namePattern);
params = new QueryParameterDefinition[1];
params[0] = new QueryParameterDefImpl(
ContentModel.PROP_NAME,
dictionaryService.getDataType(DataTypeDefinition.TEXT),
true,
namePattern);
}
else
{
params = PARAMS_ANY_NAME;
}
// determine the correct query to use
String query = null;
if (includeSubFolders)
{
query = XPATH_QUERY_DEEP_ALL;
}
else
{
query = XPATH_QUERY_SHALLOW_ALL;
}
// execute the query
List<NodeRef> nodeRefs = searchService.selectNodes(
contextNodeRef,
query,
params,
namespaceService,
false);
List<FileInfo> results = toFileInfo(nodeRefs);
// eliminate unwanted files/folders
Iterator<FileInfo> iterator = results.iterator();
@@ -420,6 +372,133 @@ public class FileFolderServiceImpl implements FileFolderService
}
return results;
}
/**
* Performs a full search, but doesn't translate the node references into
* file info objects. This allows {@link #checkExists(NodeRef, String)} to
* bypass the retrieval of node properties.
*/
public List<NodeRef> searchInternal(
NodeRef contextNodeRef,
String namePattern,
boolean fileSearch,
boolean folderSearch,
boolean includeSubFolders)
{
// shortcut if the search is requesting nothing
if (!fileSearch && !folderSearch)
{
return Collections.emptyList();
}
if (namePattern == null)
{
namePattern = LUCENE_MULTI_CHAR_WILDCARD; // default to wildcard
}
// now check if we can use Lucene to handle this query
boolean useLucene = false;
boolean anyName = namePattern.equals(LUCENE_MULTI_CHAR_WILDCARD);
if (!includeSubFolders && anyName)
{
// Lucene only handles any name or exact name
useLucene = true;
}
List<NodeRef> nodeRefs = null;
if (!useLucene) // go with the XPath queries
{
// if the name pattern is null, then we use the ANY pattern
QueryParameterDefinition[] params = null;
if (namePattern != null)
{
// the interface specifies the Lucene syntax, so perform a conversion
namePattern = SearchLanguageConversion.convert(
SearchLanguageConversion.DEF_LUCENE,
SearchLanguageConversion.DEF_XPATH_LIKE,
namePattern);
params = new QueryParameterDefinition[1];
params[0] = new QueryParameterDefImpl(
ContentModel.PROP_NAME,
dictionaryService.getDataType(DataTypeDefinition.TEXT),
true,
namePattern);
}
else
{
params = PARAMS_ANY_NAME;
}
// determine the correct query to use
String query = null;
if (includeSubFolders)
{
query = XPATH_QUERY_DEEP_ALL;
}
else
{
query = XPATH_QUERY_SHALLOW_ALL;
}
// execute the query
nodeRefs = searchService.selectNodes(
contextNodeRef,
query,
params,
namespaceService,
false);
}
else // go with Lucene queries
{
nodeRefs = luceneSearch(contextNodeRef, folderSearch, fileSearch);
}
// done
return nodeRefs;
}
private List<NodeRef> luceneSearch(NodeRef contextNodeRef, boolean folders, boolean files)
{
SearchParameters params = new SearchParameters();
params.setLanguage(SearchService.LANGUAGE_LUCENE);
params.addStore(contextNodeRef.getStoreRef());
// set the parent parameter
QueryParameterDefinition parentParamDef = new QueryParameterDefImpl(
PARAM_QNAME_PARENT,
dataTypeNodeRef,
true,
contextNodeRef.toString());
params.addQueryParameterDefinition(parentParamDef);
if (folders && files) // search for both files and folders
{
params.setQuery(LUCENE_QUERY_SHALLOW_ALL);
}
else if (folders) // search for folders only
{
params.setQuery(LUCENE_QUERY_SHALLOW_FOLDERS);
}
else if (files) // search for files only
{
params.setQuery(LUCENE_QUERY_SHALLOW_FILES);
}
else
{
throw new IllegalArgumentException("Must search for either files or folders or both");
}
ResultSet rs = searchService.query(params);
int length = rs.length();
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(length);
try
{
for (ResultSetRow row : rs)
{
nodeRefs.add(row.getNodeRef());
}
}
finally
{
rs.close();
}
// done
return nodeRefs;
}
/**
* @see #move(NodeRef, NodeRef, String)
@@ -467,10 +546,10 @@ public class FileFolderServiceImpl implements FileFolderService
targetParentRef = assocRef.getParentRef();
}
// there is nothing to do if both the name and parent folder haven't changed
boolean checkExists = true;
if (targetParentRef.equals(assocRef.getParentRef()))
{
// there is nothing to do if both the name and parent folder haven't changed
if (newName.equals(beforeFileInfo.getName()))
{
if (logger.isDebugEnabled())
@@ -484,17 +563,15 @@ public class FileFolderServiceImpl implements FileFolderService
}
else if (newName.equalsIgnoreCase(beforeFileInfo.getName()))
{
// name has only changed case so don't bother with exists check
checkExists = false;
}
}
// check for existing file or folder (if name has changed)
// check for existing file or folder
if (checkExists)
{
checkExists(targetParentRef, newName);
}
QName qname = QName.createQName(
NamespaceService.CONTENT_MODEL_1_0_URI,

View File

@@ -417,19 +417,20 @@ public class FileFolderServiceImplTest extends TestCase
{
// create a completely new path below the root
List<String> namePath = new ArrayList<String>(4);
namePath.add("A");
namePath.add("B");
namePath.add("C");
namePath.add("D");
namePath.add("AAA");
namePath.add("BBB");
namePath.add("CCC");
namePath.add("DDD");
FileInfo lastFileInfo = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
assertNotNull("First makeFolder failed", lastFileInfo);
// check that a repeat works
FileInfo lastFileInfoAgain = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
assertNotNull("Repeat makeFolders failed", lastFileInfoAgain);
assertEquals("Repeat created new leaf", lastFileInfo.getNodeRef(), lastFileInfoAgain.getNodeRef());
// check that it worked
List<FileInfo> checkInfos = fileFolderService.search(rootNodeRef, "D", false, true, true);
List<FileInfo> checkInfos = fileFolderService.search(rootNodeRef, "DDD", false, true, true);
assertEquals("Expected to find a result", 1, checkInfos.size());
// get the path
List<FileInfo> checkPathInfos = fileFolderService.getNamePath(rootNodeRef, checkInfos.get(0).getNodeRef());
@@ -442,6 +443,27 @@ public class FileFolderServiceImplTest extends TestCase
}
}
/**
* Lucene only indexes terms that are 3 characters or more
*/
public void testMakeFoldersShortNames() throws Exception
{
// create a completely new path below the root
List<String> namePath = new ArrayList<String>(4);
namePath.add("A");
namePath.add("B");
namePath.add("C");
namePath.add("D");
FileInfo lastFileInfo = fileFolderService.makeFolders(rootNodeRef, namePath, ContentModel.TYPE_FOLDER);
assertNotNull("First makeFolder failed", lastFileInfo);
// check that a repeat works
FileInfo lastFileInfoAgain = fileFolderService.makeFolders(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);