You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2018/08/22 08:40:54 UTC
svn commit: r1838616 - in
/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api:
JackrabbitValueFactory.java binary/BinaryDownload.java
binary/BinaryDownloadOptions.java binary/BinaryUpload.java
Author: mreutegg
Date: Wed Aug 22 08:40:54 2018
New Revision: 1838616
URL: http://svn.apache.org/viewvc?rev=1838616&view=rev
Log:
JCR-4355: Javadoc fixes and improvements for new direct binary access API
Patch provided by Alexander Klimetschek, with review and contribution from Julian Reschke
Modified:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitValueFactory.java
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownload.java
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownloadOptions.java
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryUpload.java
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitValueFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitValueFactory.java?rev=1838616&r1=1838615&r2=1838616&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitValueFactory.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/JackrabbitValueFactory.java Wed Aug 22 08:40:54 2018
@@ -18,12 +18,14 @@
package org.apache.jackrabbit.api;
+import java.io.InputStream;
import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFactory;
+import org.apache.jackrabbit.api.binary.BinaryDownloadOptions;
import org.apache.jackrabbit.api.binary.BinaryUpload;
import org.apache.jackrabbit.api.binary.BinaryDownload;
import org.jetbrains.annotations.NotNull;
@@ -32,42 +34,57 @@ import org.osgi.annotation.versioning.Pr
/**
* Defines optional functionality that a {@link ValueFactory} may choose to
- * provide. A {@link ValueFactory} may also implement this interface without
- * supporting all of the capabilities in this interface. Each method of the
+ * provide. A {@link ValueFactory} may also implement this interface without
+ * supporting all of the capabilities in this interface. Each method of the
* interface describes the behavior of that method if the underlying capability
* is not available.
+ *
* <p>
- * Currently this interface defines the following optional features:
+ * This interface defines the following optional features:
* <ul>
- * <li>Direct Binary Access - enable a client to upload or download binaries
+ * <li>
+ * Direct Binary Access - enable a client to upload or download binaries
* directly to/from a storage location
+ * </li>
* </ul>
+ *
* <p>
* The features are described in more detail below.
*
* <h2>Direct Binary Access</h2>
- * <p>
+ *
* The Direct Binary Access feature provides the capability for a client to
- * upload or download binaries directly to/from a storage location. For
- * example, this might be a cloud storage providing high-bandwidth direct
- * network access. This API allows for requests to be authenticated and for
- * access permission checks to take place within the repository, but for clients
- * to then access the storage location directly.
+ * upload or download binaries directly to/from a storage location. For example,
+ * this might be a cloud storage providing high-bandwidth direct network access.
+ * This API allows for requests to be authenticated and for access permission
+ * checks to take place within the repository, but for clients to then access
+ * the storage location directly.
+ *
* <p>
- * The feature consists of two parts, direct binary upload and direct binary
- * download.
+ * The feature consists of two parts, download and upload.
+ *
+ * <h3>Direct Binary Download</h3>
+ *
+ * This feature enables remote clients to download binaries directly from a
+ * storage location without streaming the binary through the Jackrabbit-based
+ * application.
+ *
* <p>
+ * For an existing {@link Binary} value that implements {@link BinaryDownload},
+ * a read-only URI (see {@link BinaryDownload#getURI(BinaryDownloadOptions)})
+ * can be retrieved and passed to a remote client, such as a browser.
*
* <h3>Direct Binary Upload</h3>
- * <p>
+ *
* This feature enables remote clients to upload binaries directly to a storage
* location.
+ *
* <p>
- * When adding binaries already present on the same JVM or server as Jackrabbit
- * or Oak, for example because they were generated locally, please use the
- * regular JCR API for {@link javax.jcr.Property#setValue(Binary) adding
- * binaries through input streams} instead. This feature is solely designed for
- * remote clients.
+ * Note: When adding binaries already present on the same JVM/server as
+ * the JCR repository, for example because they were generated locally, please
+ * use the regular JCR API {@link ValueFactory#createBinary(InputStream)}
+ * instead. This feature is solely designed for remote clients.
+ *
* <p>
* The direct binary upload process is split into 3 phases:
* <ol>
@@ -75,112 +92,93 @@ import org.osgi.annotation.versioning.Pr
* <b>Initialize</b>: A remote client makes request to the
* Jackrabbit-based application to request an upload, which calls {@link
* #initiateBinaryUpload(long, int)} and returns the resulting {@link
- * BinaryUpload information} to the remote client.
+ * BinaryUpload instructions} to the remote client.
* </li>
* <li>
* <b>Upload</b>: The remote client performs the actual binary upload
- * directly to the binary storage provider. The {@link BinaryUpload}
+ * directly to the binary storage provider. The {@link BinaryUpload}
* returned from the previous call to {@link
* #initiateBinaryUpload(long, int)} contains detailed instructions on
- * how to complete the upload successfully. For more information, see
- * the BinaryUpload documentation.
+ * how to complete the upload successfully.
* </li>
* <li>
* <b>Complete</b>: The remote client notifies the Jackrabbit-based
- * application that step 2 is complete. The upload token returned in
+ * application that step 2 is complete. The upload token returned in
* the first step (obtained by calling {@link
- * BinaryUpload#getUploadToken()} is passed by the client to {@link
+ * BinaryUpload#getUploadToken()} is passed by the application to {@link
* #completeBinaryUpload(String)}. This will provide the application
* with a regular {@link Binary JCR Binary} that can then be used to
* write JCR content including the binary (such as an nt:file structure)
- * and {@link Session#save() persist} it.
+ * and persist it using {@link Session#save}.
* </li>
* </ol>
- * <p>
- * <h3>Direct Binary Download</h3>
- * <p>
- * The direct binary download process is described in detail in {@link
- * BinaryDownload}.
*/
@ProviderType
public interface JackrabbitValueFactory extends ValueFactory {
+
/**
- * Initiate a transaction to upload binary data directly to a storage
- * location. {@link IllegalArgumentException} will be thrown if an upload
- * cannot be supported for the required parameters, or if the parameters are
- * otherwise invalid. For example, if the value of {@code maxSize} exceeds
- * the size limits for a single binary upload for the implementation or the
- * service provider, or if the value of {@code maxSize} divided by {@code
- * maxParts} exceeds the size limit for an upload or upload part of the
- * implementation or the service provider, {@link IllegalArgumentException}
- * may be thrown.
- * <p>
- * Each service provider has specific limitations on upload sizes,
- * multi-part upload support, part sizes, etc. which can result in {@link
- * IllegalArgumentException} being thrown. You should consult the
- * documentation for your underlying implementation and your service
- * provider for details.
+ * Initiate a transaction to upload a binary directly to a storage
+ * location and return {@link BinaryUpload} instructions for a remote client.
+ * Returns {@code null} if the feature is not available.
+ *
* <p>
- * If this call is successful, a {@link BinaryUpload} is returned
- * which contains the information a client needs to successfully complete
- * a direct upload.
- *
- * @param maxSize The expected maximum size of the binary to be uploaded by
- * the client. If the actual size of the binary is known, this
- * size should be used; otherwise, the client should make a best
- * guess. If a client calls this method with one size and then
- * later determines that the guess was too small, the transaction
- * should be restarted by calling this method again with the correct
- * size.
+ * {@link IllegalArgumentException} will be thrown if an upload
+ * cannot be supported for the required parameters, or if the parameters are
+ * otherwise invalid. Each service provider has specific limitations.
+ *
+ * @param maxSize The exact size of the binary to be uploaded or the
+ * estimated maximum size if the exact size is unknown.
+ * If the estimation was too small, the transaction
+ * should be restarted by invoking this method again
+ * using the correct size.
* @param maxURIs The maximum number of upload URIs that the client can
- * accept. The implementation will ensure that an upload of size
- * {@code maxSize} can be completed by splitting the value of {@code
- * maxSize} into parts, such that the size of the largest part does
- * not exceed any known implementation or service provider
- * limitations on upload part size and such that the number of parts
- * does not exceed the value of {@code maxURIs}. If this is not
- * possible, {@link IllegalArgumentException} will be thrown. A
- * client may specify -1 for this value, indicating that any number
- * of URIs may be returned.
- * @return A {@link BinaryUpload} that can be used by the client to complete
- * the upload via a call to {@link #completeBinaryUpload(String)},
+ * accept, for example due to message size limitations.
+ * A value of -1 indicates no limit.
+ * Upon a successful return, it is ensured that an upload
+ * of {@code maxSize} can be completed by splitting the
+ * binary into {@code maxURIs} parts, otherwise
+ * {@link IllegalArgumentException} will be thrown.
+ *
+ * @return A {@link BinaryUpload} providing the upload instructions,
* or {@code null} if the implementation does not support the direct
* upload feature.
+ *
* @throws IllegalArgumentException if the provided arguments are
- * invalid or if a valid upload cannot be completed given the
- * provided arguments.
- * @throws AccessDeniedException if it is determined that insufficient
- * permission exists to perform the upload.
+ * invalid or if an upload cannot be completed given the
+ * provided arguments. For example, if the value of {@code maxSize}
+ * exceeds the size limits for a single binary upload for the
+ * implementation or the service provider, or if the value of
+ * {@code maxSize} divided by {@code maxParts} exceeds the size
+ * limit for an upload or upload part.
+ *
+ * @throws AccessDeniedException if the session has insufficient
+ * permission to perform the upload.
*/
@Nullable
BinaryUpload initiateBinaryUpload(long maxSize, int maxURIs)
throws IllegalArgumentException, AccessDeniedException;
/**
- * Complete a transaction to upload binary data directly to a storage
- * location. The client must provide a valid {@code uploadToken} that can
- * only be obtained via a previous call to {@link
- * #initiateBinaryUpload(long, int)}. If the {@code uploadToken} is
- * unreadable or invalid, {@link IllegalArgumentException} will be thrown.
- * <p>
- * Calling this method does not associate the returned {@link Binary} with
- * any location in the repository. It is the responsibility of the client
- * to do this if desired.
+ * Complete the transaction of uploading a binary directly to a storage
+ * location and return a {@link Binary} to set as value for a binary
+ * JCR property. The binary is not automatically associated with
+ * any location in the JCR.
+ *
* <p>
- * The {@code uploadToken} can be obtained from the {@link
- * BinaryUpload} returned from a prior call to {@link
- * #initiateBinaryUpload(long, int)}. Clients should treat the {@code
- * uploadToken} as an immutable string, and should expect that
- * implementations will sign the string and verify the signature when this
- * method is called.
+ * The client must provide a valid upload token, obtained from
+ * {@link BinaryUpload#getUploadToken()} when this transaction was initialized
+ * using {@link #initiateBinaryUpload(long, int)}.
+ * If the {@code uploadToken} is unreadable or invalid,
+ * an {@link IllegalArgumentException} will be thrown.
+ *
+ * @param uploadToken A String identifying the upload transaction.
*
- * @param uploadToken A String that is used to identify the direct upload
- * transaction.
* @return The uploaded {@link Binary}, or {@code null} if the
* implementation does not support the direct upload feature.
- * @throws IllegalArgumentException if the {@code uploadToken} is
- * unreadable or invalid.
- * @throws RepositoryException if a repository access error occurs.
+ *
+ * @throws IllegalArgumentException if the {@code uploadToken} is invalid or
+ * does not identify a known binary upload.
+ * @throws RepositoryException if another error occurs.
*/
@Nullable
Binary completeBinaryUpload(@NotNull String uploadToken)
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownload.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownload.java?rev=1838616&r1=1838615&r2=1838616&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownload.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownload.java Wed Aug 22 08:40:54 2018
@@ -33,25 +33,72 @@ import org.osgi.annotation.versioning.Pr
@ProviderType
public interface BinaryDownload extends Binary {
/**
- * Get a URI for downloading a {@link Binary} directly from a storage
- * location with the provided {@link BinaryDownloadOptions}. This is
- * probably a signed URI with a short TTL (time to live), although the API
- * does not require it to be so.
+ *
+ * Returns a URI for downloading this binary directly from the storage location.
+ *
* <p>
- * The implementation will attempt to apply the specified {@code
- * downloadOptions} to the subsequent download. For example, if the caller
- * knows that the URI refers to a specific type of content, the caller can
- * specify that content type by setting the internet media type and
- * character encoding in the {@code downloadOptions}. The caller may also
- * use a default instance obtained via {@link BinaryDownloadOptions#DEFAULT}
- * in which case the caller is indicating that the default behavior of the
- * service provider is acceptable.
+ * Using the {@code downloadOptions} parameter, some response headers of the
+ * download request can be overwritten, if supported by the storage provider.
+ * This is necessary to pass information that is only stored in the JCR in
+ * application specific structures, and not reliably available in the binary
+ * storage.
+ *
+ * {@link BinaryDownloadOptions} supports, but is not limited to:
+ * <ul>
+ * <li>
+ * {@link BinaryDownloadOptions.BinaryDownloadOptionsBuilder#withMediaType(String) Content-Type media type}:
+ * typically available in a {@code jcr:mimeType} property
+ * </li>
+ * <li>
+ * {@link BinaryDownloadOptions.BinaryDownloadOptionsBuilder#withCharacterEncoding(String) Content-Type charset}:
+ * for media types defining a "charset", typically available in a {@code jcr:encoding} property
+ * </li>
+ * <li>
+ * {@link BinaryDownloadOptions.BinaryDownloadOptionsBuilder#withFileName(String) Content-Disposition filename}:
+ * download file name, typically taken from a JCR node name in the parent hierarchy, such as the nt:file node name
+ * </li>
+ * <li>
+ * {@link BinaryDownloadOptions.BinaryDownloadOptionsBuilder#withDispositionTypeAttachment() Content-Disposition type}:
+ * whether to show the content inline of a page (inline) or enforce a download/save as (attachment)
+ * </li>
+ * </ul>
+ *
+ * Specifying {@link BinaryDownloadOptions#DEFAULT} will use mostly empty
+ * defaults, relying on the storage provider attributes for this binary
+ * (that might be empty or different from the information in the JCR).
+ *
+ * <p>
+ * <b>Security considerations:</b>
+ *
+ * <ul>
+ * <li>
+ * The URI cannot be shared with other users. It must only be returned to
+ * authenticated requests corresponding to this session user or trusted system
+ * components.
+ * </li>
+ * <li>
+ * The URI must not be persisted for later use and will typically be time limited.
+ * </li>
+ * <li>
+ * The URI will only grant access to this particular binary.
+ * </li>
+ * <li>
+ * The client cannot infer any semantics from the URI structure and path names.
+ * It would typically include a cryptographic signature. Any change to the URI will
+ * likely result in a failing request.
+ * </li>
+ * <li>
+ * If the client is a browser, consider use of Content-Disposition type = attachment
+ * for executable media types such as HTML or Javascript if the content cannot be
+ * trusted.
+ * </li>
+ * </ul>
*
* @param downloadOptions
* A {@link BinaryDownloadOptions} instance which is used to
* request specific options on the binary to be downloaded.
* {@link BinaryDownloadOptions#DEFAULT} should be used if the
- * caller wishes to accept the service provider's default
+ * caller wishes to accept the storage provider's default
* behavior.
* @return A URI for downloading the binary directly, or {@code null} if the
* binary cannot be downloaded directly or if the underlying
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownloadOptions.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownloadOptions.java?rev=1838616&r1=1838615&r2=1838616&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownloadOptions.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryDownloadOptions.java Wed Aug 22 08:40:54 2018
@@ -184,10 +184,10 @@ public final class BinaryDownloadOptions
/**
* Sets the character encoding of the {@link BinaryDownloadOptions} object to be
- * built. This value should be a valid {@code jcr:encoding}.
+ * built. This value should be a valid {@code jcr:encoding} property value.
* <p>
* Calling this method has the effect of instructing the service
- * provider to set {@code charecterEncoding} as the "charset" parameter
+ * provider to set {@code characterEncoding} as the "charset" parameter
* of the content type in the {@code Content-Type} header field of the
* response to a request issued with a URI obtained by calling {@link
* BinaryDownload#getURI(BinaryDownloadOptions)}. This value can be
@@ -195,9 +195,9 @@ public final class BinaryDownloadOptions
* BinaryDownloadOptions#getCharacterEncoding()} on the instance returned by a
* call to {@link #build()}.
* <p>
- * Note that setting the character encoding only makes sense if the internet media type has
- * also been set. See {@link
- * #withMediaType(String)}.
+ * Note that setting the character encoding only makes sense if the internet
+ * media type has also been set, and that media type actually defines a
+ * "charset" parameter. See {@link #withMediaType(String)}.
* <p>
* The caller should ensure that the proper character encoding has been set for
* the internet media type; the implementation does not perform any validation of
@@ -216,7 +216,7 @@ public final class BinaryDownloadOptions
/**
* Sets the filename of the {@link BinaryDownloadOptions} object to be
- * built.
+ * built. This would typically be based on a JCR node name.
* <p>
* Calling this method has the effect of instructing the service
* provider to set {@code fileName} as the filename in the {@code
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryUpload.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryUpload.java?rev=1838616&r1=1838615&r2=1838616&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryUpload.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/binary/BinaryUpload.java Wed Aug 22 08:40:54 2018
@@ -25,78 +25,104 @@ import org.jetbrains.annotations.NotNull
import org.osgi.annotation.versioning.ProviderType;
/**
- * This extension interface provides a mechanism whereby a client can upload a
- * binary directly to a storage location. An object of this type can be
- * created by a call to {@link
- * JackrabbitValueFactory#initiateBinaryUpload(long, int)} which will return an
- * object of this type if the underlying implementation supports direct upload
- * functionality. When calling this method, the client indicates the expected
- * size of the binary and the number of URIs that it is willing to accept. The
- * implementation will attempt to create an instance of this class that is
- * suited to enabling the client to complete the upload successfully.
+ * Describes uploading a binary through HTTP requests in a single or multiple
+ * parts. This will be returned by
+ * {@link JackrabbitValueFactory#initiateBinaryUpload(long, int)}. A high-level
+ * overview of the process can be found in {@link JackrabbitValueFactory}.
+ *
* <p>
- * Using an instance of this class, a client can then use one or more of the
- * included URIs for uploading the binary directly by calling {@link
- * #getUploadURIs()} and iterating through the URIs returned. Multi-part
- * uploads are supported by the interface, although they may not be supported
- * by the underlying implementation.
+ * Note that although the API allows URI schemes other than "http(s)", the
+ * upload functionality is currently only defined for HTTP.
+ *
* <p>
- * Once a client finishes uploading the binary data, the client must then call
- * {@link JackrabbitValueFactory#completeBinaryUpload(String)} to complete the
- * upload. This call requires an upload token which can be obtained from an
- * instance of this class by calling {@link #getUploadToken()}.
+ * A caller usually needs to pass the information provided by this interface to
+ * a remote client that is in possession of the actual binary, who then has to
+ * upload the binary using HTTP according to the logic described below. A remote
+ * client is expected to support multi-part uploads as per the logic described
+ * below, in case multiple URIs are returned.
+ *
* <p>
- * Below is the detailed direct binary upload algorithm for the remote client.
+ * Once a remote client finishes uploading the binary data, the application must
+ * be notified and must then call
+ * {@link JackrabbitValueFactory#completeBinaryUpload(String)} to complete the
+ * upload. This completion requires the exact upload token obtained from
+ * {@link #getUploadToken()}.
+ *
+ * <h2 id="upload.algorithm">Upload algorithm</h2>
+ *
+ * A remote client will have to follow this algorithm to upload a binary based
+ * on the information provided by this interface.
+ *
* <p>
- * In this example the following variables are used:
+ * Please be aware that if the size passed to
+ * {@link JackrabbitValueFactory#initiateBinaryUpload(long, int)} was an
+ * estimation, but the actual binary is larger, there is no guarantee the
+ * upload will be possible using all {@link #getUploadURIs()} and the
+ * {@link #getMaxPartSize()}. In such cases, the application should restart the
+ * transaction using the correct size.
+ *
+ * <h3>Variables used</h3>
* <ul>
* <li>{@code fileSize}: the actual binary size (must be known at this
- * point)
- * <li>{@code minPartSize}: the value from {@link #getMinPartSize()}
- * <li>{@code maxPartSize}: the value from {@link #getMaxPartSize()}
+ * point)</li>
+ * <li>{@code minPartSize}: the value from {@link #getMinPartSize()}</li>
+ * <li>{@code maxPartSize}: the value from {@link #getMaxPartSize()}</li>
* <li>{@code numUploadURIs}: the number of entries in {@link
- * #getUploadURIs()}
- * <li>{@code uploadURIs}: the entries in {@link #getUploadURIs()}
+ * #getUploadURIs()}</li>
+ * <li>{@code uploadURIs}: the entries in {@link #getUploadURIs()}</li>
* <li>{@code partSize}: the part size to be used in the upload (to be
- * determined in the algorithm)
+ * determined in the algorithm)</li>
* </ul>
*
- * Steps:
+ * <h3>Steps</h3>
* <ol>
- * <li>If (fileSize divided by maxPartSize) is larger than numUploadURIs,
- * then the client cannot proceed and will have to request a new set of URIs
- * with the right fileSize as maxSize
- * <li>If fileSize is smaller than minPartSize, then take the first provided
- * upload URI to upload the entire binary, with partSize = fileSize</li>
+ * <li>
+ * If {@code (fileSize / maxPartSize) > numUploadURIs}, then the
+ * client cannot proceed and will have to request a new set of URIs
+ * with the right fileSize as {@code maxSize}
+ * </li>
+ * <li>
+ * If {@code fileSize < minPartSize}, then take the first provided
+ * upload URI to upload the entire binary, with
+ * {@code partSize = fileSize}
+ * </li>
* <li>
* (optional) If the client has more information to optimize, the
- * partSize can be chosen, under the condition that all of these are
- * true for the partSize:
+ * {@code partSize} can be chosen, under the condition that all of these are
+ * true:
* <ol>
- * <li>larger than minPartSize
- * <li>smaller or equal than maxPartSize (unless it is -1 =
- * unlimited)
- * <li>larger than fileSize divided by numUploadURIs
+ * <li>{@code partSize >= minPartSize}</li>
+ * <li>{@code partSize <= maxPartSize}
+ * (unless {@code maxPartSize = -1} meaning unlimited)</li>
+ * <li>{@code partSize > (fileSize / numUploadURIs)}</li>
* </ol>
* </li>
- * <li>Otherwise all part URIs are to be used and the partSize = fileSize
- * divided by numUploadURIs (integer division, discard modulo which will be
- * the last part)
- * <li>Upload: segment the binary into partSize, for each segment take the
- * next URI from uploadURIs (strictly in order), proceed with a standard
- * HTTP PUT for each (for "http(s)" URIs, otherwise currently unspecified),
- * and for the last part use whatever segment size is left
- * <li>If a segment fails during upload, retry (up to a certain time out)
- * <li>After the upload has finished successfully, notify the application,
- * for example through a complete request, passing the {@link
- * #getUploadToken() upload token}, and the application will call {@link
- * JackrabbitValueFactory#completeBinaryUpload(String)} with the token
+ * <li>
+ * Otherwise all part URIs are to be used. The {@code partSize}
+ * to use for all parts except the last would be calculated using:
+ * <pre>partSize = (fileSize + numUploadURIs - 1) / numUploadURIs</pre>
+ * </li>
+ * <li>
+ * Upload: segment the binary into {@code partSize}, for each segment take the
+ * next URI from {@code uploadURIs} (strictly in order), proceed with a standard
+ * HTTP PUT for each, and for the last part use whatever segment size is left
+ * </li>
+ * <li>
+ * If a segment fails during upload, retry (up to a certain timeout)
+ * </li>
+ * <li>
+ * After the upload has finished successfully, notify the application,
+ * for example through a complete request, passing the {@link
+ * #getUploadToken() upload token}, and the application will call {@link
+ * JackrabbitValueFactory#completeBinaryUpload(String)} with the token
+ * </li>
* </ol>
*
- * <h2>JSON view</h2>
+ * <h2>Example JSON view</h2>
*
* A JSON representation of this interface as passed back to a remote client
* might look like this:
+ *
* <pre>
* {
* "uploadToken": "aaaa-bbbb-cccc-dddd-eeee-ffff-gggg-hhhh",
@@ -110,45 +136,64 @@ import org.osgi.annotation.versioning.Pr
* ]
* }
* </pre>
- * */
+ */
@ProviderType
public interface BinaryUpload {
/**
- * Returns an Iterable of URIs that can be used for uploading binary data
- * directly to a storage location. The first URI can be used for uploading
- * binary data as a single entity, or multiple URIs can be used if the
- * client wishes to do multi-part uploads.
- * <p>
- * Clients are not necessarily required to use all of the URIs provided. A
- * client may choose to use fewer, or even only one of the URIs. However,
- * regardless of the number of URIs used, they must be consumed in sequence.
+ * Returns a list of URIs that can be used for uploading binary data
+ * directly to a storage location in one or more parts.
+ *
+ * <p>
+ * Remote clients must support multi-part uploading as per the
+ * <a href="#upload.algorithm">upload algorithm</a> described above. Clients
+ * are not necessarily required to use all of the URIs provided. A client
+ * may choose to use fewer, or even only one of the URIs. However, it must
+ * always ensure the part size is between {@link #getMinPartSize()} and
+ * {@link #getMaxPartSize()}. These can reflect strict limitations of the
+ * storage provider.
+ *
+ * <p>
+ * Regardless of the number of URIs used, they must be consumed in sequence,
+ * without skipping any, and the order of parts the original binary is split
+ * into must correspond exactly with the order of URIs.
+ *
+ * <p>
* For example, if a client wishes to upload a binary in three parts and
* there are five URIs returned, the client must use the first URI to
* upload the first part, the second URI to upload the second part, and
- * the third URI to upload the third part. The client is not required to
- * use the fourth and fifth URIs. However, using the second URI to upload
+ * the third URI to upload the third part. The client is not required to
+ * use the fourth and fifth URIs. However, using the second URI to upload
* the third part may result in either an upload failure or a corrupted
* upload; likewise, skipping the second URI to use subsequent URIs may
* result in either an upload failure or a corrupted upload.
- * <p>
- * Clients should be aware that some storage providers have limitations on
- * the minimum and maximum size of a binary payload for a single upload, so
- * clients should take these limitations into account when deciding how many
- * of the URIs to use. Underlying implementations may also choose to
- * enforce their own limitations.
+ *
* <p>
* While the API supports multi-part uploading via multiple upload URIs,
- * implementations are not required to support multi-part uploading. If the
+ * implementations are not required to support multi-part uploading. If the
* underlying implementation does not support multi-part uploading, a single
* URI will be returned regardless of the size of the data being uploaded.
+ *
* <p>
- * Some storage providers also support multi-part uploads by reusing a
- * single URI multiple times, in which case the implementation may also
- * return a single URI regardless of the size of the data being uploaded.
- * <p>
- * You should consult both the DataStore implementation documentation and
- * the storage service provider documentation for details on such matters as
- * multi-part upload support, upload minimum and maximum sizes, etc.
+ * <b>Security considerations:</b>
+ *
+ * <ul>
+ * <li>
+ * The URIs cannot be shared with other users. They must only be returned to
+ * authenticated requests corresponding to this session user or trusted system
+ * components.
+ * </li>
+ * <li>
+ * The URIs must not be persisted for later use and will typically be time limited.
+ * </li>
+ * <li>
+ * The URIs will only grant access to this particular binary.
+ * </li>
+ * <li>
+ * The client cannot infer any semantics from the URI structure and path names.
+ * It would typically include a cryptographic signature. Any change to the URIs will
+ * likely result in a failing request.
+ * </li>
+ * </ul>
*
* @return Iterable of URIs that can be used for uploading directly to a
* storage location.
@@ -157,54 +202,48 @@ public interface BinaryUpload {
Iterable<URI> getUploadURIs();
/**
- * The smallest part size a client may upload for a multi-part upload, not
- * counting the final part. This is usually either a service provider or
- * implementation limitation.
- * <p>
- * Note that the API offers no guarantees that uploading parts of this size
- * can successfully complete the requested upload using the URIs provided
- * via {@link #getUploadURIs()}. In other words, clients wishing to perform
- * a multi-part upload must split the upload into parts of at least this
- * size, but the sizes may need to be larger in order to successfully
- * complete the upload.
+ * Return the smallest possible part size in bytes. If a consumer wants to
+ * choose a custom part size, it cannot be smaller than this value. This
+ * does not apply to the final part. This value will be equal or larger than
+ * zero.
*
- * @return The smallest size acceptable for multi-part uploads.
+ * <p>
+ * Note that the API offers no guarantees that using this minimal part size
+ * is possible with the number of available {@link #getUploadURIs()}. This
+ * might not be the case if the binary is too large. Please refer to the
+ * <a href="#upload.algorithm">upload algorithm</a> for the correct use of
+ * this value.
+ *
+ * @return The smallest part size acceptable for multi-part uploads.
*/
long getMinPartSize();
/**
- * The largest part size a client may upload for a multi-part upload. This
- * is usually either a service provider or implementation limitation.
+ * Return the largest possible part size in bytes. If a consumer wants to
+ * choose a custom part size, it cannot be larger than this value.
+ * If this returns -1, the maximum is unlimited.
+ *
* <p>
- * The API guarantees that a client can successfully complete a direct
- * upload of the binary data of the requested size using the provided URIs
- * by splitting the binary data into parts of the size returned by this
- * method.
- * <p>
- * The client is not required to use part sizes of this size; smaller sizes
- * may be used so long as they are at least as large as the size returned by
- * {@link #getMinPartSize()}.
- * <p>
- * If the binary size specified by a client when calling {@link
- * JackrabbitValueFactory#initiateBinaryUpload(long, int)} ends up being
- * smaller than the actual size of the binary being uploaded, these API
- * guarantees no longer apply, and it may not be possible to complete the
- * upload using the URIs provided. In such cases, the client should restart
- * the transaction using the correct size.
+ * The API guarantees that a client can split the binary of the requested
+ * size using this maximum part size and there will be sufficient URIs
+ * available in {@link #getUploadURIs()}. Please refer to the
+ * <a href="#upload.algorithm">upload algorithm</a> for the correct use of
+ * this value.
*
- * @return The maximum size of an upload part for multi-part uploads.
+ * @return The maximum part size acceptable for multi-part uploads or -1
+ * if there is no limit.
*/
long getMaxPartSize();
/**
- * Returns the upload token to be used in a subsequent call to {@link
- * JackrabbitValueFactory#completeBinaryUpload(String)}. This upload token
- * is used by the implementation to identify this upload. Clients should
- * treat the upload token as an immutable string, as the underlying
- * implementation may choose to implement techniques to detect tampering and
- * reject the upload if the token is modified.
+ * Returns a token identifying this upload. This is required to finalize the upload
+ * at the end by calling {@link JackrabbitValueFactory#completeBinaryUpload(String)}.
+ *
+ * <p>
+ * The format of this string is implementation-dependent. Implementations must ensure
+ * that clients cannot guess tokens for existing binaries.
*
- * @return This upload's unique upload token.
+ * @return A unique token identifying this upload.
*/
@NotNull
String getUploadToken();