diff --git a/source/java/org/alfresco/repo/bulkimport/BulkImportParameters.java b/source/java/org/alfresco/repo/bulkimport/BulkImportParameters.java index edd3388043..b79b3cbe01 100644 --- a/source/java/org/alfresco/repo/bulkimport/BulkImportParameters.java +++ b/source/java/org/alfresco/repo/bulkimport/BulkImportParameters.java @@ -1,36 +1,45 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.bulkimport; import org.alfresco.service.cmr.repository.NodeRef; public class BulkImportParameters { + // MNT-17703: Provide configurable behaviour for when the target file already exists in the repository. + public enum ExistingFileMode + { + // If the file already exists... + SKIP, // skip the import from the source. + REPLACE, // replace the file, loosing any previous version history. + ADD_VERSION // create a new version of the file during import, preserving previous history. + }; + + private ExistingFileMode existingFileMode = ExistingFileMode.SKIP; private NodeRef target; - private boolean replaceExisting = false; private Integer batchSize; private Integer numThreads; private Integer loggingInterval; @@ -60,14 +69,6 @@ public class BulkImportParameters { this.target = target; } - public boolean isReplaceExisting() - { - return replaceExisting; - } - public void setReplaceExisting(boolean replaceExisting) - { - this.replaceExisting = replaceExisting; - } public Integer getBatchSize() { return batchSize; @@ -84,5 +85,40 @@ public class BulkImportParameters { this.numThreads = numThreads; } - + + /** + * @deprecated Use {@link #getExistingFileMode} (MNT-17703) + * @return + */ + public boolean isReplaceExisting() + { + return existingFileMode == ExistingFileMode.REPLACE; + } + + /** + * @deprecated Use {@link #setExistingFileMode} (MNT-17703) + * @param replaceExisting + */ + @Deprecated() + public void setReplaceExisting(boolean replaceExisting) + { + if (replaceExisting) + { + setExistingFileMode(ExistingFileMode.REPLACE); + } + else + { + setExistingFileMode(ExistingFileMode.SKIP); + } + } + + public ExistingFileMode getExistingFileMode() + { + return existingFileMode; + } + + public void setExistingFileMode(ExistingFileMode existingFileMode) + { + this.existingFileMode = existingFileMode; + } } diff --git a/source/java/org/alfresco/repo/bulkimport/ImportableItem.java b/source/java/org/alfresco/repo/bulkimport/ImportableItem.java index 2503950806..06e740f5dc 100644 --- a/source/java/org/alfresco/repo/bulkimport/ImportableItem.java +++ b/source/java/org/alfresco/repo/bulkimport/ImportableItem.java @@ -120,6 +120,10 @@ public final class ImportableItem public Set getVersionEntries() { + if (versionEntries == null) + { + return Collections.emptySet(); + } return(Collections.unmodifiableSet(versionEntries)); } diff --git a/source/java/org/alfresco/repo/bulkimport/NodeImporter.java b/source/java/org/alfresco/repo/bulkimport/NodeImporter.java index f299accd54..288a5fc5ef 100644 --- a/source/java/org/alfresco/repo/bulkimport/NodeImporter.java +++ b/source/java/org/alfresco/repo/bulkimport/NodeImporter.java @@ -23,20 +23,20 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.repo.bulkimport; - -import java.io.File; - -import org.alfresco.service.cmr.repository.NodeRef; - -/** - * Imports an importable item in the filesystem into the repository by creating a node to represent it. - * - * @since 4.0 - * - */ -public interface NodeImporter -{ - public NodeRef importImportableItem(ImportableItem importableItem, boolean replaceExisting); - public File getSourceFolder(); -} +package org.alfresco.repo.bulkimport; + +import java.io.File; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Imports an importable item in the filesystem into the repository by creating a node to represent it. + * + * @since 4.0 + * + */ +public interface NodeImporter +{ + public NodeRef importImportableItem(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode); + public File getSourceFolder(); +} diff --git a/source/java/org/alfresco/repo/bulkimport/impl/AbstractNodeImporter.java b/source/java/org/alfresco/repo/bulkimport/impl/AbstractNodeImporter.java index 8ad21457f8..256438b356 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/AbstractNodeImporter.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/AbstractNodeImporter.java @@ -1,20 +1,20 @@ /* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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, + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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. @@ -33,6 +33,7 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.bulkimport.BulkFilesystemImporter; +import org.alfresco.repo.bulkimport.BulkImportParameters; import org.alfresco.repo.bulkimport.DirectoryAnalyser; import org.alfresco.repo.bulkimport.ImportableItem; import org.alfresco.repo.bulkimport.MetadataLoader; @@ -99,7 +100,7 @@ public abstract class AbstractNodeImporter implements NodeImporter this.behaviourFilter = behaviourFilter; } - protected abstract NodeRef importImportableItemImpl(ImportableItem importableItem, boolean replaceExisting); + protected abstract NodeRef importImportableItemImpl(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode); protected abstract void importContentAndMetadata(NodeRef nodeRef, ImportableItem.ContentAndMetadata contentAndMetadata, MetadataLoader.Metadata metadata); /* @@ -181,11 +182,19 @@ public abstract class AbstractNodeImporter implements NodeImporter return(result); } - protected final int importImportableItemFile(NodeRef nodeRef, ImportableItem importableItem, MetadataLoader.Metadata metadata, NodeState nodeState) + protected final int importImportableItemFile(NodeRef nodeRef, ImportableItem importableItem, MetadataLoader.Metadata metadata, NodeState nodeState, BulkImportParameters.ExistingFileMode existingFileMode) { int result = 0; - if (importableItem.hasVersionEntries()) + if (nodeState == NodeState.REPLACED && existingFileMode == BulkImportParameters.ExistingFileMode.ADD_VERSION) + { + // It is being replaced, and ADD_VERSION is the selected method of dealing with overwrites. + Map versionProperties = new HashMap<>(); + versionProperties.put(ContentModel.PROP_VERSION_TYPE, VersionType.MAJOR); + versionService.ensureVersioningEnabled(nodeRef, versionProperties); + result = importContentVersions(nodeRef, importableItem, nodeState); + } + else if (importableItem.hasVersionEntries()) { result = importContentVersions(nodeRef, importableItem, nodeState); } @@ -242,9 +251,18 @@ public abstract class AbstractNodeImporter implements NodeImporter return(result); } - protected final Triple createOrFindNode(NodeRef target, ImportableItem importableItem, - boolean replaceExisting, MetadataLoader.Metadata metadata) + // TODO: this is a confusing method that does too many things. It doesn't just "create or find" + // but also decides whether an existing node (i.e. the 'find' in 'create or find') WILL BE + // skipped or replaced further on in the calling code. + // TODO: refactor? + protected final Triple createOrFindNode( + NodeRef target, ImportableItem importableItem, + BulkImportParameters.ExistingFileMode existingFileMode, MetadataLoader.Metadata metadata) { + // ADD_VERSION isn't strictly a replacement option, but we need to deal with it as such, at least as an + // interim measure while the new ExistingFileMode options are introduced (MNT-17703) + boolean replaceExisting = (existingFileMode == BulkImportParameters.ExistingFileMode.REPLACE || + existingFileMode == BulkImportParameters.ExistingFileMode.ADD_VERSION); Triple result = null; boolean isDirectory = false; NodeState nodeState = replaceExisting ? NodeState.REPLACED : NodeState.SKIPPED; @@ -424,14 +442,14 @@ public abstract class AbstractNodeImporter implements NodeImporter return(result); } - public NodeRef importImportableItem(ImportableItem importableItem, boolean replaceExisting) + public NodeRef importImportableItem(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode) { if(logger.isDebugEnabled()) { logger.debug("Importing " + String.valueOf(importableItem)); } - NodeRef nodeRef = importImportableItemImpl(importableItem, replaceExisting); + NodeRef nodeRef = importImportableItemImpl(importableItem, existingFileMode); // allow parent to be garbage collected //importableItem.setParent(null); diff --git a/source/java/org/alfresco/repo/bulkimport/impl/BulkImportStatusImpl.java b/source/java/org/alfresco/repo/bulkimport/impl/BulkImportStatusImpl.java index 1514bd65b3..ea4828dc45 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/BulkImportStatusImpl.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/BulkImportStatusImpl.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.bulkimport.impl; import java.io.PrintWriter; diff --git a/source/java/org/alfresco/repo/bulkimport/impl/MultiThreadedBulkFilesystemImporter.java b/source/java/org/alfresco/repo/bulkimport/impl/MultiThreadedBulkFilesystemImporter.java index b95a5b2cfd..5619f881c6 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/MultiThreadedBulkFilesystemImporter.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/MultiThreadedBulkFilesystemImporter.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.bulkimport.impl; import org.alfresco.model.ContentModel; @@ -126,7 +126,7 @@ public abstract class MultiThreadedBulkFilesystemImporter extends AbstractBulkFi { behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE); - NodeRef nodeRef = nodeImporter.importImportableItem(importableItem, bulkImportParameters.isReplaceExisting()); + NodeRef nodeRef = nodeImporter.importImportableItem(importableItem, bulkImportParameters.getExistingFileMode()); filesystemTracker.itemImported(nodeRef, importableItem); } finally diff --git a/source/java/org/alfresco/repo/bulkimport/impl/StreamingNodeImporterFactory.java b/source/java/org/alfresco/repo/bulkimport/impl/StreamingNodeImporterFactory.java index 49ca7173d6..705a8e6633 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/StreamingNodeImporterFactory.java +++ b/source/java/org/alfresco/repo/bulkimport/impl/StreamingNodeImporterFactory.java @@ -1,20 +1,20 @@ /* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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, + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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. @@ -30,6 +30,7 @@ import java.io.IOException; import java.nio.file.Files; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.bulkimport.BulkImportParameters; import org.alfresco.repo.bulkimport.ImportableItem; import org.alfresco.repo.bulkimport.MetadataLoader; import org.alfresco.repo.bulkimport.NodeImporter; @@ -109,7 +110,7 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory importImportableItemMetadata(nodeRef, contentAndMetadata.getContentFile(), metadata); } - protected NodeRef importImportableItemImpl(ImportableItem importableItem, boolean replaceExisting) + protected NodeRef importImportableItemImpl(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode) { NodeRef target = importableItem.getParent().getNodeRef(); if(target == null) @@ -120,7 +121,16 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory NodeRef result = null; MetadataLoader.Metadata metadata = loadMetadata(importableItem.getHeadRevision()); - Triple node = createOrFindNode(target, importableItem, replaceExisting, metadata); + // TODO: we'll get NodeState.REPLACED back from this method (i.e. the node WILL be replaced) + // even if we're using ExistingFileMode.ADD_VERSION - we need to do this currently, otherwise + // the file would be SKIPPED and various other checks that are only computed if replace is being used, + // wouldn't happen. + // TODO: sort this out. + Triple node = createOrFindNode( + target, + importableItem, + existingFileMode, + metadata); boolean isDirectory = node.getSecond() == null ? false : node.getSecond(); // Watch out for NPEs during unboxing! NodeState nodeState = node.getThird(); @@ -139,7 +149,7 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory } else { - numVersionProperties = importImportableItemFile(result, importableItem, metadata, nodeState); + numVersionProperties = importImportableItemFile(result, importableItem, metadata, nodeState, existingFileMode); } importStatus.incrementNodesWritten(importableItem, isDirectory, nodeState, metadata.getProperties().size() + 4, numVersionProperties); diff --git a/source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java b/source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java index 12b0109b38..7bb311c07a 100644 --- a/source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java +++ b/source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java @@ -1,47 +1,36 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.bulkimport.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.transaction.NotSupportedException; -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; - import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQueryPageDetails; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.bulkimport.BulkImportParameters; +import org.alfresco.repo.bulkimport.NodeImporter; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.action.ActionService; @@ -53,6 +42,8 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.rule.RuleService; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -62,6 +53,24 @@ import org.junit.AfterClass; import org.junit.Before; import org.springframework.context.ApplicationContext; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + /** * @since 4.0 */ @@ -159,6 +168,118 @@ public class AbstractBulkImportTests return folders; } + protected void testCanVersionDocsWithoutSpecialInputFileNameExtension( + Function importerFun) + throws IOException, SystemException, NotSupportedException, HeuristicRollbackException, + HeuristicMixedException, RollbackException + { + txn = transactionService.getUserTransaction(); + txn.begin(); + + NodeRef folderNode = topLevelFolder.getNodeRef(); + + // Import with appropriate importer. + // bulkimport-autoversion/{pass1,pass2,pass3}/example.txt contains different versions of the + // same file. Run the import once for each subfolder, the file will then get loaded + // creating a new version for each example.txt + NodeImporter nodeImporter = importerFun.apply("pass1"); + BulkImportParameters bulkImportParameters = new BulkImportParameters(); + bulkImportParameters.setTarget(folderNode); + bulkImportParameters.setExistingFileMode(BulkImportParameters.ExistingFileMode.ADD_VERSION); + bulkImportParameters.setBatchSize(1); + + ExpectedFile[] expectedFiles = new ExpectedFile[]{ + new ExpectedFile("example.txt", MimetypeMap.MIMETYPE_TEXT_PLAIN, + "This is an example file. This content is version 1.") + }; + + ExpectedFolder[] expectedFolders = new ExpectedFolder[] { }; + + // Import initial version + bulkImporter.bulkImport(bulkImportParameters, nodeImporter); + txn.commit(); + txn = transactionService.getUserTransaction(); + txn.begin(); + assertEquals(false, bulkImporter.getStatus().inProgress()); + checkFiles(folderNode, null, expectedFiles, expectedFolders); + + Map files = toMap(getFiles(folderNode, null)); + NodeRef fileNodeRef = files.get("example.txt").getNodeRef(); + assertFalse("Imported file should not yet be versioned:", versionService.isVersioned(fileNodeRef)); + + // Import revised document/version + nodeImporter = importerFun.apply("pass2"); + bulkImporter.bulkImport(bulkImportParameters, nodeImporter); + txn.commit(); + txn = transactionService.getUserTransaction(); + txn.begin(); + expectedFiles = new ExpectedFile[]{ + new ExpectedFile("example.txt", MimetypeMap.MIMETYPE_TEXT_PLAIN, + // Note that pass2 has two versions 2 and 3 in it. + "This is an example file. This content is version 3.") + }; + checkFiles(folderNode, null, expectedFiles, expectedFolders); + + // Import revised document/version + nodeImporter = importerFun.apply("pass3"); + bulkImporter.bulkImport(bulkImportParameters, nodeImporter); + + txn.commit(); + txn = transactionService.getUserTransaction(); + txn.begin(); + + expectedFiles = new ExpectedFile[]{ + new ExpectedFile("example.txt", MimetypeMap.MIMETYPE_TEXT_PLAIN, + "This is an example file. This content is version 4."), + }; + expectedFolders = new ExpectedFolder[] { + new ExpectedFolder("banana") + }; + checkFiles(folderNode, null, expectedFiles, expectedFolders); + + // Check the files in the subfolder of pass3 + NodeRef subFolder = fileFolderService.searchSimple(folderNode, "banana"); + expectedFiles = new ExpectedFile[]{ + new ExpectedFile("file.txt", MimetypeMap.MIMETYPE_TEXT_PLAIN, + "Version 2") + }; + expectedFolders = new ExpectedFolder[] { }; + checkFiles(subFolder, null, expectedFiles, expectedFolders); + + assertTrue("Imported file should be versioned:", versionService.isVersioned(fileNodeRef)); + VersionHistory history = versionService.getVersionHistory(fileNodeRef); + assertNotNull(history); + + assertEquals("Incorrect number of versions.", 4, history.getAllVersions().size()); + + Version[] versions = history.getAllVersions().toArray(new Version[4]); + + // Check the content of each version + ContentReader contentReader; + + contentReader = contentService.getReader(versions[0].getFrozenStateNodeRef(), ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + assertEquals("4.0", versions[0].getVersionLabel()); + assertEquals("This is an example file. This content is version 4.", contentReader.getContentString()); + + contentReader = contentService.getReader(versions[1].getFrozenStateNodeRef(), ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + assertEquals("3.0", versions[1].getVersionLabel()); + assertEquals("This is an example file. This content is version 3.", contentReader.getContentString()); + + contentReader = contentService.getReader(versions[2].getFrozenStateNodeRef(), ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + assertEquals("2.0", versions[2].getVersionLabel()); + assertEquals("This is an example file. This content is version 2.", contentReader.getContentString()); + + contentReader = contentService.getReader(versions[3].getFrozenStateNodeRef(), ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + assertEquals("1.0", versions[3].getVersionLabel()); + assertEquals("This is an example file. This content is version 1.", contentReader.getContentString()); + } + + + protected List getFiles(NodeRef parent, String pattern) { PagingResults page = fileFolderService.list(parent, true, false, pattern, null, null, new PagingRequest(CannedQueryPageDetails.DEFAULT_PAGE_SIZE)); @@ -184,28 +305,41 @@ public class AbstractBulkImportTests checkFiles(folder1, pattern, numExpectedFolders, numExpectedFiles, expectedFiles, expectedFolders); } + protected void checkFiles(NodeRef parent, String pattern, + ExpectedFile[] expectedFiles, ExpectedFolder[] expectedFolders) + { + int expectedFilesLength = expectedFiles != null ? expectedFiles.length : 0; + int expectedFoldersLength = expectedFolders != null ? expectedFolders.length : 0; + checkFiles(parent, pattern, expectedFoldersLength, expectedFilesLength, expectedFiles, expectedFolders); + } + protected void checkFiles(NodeRef parent, String pattern, int expectedNumFolders, int expectedNumFiles, ExpectedFile[] expectedFiles, ExpectedFolder[] expectedFolders) { Map folders = toMap(getFolders(parent, pattern)); Map files = toMap(getFiles(parent, pattern)); - assertEquals("", expectedNumFolders, folders.size()); - assertEquals("", expectedNumFiles, files.size()); + assertEquals("Incorrect number of folders", expectedNumFolders, folders.size()); + assertEquals("Incorrect number of files", expectedNumFiles, files.size()); if(expectedFiles != null) { for(ExpectedFile expectedFile : expectedFiles) { FileInfo fileInfo = files.get(expectedFile.getName()); - assertNotNull("", fileInfo); - assertNotNull("", fileInfo.getContentData()); + assertNotNull( + "Couldn't find expected file: "+expectedFile.getName()+ + ", found: "+files.keySet(), fileInfo); + assertNotNull("Content data unexpected null for "+expectedFile.getName(), fileInfo.getContentData()); assertEquals(expectedFile.getMimeType(), fileInfo.getContentData().getMimetype()); - if(fileInfo.getContentData().getMimetype() == MimetypeMap.MIMETYPE_TEXT_PLAIN + if(fileInfo.getContentData().getMimetype().equals(MimetypeMap.MIMETYPE_TEXT_PLAIN) && expectedFile.getContentContains() != null) { ContentReader reader = contentService.getReader(fileInfo.getNodeRef(), ContentModel.PROP_CONTENT); String contentContains = expectedFile.getContentContains(); - assertTrue("", reader.getContentString().indexOf(contentContains) != -1); + String actualContent = reader.getContentString(); + assertTrue("Expected contents doesn't include text: " + contentContains + + ", full text:\n"+actualContent, + actualContent.contains(contentContains)); } } } diff --git a/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java b/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java index 302cc2eac3..c9f72a337d 100644 --- a/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java +++ b/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java @@ -1,20 +1,20 @@ /* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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, + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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. @@ -25,28 +25,6 @@ */ package org.alfresco.repo.bulkimport.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.io.Serializable; -import java.nio.charset.Charset; -import java.nio.file.FileAlreadyExistsException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.zip.GZIPInputStream; - -import javax.transaction.NotSupportedException; -import javax.transaction.SystemException; - import org.alfresco.model.ContentModel; import org.alfresco.repo.action.evaluator.NoConditionEvaluator; import org.alfresco.repo.action.executer.CopyActionExecuter; @@ -70,6 +48,31 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.springframework.util.ResourceUtils; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; +import javax.transaction.SystemException; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + /** * @since 4.0 */ @@ -272,7 +275,7 @@ public class BulkImportTest extends AbstractBulkImportTests System.out.println(bulkImporter.getStatus()); - assertEquals(74, bulkImporter.getStatus().getNumberOfContentNodesCreated()); + assertEquals(74, bulkImporter.getStatus().getNumberOfContentNodesCreated()); checkFiles(folderNode, null, 2, 9, new ExpectedFile[] { new ExpectedFile("quickImg1.xls", MimetypeMap.MIMETYPE_EXCEL), @@ -628,9 +631,9 @@ public class BulkImportTest extends AbstractBulkImportTests NodeImporter nodeImporter = null; File source = ResourceUtils.getFile("classpath:bulkimport4"); - //Simulate the name of the file with an invalid encoding. - String fileName = new String("135 CarbonÔÇô13 NMR spectroscopy_DS_NS_final_cau.txt".getBytes(Charset.forName("ISO-8859-1")), - Charset.forName("UTF-8")); + //Simulate the name of the file with an invalid encoding. + String fileName = new String("135 CarbonÔÇô13 NMR spectroscopy_DS_NS_final_cau.txt".getBytes(Charset.forName("ISO-8859-1")), + Charset.forName("UTF-8")); Path dest = source.toPath().resolve("encoding"); try { @@ -638,7 +641,7 @@ public class BulkImportTest extends AbstractBulkImportTests } catch (FileAlreadyExistsException ex) { - //It is fine if the folder already exists, though it should not. + //It is fine if the folder already exists, though it should not. } Path destFile = dest.resolve(fileName); @@ -656,7 +659,7 @@ public class BulkImportTest extends AbstractBulkImportTests bulkImportParameters.setBatchSize(40); bulkImporter.bulkImport(bulkImportParameters, nodeImporter); - assertEquals(1, bulkImporter.getStatus().getNumberOfContentNodesCreated()); + assertEquals(1, bulkImporter.getStatus().getNumberOfContentNodesCreated()); checkFiles(folderNode, null, 0, 1, new ExpectedFile[] { new ExpectedFile(fileName, MimetypeMap.MIMETYPE_TEXT_PLAIN)}, @@ -665,7 +668,35 @@ public class BulkImportTest extends AbstractBulkImportTests Files.deleteIfExists(destFile); Files.deleteIfExists(dest); } - + + /** + * Simplifies calling {@ResourceUtils.getFile} so that a {@link RuntimeException} + * is thrown rather than a checked {@link FileNotFoundException} exception. + * + * @param resourceName e.g. "classpath:folder/file" + * @return File object + */ + private File resourceAsFile(String resourceName) + { + try + { + return ResourceUtils.getFile(resourceName); + } + catch (FileNotFoundException e) + { + throw new RuntimeException("Resource "+resourceName+" not found", e); + } + } + + @Test + public void canVersionDocsWithoutSpecialInputFileNameExtension() + throws HeuristicMixedException, IOException, SystemException, + HeuristicRollbackException, NotSupportedException, RollbackException + { + testCanVersionDocsWithoutSpecialInputFileNameExtension(file -> + streamingNodeImporterFactory.getNodeImporter(resourceAsFile("classpath:bulkimport-autoversion/"+file))); + } + private void unpack(Path source, Path destFile) { Path archive = source.resolve("testbulk.gz"); diff --git a/source/test-resources/bulkimport-autoversion/pass1/example.txt b/source/test-resources/bulkimport-autoversion/pass1/example.txt new file mode 100644 index 0000000000..609550b9fd --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass1/example.txt @@ -0,0 +1 @@ +This is an example file. This content is version 1. \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass2/example.txt b/source/test-resources/bulkimport-autoversion/pass2/example.txt new file mode 100644 index 0000000000..6f55b208c7 --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass2/example.txt @@ -0,0 +1 @@ +This is an example file. This content is version 3. \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass2/example.txt.v123 b/source/test-resources/bulkimport-autoversion/pass2/example.txt.v123 new file mode 100644 index 0000000000..b5aaf5e7fd --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass2/example.txt.v123 @@ -0,0 +1 @@ +This is an example file. This content is version 2. \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass3/another.txt.v1 b/source/test-resources/bulkimport-autoversion/pass3/another.txt.v1 new file mode 100644 index 0000000000..fa8212f996 --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass3/another.txt.v1 @@ -0,0 +1 @@ +This will be skipped as there is no another.txt in the repository. \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass3/example.txt b/source/test-resources/bulkimport-autoversion/pass3/example.txt new file mode 100644 index 0000000000..e31352904d --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass3/example.txt @@ -0,0 +1 @@ +This is an example file. This content is version 4. \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass3/subfolder.metadata.properties.xml b/source/test-resources/bulkimport-autoversion/pass3/subfolder.metadata.properties.xml new file mode 100644 index 0000000000..fdabbce432 --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass3/subfolder.metadata.properties.xml @@ -0,0 +1,8 @@ + + + + + + banana + This is the banana folder + diff --git a/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt b/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt new file mode 100644 index 0000000000..ff55721fa3 --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt @@ -0,0 +1 @@ +Version 2 \ No newline at end of file diff --git a/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt.v1 b/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt.v1 new file mode 100644 index 0000000000..ada091bb9c --- /dev/null +++ b/source/test-resources/bulkimport-autoversion/pass3/subfolder/file.txt.v1 @@ -0,0 +1 @@ +Version 1 \ No newline at end of file