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.models; 007 008import static org.fcrepo.kernel.api.RdfLexicon.BASIC_CONTAINER; 009import static org.fcrepo.kernel.api.RdfLexicon.DIRECT_CONTAINER; 010import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_NON_RDF_SOURCE_DESCRIPTION_URI; 011import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_WEBAC_ACL_URI; 012import static org.fcrepo.kernel.api.RdfLexicon.INDIRECT_CONTAINER; 013import static org.fcrepo.kernel.api.RdfLexicon.NON_RDF_SOURCE; 014import static org.slf4j.LoggerFactory.getLogger; 015 016import java.time.Instant; 017import java.util.stream.Stream; 018 019import javax.inject.Inject; 020 021import org.fcrepo.kernel.api.ContainmentIndex; 022import org.fcrepo.kernel.api.Transaction; 023import org.fcrepo.kernel.api.cache.UserTypesCache; 024import org.fcrepo.kernel.api.exception.PathNotFoundException; 025import org.fcrepo.kernel.api.exception.PathNotFoundRuntimeException; 026import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 027import org.fcrepo.kernel.api.exception.ResourceTypeException; 028import org.fcrepo.kernel.api.identifiers.FedoraId; 029import org.fcrepo.kernel.api.models.Binary; 030import org.fcrepo.kernel.api.models.FedoraResource; 031import org.fcrepo.kernel.api.models.ResourceFactory; 032import org.fcrepo.kernel.api.models.ResourceHeaders; 033import org.fcrepo.persistence.api.PersistentStorageSession; 034import org.fcrepo.persistence.api.PersistentStorageSessionManager; 035import org.fcrepo.persistence.api.exceptions.PersistentItemNotFoundException; 036import org.fcrepo.persistence.api.exceptions.PersistentStorageException; 037 038import org.slf4j.Logger; 039import org.springframework.beans.factory.annotation.Autowired; 040import org.springframework.beans.factory.annotation.Qualifier; 041import org.springframework.stereotype.Component; 042 043/** 044 * Implementation of ResourceFactory interface. 045 * 046 * @author whikloj 047 * @since 2019-09-23 048 */ 049@Component 050public class ResourceFactoryImpl implements ResourceFactory { 051 052 private static final Logger LOGGER = getLogger(ResourceFactoryImpl.class); 053 054 @Inject 055 private PersistentStorageSessionManager persistentStorageSessionManager; 056 057 @Autowired 058 @Qualifier("containmentIndex") 059 private ContainmentIndex containmentIndex; 060 061 @Inject 062 private UserTypesCache userTypesCache; 063 064 @Override 065 public FedoraResource getResource(final Transaction transaction, final FedoraId fedoraID) 066 throws PathNotFoundException { 067 return instantiateResource(transaction, fedoraID); 068 } 069 070 @Override 071 public <T extends FedoraResource> T getResource(final Transaction transaction, final FedoraId identifier, 072 final Class<T> clazz) throws PathNotFoundException { 073 return clazz.cast(getResource(transaction, identifier)); 074 } 075 076 077 @Override 078 public FedoraResource getContainer(final Transaction transaction, final FedoraId resourceId) { 079 final String containerId = containmentIndex.getContainedBy(transaction, resourceId); 080 if (containerId == null) { 081 return null; 082 } 083 try { 084 return getResource(transaction, FedoraId.create(containerId)); 085 } catch (final PathNotFoundException exc) { 086 return null; 087 } 088 } 089 090 /** 091 * Returns the appropriate FedoraResource class for an object based on the provided headers 092 * 093 * @param headers headers for the resource being constructed 094 * @return FedoraResource class 095 */ 096 private Class<? extends FedoraResourceImpl> getClassForTypes(final ResourceHeaders headers) { 097 final var ixModel = headers.getInteractionModel(); 098 if (BASIC_CONTAINER.getURI().equals(ixModel) || INDIRECT_CONTAINER.getURI().equals(ixModel) 099 || DIRECT_CONTAINER.getURI().equals(ixModel)) { 100 return ContainerImpl.class; 101 } 102 if (NON_RDF_SOURCE.getURI().equals(ixModel)) { 103 return BinaryImpl.class; 104 } 105 if (FEDORA_NON_RDF_SOURCE_DESCRIPTION_URI.equals(ixModel)) { 106 return NonRdfSourceDescriptionImpl.class; 107 } 108 if (FEDORA_WEBAC_ACL_URI.equals(ixModel)) { 109 return WebacAclImpl.class; 110 } 111 // TODO add the rest of the types 112 throw new ResourceTypeException("Could not identify the resource type for interaction model " + ixModel); 113 } 114 115 /** 116 * Instantiates a new FedoraResource object of the given class. 117 * 118 * @param transaction the transaction id 119 * @param identifier identifier for the new instance 120 * @return new FedoraResource instance 121 * @throws PathNotFoundException 122 */ 123 private FedoraResource instantiateResource(final Transaction transaction, 124 final FedoraId identifier) 125 throws PathNotFoundException { 126 try { 127 // For descriptions and ACLs we need the actual endpoint. 128 final var psSession = getSession(transaction); 129 final Instant versionDateTime = identifier.isMemento() ? identifier.getMementoInstant() : null; 130 131 final ResourceHeaders headers = psSession.getHeaders(identifier, versionDateTime); 132 133 // Determine the appropriate class from headers 134 final var createClass = getClassForTypes(headers); 135 136 // Retrieve standard constructor 137 final var constructor = createClass.getConstructor( 138 FedoraId.class, 139 Transaction.class, 140 PersistentStorageSessionManager.class, 141 ResourceFactory.class, 142 UserTypesCache.class); 143 144 // If identifier is to a TimeMap we need to avoid creating a original resource with a Timemap FedoraId 145 final var instantiationId = identifier.isTimemap() ? 146 FedoraId.create(identifier.getResourceId()) : identifier; 147 148 final var rescImpl = constructor.newInstance(instantiationId, transaction, 149 persistentStorageSessionManager, this, userTypesCache); 150 populateResourceHeaders(rescImpl, headers, versionDateTime); 151 152 if (headers.isDeleted()) { 153 final var rootId = FedoraId.create(identifier.getBaseId()); 154 final var tombstone = new TombstoneImpl(rootId, transaction, persistentStorageSessionManager, 155 this, rescImpl); 156 tombstone.setLastModifiedDate(headers.getLastModifiedDate()); 157 return tombstone; 158 } else if (identifier.isTimemap()) { 159 // If identifier is a TimeMap, now we can return the virtual resource. 160 return rescImpl.getTimeMap(); 161 } 162 return rescImpl; 163 } catch (final SecurityException | ReflectiveOperationException e) { 164 throw new RepositoryRuntimeException("Unable to construct object", e); 165 } catch (final PersistentItemNotFoundException e) { 166 throw new PathNotFoundException(e.getMessage(), e); 167 } catch (final PersistentStorageException e) { 168 throw new RepositoryRuntimeException(e.getMessage(), e); 169 } 170 } 171 172 private void populateResourceHeaders(final FedoraResourceImpl resc, 173 final ResourceHeaders headers, final Instant version) { 174 resc.setCreatedBy(headers.getCreatedBy()); 175 resc.setCreatedDate(headers.getCreatedDate()); 176 resc.setLastModifiedBy(headers.getLastModifiedBy()); 177 resc.setLastModifiedDate(headers.getLastModifiedDate()); 178 resc.setParentId(headers.getParent()); 179 resc.setArchivalGroupId(headers.getArchivalGroupId()); 180 resc.setEtag(headers.getStateToken()); 181 resc.setStateToken(headers.getStateToken()); 182 resc.setIsArchivalGroup(headers.isArchivalGroup()); 183 resc.setInteractionModel(headers.getInteractionModel()); 184 185 // If there's a version, then it's a memento 186 if (version != null) { 187 resc.setIsMemento(true); 188 resc.setMementoDatetime(version); 189 } 190 191 if (resc instanceof Binary) { 192 final var binary = (BinaryImpl) resc; 193 binary.setContentSize(headers.getContentSize()); 194 binary.setExternalHandling(headers.getExternalHandling()); 195 binary.setExternalUrl(headers.getExternalUrl()); 196 binary.setDigests(headers.getDigests()); 197 binary.setFilename(headers.getFilename()); 198 binary.setMimeType(headers.getMimeType()); 199 } 200 } 201 202 /** 203 * Get a session for this interaction. 204 * 205 * @param transaction The supplied transaction. 206 * @return a storage session. 207 */ 208 private PersistentStorageSession getSession(final Transaction transaction) { 209 final PersistentStorageSession session; 210 if (transaction.isReadOnly() || !transaction.isOpen()) { 211 session = persistentStorageSessionManager.getReadOnlySession(); 212 } else { 213 session = persistentStorageSessionManager.getSession(transaction); 214 } 215 return session; 216 } 217 218 @Override 219 public Stream<FedoraResource> getChildren(final Transaction transaction, final FedoraId resourceId) { 220 return containmentIndex.getContains(transaction, resourceId) 221 .map(childId -> { 222 try { 223 return getResource(transaction, FedoraId.create(childId)); 224 } catch (final PathNotFoundException e) { 225 throw new PathNotFoundRuntimeException(e.getMessage(), e); 226 } 227 }); 228 } 229}