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 com.hp.hpl.jena.graph.Triple;
019
020import org.apache.http.HttpResponse;
021import org.apache.http.StatusLine;
022import org.apache.http.client.HttpClient;
023import org.apache.http.client.methods.HttpHead;
024import org.apache.http.client.methods.HttpPost;
025import org.apache.http.client.methods.HttpPut;
026import org.fcrepo.client.AlreadyExistsException;
027import org.fcrepo.client.FedoraContent;
028import org.fcrepo.client.FedoraDatastream;
029import org.fcrepo.client.FedoraException;
030import org.fcrepo.client.FedoraObject;
031import org.fcrepo.client.FedoraRepository;
032import org.fcrepo.client.ForbiddenException;
033import org.fcrepo.client.NotFoundException;
034import org.fcrepo.client.ReadOnlyException;
035import org.fcrepo.client.utils.HttpHelper;
036import org.slf4j.Logger;
037
038import java.io.InputStream;
039import java.util.Iterator;
040import java.util.Map;
041
042import static org.apache.http.HttpStatus.SC_CONFLICT;
043import static org.apache.http.HttpStatus.SC_CREATED;
044import static org.apache.http.HttpStatus.SC_FORBIDDEN;
045import static org.apache.http.HttpStatus.SC_NOT_FOUND;
046import static org.apache.http.HttpStatus.SC_OK;
047import static org.slf4j.LoggerFactory.getLogger;
048
049/**
050 * FedoraRepositoryImpl manage httpclient instance to run requests
051 *
052 * @author lsitu
053 * @author escowles
054 * @since 2014-08-11
055 */
056public class FedoraRepositoryImpl implements FedoraRepository {
057    private static final Logger LOGGER = getLogger(FedoraRepositoryImpl.class);
058
059    protected HttpHelper httpHelper;
060    protected String repositoryURL;
061
062    protected FedoraRepositoryImpl() {
063        // for subclasses
064    }
065
066    /**
067     * Constructor that takes the repository url
068     *
069     * @param repositoryURL Fedora base URL.
070     */
071    public FedoraRepositoryImpl(final String repositoryURL) {
072        this(repositoryURL, null, null);
073    }
074
075    /**
076     * Constructor
077     *
078     * @param repositoryURL Repository base URL
079     * @param username Repository username
080     * @param password Repository password
081     */
082    public FedoraRepositoryImpl(final String repositoryURL, final String username, final String password) {
083        this.repositoryURL = repositoryURL;
084        this.httpHelper = new HttpHelper(repositoryURL, username, password, false);
085    }
086
087    /**
088     * Constructor that takes the pre-configured HttpClient
089     *
090     * @param repositoryURL Repository baseURL
091     * @param httpClient Pre-configured httpClient
092     */
093    public FedoraRepositoryImpl(final String repositoryURL, final HttpClient httpClient) {
094        this.repositoryURL = repositoryURL;
095        this.httpHelper = new HttpHelper(repositoryURL, httpClient, false);
096    }
097
098    @Override
099    public boolean exists(final String path) throws FedoraException, ForbiddenException {
100        final HttpHead head = httpHelper.createHeadMethod(path);
101        try {
102            final HttpResponse response = httpHelper.execute(head);
103            final StatusLine status = response.getStatusLine();
104            final int statusCode = status.getStatusCode();
105            final String uri = head.getURI().toString();
106            if (statusCode == SC_OK) {
107                return true;
108            } else if (statusCode == SC_NOT_FOUND) {
109                return false;
110            } else if (statusCode == SC_FORBIDDEN) {
111                LOGGER.error("request for resource {} is not authorized.", uri);
112                throw new ForbiddenException("request for resource " + uri + " is not authorized.");
113            } else {
114                LOGGER.error("error checking resource {}: {} {}", uri, statusCode, status.getReasonPhrase());
115                throw new FedoraException("error checking resource " + uri + ": " + statusCode + " " +
116                                          status.getReasonPhrase());
117            }
118        } catch (final Exception e) {
119            LOGGER.error("could not encode URI parameter", e);
120            throw new FedoraException(e);
121        } finally {
122            head.releaseConnection();
123        }
124    }
125
126    @Override
127    public FedoraDatastream getDatastream(final String path) throws FedoraException {
128        return (FedoraDatastream)httpHelper.loadProperties(new FedoraDatastreamImpl(this, httpHelper, path));
129    }
130
131    @Override
132    public FedoraObject getObject(final String path) throws FedoraException {
133        return (FedoraObject)httpHelper.loadProperties(new FedoraObjectImpl(this, httpHelper, path));
134    }
135
136    @Override
137    public FedoraDatastream createDatastream(final String path, final FedoraContent content) throws FedoraException {
138        final HttpPut put = httpHelper.createContentPutMethod(path, null, content);
139        try {
140            final HttpResponse response = httpHelper.execute(put);
141            final String uri = put.getURI().toString();
142            final StatusLine status = response.getStatusLine();
143            final int statusCode = status.getStatusCode();
144
145            if (statusCode == SC_CREATED) {
146                return getDatastream(path);
147            } else if (statusCode == SC_FORBIDDEN) {
148                LOGGER.error("request to create resource {} is not authorized.", uri);
149                throw new ForbiddenException("request to create resource " + uri + " is not authorized.");
150            } else if (statusCode == SC_CONFLICT) {
151                LOGGER.error("resource {} already exists", uri);
152                throw new AlreadyExistsException("resource " + uri + " already exists");
153            } else {
154                LOGGER.error("error creating resource {}: {} {}", uri, statusCode, status.getReasonPhrase());
155                throw new FedoraException("error retrieving resource " + uri + ": " + statusCode + " " +
156                                                  status.getReasonPhrase());
157            }
158        } catch (final Exception e) {
159            LOGGER.error("could not encode URI parameter", e);
160            throw new FedoraException(e);
161        } finally {
162            put.releaseConnection();
163        }
164    }
165
166    @Override
167    public FedoraDatastream createOrUpdateRedirectDatastream(final String path, final String url)
168            throws FedoraException {
169        final HttpPut put = httpHelper.createContentPutMethod(path, null, null);
170        try {
171            put.setHeader("Content-Type", "message/external-body; access-type=URL; URL=\"" + url + "\"");
172            final HttpResponse response = httpHelper.execute(put);
173            final String uri = put.getURI().toString();
174            final StatusLine status = response.getStatusLine();
175            final int statusCode = status.getStatusCode();
176
177            if (statusCode == SC_CREATED) {
178                return getDatastream(path);
179            } else if (statusCode == SC_FORBIDDEN) {
180                LOGGER.error("request to create resource {} is not authorized.", uri);
181                throw new ForbiddenException("request to create resource " + uri + " is not authorized.");
182            } else {
183                LOGGER.error("error creating resource {}: {} {}", uri, statusCode, status.getReasonPhrase());
184                throw new FedoraException("error creating resource " + uri + ": " + statusCode + " " +
185                        status.getReasonPhrase());
186            }
187        } catch (final Exception e) {
188            LOGGER.error("Error making or building PUT request.", e);
189            throw new FedoraException(e);
190        } finally {
191            put.releaseConnection();
192        }
193    }
194
195    @Override
196    public FedoraObject createObject(final String path) throws FedoraException {
197        final HttpPut put = httpHelper.createPutMethod(path, null);
198        try {
199            final HttpResponse response = httpHelper.execute(put);
200            final String uri = put.getURI().toString();
201            final StatusLine status = response.getStatusLine();
202            final int statusCode = status.getStatusCode();
203
204            if (statusCode == SC_CREATED) {
205                return getObject(path);
206            } else if (statusCode == SC_FORBIDDEN) {
207                LOGGER.error("request to create resource {} is not authorized.", uri);
208                throw new ForbiddenException("request to create resource " + uri + " is not authorized.");
209            } else if (statusCode == SC_CONFLICT) {
210                LOGGER.error("resource {} already exists", uri);
211                throw new AlreadyExistsException("resource " + uri + " already exists");
212            } else {
213                LOGGER.error("error creating resource {}: {} {}", uri, statusCode, status.getReasonPhrase());
214                throw new FedoraException("error retrieving resource " + uri + ": " + statusCode + " " +
215                                                  status.getReasonPhrase());
216            }
217        } catch (final Exception e) {
218            LOGGER.error("could not encode URI parameter", e);
219            throw new FedoraException(e);
220        } finally {
221            put.releaseConnection();
222        }
223    }
224
225    @Override
226    public FedoraObject createResource(final String containerPath) throws FedoraException {
227        final HttpPost post = httpHelper.createPostMethod(containerPath == null ? "" : containerPath, null);
228        try {
229            final HttpResponse response = httpHelper.execute(post);
230            final String uri = post.getURI().toString();
231            final StatusLine status = response.getStatusLine();
232            final int statusCode = status.getStatusCode();
233
234            if (statusCode == SC_CREATED) {
235                return getObject(response.getFirstHeader("Location").getValue().substring(repositoryURL.length()));
236            } else if (statusCode == SC_FORBIDDEN) {
237                LOGGER.error("request to create resource {} is not authorized.", uri);
238                throw new ForbiddenException("request to create resource " + uri + " is not authorized.");
239            } else {
240                LOGGER.error("error creating resource {}: {} {}", uri, statusCode, status.getReasonPhrase());
241                throw new FedoraException("error creating resource " + uri + ": " + statusCode + " " +
242                        status.getReasonPhrase());
243            }
244        } catch (final Exception e) {
245            LOGGER.error("could not encode URI parameter", e);
246            throw new FedoraException(e);
247        } finally {
248            post.releaseConnection();
249        }
250    }
251
252    @Override
253    public FedoraDatastream findOrCreateDatastream(final String path) throws FedoraException {
254        try {
255            return getDatastream(path);
256        } catch ( NotFoundException ex ) {
257            return createDatastream(path, null);
258        }
259    }
260
261    @Override
262    public FedoraObject findOrCreateObject(final String path) throws FedoraException {
263        try {
264            return getObject(path);
265        } catch ( NotFoundException ex ) {
266            return createObject(path);
267        }
268    }
269
270    @Override
271    public Iterator<Triple> getNodeTypes() {
272        // TODO Auto-generated method stub
273        return null;
274    }
275
276    @Override
277    public void registerNodeTypes(final InputStream cndStream) throws ReadOnlyException {
278        // TODO Auto-generated method stub
279
280    }
281
282    @Override
283    public Map<String, String> getRepositoryNamespaces() {
284        // TODO Auto-generated method stub
285        return null;
286    }
287
288    @Override
289    public void addNamespace(final String prefix, final String uri) throws ReadOnlyException {
290        // TODO Auto-generated method stub
291
292    }
293
294    @Override
295    public void removeNamespace(final String prefix) throws ReadOnlyException {
296        // TODO Auto-generated method stub
297
298    }
299
300    @Override
301    public Long getRepositoryObjectCount() {
302        // TODO Auto-generated method stub
303        return null;
304    }
305
306    @Override
307    public Long getRepositorySize() {
308        // TODO Auto-generated method stub
309        return null;
310    }
311
312    @Override
313    public boolean isWritable() {
314        return true;
315    }
316
317    @Override
318    public String getRepositoryUrl() {
319        return repositoryURL;
320    }
321
322}