diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index ee1e2de81b..a4dea1c84e 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -23,110 +23,110 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - + - - + + - - - - ${authentication.chain} - - + + + + ${authentication.chain} + + - - - - - - - - org.alfresco.repo.security.authentication.MutableAuthenticationDao - - - - - - - - - - - - + + + + + + + + org.alfresco.repo.security.authentication.MutableAuthenticationDao + + + + + + + + + + + + - - - - - - - - org.alfresco.jlan.server.SessionListener - - - - - - - + + + + + + + + org.alfresco.jlan.server.SessionListener + + + + + + + - - - - - - cifsAuthenticator - - - - org.alfresco.jlan.server.auth.ICifsAuthenticator - org.alfresco.repo.management.subsystems.ActivateableBean - - - + + + + + + cifsAuthenticator + + + + org.alfresco.jlan.server.auth.ICifsAuthenticator + org.alfresco.repo.management.subsystems.ActivateableBean + + + @@ -144,88 +144,88 @@ - - - + + + - - - - + + + + 256 - - - + + + - - - - - - - - - localAuthenticationService - - + + + + + + + + + localAuthenticationService + + - + - - - org.alfresco.repo.security.authentication.AuthenticationComponent - - - - - - - - - - ${server.transaction.mode.default} - - - + + + org.alfresco.repo.security.authentication.AuthenticationComponent + + + + + + + + + + ${server.transaction.mode.default} + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - authenticationComponent - - + + + + + + + + + + + + + + + + authenticationComponent + + - - - - - - - - org.alfresco.repo.security.sync.UserRegistrySynchronizer - org.alfresco.repo.security.sync.TestableChainingUserRegistrySynchronizer - org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizerStatus - - - + + + + + + + + org.alfresco.repo.security.sync.UserRegistrySynchronizer + org.alfresco.repo.security.sync.TestableChainingUserRegistrySynchronizer + org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizerStatus + + + - - - - - + + + + + - - - + + + - + - + - - - ${user.name.caseSensitive} - - - ${domain.name.caseSensitive} - - - ${domain.separator} - - + + + ${user.name.caseSensitive} + + + ${domain.name.caseSensitive} + + + ${domain.separator} + + - + @@ -311,44 +311,44 @@ - - - - - - - - ${spaces.store} - - - - - - - - - - - ${create.missing.people} - - - - - - - true - - - - SPLIT - - - true - - - false - - + + + + + + + + ${spaces.store} + + + + + + + + + + + ${create.missing.people} + + + + + + + true + + + + SPLIT + + + true + + + false + + @@ -365,313 +365,313 @@ - - - - - - - - - All - - - - - All - - - + + + + + + + + + All + + + + + All + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + - - - - org.alfresco.repo.security.person.HomeFolderManager - - - - - - - - - - - + + + + org.alfresco.repo.security.person.HomeFolderManager + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + + - - + + - - - - - - - - + + + + + + + + - - - ${spaces.user_homes.regex.key} - - - ${spaces.user_homes.regex.pattern} - - - ${spaces.user_homes.regex.group_order} - - + + + ${spaces.user_homes.regex.key} + + + ${spaces.user_homes.regex.pattern} + + + ${spaces.user_homes.regex.group_order} + + - - - /${spaces.company_home.childname} - - - ${spaces.store} - - + + + /${spaces.company_home.childname} + + + ${spaces.store} + + - - - - - - - - - - Consumer - - - + + + + + + + + + + Consumer + + + - - - /${spaces.company_home.childname}/${spaces.guest_home.childname} - - - ${spaces.store} - - - - - - - - + + + /${spaces.company_home.childname}/${spaces.guest_home.childname} + + + ${spaces.store} + + + + + + + + - + - - - - - - - - - false - - - - All - - - - - All - - - + + + + + + + + + false + + + + All + + + + + All + + + - - - - - - - - - - All - - - + + + + + + + + + + All + + + - - - /${spaces.company_home.childname} - - - ${spaces.store} - - + + + /${spaces.company_home.childname} + + + ${spaces.store} + + - - - /${spaces.company_home.childname}/${spaces.user_homes.childname} - - - ${spaces.store} - - + + + /${spaces.company_home.childname}/${spaces.user_homes.childname} + + + ${spaces.store} + + - - - /${spaces.company_home.childname}/${spaces.user_homes.childname} - - - ${spaces.store} - - + + + /${spaces.company_home.childname}/${spaces.user_homes.childname} + + + ${spaces.store} + + - - - - - org.alfresco.repo.security.authentication.TicketComponent - - - - - - - - - - ${authentication.ticket.validDuration} - - - - ${authentication.ticket.ticketsExpire} - - - - false - - - - - - ${authentication.ticket.expiryMode} - - - ${authentication.ticket.useSingleTicketPerUser} - - - - - - - - - + + + + + org.alfresco.repo.security.authentication.TicketComponent + + + + + + + + + + ${authentication.ticket.validDuration} + + + + ${authentication.ticket.ticketsExpire} + + + + false + + + + + + ${authentication.ticket.expiryMode} + + + ${authentication.ticket.useSingleTicketPerUser} + + + + + + + + + - - - - - %firstName%_%lastName% - + + + + + %firstName%_%lastName% + - - 10 - - + + 10 + + - - - - - - - - - + + + + + + + + + - - - - 8 - - + + + + 8 + + - - - - ${alfresco_user_store.adminusername} - - - ${alfresco_user_store.guestusername} - - + + + + ${alfresco_user_store.adminusername} + + + ${alfresco_user_store.guestusername} + + diff --git a/config/alfresco/model-specific-services-context.xml b/config/alfresco/model-specific-services-context.xml index 8f798a11e8..8a404d619e 100644 --- a/config/alfresco/model-specific-services-context.xml +++ b/config/alfresco/model-specific-services-context.xml @@ -153,9 +153,14 @@ + + + + + - + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index e4e50b005e..9e5f958d45 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -119,14 +119,14 @@ - - - - Unlock - CheckIn - CancelCheckOut - - + + + + Unlock + CheckIn + CancelCheckOut + + @@ -134,7 +134,7 @@ - + @@ -538,8 +538,8 @@ - - + + @@ -557,7 +557,7 @@ org.alfresco.service.cmr.search.CategoryService.createCategory=ACL_ALLOW org.alfresco.service.cmr.search.CategoryService.deleteClassification=ACL_ALLOW org.alfresco.service.cmr.search.CategoryService.deleteCategory=ACL_ALLOW - org.alfresco.service.cmr.search.CategoryService.getTopCategories=ACL_ALLOW + org.alfresco.service.cmr.search.CategoryService.getTopCategories=ACL_ALLOW org.alfresco.service.cmr.search.CategoryService.*=ACL_DENY @@ -796,16 +796,16 @@ org.alfresco.service.cmr.security.AuthorityService.getShortName=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.getName=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.authorityExists=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayName=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayName=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.getOrCreateZone=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.security.AuthorityService.getZone=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.getAuthorityZones=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.getAllAuthoritiesInZone=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.getAllRootAuthoritiesInZone=ACL_ALLOW - org.alfresco.service.cmr.security.AuthorityService.addAuthorityToZones=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.security.AuthorityService.removeAuthorityFromZones=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.security.AuthorityService.getDefaultZones=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.setAuthorityDisplayName=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.getAuthorityDisplayName=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.getOrCreateZone=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.getZone=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.getAuthorityZones=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.getAllAuthoritiesInZone=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.getAllRootAuthoritiesInZone=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.addAuthorityToZones=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.removeAuthorityFromZones=ACL_METHOD.ROLE_ADMINISTRATOR + org.alfresco.service.cmr.security.AuthorityService.getDefaultZones=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.*=ACL_DENY @@ -1200,7 +1200,7 @@ - + @@ -1212,7 +1212,7 @@ org.alfresco.service.cmr.security.PublicServiceAccessService.hasAccess=ACL_ALLOW - + @@ -1230,5 +1230,5 @@ - + diff --git a/pom.xml b/pom.xml index 6d7edeea5c..a29f42a67f 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.alfresco alfresco-text-gen - 1.0-SNAPSHOT + 1.1 org.alfresco.services @@ -85,11 +85,6 @@ commons-lang commons-lang - - org.apache.commons - commons-math3 - 3.3 - commons-pool commons-pool @@ -1001,6 +996,7 @@ **/org/alfresco/AllUnitTestsSuite.java **/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java + **/org/alfresco/repo/model/filefolder/FileFolderLoaderTest.java diff --git a/source/java/org/alfresco/repo/batch/BatchProcessor.java b/source/java/org/alfresco/repo/batch/BatchProcessor.java index 738d456c63..4be50b4146 100644 --- a/source/java/org/alfresco/repo/batch/BatchProcessor.java +++ b/source/java/org/alfresco/repo/batch/BatchProcessor.java @@ -40,9 +40,9 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.node.integrity.IntegrityException; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.util.TraceableThreadFactory; +import org.alfresco.util.transaction.TransactionListenerAdapter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEventPublisher; diff --git a/source/java/org/alfresco/repo/content/AbstractContentReader.java b/source/java/org/alfresco/repo/content/AbstractContentReader.java index b6261650c0..fa23a98deb 100644 --- a/source/java/org/alfresco/repo/content/AbstractContentReader.java +++ b/source/java/org/alfresco/repo/content/AbstractContentReader.java @@ -510,7 +510,7 @@ public abstract class AbstractContentReader extends AbstractContentAccessor impl // done return content; } - catch (IOException e) + catch (Exception e) { throw new ContentIOException("Failed to copy content to string: \n" + " accessor: " + this, diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 2de1a42bde..e002799988 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -63,7 +63,6 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.TransactionAwareSingleton; -import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.TransactionalDao; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -93,6 +92,7 @@ import org.alfresco.util.Pair; import org.alfresco.util.PropertyCheck; import org.alfresco.util.ReadWriteLockExecuter; import org.alfresco.util.ValueProtectingMap; +import org.alfresco.util.transaction.TransactionListenerAdapter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.dao.ConcurrencyFailureException; diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderLoader.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderLoader.java index c01428d08d..500364fb34 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderLoader.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderLoader.java @@ -18,10 +18,35 @@ */ package org.alfresco.repo.model.filefolder; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.RepositoryState; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.filestore.SpoofedTextContentReader; import org.alfresco.repo.model.Repository; +import org.alfresco.repo.node.MLPropertyInterceptor; +import org.alfresco.repo.policy.BehaviourFilter; +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.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.random.NormalDistributionHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Class to aid in the generation of file-folder data structures for load test purposes. @@ -32,53 +57,273 @@ import org.alfresco.service.transaction.TransactionService; * WARNING: This class may be used but will probably NOT be considered part of the public API i.e. * will probably change in line with Alfresco's internal requirements; nevertheless, backward * compatibility will be maintained where practical. + *

+ * Timestamp propagation to the containing folder is disabled in order to reduce overhead. * * @author Derek Hulley * @since 5.1 */ public class FileFolderLoader { + private static Log logger = LogFactory.getLog(FileFolderLoader.class); + private final RepositoryState repoState; private final TransactionService transactionService; private final Repository repositoryHelper; + private final FileFolderService fileFolderService; + private final NodeService nodeService; + private final ContentService contentService; + private final BehaviourFilter policyBehaviourFilter; + private final NormalDistributionHelper normalDistribution; /** * @param repoState keep track of repository readiness * @param transactionService ensure proper rollback, where required * @param repositoryHelper access standard repository paths + * @param fileFolderService perform actual file-folder manipulation */ - public FileFolderLoader(RepositoryState repoState, TransactionService transactionService, Repository repositoryHelper) + public FileFolderLoader( + RepositoryState repoState, + TransactionService transactionService, + Repository repositoryHelper, + FileFolderService fileFolderService, + NodeService nodeService, + ContentService contentService, + BehaviourFilter policyBehaviourFilter) { this.repoState = repoState; this.transactionService = transactionService; this.repositoryHelper = repositoryHelper; + this.fileFolderService = fileFolderService; + this.nodeService = nodeService; + this.contentService = contentService; + this.policyBehaviourFilter = policyBehaviourFilter; + + this.normalDistribution = new NormalDistributionHelper(); } /** + * @return the helper for accessing common repository paths + */ + public Repository getRepository() + { + return repositoryHelper; + } + + /**

+ * Attempt to create a given number of text files within a specified folder. The load tolerates failures unless these + * prevent any files from being created. Options exist to control the file size and text content distributions. + * The cm:auditable aspect automatically applied to each node as part of Alfresco. + * Additionally, extra residual text properties can be added in order to increase the size of the database storage.

+ *

+ * The files are created regardless of the read-write state of the server.

+ *

+ * The current thread's authentication determines the user context and the authenticated user has to have sufficient + * permissions to {@link PermissionService#CREATE_CHILDREN create children} within the folder. This will be enforced + * by the {@link FileFolderService}.

* - * @param folderPath the full path to the folder + * @param folderPath the full path to the folder within the context of the + * {@link Repository#getCompanyHome() Alfresco Company Home} folder e.g. + *
/Sites/Site.default.00009/documentLibrary
. * @param fileCount the number of files to create + * @param filesPerTxn the number of files to create in a transaction. Any failures within a + * transaction (batch) will force the transaction to rollback; normal + * {@link RetryingTransactionHelper#doInTransaction(org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback) retrying semantics } + * are employed. * @param minFileSize the smallest file size (all sizes within 1 standard deviation of the mean) * @param maxFileSize the largest file size (all sizes within 1 standard deviation of the mean) - * @param uniqueContentCount the total number of unique files that can be generated i.e. each file will be - * one of a total number of unique files. + * @param maxUniqueDocuments the maximum number of unique documents that should be created globally. + * A value of 1 means that all documents will be the same document; + * 10,000,000 will mean that there will be 10M unique text sequences used. * @param forceBinaryStorage true to actually write the spoofed text data to the binary store - * i.e. the physical underlying storage will have a real file + * i.e. the physical underlying storage will contain the binary data, allowing + * IO to be realistically stressed if that is a requirement. To save disk + * space, set this value to false, which will see all file data get + * generated on request using a repeatable algorithm. + * @param descriptionCount the number of cm:description multilingual entries to create. The current locale + * is used for the first entry and additional locales are added using the + * {@link Locale#getISOLanguages() Java basic languages list}. The total count cannot + * exceed the total number of languages available. + * TODO: Note that the actual text stored is not (yet) localized. + * @param descriptionSize the size (in bytes) for each cm:description property created; values from 16 bytes to 1024 bytes are supported * @return the number of files successfully created * @throws FileNotFoundException if the folder path does not exist * @throws IllegalStateException if the repository is not ready */ public int createFiles( - String folderPath, - int fileCount, - long minFileSize, long maxFileSize, - boolean forceBinaryStorage, - long uniqueContentCount) throws FileNotFoundException + final String folderPath, + final int fileCount, + final int filesPerTxn, + final long minFileSize, long maxFileSize, + final long maxUniqueDocuments, + final boolean forceBinaryStorage, + final int descriptionCount, final long descriptionSize + ) throws FileNotFoundException { if (repoState.isBootstrapping()) { throw new IllegalStateException("Repository is still bootstrapping."); } - return 0; + if (minFileSize > maxFileSize) + { + throw new IllegalArgumentException("Min/max file sizes incorrect: " + minFileSize + "-" + maxFileSize); + } + if (filesPerTxn < 1) + { + throw new IllegalArgumentException("'filesPerTxn' must be 1 or more."); + } + if (descriptionCount < 0 || descriptionCount > Locale.getISOLanguages().length) + { + throw new IllegalArgumentException("'descriptionCount' exceeds the number of languages available."); + } + if (descriptionSize < 16L || descriptionSize > 1024L) + { + throw new IllegalArgumentException("'descriptionSize' can be anything from 16 to 1024 bytes."); + } + + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + // Locate the folder; this MUST work + RetryingTransactionCallback findFolderWork = new RetryingTransactionCallback() + { + @Override + public NodeRef execute() throws Throwable + { + String folderPathFixed = folderPath; + // Homogenise the path + if (!folderPath.startsWith("/")) + { + folderPathFixed = "/" + folderPath; + } + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + // Special case for the root + if (folderPath.equals("/")) + { + return companyHomeNodeRef; + } + List folderPathElements = Arrays.asList(folderPathFixed.substring(1).split("/")); + FileInfo folderInfo = fileFolderService.resolveNamePath(companyHomeNodeRef, folderPathElements, true); + // Done + return folderInfo.getNodeRef(); + } + }; + NodeRef folderNodeRef = txnHelper.doInTransaction(findFolderWork, false, true); + // Create files + int created = createFiles( + folderNodeRef, fileCount, filesPerTxn, minFileSize, maxFileSize, maxUniqueDocuments, forceBinaryStorage, + descriptionCount, descriptionSize); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Created " + created + " files in folder " + folderPath); + } + return created; + } + + private int createFiles( + final NodeRef folderNodeRef, + final int fileCount, + final int filesPerTxn, + final long minFileSize, final long maxFileSize, + final long maxUniqueDocuments, + final boolean forceBinaryStorage, + final int descriptionCount, final long descriptionSize) + { + final String nameBase = UUID.randomUUID().toString(); + + final AtomicInteger count = new AtomicInteger(0); + RetryingTransactionCallback createFilesWork = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // Disable timestamp propagation to the parent by disabling cm:auditable + policyBehaviourFilter.disableBehaviour(folderNodeRef, ContentModel.ASPECT_AUDITABLE); + + for (int i = 0; i < filesPerTxn; i++) + { + // Only create files while we need; we may need to do fewer in the last txn + if (count.get() >= fileCount) + { + break; + } + // Each load has it's own base name + String name = String.format("%s-%6d.txt", nameBase, count.get()); + // Create a file + FileInfo fileInfo = fileFolderService.create( + folderNodeRef, + name, + ContentModel.TYPE_CONTENT, ContentModel.ASSOC_CONTAINS); + NodeRef fileNodeRef = fileInfo.getNodeRef(); + // Spoofed document + Locale locale = Locale.ENGLISH; + long seed = (long) (Math.random() * maxUniqueDocuments); + long size = normalDistribution.getValue(minFileSize, maxFileSize); + String contentUrl = SpoofedTextContentReader.createContentUrl(locale, seed, size); + SpoofedTextContentReader reader = new SpoofedTextContentReader(contentUrl); + if (forceBinaryStorage) + { + // Stream the text into the real storage + ContentWriter writer = contentService.getWriter(fileNodeRef, ContentModel.PROP_CONTENT, true); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent(reader); + } + else + { + // Just use the URL + ContentData contentData = reader.getContentData(); + nodeService.setProperty(fileNodeRef, ContentModel.PROP_CONTENT, contentData); + } + // Store the description, if required + if (descriptionCount > 0) + { + // Add the cm:description additional properties + boolean wasMLAware = MLPropertyInterceptor.setMLAware(true); + MLText descriptions = new MLText(); + String[] languages = Locale.getISOLanguages(); + String defaultLanguage = Locale.getDefault().getLanguage(); + // Create cm:description translations + for (int descriptionNum = -1; descriptionNum < (descriptionCount-1); descriptionNum++) + { + String language = null; + // Use the default language for the first description + if (descriptionNum == -1) + { + language = defaultLanguage; + } + else if (languages[descriptionNum].equals(defaultLanguage)) + { + // Skip the default language, if we hit it + continue; + } + else + { + language = languages[descriptionNum]; + } + Locale languageLocale = new Locale(language); + // For the cm:description, create new reader with a seed that changes each time + String descriptionUrl = SpoofedTextContentReader.createContentUrl(locale, seed + descriptionNum, descriptionSize); + SpoofedTextContentReader readerDescription = new SpoofedTextContentReader(descriptionUrl); + String description = readerDescription.getContentString(); + descriptions.put(languageLocale, description); + } + nodeService.setProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION, descriptions); + MLPropertyInterceptor.setMLAware(wasMLAware); + } + // Success + count.incrementAndGet(); + } + return null; + } + }; + // Batches + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + int txnCount = (int) Math.ceil((double)fileCount / (double)filesPerTxn); + for (int i = 0; i < txnCount; i++) + { + txnHelper.doInTransaction(createFilesWork, false, true); + } + // Done + return count.get(); } } diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 3c42d963f3..f6a51d5b1f 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -33,7 +33,6 @@ import java.util.ResourceBundle.Control; import java.util.Set; import java.util.Stack; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQueryFactory; import org.alfresco.query.CannedQueryResults; @@ -286,6 +285,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return fileInfo; } + @Override public List toFileInfoList(List nodeRefs) { List fileInfos = new LinkedList(); @@ -319,37 +319,13 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } } - /** - * Checks the type for whether it is a file or folder. All invalid types - * lead to runtime exceptions. - * - * @param typeQName the type to check - * @return Returns true if the type is a valid folder type, false if it is a file. - * @throws AlfrescoRuntimeException if the type is not handled by this service - */ - private boolean isFolder(QName typeQName) throws InvalidTypeException - { - FileFolderServiceType type = getType(typeQName); - - switch (type) - { - case FILE: - return false; - case FOLDER: - return true; - case SYSTEM_FOLDER: - throw new InvalidTypeException("This service should ignore type " + ContentModel.TYPE_SYSTEM_FOLDER); - case INVALID: - default: - throw new InvalidTypeException("Type is not handled by this service: " + typeQName); - } - } - + @Override public boolean exists(NodeRef nodeRef) { return nodeService.exists(nodeRef); } + @Override public FileFolderServiceType getType(QName typeQName) { if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_FOLDER)) @@ -373,6 +349,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } } + @Override public List list(NodeRef contextNodeRef) { // execute the query @@ -481,10 +458,11 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } + @Override public PagingResults list(NodeRef rootNodeRef, Set searchTypeQNames, Set ignoreAspectQNames, List> sortProps, PagingRequest pagingRequest) { - CannedQueryResults results = listImpl(rootNodeRef, null, searchTypeQNames, ignoreAspectQNames, sortProps, pagingRequest); - return getPagingResults(pagingRequest, results); + CannedQueryResults results = listImpl(rootNodeRef, null, searchTypeQNames, ignoreAspectQNames, sortProps, pagingRequest); + return getPagingResults(pagingRequest, results); } private CannedQueryResults listImpl(NodeRef contextNodeRef, boolean files, boolean folders) @@ -500,16 +478,6 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi // note: similar to getChildAssocs(contextNodeRef, searchTypeQNames) but enables paging features, including max items, sorting etc (with permissions per-applied) - /** - * - * @param contextNodeRef - * @param pattern - * @param searchTypeQNames - * @param ignoreAspectQNames - * @param sortProps - * @param pagingRequest - * @return - */ private CannedQueryResults listImpl(NodeRef contextNodeRef, String pattern, Set searchTypeQNames, Set ignoreAspectQNames, List> sortProps, PagingRequest pagingRequest) { Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); @@ -537,6 +505,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return results; } + @Override public List listFiles(NodeRef contextNodeRef) { // execute the query @@ -551,6 +520,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return results; } + @Override public List listFolders(NodeRef contextNodeRef) { // execute the query @@ -565,8 +535,8 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return results; } - public List listDeepFolders(NodeRef contextNodeRef, - SubFolderFilter filter) + @Override + public List listDeepFolders(NodeRef contextNodeRef, SubFolderFilter filter) { List nodeRefs = listSimpleDeep(contextNodeRef, false, true, filter); @@ -614,6 +584,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return resultNodeRef; } + @Override public NodeRef searchSimple(NodeRef contextNodeRef, String name) { ParameterCheck.mandatory("name", name); @@ -634,6 +605,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi /** * @see #search(NodeRef, String, boolean, boolean, boolean) */ + @Override public List search(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders) { return search(contextNodeRef, namePattern, true, true, includeSubFolders); @@ -644,6 +616,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi /** * Full search with all options */ + @Override public List search( NodeRef contextNodeRef, String namePattern, @@ -971,6 +944,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi /** * @see #move(NodeRef, NodeRef, String) */ + @Override public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException { return moveOrCopy(sourceNodeRef, null, null, newName, true); @@ -1006,6 +980,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi /** * @see #moveOrCopy(NodeRef, NodeRef, String, boolean) */ + @Override public FileInfo copy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException { return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, false); @@ -1238,11 +1213,13 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return systemPaths.contains(prefixedPath); } + @Override public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException { return createImpl(parentNodeRef, name, typeQName, null); } + @Override public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException { return createImpl(parentNodeRef, name, typeQName, assocQName); @@ -1262,7 +1239,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi case SYSTEM_FOLDER: throw new InvalidTypeException("System Folders are not handled by this service :" + typeQName); case INVALID: - throw new InvalidTypeException("Type is not handled by this service: " + typeQName); + throw new InvalidTypeException("Type is not handled by this service: " + typeQName); case FILE: case FOLDER: default: @@ -1303,6 +1280,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return fileInfo; } + @Override public void delete(NodeRef nodeRef) { nodeService.deleteNode(nodeRef); @@ -1364,6 +1342,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi * including the destination file or folder * @throws FileNotFoundException if the node could not be found */ + @Override public List getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException { // check the root @@ -1450,6 +1429,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi * including the destination file or folder * @throws FileNotFoundException if the node could not be found */ + @Override public List getNameOnlyPath(NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException { // check the root @@ -1522,11 +1502,13 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } } + @Override public FileInfo resolveNamePath(NodeRef rootNodeRef, List pathElements) throws FileNotFoundException { return resolveNamePath(rootNodeRef, pathElements, true); } + @Override public FileInfo resolveNamePath(NodeRef rootNodeRef, List pathElements, boolean mustExist) throws FileNotFoundException { if (pathElements.size() == 0) @@ -1540,12 +1522,13 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi for (int i = 0; i < folderCount; i++) { String pathElement = pathElements.get(i); + currentPath.append("/").append(pathElement); NodeRef folderNodeRef = searchSimple(parentNodeRef, pathElement); if (folderNodeRef == null) { if (mustExist) { - throw new FileNotFoundException("Folder not found: " + currentPath); + throw new FileNotFoundException("Folder not found: " + currentPath + " (in " + rootNodeRef + ")"); } else { @@ -1556,12 +1539,13 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } // we have resolved the folder path - resolve the last component String pathElement = pathElements.get(pathElements.size() - 1); + currentPath.append("/").append(pathElement); NodeRef fileNodeRef = searchSimple(parentNodeRef, pathElement); if (fileNodeRef == null) { if (mustExist) { - throw new FileNotFoundException("File not found: " + currentPath); + throw new FileNotFoundException("File not found: " + currentPath + " (in " + rootNodeRef + ")"); } else { @@ -1580,6 +1564,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return result; } + @Override public FileInfo getFileInfo(NodeRef nodeRef) { try @@ -1592,6 +1577,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi } } + @Override public ContentReader getReader(NodeRef nodeRef) { FileInfo fileInfo = toFileInfo(nodeRef, false); @@ -1602,6 +1588,7 @@ public class FileFolderServiceImpl extends AbstractBaseCopyService implements Fi return contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); } + @Override public ContentWriter getWriter(NodeRef nodeRef) { FileInfo fileInfo = toFileInfo(nodeRef, false); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index fbd53be94a..7e2e901e95 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -52,7 +52,6 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; @@ -86,6 +85,7 @@ import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyMap; +import org.alfresco.util.transaction.TransactionListenerAdapter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; diff --git a/source/test-java/org/alfresco/repo/model/ModelTestSuite.java b/source/test-java/org/alfresco/repo/model/ModelTestSuite.java index 26c31931f7..390d82f1a4 100644 --- a/source/test-java/org/alfresco/repo/model/ModelTestSuite.java +++ b/source/test-java/org/alfresco/repo/model/ModelTestSuite.java @@ -19,6 +19,7 @@ package org.alfresco.repo.model; import org.alfresco.repo.model.filefolder.FileFolderDuplicateChildTest; +import org.alfresco.repo.model.filefolder.FileFolderLoaderTest; import org.alfresco.repo.model.filefolder.FileFolderServiceImplTest; import org.alfresco.repo.model.filefolder.FileFolderServicePropagationTest; import org.alfresco.repo.model.filefolder.HiddenAspectTest; @@ -52,7 +53,7 @@ import org.junit.runners.Suite.SuiteClasses; // These need to come afterwards, as they insert extra // interceptors which would otherwise confuse things FileFolderServiceImplTest.class, - // TODO + FileFolderLoaderTest.class, FileFolderDuplicateChildTest.class, FileFolderServicePropagationTest.class }) diff --git a/source/test-java/org/alfresco/repo/model/filefolder/FileFolderLoaderTest.java b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderLoaderTest.java new file mode 100644 index 0000000000..86490edf2d --- /dev/null +++ b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderLoaderTest.java @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.model.filefolder; + +import java.util.List; +import java.util.Locale; + +import junit.framework.TestCase; +import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.node.MLPropertyInterceptor; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.AccessDeniedException; +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.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.ApplicationContextHelper; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.context.ApplicationContext; + +import static org.junit.Assert.assertNotEquals; + +/** + * @see org.alfresco.repo.model.filefolder.FileFolderLoader + * @author Derek Hulley + * @since 5.1 + */ +@Category(OwnJVMTestsCategory.class) +public class FileFolderLoaderTest extends TestCase +{ + private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private FileFolderLoader fileFolderLoader; + private FileFolderService fileFolderService; + private PermissionService permissionService; + private TransactionService transactionService; + private NodeService nodeService; + private String sharedHomePath; + private NodeRef hiddenFolderNodeRef; + private String hiddenFolderPath; + private NodeRef readOnlyFolderNodeRef; + private String readOnlyFolderPath; + private NodeRef writeFolderNodeRef; + private String writeFolderPath; + + @Override + public void setUp() throws Exception + { + AuthenticationUtil.pushAuthentication(); + + RunAsWork setUpWork = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + fileFolderLoader = (FileFolderLoader) ctx.getBean("FileFolderLoader"); + fileFolderService = (FileFolderService) ctx.getBean("FileFolderService"); + permissionService = (PermissionService) ctx.getBean("PermissionService"); + transactionService = (TransactionService) ctx.getBean("TransactionService"); + nodeService = (NodeService) ctx.getBean("nodeService"); + NodeRef companyHomeNodeRef = fileFolderLoader.getRepository().getCompanyHome(); + NodeRef sharedHomeNodeRef = fileFolderLoader.getRepository().getSharedHome(); + List sharedHomeFileInfos = fileFolderService.getNamePath(companyHomeNodeRef, sharedHomeNodeRef); + sharedHomePath = "/" + sharedHomeFileInfos.get(0).getName(); + + // Create a folder that will be invisible to all normal users + FileInfo hiddenFolderInfo = fileFolderService.create(sharedHomeNodeRef, "HideThis", ContentModel.TYPE_FOLDER); + hiddenFolderNodeRef = hiddenFolderInfo.getNodeRef(); + hiddenFolderPath = sharedHomePath + "/HideThis"; + permissionService.setInheritParentPermissions(hiddenFolderNodeRef, false); + + // Create a folder that will be read-only + FileInfo readOnlyFolderInfo = fileFolderService.create(sharedHomeNodeRef, "ReadOnlyThis", ContentModel.TYPE_FOLDER); + readOnlyFolderNodeRef = readOnlyFolderInfo.getNodeRef(); + readOnlyFolderPath = sharedHomePath + "/ReadOnlyThis"; + permissionService.setInheritParentPermissions(readOnlyFolderNodeRef, false); + permissionService.setPermission(readOnlyFolderNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + + // Create a folder to write to + FileInfo writeFolderInfo = fileFolderService.create(sharedHomeNodeRef, "WriteThis", ContentModel.TYPE_FOLDER); + writeFolderNodeRef = writeFolderInfo.getNodeRef(); + writeFolderPath = sharedHomePath + "/WriteThis"; + + // Done + return null; + } + }; + AuthenticationUtil.runAsSystem(setUpWork); + } + + @Override + public void tearDown() throws Exception + { + RunAsWork setUpWork = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + fileFolderService.delete(hiddenFolderNodeRef); + fileFolderService.delete(readOnlyFolderNodeRef); + fileFolderService.delete(writeFolderNodeRef); + // Done + return null; + } + }; + AuthenticationUtil.runAsSystem(setUpWork); + + AuthenticationUtil.popAuthentication(); + } + + @Test + public void testBasic() + { + assertNotNull(fileFolderLoader); + assertNotNull(sharedHomePath); + } + + @Test + public void testIllegalArgs_MinMax() throws Exception + { + try + { + fileFolderLoader.createFiles( + sharedHomePath, + 1, 256, 100L, 10L, Long.MAX_VALUE, false, + 10, 256); + fail("Should detect min/max size issue."); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + @Test + public void testIllegalArgs_DescriptionCount() throws Exception + { + try + { + fileFolderLoader.createFiles( + sharedHomePath, + 1, 256, 1024L, 10L, Long.MAX_VALUE, false, + Integer.MAX_VALUE, 256); + fail("Should detect description count issue."); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + @Test + public void testIllegalArgs_DescriptionSize() throws Exception + { + try + { + fileFolderLoader.createFiles( + sharedHomePath, + 1, 256, 1024L, 10L, Long.MAX_VALUE, false, + 10, Long.MAX_VALUE); + fail("Should detect description size issue."); + } + catch (IllegalArgumentException e) + { + // Expected + } + } + + @Test + public void testNoPermissionsAtAll() throws Exception + { + try + { + fileFolderLoader.createFiles( + sharedHomePath, + 0, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + fail("No permissions to see folder."); + } + catch (AuthenticationCredentialsNotFoundException e) + { + // Expected + } + } + + @Test + public void testNoPermissionsToFindFolder() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser("BOB-1"); + fileFolderLoader.createFiles( + hiddenFolderPath, + 0, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + fail("No permissions to see folder."); + } + catch (AccessDeniedException e) + { + // Expected + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + @Test + public void testFolderMissing() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + fileFolderLoader.createFiles( + sharedHomePath + "/Missing", + 0, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + fail("Folder does not exist"); + } + catch (AlfrescoRuntimeException e) + { + // Expected + assertTrue(e.getCause() instanceof FileNotFoundException); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + @Test + public void testNoPermissionsToWriteToFolder() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser("BOB-1"); + fileFolderLoader.createFiles( + readOnlyFolderPath, + 1, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + fail("Folder is read only. Should not be able to write to it."); + } + catch (AccessDeniedException e) + { + // Expected + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + /** + * Zero files + */ + @Test + public void testLoad_ZeroFiles() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + int created = fileFolderLoader.createFiles( + writeFolderPath, + 0, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + assertEquals("Incorrect number of files generated.", 0, created); + // Count + assertEquals(0, nodeService.countChildAssocs(writeFolderNodeRef, true)); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + /** + * One file + */ + @Test + public void testLoad_OneFile() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + int created = fileFolderLoader.createFiles( + writeFolderPath, + 1, 256, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + assertEquals("Incorrect number of files generated.", 1, created); + // Check the descriptions + RetryingTransactionCallback checkCallback = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + MLPropertyInterceptor.setMLAware(true); + List childAssocs = nodeService.getChildAssocs(writeFolderNodeRef); + // Count + assertEquals(1, childAssocs.size()); + NodeRef fileNodeRef = childAssocs.get(0).getChildRef(); + MLText descriptions = (MLText) nodeService.getProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION); + assertNotNull("No descriptions added", descriptions); + assertEquals("Incorrect number of unique descriptions added: ", 10, descriptions.size()); + assertTrue("Expect the default language to be present. ", + descriptions.containsKey(new Locale(Locale.getDefault().getLanguage()))); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(checkCallback, true); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + /** + * 100 files; 10 per txn + */ + @Test + public void testLoad_02() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + int created = fileFolderLoader.createFiles( + writeFolderPath, + 100, 10, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + assertEquals("Incorrect number of files generated.", 100, created); + // Count + assertEquals(100, nodeService.countChildAssocs(writeFolderNodeRef, true)); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + /** + * 15 files; 10 per txn; spoofed; different + */ + @Test + public void testLoad_03() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + int created = fileFolderLoader.createFiles( + writeFolderPath, + 15, 10, 1024L, 1024L, Long.MAX_VALUE, false, + 10, 256L); + assertEquals("Incorrect number of files generated.", 15, created); + // Count + assertEquals(15, nodeService.countChildAssocs(writeFolderNodeRef, true)); + // Check the files + List fileInfos = fileFolderService.listFiles(writeFolderNodeRef); + String lastText = null; + String lastDescr = null; + String lastUrl = null; + for (FileInfo fileInfo : fileInfos) + { + NodeRef fileNodeRef = fileInfo.getNodeRef(); + // The URLs must all be unique as we wrote the physical binaries + ContentReader reader = fileFolderService.getReader(fileNodeRef); + assertEquals("UTF-8", reader.getEncoding()); + assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, reader.getMimetype()); + assertEquals(1024L, reader.getSize()); + if (lastUrl == null) + { + lastUrl = reader.getContentUrl(); + } + else + { + assertNotEquals("We expect different URLs: ", lastUrl, reader.getContentUrl()); + lastUrl = reader.getContentUrl(); + } + // Check content + if (lastText == null) + { + lastText = reader.getContentString(); + } + else + { + String currentStr = reader.getContentString(); + assertNotEquals("All text must differ due to varying seed. ", lastText, currentStr); + lastText = currentStr; + } + // Check description + if (lastDescr == null) + { + lastDescr = (String) nodeService.getProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION); + assertEquals("cm:description length is incorrect. ", 256, lastDescr.getBytes().length); + } + else + { + String currentDescr = (String) nodeService.getProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION); + assertNotEquals("All descriptions must differ due to varying seed. ", lastDescr, currentDescr); + lastDescr = currentDescr; + } + } + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } + + /** + * 10 files; 10 per txn; force storage; identical + */ + @Test + public void testLoad_04() throws Exception + { + try + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + int created = fileFolderLoader.createFiles( + writeFolderPath, + 10, 10, 1024L, 1024L, 1L, true, + 10, 256L); + assertEquals("Incorrect number of files generated.", 10, created); + // Count + assertEquals(10, nodeService.countChildAssocs(writeFolderNodeRef, true)); + // Check the files + List fileInfos = fileFolderService.listFiles(writeFolderNodeRef); + String lastText = null; + String lastDescr = null; + String lastUrl = null; + for (FileInfo fileInfo : fileInfos) + { + NodeRef fileNodeRef = fileInfo.getNodeRef(); + // The URLs must all be unique as we wrote the physical binaries + ContentReader reader = fileFolderService.getReader(fileNodeRef); + assertEquals("UTF-8", reader.getEncoding()); + assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, reader.getMimetype()); + assertEquals(1024L, reader.getSize()); + if (lastUrl == null) + { + lastUrl = reader.getContentUrl(); + } + else + { + assertNotEquals("We expect unique URLs: ", lastUrl, reader.getContentUrl()); + lastUrl = reader.getContentUrl(); + } + // Check content + if (lastText == null) + { + lastText = reader.getContentString(); + } + else + { + String currentStr = reader.getContentString(); + assertEquals("All text must be identical due to same seed. ", lastText, currentStr); + lastText = currentStr; + } + // Check description + if (lastDescr == null) + { + lastDescr = (String) nodeService.getProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION); + assertEquals("cm:description length is incorrect. ", 256, lastDescr.getBytes().length); + } + else + { + String currentDescr = (String) nodeService.getProperty(fileNodeRef, ContentModel.PROP_DESCRIPTION); + assertEquals("All descriptions must be identical due to varying seed. ", lastDescr, currentDescr); + lastDescr = currentDescr; + } + } + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } +} diff --git a/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index 16a8a224d8..4901dc0c4e 100644 --- a/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -186,12 +186,12 @@ public class FileFolderServiceImplTest extends TestCase QName.createQName(NamespaceService.ALFRESCO_URI, "working root1"), QName.createQName("http://www.alfresco.org/test/filefoldertest/1.0", "folder")).getChildRef(); nodeService.createNode( - workingRootNodeRef1, + workingRootNodeRef1, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.ALFRESCO_URI, "node1"), ContentModel.TYPE_CONTENT).getChildRef(); nodeService.createNode( - workingRootNodeRef1, + workingRootNodeRef1, QName.createQName("http://www.alfresco.org/test/filefoldertest/1.0", "contains1"), QName.createQName(NamespaceService.ALFRESCO_URI, "node2"), ContentModel.TYPE_CONTENT).getChildRef(); @@ -265,20 +265,20 @@ public class FileFolderServiceImplTest extends TestCase public void testShallowFilesAndFoldersListWithLocale() throws Exception { - Locale savedLocale = I18NUtil.getContentLocaleOrNull(); - try - { - I18NUtil.setContentLocale(Locale.CANADA); - List files = fileFolderService.list(workingRootNodeRef); - // check - String[] expectedNames = new String[] - { NAME_L0_FILE_A, NAME_L0_FILE_B, NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C }; - checkFileList(files, 2, 3, expectedNames); - } - finally - { - I18NUtil.setContentLocale(savedLocale); - } + Locale savedLocale = I18NUtil.getContentLocaleOrNull(); + try + { + I18NUtil.setContentLocale(Locale.CANADA); + List files = fileFolderService.list(workingRootNodeRef); + // check + String[] expectedNames = new String[] + { NAME_L0_FILE_A, NAME_L0_FILE_B, NAME_L0_FOLDER_A, NAME_L0_FOLDER_B, NAME_L0_FOLDER_C }; + checkFileList(files, 2, 3, expectedNames); + } + finally + { + I18NUtil.setContentLocale(savedLocale); + } } public void testShallowFilesOnlyList() throws Exception @@ -1046,17 +1046,17 @@ public class FileFolderServiceImplTest extends TestCase */ public void testGetType() throws Exception { - Locale savedLocale = I18NUtil.getContentLocaleOrNull(); - try - { - I18NUtil.setContentLocale(Locale.CANADA); - FileFolderServiceType type = fileFolderService.getType(ContentModel.TYPE_FOLDER); - assertEquals("Type incorrect for folder", FileFolderServiceType.FOLDER, type); - } - finally - { + Locale savedLocale = I18NUtil.getContentLocaleOrNull(); + try + { + I18NUtil.setContentLocale(Locale.CANADA); + FileFolderServiceType type = fileFolderService.getType(ContentModel.TYPE_FOLDER); + assertEquals("Type incorrect for folder", FileFolderServiceType.FOLDER, type); + } + finally + { I18NUtil.setContentLocale(savedLocale); - } + } } public void testETHREEOH_3088_MoveIntoSelf() throws Exception @@ -1348,7 +1348,7 @@ public class FileFolderServiceImplTest extends TestCase { // sanity checks only (see also GetChildrenCannedQueryTest) - I18NUtil.setContentLocale(Locale.CANADA); + I18NUtil.setContentLocale(Locale.CANADA); // test 1 PagingRequest pagingRequest = new PagingRequest(100, null); @@ -1397,7 +1397,7 @@ public class FileFolderServiceImplTest extends TestCase public void testALF12758() { - // test that the FileFolderService returns only cm:contains children + // test that the FileFolderService returns only cm:contains children PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE); PagingResults pagingResults = fileFolderService.list(workingRootNodeRef1, true, true, null, null, null, pagingRequest); assertNotNull(pagingResults); @@ -1438,47 +1438,47 @@ public class FileFolderServiceImplTest extends TestCase public void testList_HiddenFiles() { - // Test that hidden files are not returned for clients that should not be able to see them, - // and that the total result count is correct. + // Test that hidden files are not returned for clients that should not be able to see them, + // and that the total result count is correct. - Client saveClient = FileFilterMode.setClient(Client.webdav); - try - { - // create some hidden files - NodeRef nodeRef = fileFolderService.create(workingRootNodeRef, "" + System.currentTimeMillis(), ContentModel.TYPE_CONTENT).getNodeRef(); - NodeRef nodeRef1 = fileFolderService.create(nodeRef, "parent", ContentModel.TYPE_CONTENT).getNodeRef(); - for(int i = 0; i < 10; i++) - { - fileFolderService.create(nodeRef1, ".child" + i, ContentModel.TYPE_CONTENT).getNodeRef(); - } - - // and some visible files - for(int i = 0; i < 10; i++) - { - fileFolderService.create(nodeRef1, "visiblechild" + i, ContentModel.TYPE_CONTENT).getNodeRef(); - } + Client saveClient = FileFilterMode.setClient(Client.webdav); + try + { + // create some hidden files + NodeRef nodeRef = fileFolderService.create(workingRootNodeRef, "" + System.currentTimeMillis(), ContentModel.TYPE_CONTENT).getNodeRef(); + NodeRef nodeRef1 = fileFolderService.create(nodeRef, "parent", ContentModel.TYPE_CONTENT).getNodeRef(); + for(int i = 0; i < 10; i++) + { + fileFolderService.create(nodeRef1, ".child" + i, ContentModel.TYPE_CONTENT).getNodeRef(); + } + + // and some visible files + for(int i = 0; i < 10; i++) + { + fileFolderService.create(nodeRef1, "visiblechild" + i, ContentModel.TYPE_CONTENT).getNodeRef(); + } - // switch to a client that should not see the hidden files - saveClient = FileFilterMode.setClient(Client.cmis); - PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE); - pagingRequest.setRequestTotalCountMax(10000); // need this so that total count is set + // switch to a client that should not see the hidden files + saveClient = FileFilterMode.setClient(Client.cmis); + PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE); + pagingRequest.setRequestTotalCountMax(10000); // need this so that total count is set - PagingResults results = fileFolderService.list(nodeRef1, true, true, null, null, pagingRequest); - Pair totalResultCount = results.getTotalResultCount(); - assertNotNull(totalResultCount.getFirst()); - assertEquals("Total result lower count should be 10", 10, totalResultCount.getFirst().intValue()); - assertNotNull(totalResultCount.getSecond()); - assertEquals("Total result upper count should be 10", 10, totalResultCount.getSecond().intValue()); - for(FileInfo fileInfo : results.getPage()) - { - assertTrue(fileInfo.getName().startsWith("visiblechild")); - } - assertEquals("Expected only 10 results", 10, results.getPage().size()); - } - finally - { - FileFilterMode.setClient(saveClient); - } + PagingResults results = fileFolderService.list(nodeRef1, true, true, null, null, pagingRequest); + Pair totalResultCount = results.getTotalResultCount(); + assertNotNull(totalResultCount.getFirst()); + assertEquals("Total result lower count should be 10", 10, totalResultCount.getFirst().intValue()); + assertNotNull(totalResultCount.getSecond()); + assertEquals("Total result upper count should be 10", 10, totalResultCount.getSecond().intValue()); + for(FileInfo fileInfo : results.getPage()) + { + assertTrue(fileInfo.getName().startsWith("visiblechild")); + } + assertEquals("Expected only 10 results", 10, results.getPage().size()); + } + finally + { + FileFilterMode.setClient(saveClient); + } } public void testList_notCheckedOut_ALF_13602() @@ -1637,7 +1637,7 @@ public class FileFolderServiceImplTest extends TestCase assertFalse(copyInfo.getName().contains("(Working Copy)")); assertTrue(copyInfo.getName().substring(0, copyExtIndex).startsWith(checkedOutName.substring(0, origExtIndex))); } - + public void testSortingCustomFields() { // Test sorting based on MNT-11120 @@ -1734,5 +1734,5 @@ public class FileFolderServiceImplTest extends TestCase results = fileFolderService.list(parentTestRef, true, false, null, sortProps, pagingRequest); expectedNames = new String[] { "B-null", "A-foo", "A-bar", "B-baz", "A-null", "B-biz" }; checkFileList(pageRes, 6, 0, expectedNames); - } + } } diff --git a/source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java b/source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java index 096f5fc6cd..5e52863a65 100644 --- a/source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java +++ b/source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java @@ -52,6 +52,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.Pair; +import org.alfresco.util.transaction.TransactionListenerAdapter; import org.apache.commons.lang.mutable.MutableInt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -725,8 +726,11 @@ public class RetryingTransactionHelperTest extends TestCase assertFalse("New transaction has not started", txnId.equals(listener.newTxnId)); } - private void runThreads(final RetryingTransactionHelper txnHelper, final List caughtExceptions, - Pair... startDurationPairs) + @SuppressWarnings("unchecked") + private void runThreads( + final RetryingTransactionHelper txnHelper, + final List caughtExceptions, + final Pair... startDurationPairs) { ExecutorService executorService = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(10));