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);