001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.client.impl;
017
018import static org.apache.http.HttpStatus.SC_CONFLICT;
019import static org.apache.http.HttpStatus.SC_CREATED;
020import static org.apache.http.HttpStatus.SC_FORBIDDEN;
021import static org.apache.http.HttpStatus.SC_NO_CONTENT;
022import static org.apache.http.HttpStatus.SC_NOT_FOUND;
023import static org.apache.http.HttpStatus.SC_OK;
024
025import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
026
027import static org.fcrepo.kernel.api.RdfLexicon.DESCRIBES;
028import static org.fcrepo.kernel.api.RdfLexicon.HAS_ORIGINAL_NAME;
029import static org.fcrepo.kernel.api.RdfLexicon.HAS_MIME_TYPE;
030import static org.fcrepo.kernel.api.RdfLexicon.HAS_SIZE;
031import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE;
032
033import static org.slf4j.LoggerFactory.getLogger;
034
035import java.io.InputStream;
036import java.net.URI;
037import java.net.URISyntaxException;
038
039import org.fcrepo.client.ForbiddenException;
040import org.fcrepo.client.NotFoundException;
041
042import com.hp.hpl.jena.graph.Node;
043import com.hp.hpl.jena.graph.NodeFactory;
044import com.hp.hpl.jena.graph.Graph;
045import com.hp.hpl.jena.graph.Triple;
046import com.hp.hpl.jena.rdf.model.Property;
047
048import org.apache.http.HttpResponse;
049import org.apache.http.StatusLine;
050import org.apache.http.client.methods.HttpGet;
051import org.apache.http.client.methods.HttpPut;
052
053import org.apache.jena.atlas.lib.NotImplemented;
054
055import org.fcrepo.client.FedoraContent;
056import org.fcrepo.client.FedoraDatastream;
057import org.fcrepo.client.FedoraException;
058import org.fcrepo.client.FedoraObject;
059import org.fcrepo.client.FedoraRepository;
060import org.fcrepo.client.utils.HttpHelper;
061
062import org.fcrepo.kernel.api.FedoraJcrTypes;
063import org.slf4j.Logger;
064
065/**
066 * A Fedora Datastream Impl.
067 *
068 * @author escowles
069 * @since 2014-08-25
070 */
071public class FedoraDatastreamImpl extends FedoraResourceImpl implements FedoraDatastream {
072    private static final Logger LOGGER = getLogger(FedoraDatastreamImpl.class);
073    protected static final Property REST_API_DIGEST = createProperty(REPOSITORY_NAMESPACE + "digest");
074    private boolean hasContent;
075    private Node contentSubject;
076
077    /**
078     * Constructor for FedoraDatastreamImpl
079     *
080     * @param repository Repository that created this object.
081     * @param httpHelper HTTP helper for making repository requests
082     * @param path Path of the datastream in the repository
083     */
084    public FedoraDatastreamImpl(final FedoraRepository repository, final HttpHelper httpHelper, final String path) {
085        super(repository, httpHelper, path);
086        contentSubject = NodeFactory.createURI(
087                repository.getRepositoryUrl() + path.substring(0, path.lastIndexOf("/")) );
088    }
089
090    @Override
091    public void setGraph( final Graph graph ) {
092        super.setGraph( graph );
093        hasContent = getTriple( subject, DESCRIBES ) != null;
094    }
095
096    @Override
097    public String getPropertiesPath() {
098        return path + "/" + FedoraJcrTypes.FCR_METADATA;
099    }
100
101    @Override
102    public boolean hasContent() throws FedoraException {
103        return hasContent;
104    }
105
106    @Override
107    public FedoraObject getObject() throws FedoraException {
108        final String contentPath = path.substring(0, path.lastIndexOf("/"));
109        return repository.getObject( contentPath.substring(0, contentPath.lastIndexOf("/")) );
110    }
111
112    @Override
113    public String getName() {
114        final String p = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
115        final String[] paths = p.split("/");
116        return paths[paths.length - 2];
117    }
118
119    @Override
120    public URI getContentDigest() throws FedoraException {
121        final Node contentDigest = getObjectValue( REST_API_DIGEST );
122        try {
123            if ( contentDigest == null ) {
124                return null;
125            }
126
127            return new URI( contentDigest.getURI() );
128        } catch ( final URISyntaxException e ) {
129            throw new FedoraException("Error parsing checksum URI: " + contentDigest.getURI(), e);
130        }
131    }
132
133    @Override
134    public Long getContentSize() throws FedoraException {
135        final Node size = getObjectValue( HAS_SIZE );
136        if ( size == null ) {
137            return null;
138        }
139
140        return new Long( size.getLiteralValue().toString() );
141    }
142
143    @Override
144    public String getFilename() throws FedoraException {
145        final Node filename = getObjectValue( HAS_ORIGINAL_NAME );
146        if ( filename == null ) {
147            return null;
148        }
149
150        return filename.getLiteralValue().toString();
151    }
152
153    @Override
154    public String getContentType() throws FedoraException {
155        final Node contentType = getObjectValue( HAS_MIME_TYPE );
156        if ( contentType == null ) {
157            return null;
158        }
159
160        return contentType.getLiteralValue().toString();
161    }
162
163    @Override
164    public void updateContent( final FedoraContent content ) throws FedoraException {
165        final HttpPut put = httpHelper.createContentPutMethod( path, null, content );
166
167        try {
168            final HttpResponse response = httpHelper.execute( put );
169            final StatusLine status = response.getStatusLine();
170            final String uri = put.getURI().toString();
171
172            if ( status.getStatusCode() == SC_CREATED
173                    || status.getStatusCode() == SC_NO_CONTENT) {
174                LOGGER.debug("content updated successfully for resource {}", uri);
175            } else if ( status.getStatusCode() == SC_FORBIDDEN) {
176                LOGGER.error("request for resource {} is not authorized.", uri);
177                throw new ForbiddenException("request for resource " + uri + " is not authorized.");
178            } else if ( status.getStatusCode() == SC_NOT_FOUND) {
179                LOGGER.error("resource {} does not exist, cannot retrieve", uri);
180                throw new NotFoundException("resource " + uri + " does not exist, cannot retrieve");
181            } else if ( status.getStatusCode() == SC_CONFLICT) {
182                LOGGER.error("checksum mismatch for {}", uri);
183                throw new FedoraException("checksum mismatch for resource " + uri);
184            } else {
185                LOGGER.error("error retrieving resource {}: {} {}", uri, status.getStatusCode(),
186                             status.getReasonPhrase());
187                throw new FedoraException("error retrieving resource " + uri + ": " + status.getStatusCode() + " " +
188                                          status.getReasonPhrase());
189            }
190
191            // update properties from server
192            httpHelper.loadProperties(this);
193
194        } catch (final FedoraException e) {
195            throw e;
196        } catch (final Exception e) {
197            LOGGER.error("could not encode URI parameter", e);
198            throw new FedoraException(e);
199        } finally {
200            put.releaseConnection();
201        }
202    }
203
204    @Override
205    public InputStream getContent() throws FedoraException {
206        final HttpGet get = httpHelper.createGetMethod( path, null );
207        final String uri = get.getURI().toString();
208
209        try {
210            final HttpResponse response = httpHelper.execute( get );
211            final StatusLine status = response.getStatusLine();
212
213            if ( status.getStatusCode() == SC_OK) {
214                return response.getEntity().getContent();
215            } else if ( status.getStatusCode() == SC_FORBIDDEN) {
216                LOGGER.error("request for resource {} is not authorized.", uri);
217                throw new ForbiddenException("request for resource " + uri + " is not authorized.");
218            } else if ( status.getStatusCode() == SC_NOT_FOUND) {
219                LOGGER.error("resource {} does not exist, cannot retrieve", uri);
220                throw new NotFoundException("resource " + uri + " does not exist, cannot retrieve");
221            } else {
222                LOGGER.error("error retrieving resource {}: {} {}", uri, status.getStatusCode(),
223                             status.getReasonPhrase());
224                throw new FedoraException("error retrieving resource " + uri + ": " + status.getStatusCode() + " " +
225                                          status.getReasonPhrase());
226            }
227        } catch (final Exception e) {
228            LOGGER.error("could not encode URI parameter", e);
229            throw new FedoraException(e);
230        } finally {
231            get.releaseConnection();
232        }
233    }
234
235    @Override
236    public void checkFixity() {
237        throw new NotImplemented("Method checkFixity() is not implemented");
238    }
239
240    private Node getObjectValue( final Property property ) {
241        if ( !hasContent ) {
242            return null;
243        }
244
245        final Triple t = getTriple( contentSubject, property );
246        if ( t == null ) {
247            return null;
248        }
249
250        return t.getObject();
251    }
252}