001/* 002 * The contents of this file are subject to the license and copyright 003 * detailed in the LICENSE and NOTICE files at the root of the source 004 * tree. 005 */ 006package org.fcrepo.persistence.ocfl.impl; 007 008import org.fcrepo.config.OcflPropsConfig; 009import org.fcrepo.kernel.api.ReadOnlyTransaction; 010import org.fcrepo.kernel.api.Transaction; 011import org.fcrepo.persistence.api.PersistentStorageSession; 012import org.fcrepo.persistence.api.PersistentStorageSessionManager; 013import org.fcrepo.persistence.ocfl.api.FedoraToOcflObjectIndex; 014import org.fcrepo.storage.ocfl.OcflObjectSessionFactory; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017import org.springframework.beans.factory.annotation.Autowired; 018import org.springframework.stereotype.Component; 019 020import javax.inject.Inject; 021import java.io.IOException; 022import java.nio.file.FileVisitResult; 023import java.nio.file.Files; 024import java.nio.file.Path; 025import java.nio.file.SimpleFileVisitor; 026import java.nio.file.attribute.BasicFileAttributes; 027import java.util.Map; 028import java.util.concurrent.ConcurrentHashMap; 029 030/** 031 * OCFL implementation of PersistentStorageSessionManager 032 * 033 * @author whikloj 034 * @author dbernstein 035 * @since 2019-09-20 036 */ 037@Component 038public class OcflPersistentSessionManager implements PersistentStorageSessionManager { 039 040 private static final Logger LOGGER = LoggerFactory.getLogger(OcflPersistentSessionManager.class); 041 042 private volatile PersistentStorageSession readOnlySession; 043 044 private Map<String, PersistentStorageSession> sessionMap; 045 046 @Inject 047 private OcflObjectSessionFactory objectSessionFactory; 048 049 @Inject 050 private FedoraToOcflObjectIndex ocflIndex; 051 052 @Inject 053 private ReindexService reindexService; 054 055 @Inject 056 private OcflPropsConfig ocflPropsConfig; 057 058 /** 059 * Default constructor 060 */ 061 @Autowired 062 public OcflPersistentSessionManager() { 063 this.sessionMap = new ConcurrentHashMap<>(); 064 } 065 066 @Override 067 public PersistentStorageSession getSession(final Transaction transaction) { 068 if (transaction == null) { 069 throw new IllegalArgumentException("session id must be non-null"); 070 } 071 072 return sessionMap.computeIfAbsent(transaction.getId(), key -> { 073 LOGGER.debug("Creating storage session {}", transaction); 074 return new OcflPersistentStorageSessionMetrics( 075 new OcflPersistentStorageSession( 076 transaction, 077 ocflIndex, 078 objectSessionFactory, 079 reindexService)); 080 }); 081 } 082 083 @Override 084 public PersistentStorageSession getReadOnlySession() { 085 var localSession = this.readOnlySession; 086 087 if (localSession == null) { 088 synchronized (this) { 089 localSession = this.readOnlySession; 090 if (localSession == null) { 091 this.readOnlySession = new OcflPersistentStorageSessionMetrics( 092 new OcflPersistentStorageSession(ReadOnlyTransaction.INSTANCE, 093 ocflIndex, objectSessionFactory, reindexService)); 094 localSession = this.readOnlySession; 095 } 096 } 097 } 098 099 return localSession; 100 } 101 102 @Override 103 public PersistentStorageSession removeSession(final String sessionId) { 104 LOGGER.debug("Removing storage session {}", sessionId); 105 return sessionMap.remove(sessionId); 106 } 107 108 @Override 109 public void clearAllSessions() { 110 LOGGER.debug("Clearing all storage sessions"); 111 sessionMap.clear(); 112 ocflIndex.clearAllTransactions(); 113 try { 114 deleteStagingDirectories(); 115 } catch (IOException e) { 116 LOGGER.error("Failed to delete OCFL staging directories", e); 117 } 118 } 119 120 /** 121 * Deletes all of the staging directories within the root staging directory 122 * @throws IOException 123 */ 124 private void deleteStagingDirectories() throws IOException { 125 // Delete 126 Files.walkFileTree(ocflPropsConfig.getFedoraOcflStaging(), new SimpleFileVisitor<>() { 127 @Override 128 public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { 129 // Delete the file 130 Files.delete(file); 131 return FileVisitResult.CONTINUE; 132 } 133 134 @Override 135 public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { 136 // Delete the directory after its contents have been deleted, excluding the root staging directory 137 if (!dir.equals(ocflPropsConfig.getFedoraOcflStaging())) { 138 Files.delete(dir); 139 } 140 return FileVisitResult.CONTINUE; 141 } 142 }); 143 } 144}