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