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.kernel.impl.services; 007 008import static java.lang.String.format; 009 010import java.util.Optional; 011import java.util.stream.Stream; 012 013import javax.inject.Inject; 014 015import org.fcrepo.kernel.api.Transaction; 016import org.fcrepo.kernel.api.auth.ACLHandle; 017import org.fcrepo.kernel.api.exception.PathNotFoundException; 018import org.fcrepo.kernel.api.exception.PathNotFoundRuntimeException; 019import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 020import org.fcrepo.kernel.api.identifiers.FedoraId; 021import org.fcrepo.kernel.api.models.Binary; 022import org.fcrepo.kernel.api.models.Container; 023import org.fcrepo.kernel.api.models.FedoraResource; 024import org.fcrepo.kernel.api.models.NonRdfSourceDescription; 025import org.fcrepo.kernel.api.models.ResourceFactory; 026import org.fcrepo.kernel.api.models.Tombstone; 027import org.fcrepo.persistence.api.PersistentStorageSession; 028import org.fcrepo.persistence.api.PersistentStorageSessionManager; 029import org.fcrepo.persistence.api.exceptions.PersistentStorageException; 030 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import com.github.benmanes.caffeine.cache.Cache; 035 036/** 037 * Shared delete/purge code. 038 * @author whikloj 039 */ 040abstract public class AbstractDeleteResourceService extends AbstractService { 041 042 private final static Logger log = LoggerFactory.getLogger(AbstractDeleteResourceService.class); 043 044 @Inject 045 protected ResourceFactory resourceFactory; 046 047 @Inject 048 protected PersistentStorageSessionManager psManager; 049 050 @Inject 051 private Cache<String, Optional<ACLHandle>> authHandleCache; 052 053 /** 054 * The starts the service, does initial checks and setups for processing. 055 * @param tx the transaction. 056 * @param fedoraResource the resource to start delete/purging. 057 * @param userPrincipal the user performing the action. 058 */ 059 public void perform(final Transaction tx, final FedoraResource fedoraResource, final String userPrincipal) { 060 final String fedoraResourceId = fedoraResource.getId(); 061 062 if (fedoraResource instanceof NonRdfSourceDescription) { 063 throw new RepositoryRuntimeException( 064 format("A NonRdfSourceDescription cannot be deleted independently of the NonRDFSource: %s", 065 fedoraResourceId)); 066 } 067 068 try { 069 log.debug("operating on {}", fedoraResourceId); 070 final PersistentStorageSession pSession = this.psManager.getSession(tx); 071 deleteDepthFirst(tx, pSession, fedoraResource, userPrincipal); 072 } catch (final PersistentStorageException ex) { 073 throw new RepositoryRuntimeException(format("failed to delete/purge resource %s", fedoraResourceId), ex); 074 } 075 } 076 077 /** 078 * Code to perform the recursion of containers. 079 * @param tx the transaction 080 * @param pSession the persistent storage session 081 * @param fedoraResource the current resource to check for any children. 082 * @param userPrincipal the user performing the action. 083 * @throws PersistentStorageException any problems accessing the underlying storage. 084 */ 085 private void deleteDepthFirst(final Transaction tx, final PersistentStorageSession pSession, 086 final FedoraResource fedoraResource, final String userPrincipal) 087 throws PersistentStorageException { 088 089 final FedoraId fedoraId = fedoraResource.getFedoraId(); 090 091 if (fedoraResource instanceof Container) { 092 final Stream<String> children = getContained(tx, fedoraResource); 093 children.forEach(childResourceId -> { 094 try { 095 096 final FedoraResource res = resourceFactory.getResource(tx, FedoraId.create(childResourceId)); 097 if (res instanceof Tombstone) { 098 deleteDepthFirst(tx, pSession, ((Tombstone) res).getDeletedObject(), userPrincipal); 099 } else { 100 deleteDepthFirst(tx, pSession, res, userPrincipal); 101 } 102 } catch (final PathNotFoundException ex) { 103 log.error("Path not found for {}: {}", fedoraId.getFullId(), ex.getMessage()); 104 throw new PathNotFoundRuntimeException(ex.getMessage(), ex); 105 } catch (final PersistentStorageException ex) { 106 throw new RepositoryRuntimeException(format("failed to delete resource %s", fedoraId.getFullId()), 107 ex); 108 } 109 }); 110 } else if (fedoraResource instanceof Binary) { 111 doAction(tx, pSession, fedoraResource.getDescription().getFedoraId(), userPrincipal); 112 } 113 114 //delete/purge the acl if this is not the acl 115 if (!fedoraResource.isAcl()) { 116 final FedoraResource acl = fedoraResource.getAcl(); 117 if (acl != null) { 118 doAction(tx, pSession, acl.getFedoraId(), userPrincipal); 119 // Flush ACL cache on any ACL creation/update/deletion. 120 authHandleCache.invalidateAll(); 121 } 122 } else { 123 // Flush ACL cache on any ACL creation/update/deletion. 124 authHandleCache.invalidateAll(); 125 } 126 127 //delete/purge the resource itself 128 doAction(tx, pSession, fedoraId, userPrincipal); 129 } 130 131 /** 132 * Get the contained resources to act upon. 133 * @param tx the transaction this occurs in. 134 * @param resource the parent resource to find contained resources for. 135 * @return stream of child ids. 136 */ 137 abstract protected Stream<String> getContained(final Transaction tx, final FedoraResource resource); 138 139 /** 140 * Perform the actual delete or purge action 141 * @param tx the transaction this occurs in. 142 * @param pSession the persistent storage session. 143 * @param resourceId the resource to perform the action on. 144 * @param userPrincipal the user performing the action 145 * @throws PersistentStorageException if problem performing the action. 146 */ 147 abstract protected void doAction(final Transaction tx, final PersistentStorageSession pSession, 148 final FedoraId resourceId, final String userPrincipal) 149 throws PersistentStorageException; 150}