001/*
002 * Copyright 2019 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.fcrepo.migration;
018
019import com.fasterxml.jackson.annotation.JsonInclude;
020import com.fasterxml.jackson.databind.ObjectMapper;
021import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
022import com.github.benmanes.caffeine.cache.Caffeine;
023import io.ocfl.api.OcflConfig;
024import io.ocfl.api.model.DigestAlgorithm;
025import io.ocfl.api.DigestAlgorithmRegistry;
026import io.ocfl.core.OcflRepositoryBuilder;
027import io.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig;
028import io.ocfl.core.path.mapper.LogicalPathMappers;
029import io.ocfl.core.storage.OcflStorageBuilder;
030import org.apache.commons.lang3.SystemUtils;
031import org.fcrepo.migration.handlers.ocfl.PlainOcflObjectSessionFactory;
032import org.fcrepo.storage.ocfl.CommitType;
033import org.fcrepo.storage.ocfl.DefaultOcflObjectSessionFactory;
034import org.fcrepo.storage.ocfl.OcflObjectSessionFactory;
035import org.fcrepo.storage.ocfl.cache.CaffeineCache;
036import org.springframework.beans.factory.FactoryBean;
037
038import java.nio.file.Path;
039import java.time.Duration;
040
041import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS;
042
043/**
044 * Spring FactoryBean for easy OcflObjectSessionFactory creation.
045 *
046 * @author pwinckles
047 */
048public class OcflSessionFactoryFactoryBean implements FactoryBean<OcflObjectSessionFactory> {
049
050    private final Path ocflRoot;
051    private final Path stagingDir;
052    private final MigrationType migrationType;
053    private final String user;
054    private final String userUri;
055    private final DigestAlgorithm digestAlgorithm;
056    private final boolean disableChecksumValidation;
057
058    /**
059     * @param ocflRoot OCFL storage root
060     * @param stagingDir OCFL staging dir
061     * @param migrationType migration type
062     * @param user user to add to OCFL versions
063     * @param userUri user's address
064     * @param digestAlgorithm The digest algorithm to use.
065     * @param disableChecksumValidation whether to verify fedora3 checksums or not
066     */
067    public OcflSessionFactoryFactoryBean(final Path ocflRoot,
068                                         final Path stagingDir,
069                                         final MigrationType migrationType,
070                                         final String user,
071                                         final String userUri,
072                                         final DigestAlgorithm digestAlgorithm,
073                                         final boolean disableChecksumValidation) {
074        this.ocflRoot = ocflRoot;
075        this.stagingDir = stagingDir;
076        this.migrationType = migrationType;
077        this.user = user;
078        this.userUri = userUri;
079        this.digestAlgorithm = digestAlgorithm;
080        this.disableChecksumValidation = disableChecksumValidation;
081    }
082
083    /**
084     * @param ocflRoot OCFL storage root
085     * @param stagingDir OCFL staging dir
086     * @param migrationType migration type
087     * @param user user to add to OCFL versions
088     * @param userUri user's address
089     * @param disableChecksumValidation whether to verify fedora3 checksums or not
090     */
091    public OcflSessionFactoryFactoryBean(final Path ocflRoot,
092                                         final Path stagingDir,
093                                         final MigrationType migrationType,
094                                         final String user,
095                                         final String userUri,
096                                         final boolean disableChecksumValidation) {
097        this(ocflRoot, stagingDir, migrationType, user, userUri,
098                DigestAlgorithmRegistry.sha512, disableChecksumValidation);
099    }
100
101    @Override
102    public OcflObjectSessionFactory getObject() {
103        final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ?
104                LogicalPathMappers.percentEncodingWindowsMapper() : LogicalPathMappers.percentEncodingLinuxMapper();
105
106        final var config = new OcflConfig();
107        config.setDefaultDigestAlgorithm(this.digestAlgorithm);
108
109        final var ocflRepo =  new OcflRepositoryBuilder()
110                .defaultLayoutConfig(new HashedNTupleLayoutConfig())
111                .logicalPathMapper(logicalPathMapper)
112                .storage(OcflStorageBuilder.builder().fileSystem(ocflRoot).build())
113                .workDir(stagingDir)
114                .ocflConfig(config)
115                .buildMutable();
116
117        if (migrationType == MigrationType.FEDORA_OCFL) {
118            final var objectMapper = new ObjectMapper()
119                    .configure(WRITE_DATES_AS_TIMESTAMPS, false)
120                    .registerModule(new JavaTimeModule())
121                    .setSerializationInclusion(JsonInclude.Include.NON_NULL);
122
123            final var headersCache = Caffeine.newBuilder()
124                    .maximumSize(512)
125                    .expireAfterAccess(Duration.ofMinutes(10))
126                    .build();
127
128            final var rootIdCache = Caffeine.newBuilder()
129                    .maximumSize(512)
130                    .expireAfterAccess(Duration.ofMinutes(10))
131                    .build();
132
133            return new DefaultOcflObjectSessionFactory(ocflRepo, stagingDir, objectMapper,
134                    new CaffeineCache<>(headersCache),
135                    new CaffeineCache<>(rootIdCache),
136                    CommitType.NEW_VERSION,
137                    "Generated by Fedora 3 to Fedora 6 migration", user, userUri);
138        } else {
139            return new PlainOcflObjectSessionFactory(ocflRepo, stagingDir,
140                    "Generated by Fedora 3 to Fedora 6 migration", user, userUri,
141                    disableChecksumValidation);
142        }
143    }
144
145    @Override
146    public Class<?> getObjectType() {
147        return OcflObjectSessionFactory.class;
148    }
149
150    @Override
151    public boolean isSingleton() {
152        return true;
153    }
154
155}