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