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.modeshape; 019 020import static org.apache.jena.datatypes.xsd.XSDDatatype.XSDstring; 021import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_DESCRIPTION; 022import static org.fcrepo.kernel.modeshape.FedoraJcrConstants.FIELD_DELIMITER; 023import static org.fcrepo.kernel.modeshape.services.functions.JcrPropertyFunctions.property2values; 024 025import static org.slf4j.LoggerFactory.getLogger; 026 027import java.net.URI; 028import java.util.Optional; 029 030import javax.jcr.Node; 031import javax.jcr.PathNotFoundException; 032import javax.jcr.Property; 033import javax.jcr.RepositoryException; 034import javax.jcr.Value; 035 036import org.apache.jena.rdf.model.Resource; 037import org.fcrepo.kernel.api.RdfStream; 038import org.fcrepo.kernel.api.exception.RepositoryRuntimeException; 039import org.fcrepo.kernel.api.identifiers.IdentifierConverter; 040import org.fcrepo.kernel.api.models.FedoraBinary; 041import org.fcrepo.kernel.api.models.FedoraResource; 042import org.fcrepo.kernel.api.utils.ContentDigest; 043import org.slf4j.Logger; 044 045/** 046 * Abstract class representing the content of a binary resource 047 * 048 * @author bbpennel 049 */ 050public abstract class AbstractFedoraBinary extends FedoraResourceImpl implements FedoraBinary { 051 052 private static final Logger LOGGER = getLogger(AbstractFedoraBinary.class); 053 054 protected static final String DEFAULT_MIME_TYPE = "application/octet-stream"; 055 056 protected AbstractFedoraBinary(final Node node) { 057 super(node); 058 } 059 060 @Override 061 public FedoraResource getDescription() { 062 final Node descNode = getDescriptionNodeOrNull(); 063 if (descNode == null) { 064 return null; 065 } 066 return new NonRdfSourceDescriptionImpl(getDescriptionNode()); 067 } 068 069 @Override 070 protected Node getDescriptionNode() { 071 072 try { 073 final Node node = getNode(); 074 if (isMemento()) { 075 final String mementoName = node.getName(); 076 return node.getParent().getParent().getNode(FEDORA_DESCRIPTION) 077 .getNode(LDPCV_TIME_MAP).getNode(mementoName); 078 } 079 return getNode().getNode(FEDORA_DESCRIPTION); 080 } catch (final RepositoryException e) { 081 082 // ignore error as Desc memento may not be there yet 083 if (isMemento()) { 084 return null; 085 } 086 throw new RepositoryRuntimeException(e); 087 } 088 } 089 090 protected Node getDescriptionNodeOrNull() { 091 try { 092 return getDescriptionNode(); 093 } catch (final RepositoryRuntimeException e) { 094 if (e.getCause() instanceof PathNotFoundException) { 095 return null; 096 } 097 throw new RepositoryRuntimeException(e); 098 } 099 } 100 101 102 /* 103 * (non-Javadoc) 104 * @see org.fcrepo.kernel.api.models.FedoraBinary#getContentSize() 105 */ 106 @Override 107 public long getContentSize() { 108 try { 109 if (hasDescriptionProperty(CONTENT_SIZE)) { 110 return getDescriptionProperty(CONTENT_SIZE).getLong(); 111 } 112 } catch (final RepositoryException e) { 113 LOGGER.warn("Could not get contentSize(): {}", e.getMessage()); 114 } 115 return -1L; 116 } 117 118 /* 119 * (non-Javadoc) 120 * @see org.fcrepo.kernel.api.models.FedoraBinary#getContentDigest() 121 */ 122 @Override 123 public URI getContentDigest() { 124 125 LOGGER.debug("getContentDigest getting digest info"); 126 try { 127 // Determine which digest algorithm to use 128 final String algorithm = hasDescriptionProperty(DEFAULT_DIGEST_ALGORITHM) ? property2values.apply( 129 getDescriptionProperty(DEFAULT_DIGEST_ALGORITHM)).findFirst().get().getString() 130 : ContentDigest.DEFAULT_ALGORITHM; 131 final String algorithmWithoutStringType = algorithm.replace(FIELD_DELIMITER + XSDstring.getURI(), ""); 132 133 if (hasDescriptionProperty(CONTENT_DIGEST)) { 134 // Select the stored digest that matches the digest algorithm 135 final Optional<Value> digestValue = property2values.apply(getDescriptionProperty(CONTENT_DIGEST)) 136 .filter(digest -> { 137 try { 138 final URI digestUri = URI.create(digest.getString()); 139 return algorithmWithoutStringType.equalsIgnoreCase(ContentDigest.getAlgorithm(digestUri)); 140 141 } catch (final RepositoryException e) { 142 LOGGER.warn("Exception thrown when getting digest property {}, {}", digest, e.getMessage()); 143 return false; 144 } 145 }).findFirst(); 146 147 // Success, return the digest value 148 if (digestValue.isPresent()) { 149 return URI.create(digestValue.get().getString()); 150 } 151 } 152 LOGGER.warn("No digest value was found to match the algorithm: {}", algorithmWithoutStringType); 153 } catch (final RepositoryException e) { 154 LOGGER.warn("Could not get content digest: {}", e.getMessage()); 155 } 156 157 return ContentDigest.missingChecksum(); 158 } 159 160 161 /* 162 * (non-Javadoc) 163 * @see org.fcrepo.kernel.api.models.FedoraBinary#isProxy() 164 */ 165 @Override 166 public Boolean isProxy() { 167 return hasProperty(PROXY_FOR); 168 } 169 170 /* 171 * (non-Javadoc) 172 * @see org.fcrepo.kernel.api.models.FedoraBinary#isRedirect() 173 */ 174 @Override 175 public Boolean isRedirect() { 176 return hasProperty(REDIRECTS_TO); 177 } 178 179 /* 180 * (non-Javadoc) 181 * @see org.fcrepo.kernel.api.models.FedoraBinary#getProxyURL() 182 */ 183 @Override 184 public String getProxyURL() { 185 try { 186 if (hasProperty(PROXY_FOR)) { 187 return getProperty(PROXY_FOR).getString(); 188 } 189 return null; 190 } catch (final RepositoryException e) { 191 throw new RepositoryRuntimeException(e); 192 } 193 } 194 195 /* 196 * (non-Javadoc) 197 * @see org.fcrepo.kernel.api.models.FedoraBinary#setProxyURL() 198 */ 199 @Override 200 public void setProxyURL(final String url) throws RepositoryRuntimeException { 201 try { 202 getNode().setProperty(PROXY_FOR, url); 203 // clear redirect property, in case it used to be a redirect 204 getNode().setProperty(REDIRECTS_TO, (Value) null); 205 } catch (final RepositoryException e) { 206 throw new RepositoryRuntimeException(e); 207 } 208 } 209 210 /* 211 * (non-Javadoc) 212 * @see org.fcrepo.kernel.api.models.FedoraBinary#getRedirectURL() 213 */ 214 @Override 215 public String getRedirectURL() { 216 try { 217 if (hasProperty(REDIRECTS_TO)) { 218 return getProperty(REDIRECTS_TO).getString(); 219 } 220 return null; 221 } catch (final RepositoryException e) { 222 throw new RepositoryRuntimeException(e); 223 } 224 } 225 226 /* 227 * (non-Javadoc) 228 * @see org.fcrepo.kernel.api.models.FedoraBinary#setRedirectURL() 229 */ 230 @Override 231 public void setRedirectURL(final String url) throws RepositoryRuntimeException { 232 try { 233 getNode().setProperty(REDIRECTS_TO, url); 234 // clear proxy property in case it used to be a proxy 235 getNode().setProperty(PROXY_FOR, (Value) null); 236 } catch (final RepositoryException e) { 237 throw new RepositoryRuntimeException(e); 238 } 239 } 240 241 protected String getMimeTypeValue() { 242 try { 243 if (hasDescriptionProperty(HAS_MIME_TYPE)) { 244 return getDescriptionProperty(HAS_MIME_TYPE).getString() 245 .replace(FIELD_DELIMITER + XSDstring.getURI(), ""); 246 } 247 } catch (final RepositoryRuntimeException e) { 248 if (!(e.getCause() instanceof PathNotFoundException) || !isMemento()) { 249 throw e; 250 } 251 } catch (final RepositoryException e) { 252 throw new RepositoryRuntimeException(e); 253 } 254 return DEFAULT_MIME_TYPE; 255 } 256 257 /* 258 * (non-Javadoc) 259 * @see org.fcrepo.kernel.api.models.FedoraBinary#getFilename() 260 */ 261 @Override 262 public String getFilename() { 263 try { 264 if (hasDescriptionProperty(FILENAME)) { 265 return getDescriptionProperty(FILENAME).getString().replace(FIELD_DELIMITER + XSDstring.getURI(), ""); 266 } 267 return node.getName(); 268 } catch (final RepositoryException e) { 269 throw new RepositoryRuntimeException(e); 270 } 271 } 272 273 @Override 274 public RdfStream getFixity(final IdentifierConverter<Resource, FedoraResource> idTranslator) { 275 return getFixity(idTranslator, getContentDigest(), getContentSize()); 276 } 277 278 /** 279 * When deleting the binary, we also need to clean up the description document. 280 */ 281 @Override 282 public void delete() { 283 final FedoraResource description = getDescription(); 284 285 if (description != null) { 286 description.delete(); 287 } 288 289 super.delete(); 290 } 291 292 /** 293 * Check of the property exists on the description of this binary. 294 * 295 * @param relPath - path to the property 296 * @return true if property exists. 297 */ 298 protected boolean hasDescriptionProperty(final String relPath) { 299 300 try { 301 final Node descNode = getDescriptionNodeOrNull(); 302 if (descNode == null) { 303 return false; 304 } 305 return descNode.hasProperty(relPath); 306 } catch (final RepositoryException e) { 307 throw new RepositoryRuntimeException(e); 308 } 309 } 310 311 /** 312 * Return the description property for this binary. 313 * 314 * @param relPath - path to the property 315 * @return Property object 316 */ 317 private Property getDescriptionProperty(final String relPath) { 318 try { 319 return getDescriptionNode().getProperty(relPath); 320 } catch (final RepositoryException e) { 321 throw new RepositoryRuntimeException(e); 322 } 323 } 324 325 /** 326 * Set the content size 327 * 328 * @param size the new value of the content size. 329 */ 330 protected void setContentSize(final long size) { 331 try { 332 getDescriptionNode().setProperty(CONTENT_SIZE, size); 333 } catch (final RepositoryException e) { 334 throw new RepositoryRuntimeException(e); 335 } 336 } 337}