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.client;
019
020import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE;
021import static org.fcrepo.client.FedoraHeaderConstants.DIGEST;
022import static org.fcrepo.client.FedoraHeaderConstants.IF_MATCH;
023import static org.fcrepo.client.FedoraHeaderConstants.IF_UNMODIFIED_SINCE;
024
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.net.URI;
030import java.util.StringJoiner;
031
032import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
033import org.apache.http.entity.InputStreamEntity;
034
035/**
036 * Request builder which includes a body component
037 * 
038 * @author bbpennel
039 */
040public abstract class BodyRequestBuilder extends
041        RequestBuilder {
042
043    private StringJoiner digestJoiner;
044
045    /**
046     * Instantiate builder
047     * 
048     * @param uri uri request will be issued to
049     * @param client the client
050     */
051    protected BodyRequestBuilder(final URI uri, final FcrepoClient client) {
052        super(uri, client);
053    }
054
055    /**
056     * Add a body to this request from a stream, with application/octet-stream as its content type
057     * 
058     * @param stream InputStream of the content to be sent to the server
059     * @return this builder
060     */
061    protected BodyRequestBuilder body(final InputStream stream) {
062        return body(stream, null);
063    }
064
065    /**
066     * Add a body to this request as a stream with the given content type
067     * 
068     * @param stream InputStream of the content to be sent to the server
069     * @param contentType the Content-Type of the body
070     * @return this builder
071     */
072    protected BodyRequestBuilder body(final InputStream stream, final String contentType) {
073        if (stream != null) {
074            String type = contentType;
075            if (type == null) {
076                type = "application/octet-stream";
077            }
078
079            ((HttpEntityEnclosingRequestBase) request).setEntity(new InputStreamEntity(stream));
080            request.addHeader(CONTENT_TYPE, type);
081        }
082
083        return this;
084    }
085
086    /**
087     * Add the given file as the body for this request with the provided content type
088     * 
089     * @param file File containing the content to be sent to the server
090     * @param contentType the Content-Type of the body
091     * @return this builder
092     * @throws IOException when unable to stream the body file
093     */
094    protected BodyRequestBuilder body(final File file, final String contentType) throws IOException {
095        return body(new FileInputStream(file), contentType);
096    }
097
098    /**
099     * Provide a SHA-1 checksum for the body of this request.
100     * 
101     * @deprecated Use {@link #digestSha1(java.lang.String)}.
102     * @param digest sha-1 checksum to provide as the digest for the request body
103     * @return this builder
104     */
105    @Deprecated
106    protected BodyRequestBuilder digest(final String digest) {
107        return digestSha1(digest);
108    }
109
110    /**
111     * Provide a checksum for the body of this request
112     * 
113     * @param digest checksum to provide as the digest for the request body
114     * @param alg abbreviated algorithm identifier for the type of checksum being
115     *      added (for example, sha1, md5, etc)
116     * @return this builder
117     */
118    protected BodyRequestBuilder digest(final String digest, final String alg) {
119        if (digest != null) {
120            if (digestJoiner == null) {
121                digestJoiner = new StringJoiner(", ");
122            }
123            digestJoiner.add(alg + "=" + digest);
124            request.setHeader(DIGEST, digestJoiner.toString());
125        }
126        return this;
127    }
128
129    /**
130     * Provide a SHA-1 checksum for the body of this request.
131     * 
132     * @param digest sha-1 checksum to provide as the digest for the request body
133     * @return this builder
134     */
135    protected BodyRequestBuilder digestSha1(final String digest) {
136        return digest(digest, "sha1");
137    }
138
139    /**
140     * Provide a MD5 checksum for the body of this request
141     * 
142     * @param digest MD5 checksum to provide as the digest for the request body
143     * @return this builder
144     */
145    protected BodyRequestBuilder digestMd5(final String digest) {
146        return digest(digest, "md5");
147    }
148
149    /**
150     * Provide a SHA-256 checksum for the body of this request
151     * 
152     * @param digest sha-256 checksum to provide as the digest for the request body
153     * @return this builder
154     */
155    protected BodyRequestBuilder digestSha256(final String digest) {
156        return digest(digest, "sha256");
157    }
158
159    /**
160     * Provide a if-unmodified-since header for this request
161     * 
162     * @param modified date to provide as the if-unmodified-since header
163     * @return this builder
164     */
165    public BodyRequestBuilder ifUnmodifiedSince(final String modified) {
166        if (modified != null) {
167            request.setHeader(IF_UNMODIFIED_SINCE, modified);
168        }
169        return this;
170    }
171
172    /**
173     * Provide an etag for the if-match header for this request
174     * 
175     * @param etag etag to provide as the if-match header
176     * @return this builder
177     */
178    protected BodyRequestBuilder ifMatch(final String etag) {
179        if (etag != null) {
180            request.setHeader(IF_MATCH, etag);
181        }
182        return this;
183    }
184}