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 org.apache.commons.io.input.BoundedInputStream;
009import org.fcrepo.kernel.api.RdfStream;
010import org.fcrepo.kernel.api.Transaction;
011import org.fcrepo.kernel.api.cache.UserTypesCache;
012import org.fcrepo.kernel.api.exception.ItemNotFoundException;
013import org.fcrepo.kernel.api.exception.PathNotFoundException;
014import org.fcrepo.kernel.api.exception.PathNotFoundRuntimeException;
015import org.fcrepo.kernel.api.exception.RepositoryRuntimeException;
016import org.fcrepo.kernel.api.identifiers.FedoraId;
017import org.fcrepo.kernel.api.models.Binary;
018import org.fcrepo.kernel.api.models.ExternalContent;
019import org.fcrepo.kernel.api.models.FedoraResource;
020import org.fcrepo.kernel.api.models.ResourceFactory;
021import org.fcrepo.persistence.api.PersistentStorageSessionManager;
022import org.fcrepo.persistence.api.exceptions.PersistentItemNotFoundException;
023import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
024
025import java.io.BufferedInputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.net.URI;
029import java.util.Collection;
030import java.util.List;
031
032import static org.fcrepo.kernel.api.RdfLexicon.FEDORA_BINARY;
033import static org.fcrepo.kernel.api.models.ExternalContent.PROXY;
034
035
036/**
037 * Implementation of a Non-RDF resource.
038 *
039 * @author bbpennel
040 */
041public class BinaryImpl extends FedoraResourceImpl implements Binary {
042
043    private static final URI FEDORA_BINARY_URI = URI.create(FEDORA_BINARY.getURI());
044
045    private String externalHandling;
046
047    private String externalUrl;
048
049    private Long contentSize;
050
051    private String filename;
052
053    private String mimeType;
054
055    private Collection<URI> digests;
056
057    /**
058     * Construct the binary
059     *
060     * @param fedoraID fedora identifier
061     * @param transaction transaction
062     * @param pSessionManager session manager
063     * @param resourceFactory resource factory
064     * @param userTypesCache the user types cache
065     */
066    public BinaryImpl(final FedoraId fedoraID,
067                      final Transaction transaction,
068                      final PersistentStorageSessionManager pSessionManager,
069                      final ResourceFactory resourceFactory,
070                      final UserTypesCache userTypesCache) {
071        super(fedoraID, transaction, pSessionManager, resourceFactory, userTypesCache);
072    }
073
074    @Override
075    public InputStream getContent() {
076        try {
077            if (isProxy() || isRedirect()) {
078                // non-external streams are already buffered
079                return new BufferedInputStream(URI.create(getExternalURL()).toURL().openStream());
080            } else {
081                return getSession().getBinaryContent(getFedoraId().asResourceId(), getMementoDatetime());
082            }
083        } catch (final PersistentItemNotFoundException e) {
084            throw new ItemNotFoundException("Unable to find content for " + getId()
085                    + " version " + getMementoDatetime(), e);
086        } catch (final PersistentStorageException | IOException e) {
087            throw new RepositoryRuntimeException(e.getMessage(), e);
088        }
089    }
090
091    @Override
092    public InputStream getRange(final long start, final long end) {
093        try {
094            if (isProxy() || isRedirect()) {
095                // non-external streams are already buffered
096                final long length = end + 1;
097                final var stream = new BoundedInputStream(URI.create(getExternalURL()).toURL().openStream(), length);
098                stream.skip(start);
099                return stream;
100            } else {
101                return getSession().getBinaryRange(getFedoraId().asResourceId(), getMementoDatetime(), start, end);
102            }
103        } catch (final PersistentItemNotFoundException e) {
104            throw new ItemNotFoundException("Unable to find content for " + getId()
105                    + " version " + getMementoDatetime(), e);
106        } catch (final PersistentStorageException | IOException e) {
107            throw new RepositoryRuntimeException(e.getMessage(), e);
108        }
109    }
110
111    @Override
112    public long getContentSize() {
113        return contentSize;
114    }
115
116    @Override
117    public Collection<URI> getContentDigests() {
118        if (digests == null) {
119            return null;
120        }
121        return digests;
122    }
123
124    @Override
125    public Boolean isProxy() {
126        return PROXY.equals(externalHandling);
127    }
128
129    @Override
130    public Boolean isRedirect() {
131        return ExternalContent.REDIRECT.equals(externalHandling);
132    }
133
134    @Override
135    public String getExternalURL() {
136        return externalUrl;
137    }
138
139    @Override
140    public String getMimeType() {
141        return mimeType;
142    }
143
144    @Override
145    public String getFilename() {
146        return filename;
147    }
148
149    @Override
150    public FedoraResource getDescription() {
151        try {
152            final FedoraId descId = getFedoraId().asDescription();
153            if (this.isMemento()) {
154                final var descIdAsMemento = descId.asMemento(getMementoDatetime());
155                return resourceFactory.getResource(transaction, descIdAsMemento);
156            }
157            return resourceFactory.getResource(transaction, descId);
158        } catch (final PathNotFoundException e) {
159            throw new PathNotFoundRuntimeException(e.getMessage(), e);
160        }
161    }
162
163    /**
164     * @param externalHandling the externalHandling to set
165     */
166    protected void setExternalHandling(final String externalHandling) {
167        this.externalHandling = externalHandling;
168    }
169
170    /**
171     * @param externalUrl the externalUrl to set
172     */
173    protected void setExternalUrl(final String externalUrl) {
174        this.externalUrl = externalUrl;
175    }
176
177    /**
178     * @param contentSize the contentSize to set
179     */
180    protected void setContentSize(final Long contentSize) {
181        this.contentSize = contentSize;
182    }
183
184    /**
185     * @param filename the filename to set
186     */
187    protected void setFilename(final String filename) {
188        this.filename = filename;
189    }
190
191    /**
192     * @param mimeType the mimeType to set
193     */
194    protected void setMimeType(final String mimeType) {
195        this.mimeType = mimeType;
196    }
197
198    /**
199     * @param digests the digests to set
200     */
201    protected void setDigests(final Collection<URI> digests) {
202        this.digests = digests;
203    }
204
205    @Override
206    public List<URI> getSystemTypes(final boolean forRdf) {
207        var types = resolveSystemTypes(forRdf);
208
209        if (types == null) {
210            types = super.getSystemTypes(forRdf);
211            // Add fedora:Binary type.
212            types.add(FEDORA_BINARY_URI);
213        }
214
215        return types;
216    }
217
218    @Override
219    public RdfStream getTriples() {
220        return getDescription().getTriples();
221    }
222}