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 edu.wisc.library.ocfl.api.OcflConfig;
024import edu.wisc.library.ocfl.api.model.DigestAlgorithm;
025import edu.wisc.library.ocfl.core.OcflRepositoryBuilder;
026import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig;
027import edu.wisc.library.ocfl.core.path.mapper.LogicalPathMappers;
028import edu.wisc.library.ocfl.core.storage.filesystem.FileSystemOcflStorage;
029import org.apache.commons.lang3.SystemUtils;
030import org.fcrepo.migration.handlers.ocfl.PlainOcflObjectSessionFactory;
031import org.fcrepo.storage.ocfl.CommitType;
032import org.fcrepo.storage.ocfl.DefaultOcflObjectSessionFactory;
033import org.fcrepo.storage.ocfl.OcflObjectSessionFactory;
034import org.fcrepo.storage.ocfl.cache.CaffeineCache;
035import org.springframework.beans.factory.FactoryBean;
036
037import java.nio.file.Path;
038import java.time.Duration;
039
040import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS;
041
042/**
043 * Spring FactoryBean for easy OcflObjectSessionFactory creation.
044 *
045 * @author pwinckles
046 */
047public class OcflSessionFactoryFactoryBean implements FactoryBean<OcflObjectSessionFactory> {
048
049    private final Path ocflRoot;
050    private final Path stagingDir;
051    private final MigrationType migrationType;
052    private final String user;
053    private final String userUri;
054    private final DigestAlgorithm digestAlgorithm;
055    private final boolean disableChecksumValidation;
056
057    /**
058     * @param ocflRoot OCFL storage root
059     * @param stagingDir OCFL staging dir
060     * @param migrationType migration type
061     * @param user user to add to OCFL versions
062     * @param userUri user's address
063     * @param digestAlgorithm The digest algorithm to use.
064     * @param disableChecksumValidation whether to verify fedora3 checksums or not
065     */
066    public OcflSessionFactoryFactoryBean(final Path ocflRoot,
067                                         final Path stagingDir,
068                                         final MigrationType migrationType,
069                                         final String user,
070                                         final String userUri,
071                                         final DigestAlgorithm digestAlgorithm,
072                                         final boolean disableChecksumValidation) {
073        this.ocflRoot = ocflRoot;
074        this.stagingDir = stagingDir;
075        this.migrationType = migrationType;
076        this.user = user;
077        this.userUri = userUri;
078        this.digestAlgorithm = digestAlgorithm;
079        this.disableChecksumValidation = disableChecksumValidation;
080    }
081
082    /**
083     * @param ocflRoot OCFL storage root
084     * @param stagingDir OCFL staging dir
085     * @param migrationType migration type
086     * @param user user to add to OCFL versions
087     * @param userUri user's address
088     * @param disableChecksumValidation whether to verify fedora3 checksums or not
089     */
090    public OcflSessionFactoryFactoryBean(final Path ocflRoot,
091                                         final Path stagingDir,
092                                         final MigrationType migrationType,
093                                         final String user,
094                                         final String userUri,
095                                         final boolean disableChecksumValidation) {
096        this(ocflRoot, stagingDir, migrationType, user, userUri, DigestAlgorithm.sha512, disableChecksumValidation);
097    }
098
099    @Override
100    public OcflObjectSessionFactory getObject() {
101        final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ?
102                LogicalPathMappers.percentEncodingWindowsMapper() : LogicalPathMappers.percentEncodingLinuxMapper();
103
104        final var config = new OcflConfig();
105        config.setDefaultDigestAlgorithm(this.digestAlgorithm);
106
107        final var ocflRepo =  new OcflRepositoryBuilder()
108                .defaultLayoutConfig(new HashedNTupleLayoutConfig())
109                .logicalPathMapper(logicalPathMapper)
110                .storage(FileSystemOcflStorage.builder().repositoryRoot(ocflRoot).build())
111                .workDir(stagingDir)
112                .ocflConfig(config)
113                .buildMutable();
114
115        if (migrationType == MigrationType.FEDORA_OCFL) {
116            final var objectMapper = new ObjectMapper()
117                    .configure(WRITE_DATES_AS_TIMESTAMPS, false)
118                    .registerModule(new JavaTimeModule())
119                    .setSerializationInclusion(JsonInclude.Include.NON_NULL);
120
121            final var headersCache = Caffeine.newBuilder()
122                    .maximumSize(512)
123                    .expireAfterAccess(Duration.ofMinutes(10))
124                    .build();
125
126            final var rootIdCache = Caffeine.newBuilder()
127                    .maximumSize(512)
128                    .expireAfterAccess(Duration.ofMinutes(10))
129                    .build();
130
131            return new DefaultOcflObjectSessionFactory(ocflRepo, stagingDir, objectMapper,
132                    new CaffeineCache<>(headersCache),
133                    new CaffeineCache<>(rootIdCache),
134                    CommitType.NEW_VERSION,
135                    "Generated by Fedora 3 to Fedora 6 migration", user, userUri);
136        } else {
137            return new PlainOcflObjectSessionFactory(ocflRepo, stagingDir,
138                    "Generated by Fedora 3 to Fedora 6 migration", user, userUri,
139                    disableChecksumValidation);
140        }
141    }
142
143    @Override
144    public Class<?> getObjectType() {
145        return OcflObjectSessionFactory.class;
146    }
147
148    @Override
149    public boolean isSingleton() {
150        return true;
151    }
152
153}