diff --git a/config/alfresco/extension/bm-remote-loader-context.xml.sample b/config/alfresco/extension/bm-remote-loader-context.xml.sample index eb2a78757b..83df9e444c 100644 --- a/config/alfresco/extension/bm-remote-loader-context.xml.sample +++ b/config/alfresco/extension/bm-remote-loader-context.xml.sample @@ -51,6 +51,12 @@ + + + + + + diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java b/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java index 01904c1448..a83bf1fad6 100644 --- a/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java +++ b/source/java/org/alfresco/repo/model/filefolder/loader/FileFolderRemoteLoader.java @@ -265,7 +265,7 @@ public class FileFolderRemoteLoader String valuesStr = properties.getProperty(propertyName); FileFolderRemoteLoader.checkProperty(propertyName, valuesStr); // Parse it into the well-known values - String[] strValues = new String[] {"1", "0", "0", "1", "false"}; + String[] strValues = new String[] {"1", "0", "0", "1", "false", "1"}; int index = 0; StringTokenizer tokenizer = new StringTokenizer(valuesStr, ","); while (tokenizer.hasMoreTokens()) @@ -286,6 +286,7 @@ public class FileFolderRemoteLoader long testTotal = 0L; long testDepth = 1L; boolean testVerbose = false; + long filesPerUpload = 1; try { testCount = Long.parseLong(strValues[0]); @@ -293,12 +294,13 @@ public class FileFolderRemoteLoader testTotal = Long.parseLong(strValues[2]); testDepth = Long.parseLong(strValues[3]); testVerbose = Boolean.parseBoolean(strValues[4]); + filesPerUpload = Long.parseLong(strValues[5]); } catch (Throwable e) { throw new LoaderClientException( "Unable to parse the loader configuration for '" + name + "'. " + LoaderSession.getLineEnding() + - "The correct format is [threadCount], [period(ms)], [total], [folder depth], [verbose]"); + "The correct format is [threadCount], [period(ms)], [total], [folder depth], [verbose]<, [filesPerUpload]>"); } // Construct @@ -307,7 +309,7 @@ public class FileFolderRemoteLoader AbstractLoaderThread thread = null; if (type.equals("upload")) { - thread = new LoaderUploadThread(session, name, testPeriod, testTotal, testDepth, testVerbose); + thread = new LoaderUploadThread(session, name, testPeriod, testTotal, testDepth, testVerbose, filesPerUpload); } else if (type.equals("totals")) { diff --git a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java index 421cab2ddb..261f1d3a70 100644 --- a/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java +++ b/source/java/org/alfresco/repo/model/filefolder/loader/LoaderUploadThread.java @@ -27,6 +27,7 @@ package org.alfresco.repo.model.filefolder.loader; import java.io.File; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import net.sf.ehcache.Cache; @@ -60,15 +61,19 @@ public class LoaderUploadThread extends AbstractLoaderThread pathCache.setCache(cache); } + private int filesPerUpload; + public LoaderUploadThread( LoaderSession session, String loaderName, long testPeriod, long testTotal, long testLoadDepth, - boolean verbose) + boolean verbose, + long filesPerUpload) { super(session, loaderName, testPeriod, testTotal, testLoadDepth, verbose); + this.filesPerUpload = (int) filesPerUpload; } /** @@ -101,7 +106,7 @@ public class LoaderUploadThread extends AbstractLoaderThread FileInfo folderInfo = serverProxy.fileFolderRemote.makeFolders( serverProxy.ticket, currentParentNodeRef, - currentPath, + Collections.singletonList(pathElement), ContentModel.TYPE_FOLDER); currentParentNodeRef = folderInfo.getNodeRef(); // Cache the new node @@ -119,29 +124,41 @@ public class LoaderUploadThread extends AbstractLoaderThread // Make sure the folder exists NodeRef folderNodeRef = makeFolders(serverProxy.ticket, serverProxy, workingRootNodeRef, folderPath); - // Get a random file - File file = getFile(); - byte[] bytes = FileCopyUtils.copyToByteArray(file); - // Get the extension - String filename = GUID.generate(); - int index = file.getName().lastIndexOf('.'); - if (index > 0) + // Build a set of files to upload + byte[][] bytes = new byte[filesPerUpload][]; + String[] filenames = new String[filesPerUpload]; + for (int i = 0; i < filesPerUpload; i++) { - String ext = file.getName().substring(index + 1, file.getName().length()); - filename += ("." + ext); + // Get a random file + File file = getFile(); + bytes[i] = FileCopyUtils.copyToByteArray(file); + // Get the extension + filenames[i] = GUID.generate(); + int index = file.getName().lastIndexOf('.'); + if (index > 0) + { + String ext = file.getName().substring(index + 1, file.getName().length()); + filenames[i] += ("." + ext); + } } // Upload it - FileInfo fileInfo = serverProxy.fileFolderRemote.create( + FileInfo[] fileInfos = serverProxy.loaderRemote.uploadContent( serverProxy.ticket, folderNodeRef, - filename, - ContentModel.TYPE_CONTENT); - NodeRef fileNodeRef = fileInfo.getNodeRef(); - serverProxy.fileFolderRemote.putContent(serverProxy.ticket, fileNodeRef, bytes, filename); + filenames, + bytes); // Done - String msg = String.format("Uploaded %s to folder: %s", filename, folderPath.toString()); + String msg = String.format("Uploaded %d files to folder: %s", fileInfos.length, folderPath.toString()); return msg; } + + @Override + public String getSummary() + { + String summary = super.getSummary(); + summary += (String.format("%d files per iteration", filesPerUpload)); + return summary; + } } diff --git a/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java b/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java index 24e2faae2a..b2389d843b 100644 --- a/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java +++ b/source/java/org/alfresco/repo/remote/LoaderRemoteServer.java @@ -24,17 +24,24 @@ */ package org.alfresco.repo.remote; +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; import java.util.List; import net.sf.acegisecurity.Authentication; import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.encoding.ContentCharsetFinder; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.remote.LoaderRemote; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -67,6 +74,8 @@ public class LoaderRemoteServer implements LoaderRemote private AuthenticationService authenticationService; private NodeService nodeService; private NodeDaoService nodeDaoService; + private FileFolderService fileFolderService; + private MimetypeService mimetypeService; /** * @param transactionService provides transactional support and retrying @@ -100,6 +109,22 @@ public class LoaderRemoteServer implements LoaderRemote this.nodeDaoService = nodeDaoService; } + /** + * @param fileFolderService the file-specific service + */ + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + /** + * @param mimetypeService used to determine encoding, etc + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + /** * {@inheritDoc} */ @@ -236,4 +261,64 @@ public class LoaderRemoteServer implements LoaderRemote AuthenticationUtil.setCurrentAuthentication(authentication); } } + + public FileInfo[] uploadContent( + String ticket, + final NodeRef folderNodeRef, + final String[] filenames, + final byte[][] bytes) + { + if (filenames.length < bytes.length) + { + throw new IllegalArgumentException("The number of files must match the number of binary byte arrays given."); + } + + Authentication authentication = AuthenticationUtil.getCurrentAuthentication(); + try + { + authenticationService.validate(ticket); + + // Make the call + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public FileInfo[] execute() throws Throwable + { + FileInfo[] results = new FileInfo[filenames.length]; + // Create each file + for (int i = 0; i < filenames.length; i++) + { + // Create the file + FileInfo newFileInfo = fileFolderService.create( + folderNodeRef, + filenames[i], + ContentModel.TYPE_CONTENT); + results[i] = newFileInfo; + NodeRef newFileNodeRef = newFileInfo.getNodeRef(); + + // Guess the mimetype + String mimetype = mimetypeService.guessMimetype(filenames[i]); + // Get a writer + ContentWriter writer = fileFolderService.getWriter(newFileNodeRef); + // Make a stream + ByteArrayInputStream is = new ByteArrayInputStream(bytes[i]); + // Guess the encoding + ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder(); + Charset charset = charsetFinder.getCharset(is, mimetype); + // Set metadata + writer.setEncoding(charset.name()); + writer.setMimetype(mimetype); + // Write the stream + writer.putContent(is); + } + // Done + return results; + } + }; + return retryingTransactionHelper.doInTransaction(callback, false, true); + } + finally + { + AuthenticationUtil.setCurrentAuthentication(authentication); + } + } } diff --git a/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java b/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java index fc1ffe9777..f9a238eb6a 100644 --- a/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java +++ b/source/java/org/alfresco/service/cmr/remote/LoaderRemote.java @@ -24,6 +24,7 @@ */ package org.alfresco.service.cmr.remote; +import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -72,4 +73,15 @@ public interface LoaderRemote * @return Returns the total number of nodes for the given ADM store */ public int getNodeCount(String ticket, StoreRef storeRef); + + /** + * Upload multiple files to a folder. + * + * @param ticket the authentication ticket + * @param folderNodeRef the folder to upload to + * @param filenames the names of the files to upload + * @param bytes the contents of the files + * @return Returns the details of each file created + */ + public FileInfo[] uploadContent(String ticket, NodeRef folderNodeRef, String[] filenames, byte[][] bytes); }