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.client; 007 008import static org.fcrepo.client.FedoraHeaderConstants.CONTENT_TYPE; 009import static org.fcrepo.client.FedoraHeaderConstants.DIGEST; 010import static org.fcrepo.client.FedoraHeaderConstants.IF_MATCH; 011import static org.fcrepo.client.FedoraHeaderConstants.IF_STATE_TOKEN; 012import static org.fcrepo.client.FedoraHeaderConstants.IF_UNMODIFIED_SINCE; 013import static org.fcrepo.client.FedoraHeaderConstants.LINK; 014import static org.fcrepo.client.LinkHeaderConstants.ACL_REL; 015import static org.fcrepo.client.LinkHeaderConstants.EXTERNAL_CONTENT_HANDLING; 016import static org.fcrepo.client.LinkHeaderConstants.EXTERNAL_CONTENT_REL; 017import static org.fcrepo.client.LinkHeaderConstants.TYPE_REL; 018 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.net.URI; 024import java.util.StringJoiner; 025 026import org.apache.commons.lang3.StringUtils; 027import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 028import org.apache.http.entity.InputStreamEntity; 029import org.fcrepo.client.FcrepoLink.Builder; 030 031/** 032 * Request builder which includes a body component 033 * 034 * @author bbpennel 035 */ 036public abstract class BodyRequestBuilder extends 037 RequestBuilder { 038 039 private StringJoiner digestJoiner; 040 041 /** 042 * Instantiate builder 043 * 044 * @param uri uri request will be issued to 045 * @param client the client 046 */ 047 protected BodyRequestBuilder(final URI uri, final FcrepoClient client) { 048 super(uri, client); 049 } 050 051 /** 052 * Add a body to this request from a stream, with application/octet-stream as its content type 053 * 054 * @param stream InputStream of the content to be sent to the server 055 * @return this builder 056 */ 057 protected BodyRequestBuilder body(final InputStream stream) { 058 return body(stream, null); 059 } 060 061 /** 062 * Add a body to this request as a stream with the given content type 063 * 064 * @param stream InputStream of the content to be sent to the server 065 * @param contentType the Content-Type of the body 066 * @return this builder 067 */ 068 protected BodyRequestBuilder body(final InputStream stream, final String contentType) { 069 if (stream != null) { 070 String type = contentType; 071 if (type == null) { 072 type = "application/octet-stream"; 073 } 074 075 ((HttpEntityEnclosingRequestBase) request).setEntity(new InputStreamEntity(stream)); 076 request.addHeader(CONTENT_TYPE, type); 077 } 078 079 return this; 080 } 081 082 /** 083 * Add the given file as the body for this request with the provided content type 084 * 085 * @param file File containing the content to be sent to the server 086 * @param contentType the Content-Type of the body 087 * @return this builder 088 * @throws IOException when unable to stream the body file 089 */ 090 protected BodyRequestBuilder body(final File file, final String contentType) throws IOException { 091 return body(new FileInputStream(file), contentType); 092 } 093 094 /** 095 * Add the given URI to the request as the location a Non-RDF Source binary should use for external content. The 096 * handling parameter must be supplied, and informs the server of how to process the request. 097 * 098 * @param contentURI URI of the external content. 099 * @param contentType Mimetype to supply for the external content. 100 * @param handling Name of the handling method, used by the server to determine how to process the external 101 * content URI. Standard values can be found in {@link ExternalContentHandling}. 102 * @return this builder 103 */ 104 protected BodyRequestBuilder externalContent(final URI contentURI, final String contentType, 105 final String handling) { 106 final Builder linkBuilder = FcrepoLink.fromUri(contentURI) 107 .rel(EXTERNAL_CONTENT_REL) 108 .param(EXTERNAL_CONTENT_HANDLING, handling); 109 110 if (StringUtils.isNotBlank(contentType)) { 111 linkBuilder.type(contentType); 112 } 113 114 request.addHeader(LINK, linkBuilder.build().toString()); 115 return this; 116 } 117 118 /** 119 * Provide a SHA-1 checksum for the body of this request. 120 * 121 * @deprecated Use {@link #digestSha1(java.lang.String)}. 122 * @param digest sha-1 checksum to provide as the digest for the request body 123 * @return this builder 124 */ 125 @Deprecated 126 protected BodyRequestBuilder digest(final String digest) { 127 return digestSha1(digest); 128 } 129 130 /** 131 * Provide a checksum for the body of this request 132 * 133 * @param digest checksum to provide as the digest for the request body 134 * @param alg abbreviated algorithm identifier for the type of checksum being 135 * added (for example, sha1, md5, etc) 136 * @return this builder 137 */ 138 protected BodyRequestBuilder digest(final String digest, final String alg) { 139 if (digest != null) { 140 if (digestJoiner == null) { 141 digestJoiner = new StringJoiner(", "); 142 } 143 digestJoiner.add(alg + "=" + digest); 144 request.setHeader(DIGEST, digestJoiner.toString()); 145 } 146 return this; 147 } 148 149 /** 150 * Provide a SHA-1 checksum for the body of this request. 151 * 152 * @param digest sha-1 checksum to provide as the digest for the request body 153 * @return this builder 154 */ 155 protected BodyRequestBuilder digestSha1(final String digest) { 156 return digest(digest, "sha"); 157 } 158 159 /** 160 * Provide a MD5 checksum for the body of this request 161 * 162 * @param digest MD5 checksum to provide as the digest for the request body 163 * @return this builder 164 */ 165 protected BodyRequestBuilder digestMd5(final String digest) { 166 return digest(digest, "md5"); 167 } 168 169 /** 170 * Provide a SHA-256 checksum for the body of this request 171 * 172 * @param digest sha-256 checksum to provide as the digest for the request body 173 * @return this builder 174 */ 175 protected BodyRequestBuilder digestSha256(final String digest) { 176 return digest(digest, "sha256"); 177 } 178 179 /** 180 * Add an interaction model to the request 181 * 182 * @param interactionModelUri URI of the interaction model 183 * @return this builder 184 */ 185 protected BodyRequestBuilder addInteractionModel(final String interactionModelUri) { 186 if (interactionModelUri != null) { 187 final FcrepoLink link = FcrepoLink.fromUri(interactionModelUri) 188 .rel(TYPE_REL) 189 .build(); 190 request.addHeader(LINK, link.toString()); 191 } 192 return this; 193 } 194 195 /** 196 * Provide a if-unmodified-since header for this request 197 * 198 * @param modified date to provide as the if-unmodified-since header 199 * @return this builder 200 */ 201 public BodyRequestBuilder ifUnmodifiedSince(final String modified) { 202 if (modified != null) { 203 request.setHeader(IF_UNMODIFIED_SINCE, modified); 204 } 205 return this; 206 } 207 208 /** 209 * Provide an etag for the if-match header for this request 210 * 211 * @param etag etag to provide as the if-match header 212 * @return this builder 213 */ 214 protected BodyRequestBuilder ifMatch(final String etag) { 215 if (etag != null) { 216 request.setHeader(IF_MATCH, etag); 217 } 218 return this; 219 } 220 221 /** 222 * Provide the URI to an ACL for this request 223 * 224 * @param aclUri URI to the ACL 225 * @return this builder 226 */ 227 protected BodyRequestBuilder linkAcl(final String aclUri) { 228 if (aclUri != null) { 229 final FcrepoLink link = FcrepoLink.fromUri(aclUri) 230 .rel(ACL_REL) 231 .build(); 232 request.addHeader(LINK, link.toString()); 233 } 234 return this; 235 } 236 237 /** 238 * Provide a value for the if-state-token header for this request. 239 * 240 * @param token state token value 241 * @return this builder 242 */ 243 protected BodyRequestBuilder ifStateToken(final String token) { 244 if (token != null) { 245 request.setHeader(IF_STATE_TOKEN, token); 246 } 247 return this; 248 } 249}