diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml
index db0255e93e..56998af2d4 100644
--- a/config/alfresco/patch/patch-services-context.xml
+++ b/config/alfresco/patch/patch-services-context.xml
@@ -347,13 +347,17 @@
0
12
13
+ alfresco/bootstrap/example_javascripts.acp
-
+
+
+
+
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ScriptsFolderPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ScriptsFolderPatch.java
index 2c1a688fba..6fea10daa8 100644
--- a/source/java/org/alfresco/repo/admin/patch/impl/ScriptsFolderPatch.java
+++ b/source/java/org/alfresco/repo/admin/patch/impl/ScriptsFolderPatch.java
@@ -16,6 +16,7 @@
*/
package org.alfresco.repo.admin.patch.impl;
+import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
@@ -25,13 +26,17 @@ import java.util.Properties;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.AbstractPatch;
+import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.view.ImporterService;
+import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.QName;
import org.springframework.context.MessageSource;
+import org.springframework.core.io.ClassPathResource;
/**
* Ensures that the scripts folder is present.
@@ -59,32 +64,44 @@ public class ScriptsFolderPatch extends AbstractPatch
private static final String PROPERTY_ICON = "space-icon-default";
private ImporterBootstrap importerBootstrap;
+ private ImporterService importerService;
private MessageSource messageSource;
protected NodeRef dictionaryNodeRef;
protected Properties configuration;
protected NodeRef scriptsFolderNodeRef;
+ private String scriptsACP;
+
public void setImporterBootstrap(ImporterBootstrap importerBootstrap)
{
this.importerBootstrap = importerBootstrap;
}
+
+ public void setImporterService(ImporterService importerService)
+ {
+ this.importerService = importerService;
+ }
public void setMessageSource(MessageSource messageSource)
{
this.messageSource = messageSource;
}
- /**
+ public void setScriptsACP(String scriptsACP)
+ {
+ this.scriptsACP = scriptsACP;
+ }
+
+ /**
* Ensure that required common properties have been set
*/
protected void checkCommonProperties() throws Exception
{
- if (importerBootstrap == null)
- {
- throw new PatchException("'importerBootstrap' property has not been set");
- }
- else if (namespaceService == null)
+ checkPropertyNotNull(importerBootstrap, "importerBootstrap");
+ checkPropertyNotNull(importerService, "importerService");
+ checkPropertyNotNull(messageSource, "messageSource");
+ if (namespaceService == null)
{
throw new PatchException("'namespaceService' property has not been set");
}
@@ -96,6 +113,7 @@ public class ScriptsFolderPatch extends AbstractPatch
{
throw new PatchException("'nodeService' property has not been set");
}
+ checkPropertyNotNull(scriptsACP, "scriptsACP");
}
/**
@@ -193,6 +211,19 @@ public class ScriptsFolderPatch extends AbstractPatch
{
// create it
createFolder();
+
+ // import the content
+ try
+ {
+ authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
+
+ importContent();
+ }
+ finally
+ {
+ authenticationComponent.clearCurrentSecurityContext();
+ }
+
msg = I18NUtil.getMessage(MSG_CREATED, scriptsFolderNodeRef);
}
else
@@ -251,4 +282,13 @@ public class ScriptsFolderPatch extends AbstractPatch
// done
}
+
+ private void importContent() throws IOException
+ {
+ // import the content
+ ClassPathResource acpResource = new ClassPathResource(this.scriptsACP);
+ ACPImportPackageHandler acpHandler = new ACPImportPackageHandler(acpResource.getFile(), null);
+ Location importLocation = new Location(this.scriptsFolderNodeRef);
+ importerService.importView(acpHandler, importLocation, null, null);
+ }
}
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
index fd99ea8596..f6842e11aa 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java
@@ -17,13 +17,15 @@
package org.alfresco.repo.model.filefolder;
import java.io.File;
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.repo.transaction.TransactionUtil.TransactionWork;
import org.alfresco.service.ServiceRegistry;
@@ -38,6 +40,8 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
/**
@@ -53,9 +57,12 @@ import org.springframework.context.ApplicationContext;
*/
public class FileFolderPerformanceTester extends TestCase
{
+ private static Log logger = LogFactory.getLog(FileFolderPerformanceTester.class);
+
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
+ private AuthenticationComponent authenticationComponent;
private NodeService nodeService;
private FileFolderService fileFolderService;
private StoreRef storeRef;
@@ -67,9 +74,13 @@ public class FileFolderPerformanceTester extends TestCase
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
transactionService = serviceRegistry.getTransactionService();
+ authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent");
nodeService = serviceRegistry.getNodeService();
fileFolderService = serviceRegistry.getFileFolderService();
+ // authenticate
+ authenticationComponent.setSystemUserAsCurrentUser();
+
// create a folder root to work in
storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, getName() + "_" + System.currentTimeMillis());
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
@@ -92,85 +103,166 @@ public class FileFolderPerformanceTester extends TestCase
* are added one to each folder until each folder has the presribed number of files within it.
* This can therefore be used to test the performance when the L2 cache sizes are exceeded.
*
- * Each creation (file or folder) uses the REQUIRES_NEW transaction declaration.
+ * Each creation (file or folder) uses the PROPAGATION REQUIRED transaction declaration.
*
* @param parentNodeRef the level zero parent
+ * @param randomOrder true if each thread must put the children into the folders in a random order
* @return Returns the average time (ms) to create the files only
*/
- private double buildStructure(final NodeRef parentNodeRef, final int folderCount, final int fileCount)
+ private void buildStructure(
+ final NodeRef parentNodeRef,
+ final int threadCount,
+ final boolean randomOrder,
+ final int folderCount,
+ final int fileCount,
+ final double[] dumpPoints)
{
- List folders = new ArrayList(folderCount);
- for (int i = 0; i < folderCount; i++)
+ TransactionWork createFoldersWork = new TransactionWork()
{
- TransactionWork createFolderWork = new TransactionWork()
+ public NodeRef[] doWork() throws Exception
{
- public FileInfo doWork() throws Exception
+ NodeRef[] folders = new NodeRef[folderCount];
+ for (int i = 0; i < folderCount; i++)
{
FileInfo folderInfo = fileFolderService.create(
parentNodeRef,
GUID.generate(),
ContentModel.TYPE_FOLDER);
- // done
- return folderInfo;
+ // keep the reference
+ folders[i] = folderInfo.getNodeRef();
}
- };
- FileInfo folderInfo = TransactionUtil.executeInUserTransaction(transactionService, createFolderWork);
- // keep the reference
- folders.add(folderInfo.getNodeRef());
- }
- // now progress around the folders until they have been populated
- long start = System.currentTimeMillis();
- for (int i = 0; i < fileCount; i++)
+ return folders;
+ }
+ };
+ final NodeRef[] folders = TransactionUtil.executeInUserTransaction(
+ transactionService,
+ createFoldersWork);
+ // the worker that will load the files into the folders
+ Runnable runnable = new Runnable()
{
- for (final NodeRef folderRef : folders)
+ private long start;
+ public void run()
{
- TransactionWork createFileWork = new TransactionWork()
+ // authenticate
+ authenticationComponent.setSystemUserAsCurrentUser();
+
+ // progress around the folders until they have been populated
+ start = System.currentTimeMillis();
+ int nextDumpNumber = 0;
+ for (int i = 0; i < fileCount; i++)
{
- public FileInfo doWork() throws Exception
+ // must we dump results
+ double completedCount = (double) i;
+ double nextDumpCount = (dumpPoints == null || dumpPoints.length == 0 || nextDumpNumber >= dumpPoints.length)
+ ? -1.0
+ : (double) fileCount * dumpPoints[nextDumpNumber];
+ if ((nextDumpCount - 0.5) < completedCount && completedCount < (nextDumpCount + 0.5))
{
- FileInfo fileInfo = fileFolderService.create(
- folderRef,
- GUID.generate(),
- ContentModel.TYPE_CONTENT);
- NodeRef nodeRef = fileInfo.getNodeRef();
- // write the content
- ContentWriter writer = fileFolderService.getWriter(nodeRef);
- writer.putContent(dataFile);
- // done
- return fileInfo;
+ dumpResults(i);
+ nextDumpNumber++;
}
- };
- TransactionUtil.executeInUserTransaction(transactionService, createFileWork);
+ // shuffle folders if required
+ List foldersList = Arrays.asList(folders);
+ if (randomOrder)
+ {
+ // shuffle folder list
+ Collections.shuffle(foldersList);
+ }
+ for (int j = 0; j < folders.length; j++)
+ {
+ if (logger.isDebugEnabled())
+ {
+ String msg = String.format(
+ "Thread %s loading file %4d into folder %4d",
+ Thread.currentThread().getName(),
+ i, j);
+ logger.debug(msg);
+ }
+ final NodeRef folderRef = folders[j];
+ TransactionWork createFileWork = new TransactionWork()
+ {
+ public FileInfo doWork() throws Exception
+ {
+ FileInfo fileInfo = fileFolderService.create(
+ folderRef,
+ GUID.generate(),
+ ContentModel.TYPE_CONTENT);
+ NodeRef nodeRef = fileInfo.getNodeRef();
+ // write the content
+ ContentWriter writer = fileFolderService.getWriter(nodeRef);
+ writer.putContent(dataFile);
+ // done
+ return fileInfo;
+ }
+ };
+ TransactionUtil.executeInUserTransaction(transactionService, createFileWork);
+ }
+ }
+ dumpResults(fileCount);
+ }
+ private void dumpResults(int currentFileCount)
+ {
+ long end = System.currentTimeMillis();
+ long time = (end - start);
+ double average = (double) time / (double) (folderCount * currentFileCount);
+ double percentComplete = (double) currentFileCount / (double) fileCount * 100.0;
+ System.out.println(
+ "[" + Thread.currentThread().getName() + "] \n" +
+ " Created " + currentFileCount + " files in each of " + folderCount + " folders: \n" +
+ " Progress: " + String.format("%9.2f", percentComplete) + " percent complete \n" +
+ " Average: " + String.format("%10.2f", average) + " ms per file \n" +
+ " Average: " + String.format("%10.2f", 1000.0/average) + " files per second");
+ }
+ };
+
+ // kick off the required number of threads
+ System.out.println(
+ "Starting " + threadCount +
+ " threads loading " + fileCount +
+ " files in each of " + folderCount +
+ " folders (" +
+ (randomOrder ? "shuffled" : "in order") + ").");
+ 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("FileLoader-%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
}
}
- long end = System.currentTimeMillis();
- long time = (end - start);
- double average = (double) time / (double) (folderCount * fileCount);
- // done
- return average;
- }
-
- private void timeBuildStructure(NodeRef parentNodeRef, int folderCount, int fileCount)
- {
- System.out.println("Starting load of " + fileCount + " files in each of " + folderCount + " folders");
- double average = buildStructure(parentNodeRef, folderCount, fileCount);
- System.out.println(
- "[" + getName() + "] \n" +
- " Created " + fileCount + " files in each of " + folderCount + " folders: \n" +
- " Average: " + String.format("%10.2f", average) + "ms per file \n" +
- " Average: " + String.format("%10.2f", 1000.0/average) + " files per second");
}
public void test1Folder10Children() throws Exception
{
- timeBuildStructure(rootFolderRef, 1, 10);
+ buildStructure(rootFolderRef, 2, false, 1, 10, null);
}
public void test10Folders100ChildrenMultiTxn() throws Exception
{
- timeBuildStructure(rootFolderRef, 10, 100);
+ buildStructure(rootFolderRef, 2, false, 10, 100, new double[] {0.50});
}
//
+// public void test10Folders100ChildrenMultiTxnMultiThread() throws Exception
+// {
+// buildStructure(4, rootFolderRef, 10, 100);
+// }
+//
+// public void test1000Folders1000ChildrenMultiTxnMultiThread() throws Exception
+// {
+// buildStructure(rootFolderRef, 4, true, 1000, 1000);
+// }
+//
// public void test100Folders1Child() throws Exception
// {
// timeBuildStructure(rootFolderRef, 100, 1);