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 org.apache.jena.rdf.model.Resource;
021import org.fcrepo.kernel.api.exception.InvalidChecksumException;
022import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
023import org.fcrepo.kernel.api.exception.UnsupportedAlgorithmException;
024import org.fcrepo.kernel.api.identifiers.IdentifierConverter;
025import org.fcrepo.kernel.api.models.FedoraBinary;
026import org.fcrepo.kernel.api.models.FedoraResource;
027import org.fcrepo.kernel.api.RdfStream;
028import org.fcrepo.kernel.api.services.policy.StoragePolicyDecisionPoint;
029import org.fcrepo.kernel.api.TripleCategory;
030
031import static org.fcrepo.kernel.modeshape.utils.FedoraTypesUtils.isFedoraBinary;
032
033import javax.jcr.Node;
034import javax.jcr.RepositoryException;
035
036import java.io.InputStream;
037import java.net.URI;
038import java.util.Collection;
039import java.util.Set;
040
041/**
042 * @author cabeer
043 * @since 9/19/14
044 */
045public class FedoraBinaryImpl extends AbstractFedoraBinary {
046
047    private static final String LOCAL_FILE_ACCESS_TYPE = "file";
048
049    private static final String URL_ACCESS_TYPE = "http";
050
051    private FedoraBinary wrappedBinary;
052
053    /**
054     * Wrap an existing Node as a Fedora Binary
055     * @param node the node
056     */
057    public FedoraBinaryImpl(final Node node) {
058        super(node);
059    }
060
061    /**
062     * Get the proxied binary content object wrapped by this object
063     *
064     * @return the fedora binary
065     */
066    private FedoraBinary getBinary() {
067        return getBinary(null);
068    }
069
070    /**
071     * Get the proxied binary content object wrapped by this object
072     *
073     * @return the fedora binary
074     */
075    private FedoraBinary getBinary(final String extUrl) {
076        if (wrappedBinary == null) {
077            wrappedBinary = getBinaryImplementation(extUrl);
078        }
079        return wrappedBinary;
080    }
081
082    /**
083     * Return the proper object that can handle this type of binary
084     * @param extUrl URL of external binary, else null if it's internal to modeshape.
085     * @return the fedora binary, of the proper kind based on the URL or lack there of
086     */
087    private FedoraBinary getBinaryImplementation(final String extUrl) {
088        String url = extUrl;
089        if (url == null || url.isEmpty()) {
090            url = getURLInfo();
091        }
092
093        if (url != null) {
094            if (url.toLowerCase().startsWith(LOCAL_FILE_ACCESS_TYPE)) {
095                return new LocalFileBinary(getNode());
096            } else if (url.toLowerCase().startsWith(URL_ACCESS_TYPE)) {
097                return new UrlBinary(getNode());
098            }
099        }
100
101        return new InternalFedoraBinary(getNode());
102    }
103
104    /**
105     * Fetch the URL of the external binary
106     * @return String containing the URL location of external binary, else null if it's an internal binary
107     */
108    private String getURLInfo() {
109        try {
110            if (hasProperty(PROXY_FOR)) {
111                return getNode().getProperty(PROXY_FOR).getValue().getString();
112            } else if (hasProperty(REDIRECTS_TO)) {
113                return getNode().getProperty(REDIRECTS_TO).getValue().getString();
114            }
115        } catch (RepositoryException e) {
116            throw new RepositoryRuntimeException(e);
117        }
118
119        return null;
120    }
121
122    /*
123     * (non-Javadoc)
124     * @see org.fcrepo.kernel.api.models.FedoraBinary#getContent()
125     */
126    @Override
127    public InputStream getContent() {
128        return getBinary().getContent();
129    }
130
131    @Override
132    public void setExternalContent(final String contentType,
133                                   final Collection<URI> checksums, final String originalFileName,
134                                   final String externalHandling, final String externalUrl)
135            throws InvalidChecksumException {
136
137        // Clear the wrapped binary object prior to setting the content
138        wrappedBinary = null;
139        getBinary(externalUrl).setExternalContent(contentType, checksums, originalFileName,
140                externalHandling, externalUrl);
141    }
142
143    /*
144     * (non-Javadoc)
145     * @see org.fcrepo.kernel.api.models.FedoraBinary#setContent(java.io.InputStream,
146     * java.lang.String, java.net.URI, java.lang.String,
147     * org.fcrepo.kernel.api.services.policy.StoragePolicyDecisionPoint)
148     */
149    @Override
150    public void setContent(final InputStream content, final String contentType,
151                           final Collection<URI> checksums, final String originalFileName,
152                           final StoragePolicyDecisionPoint storagePolicyDecisionPoint)
153            throws InvalidChecksumException {
154
155        // Clear the wrapped binary object prior to setting the content
156        wrappedBinary = null;
157        // Need to pass the new filename to get the correct implementation.
158        getBinary().setContent(content, contentType, checksums, originalFileName,
159                storagePolicyDecisionPoint);
160    }
161
162    /*
163     * (non-Javadoc)
164     * @see org.fcrepo.kernel.api.models.FedoraBinary#getContentSize()
165     */
166    @Override
167    public long getContentSize() {
168        return getBinary().getContentSize();
169    }
170
171    /*
172     * (non-Javadoc)
173     * @see org.fcrepo.kernel.api.models.FedoraBinary#getContentDigest()
174     */
175    @Override
176    public URI getContentDigest() {
177        return getBinary().getContentDigest();
178    }
179
180    /*
181     * (non-Javadoc)
182     * @see org.fcrepo.kernel.api.models.FedoraBinary#getMimeType()
183     */
184    @Override
185    public String getMimeType() {
186        return getBinary().getMimeType();
187    }
188
189    /*
190     * (non-Javadoc)
191     * @see org.fcrepo.kernel.api.models.FedoraBinary#getFilename()
192     */
193    @Override
194    public String getFilename() {
195        return getBinary().getFilename();
196    }
197
198    @Override
199    public RdfStream getFixity(final IdentifierConverter<Resource, FedoraResource> idTranslator) {
200        return getBinary().getFixity(idTranslator);
201    }
202
203    @Override
204    public RdfStream getFixity(final IdentifierConverter<Resource, FedoraResource> idTranslator,
205                               final URI digestUri,
206                               final long size) {
207
208        return getBinary().getFixity(idTranslator, digestUri, size);
209    }
210
211    @Override
212    public Collection<URI> checkFixity( final IdentifierConverter<Resource, FedoraResource> idTranslator,
213                                        final Collection<String> algorithms)
214                                            throws UnsupportedAlgorithmException {
215
216        return getBinary().checkFixity(idTranslator, algorithms);
217    }
218
219    /**
220     * When deleting the binary, we also need to clean up the description document.
221     */
222    @Override
223    public void delete() {
224        getBinary().delete();
225    }
226
227    @Override
228    protected boolean hasDescriptionProperty(final String relPath) {
229        try {
230            final Node descNode = getDescriptionNodeOrNull();
231            if (descNode == null) {
232                return false;
233            }
234            return descNode.hasProperty(relPath);
235        } catch (final RepositoryException e) {
236            throw new RepositoryRuntimeException(e);
237        }
238    }
239
240    @Override
241    public RdfStream getTriples(final IdentifierConverter<Resource, FedoraResource> idTranslator,
242                                final Set<? extends TripleCategory> contexts) {
243        return getDescription().getTriples(idTranslator, contexts);
244    }
245
246    /**
247     * Check if the given node is a Fedora binary
248     *
249     * @param node the given node
250     * @return whether the given node is a Fedora binary
251     */
252    public static boolean hasMixin(final Node node) {
253        return isFedoraBinary.test(node);
254    }
255}