001package org.fcrepo.migration.pidlist; 002 003import static org.junit.Assert.assertEquals; 004 005import java.io.File; 006import java.util.Map; 007import java.util.Set; 008import java.util.stream.Collectors; 009import javax.xml.stream.XMLStreamException; 010 011import io.ocfl.api.OcflRepository; 012import io.ocfl.api.model.DigestAlgorithm; 013import io.ocfl.api.DigestAlgorithmRegistry; 014import io.ocfl.api.model.FileDetails; 015import io.ocfl.api.model.ObjectDetails; 016import io.ocfl.core.OcflRepositoryBuilder; 017import io.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig; 018import io.ocfl.core.path.mapper.LogicalPathMappers; 019import io.ocfl.core.storage.OcflStorageBuilder; 020import org.apache.commons.io.FileUtils; 021import org.apache.commons.lang3.SystemUtils; 022import org.fcrepo.migration.MigrationType; 023import org.fcrepo.migration.Migrator; 024import org.fcrepo.migration.OcflSessionFactoryFactoryBean; 025import org.fcrepo.migration.ResourceMigrationType; 026import org.fcrepo.migration.foxml.LegacyFSIDResolver; 027import org.fcrepo.migration.foxml.NativeFoxmlDirectoryObjectSource; 028import org.fcrepo.migration.handlers.ObjectAbstractionStreamingFedoraObjectHandler; 029import org.fcrepo.migration.handlers.ocfl.ArchiveGroupHandler; 030import org.fcrepo.storage.ocfl.OcflObjectSessionFactory; 031import org.junit.Before; 032import org.junit.Test; 033 034/** 035 * Integration tests to test migration of head only datastream cases 036 * 037 * @author mikejritter 038 */ 039public class HeadOnlyIT { 040 041 private final String user = "fedoraAdmin"; 042 private final String idPrefix = "info:fedora/"; 043 private final String testPid = idPrefix + "example:1"; 044 private final boolean disableChecksum = false; 045 private final boolean disableDc = false; 046 047 private final DigestAlgorithm digestAlgorithm = DigestAlgorithmRegistry.sha512; 048 private final MigrationType migrationType = MigrationType.FEDORA_OCFL; 049 050 private LegacyFSIDResolver idResolver; 051 private NativeFoxmlDirectoryObjectSource objectSource; 052 private OcflObjectSessionFactory ocflObjectSessionFactory; 053 054 private File storage; 055 private File staging; 056 private File workingDir; 057 058 @Before 059 public void setup() throws Exception { 060 // Create directories expected in this test 061 storage = new File("target/test/ocfl/head-it/storage"); 062 staging = new File("target/test/ocfl/head-it/staging"); 063 workingDir = new File("target/test/ocfl/head-it/work"); 064 065 if (storage.exists()) { 066 FileUtils.forceDelete(storage); 067 } 068 if (staging.exists()) { 069 FileUtils.forceDelete(staging); 070 } 071 if (workingDir.exists()) { 072 FileUtils.forceDelete(workingDir); 073 } 074 075 storage.mkdirs(); 076 staging.mkdirs(); 077 workingDir.mkdirs(); 078 079 // Init Fedora3 classes 080 final var f3Hostname = "fedora.info"; 081 082 final var f3ObjectDir = new File("src/test/resources/legacyFS-multiple-versions/objects/2015/0430/16/01"); 083 final var f3DatastreamDir = 084 new File("src/test/resources/legacyFS-multiple-versions/datastreams/2015/0430/16/01"); 085 086 idResolver = new LegacyFSIDResolver(f3DatastreamDir); 087 objectSource = new NativeFoxmlDirectoryObjectSource(f3ObjectDir, idResolver, f3Hostname); 088 089 // Init OCFL classes 090 final var userUri = idPrefix + user; 091 ocflObjectSessionFactory = 092 new OcflSessionFactoryFactoryBean(storage.toPath(), staging.toPath(), migrationType, user, userUri, 093 digestAlgorithm, disableChecksum).getObject(); 094 } 095 096 @Test 097 public void testMigrateHeadOnly() throws XMLStreamException { 098 final var agh = archiveGroupHandler(true); 099 final var handler = new ObjectAbstractionStreamingFedoraObjectHandler(agh); 100 final var migrator = new Migrator(); 101 migrator.setSource(objectSource); 102 migrator.setHandler(handler); 103 104 migrator.run(); 105 106 // check that there's only single version for each datastream 107 final var ocflRepository = repository(); 108 final var objectDetails = ocflRepository.describeObject(testPid); 109 final var fileVersions = collectDatastreamVersions(objectDetails); 110 111 fileVersions.values().forEach(versions -> assertEquals(1, versions.size())); 112 } 113 114 @Test 115 public void testMigrateHeadOnlyDisabled() throws XMLStreamException { 116 final var agh = archiveGroupHandler(false); 117 final var handler = new ObjectAbstractionStreamingFedoraObjectHandler(agh); 118 final var migrator = new Migrator(); 119 migrator.setSource(objectSource); 120 migrator.setHandler(handler); 121 122 migrator.run(); 123 final var ocflRepository = repository(); 124 final var objectDetails = ocflRepository.describeObject(testPid); 125 final var fileVersions = collectDatastreamVersions(objectDetails); 126 127 // from the foxml, two datastreams have versions: DS1 and DC 128 final var dcVersions = fileVersions.get("/DC"); 129 final var ds1Versions = fileVersions.get("/DS1"); 130 131 assertEquals(2, dcVersions.size()); 132 assertEquals(2, ds1Versions.size()); 133 } 134 135 private ArchiveGroupHandler archiveGroupHandler(final boolean headOnly) { 136 final boolean foxmlFile = false; 137 final boolean deleteInactive = false; 138 final boolean datastreamExtensions = false; 139 final ResourceMigrationType resourceMigrationType = ResourceMigrationType.ARCHIVAL; 140 return new ArchiveGroupHandler(ocflObjectSessionFactory, migrationType, resourceMigrationType, 141 datastreamExtensions, deleteInactive, foxmlFile, user, idPrefix, 142 headOnly, disableChecksum, disableDc); 143 } 144 145 private OcflRepository repository() { 146 final var ocflStorage = OcflStorageBuilder.builder() 147 .fileSystem(storage.toPath()) 148 .build(); 149 150 final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ? LogicalPathMappers.percentEncodingWindowsMapper() 151 : LogicalPathMappers.percentEncodingLinuxMapper(); 152 153 return new OcflRepositoryBuilder().storage(ocflStorage) 154 .defaultLayoutConfig(new HashedNTupleLayoutConfig()) 155 .logicalPathMapper(logicalPathMapper) 156 .workDir(staging.toPath()) 157 .build(); 158 } 159 160 private Map<String, Set<String>> collectDatastreamVersions(final ObjectDetails objectDetails) { 161 return objectDetails.getVersionMap() 162 .values().stream() 163 .flatMap(details -> details.getFiles().stream()) 164 .map(FileDetails::getStorageRelativePath) 165 .filter(string -> !string.contains(".fcrepo") && !string.contains(".nt")) 166 .collect(Collectors.groupingBy(s -> s.substring(s.lastIndexOf("/")), Collectors.toSet())); 167 } 168}