001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.persistence.ocfl.impl; 019 020import static org.fcrepo.persistence.ocfl.impl.OcflPersistentStorageUtils.createFilesystemRepository; 021import static org.fcrepo.persistence.ocfl.impl.OcflPersistentStorageUtils.createS3Repository; 022 023import java.io.IOException; 024import java.net.URI; 025import java.util.concurrent.TimeUnit; 026 027import javax.inject.Inject; 028import javax.sql.DataSource; 029 030import org.fcrepo.config.MetricsConfig; 031import org.fcrepo.config.OcflPropsConfig; 032import org.fcrepo.config.Storage; 033import org.fcrepo.storage.ocfl.CommitType; 034import org.fcrepo.storage.ocfl.DefaultOcflObjectSessionFactory; 035import org.fcrepo.storage.ocfl.validation.ObjectValidator; 036import org.fcrepo.storage.ocfl.OcflObjectSessionFactory; 037import org.fcrepo.storage.ocfl.ResourceHeaders; 038import org.fcrepo.storage.ocfl.cache.Cache; 039import org.fcrepo.storage.ocfl.cache.CaffeineCache; 040import org.fcrepo.storage.ocfl.cache.NoOpCache; 041 042import org.apache.commons.lang3.StringUtils; 043import org.springframework.context.annotation.Bean; 044import org.springframework.context.annotation.Configuration; 045 046import com.github.benmanes.caffeine.cache.Caffeine; 047 048import edu.wisc.library.ocfl.api.MutableOcflRepository; 049import io.micrometer.core.instrument.MeterRegistry; 050import io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics; 051import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 052import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 053import software.amazon.awssdk.regions.Region; 054import software.amazon.awssdk.services.s3.S3Client; 055 056/** 057 * A Configuration for OCFL dependencies 058 * 059 * @author dbernstein 060 * @since 6.0.0 061 */ 062 063@Configuration 064public class OcflPersistenceConfig { 065 066 @Inject 067 private OcflPropsConfig ocflPropsConfig; 068 069 @Inject 070 private MetricsConfig metricsConfig; 071 072 @Inject 073 private MeterRegistry meterRegistry; 074 075 @Inject 076 private DataSource dataSource; 077 078 /** 079 * Create an OCFL Repository 080 * @return the repository 081 */ 082 @Bean 083 public MutableOcflRepository repository() throws IOException { 084 if (ocflPropsConfig.getStorage() == Storage.OCFL_S3) { 085 return createS3Repository( 086 dataSource, 087 s3Client(), 088 ocflPropsConfig.getOcflS3Bucket(), 089 ocflPropsConfig.getOcflS3Prefix(), 090 ocflPropsConfig.getOcflTemp(), 091 ocflPropsConfig.getDefaultDigestAlgorithm(), 092 ocflPropsConfig.isOcflS3DbEnabled()); 093 } else { 094 return createFilesystemRepository(ocflPropsConfig.getOcflRepoRoot(), ocflPropsConfig.getOcflTemp(), 095 ocflPropsConfig.getDefaultDigestAlgorithm()); 096 } 097 } 098 099 @Bean 100 public OcflObjectSessionFactory ocflObjectSessionFactory() throws IOException { 101 final var objectMapper = OcflPersistentStorageUtils.objectMapper(); 102 103 final var factory = new DefaultOcflObjectSessionFactory(repository(), 104 ocflPropsConfig.getFedoraOcflStaging(), 105 objectMapper, 106 createCache("resourceHeadersCache"), 107 createCache("rootIdCache"), 108 commitType(), 109 "Authored by Fedora 6", 110 "fedoraAdmin", 111 "info:fedora/fedoraAdmin"); 112 factory.useUnsafeWrite(ocflPropsConfig.isUnsafeWriteEnabled()); 113 return factory; 114 } 115 116 @Bean 117 public ObjectValidator objectValidator() throws IOException { 118 final var objectMapper = OcflPersistentStorageUtils.objectMapper(); 119 return new ObjectValidator(repository(), objectMapper.readerFor(ResourceHeaders.class)); 120 } 121 122 private CommitType commitType() { 123 if (ocflPropsConfig.isAutoVersioningEnabled()) { 124 return CommitType.NEW_VERSION; 125 } 126 return CommitType.UNVERSIONED; 127 } 128 129 private S3Client s3Client() { 130 final var builder = S3Client.builder(); 131 132 if (StringUtils.isNotBlank(ocflPropsConfig.getAwsRegion())) { 133 builder.region(Region.of(ocflPropsConfig.getAwsRegion())); 134 } 135 136 if (StringUtils.isNotBlank(ocflPropsConfig.getS3Endpoint())) { 137 builder.endpointOverride(URI.create(ocflPropsConfig.getS3Endpoint())); 138 } 139 140 if (ocflPropsConfig.isPathStyleAccessEnabled()) { 141 builder.serviceConfiguration(config -> config.pathStyleAccessEnabled(true)); 142 } 143 144 if (StringUtils.isNoneBlank(ocflPropsConfig.getAwsAccessKey(), ocflPropsConfig.getAwsSecretKey())) { 145 builder.credentialsProvider(StaticCredentialsProvider.create( 146 AwsBasicCredentials.create(ocflPropsConfig.getAwsAccessKey(), ocflPropsConfig.getAwsSecretKey()))); 147 } 148 149 // May want to do additional HTTP client configuration, connection pool, etc 150 151 return builder.build(); 152 } 153 154 private <K, V> Cache<K, V> createCache(final String metricName) { 155 if (ocflPropsConfig.isResourceHeadersCacheEnabled()) { 156 final var builder = Caffeine.newBuilder(); 157 158 if (metricsConfig.isMetricsEnabled()) { 159 builder.recordStats(); 160 } 161 162 final var cache = builder 163 .maximumSize(ocflPropsConfig.getResourceHeadersCacheMaxSize()) 164 .expireAfterAccess(ocflPropsConfig.getResourceHeadersCacheExpireAfterSeconds(), TimeUnit.SECONDS) 165 .build(); 166 167 if (metricsConfig.isMetricsEnabled()) { 168 CaffeineCacheMetrics.monitor(meterRegistry, cache, metricName); 169 } 170 171 return new CaffeineCache<>(cache); 172 } 173 174 return new NoOpCache<>(); 175 } 176 177}