All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ionoscloud.s3.ApiAsyncClient Maven / Gradle / Ivy

The newest version!
package com.ionoscloud.s3;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams;
import com.ionoscloud.s3.credentials.Credentials;
import com.ionoscloud.s3.credentials.Provider;
import com.ionoscloud.s3.credentials.StaticProvider;
import com.ionoscloud.s3.errors.BucketPolicyTooLargeException;
import com.ionoscloud.s3.errors.ErrorResponseException;
import com.ionoscloud.s3.errors.InsufficientDataException;
import com.ionoscloud.s3.errors.InternalException;
import com.ionoscloud.s3.errors.InvalidResponseException;
import com.ionoscloud.s3.errors.ServerException;
import com.ionoscloud.s3.errors.XmlParserException;
import com.ionoscloud.s3.http.HttpUtils;
import com.ionoscloud.s3.http.Method;
import com.ionoscloud.s3.messages.Bucket;
import com.ionoscloud.s3.messages.CopyObjectResult;
import com.ionoscloud.s3.messages.CreateBucketConfiguration;
import com.ionoscloud.s3.messages.DeleteError;
import com.ionoscloud.s3.messages.DeleteObject;
import com.ionoscloud.s3.messages.Item;
import com.ionoscloud.s3.messages.LegalHold;
import com.ionoscloud.s3.messages.LifecycleConfiguration;
import com.ionoscloud.s3.messages.ListAllMyBucketsResult;
import com.ionoscloud.s3.messages.NotificationConfiguration;
import com.ionoscloud.s3.messages.NotificationRecords;
import com.ionoscloud.s3.messages.ObjectLockConfiguration;
import com.ionoscloud.s3.messages.Part;
import com.ionoscloud.s3.messages.ReplicationConfiguration;
import com.ionoscloud.s3.messages.Retention;
import com.ionoscloud.s3.messages.SelectObjectContentRequest;
import com.ionoscloud.s3.messages.SseConfiguration;
import com.ionoscloud.s3.messages.Tags;
import com.ionoscloud.s3.messages.VersioningConfiguration;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.xerial.snappy.SnappyFramedOutputStream;

/**
 * Simple Storage Service (aka S3) client to perform bucket and object operations asynchronously.
 *
 * 

Bucket operations

* *
    *
  • Create, list and delete buckets. *
  • Put, get and delete bucket lifecycle configuration. *
  • Put, get and delete bucket policy configuration. *
  • Put, get and delete bucket encryption configuration. *
  • Put and get bucket default retention configuration. *
  • Put and get bucket notification configuration. *
  • Enable and disable bucket versioning. *
* *

Object operations

* *
    *
  • Put, get, delete and list objects. *
  • Create objects by combining existing objects. *
  • Put and get object retention and legal hold. *
  • Filter object content by SQL statement. *
* *

If access/secret keys are provided, all S3 operation requests are signed using AWS Signature * Version 4; else they are performed anonymously. * *

Examples on using this library are available here. * *

Use {@code ApiAsyncClient.builder()} to create S3 client. * *

{@code
 * // Create client with anonymous access.
 * ApiAsyncClient apiAsyncClient =
 *     ApiAsyncClient.builder().endpoint(System.getenv("IONOS_API_URL")).build();
 *
 * // Create client with credentials.
 * ApiAsyncClient apiAsyncClient =
 *     ApiAsyncClient.builder()
 *         .endpoint(System.getenv("IONOS_API_URL"))
 *         .credentials(System.getenv("IONOS_S3_ACCESS_KEY"), System.getenv("IONOS_S3_SECRET_KEY"))
 *         .build();
 * }
*/ public class ApiAsyncClient extends S3Base { private ApiAsyncClient( HttpUrl baseUrl, String awsS3Prefix, String awsDomainSuffix, boolean awsDualstack, boolean useVirtualStyle, String region, Provider provider, OkHttpClient httpClient) { super( baseUrl, awsS3Prefix, awsDomainSuffix, awsDualstack, useVirtualStyle, region, provider, httpClient); } protected ApiAsyncClient(ApiAsyncClient client) { super(client); } /** * Gets information of an object asynchronously. * *
Example:{@code
   * // Get information of an object.
   * CompletableFuture future =
   *     apiAsyncClient.statObject(
   *         StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build());
   *
   * // Get information of SSE-C encrypted object.
   * CompletableFuture future =
   *     apiAsyncClient.statObject(
   *         StatObjectArgs.builder()
   *             .bucket("my-bucketname")
   *             .object("my-objectname")
   *             .ssec(ssec)
   *             .build());
   *
   * // Get information of a versioned object.
   * CompletableFuture future =
   *     apiAsyncClient.statObject(
   *         StatObjectArgs.builder()
   *             .bucket("my-bucketname")
   *             .object("my-objectname")
   *             .versionId("version-id")
   *             .build());
   *
   * // Get information of a SSE-C encrypted versioned object.
   * CompletableFuture future =
   *     apiAsyncClient.statObject(
   *         StatObjectArgs.builder()
   *             .bucket("my-bucketname")
   *             .object("my-objectname")
   *             .versionId("version-id")
   *             .ssec(ssec)
   *             .build());
   * }
* * @param args {@link StatObjectArgs} object. * @return {@link CompletableFuture}<{@link StatObjectResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. * @see StatObjectResponse */ public CompletableFuture statObject(StatObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { return super.statObjectAsync(args); } /** * Gets data from offset to length of a SSE-C encrypted object asynchronously. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.getObject(
   *     GetObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .offset(offset)
   *         .length(len)
   *         .ssec(ssec)
   *         .build()
   * }
* * @param args Object of {@link GetObjectArgs} * @return {@link CompletableFuture}<{@link GetObjectResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. * @see GetObjectResponse */ public CompletableFuture getObject(GetObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); args.validateSsec(this.baseUrl); return executeGetAsync( args, args.getHeaders(), (args.versionId() != null) ? newMultimap("versionId", args.versionId()) : null) .thenApply( response -> { return new GetObjectResponse( response.headers(), args.bucket(), args.region(), args.object(), response.body().byteStream()); }); } private void downloadObject( String filename, boolean overwrite, StatObjectResponse statObjectResponse, GetObjectResponse getObjectResponse) throws IOException { OutputStream os = null; try { Path filePath = Paths.get(filename); String tempFilename = filename + "." + S3Escaper.encode(statObjectResponse.etag()) + ".part.ionos"; Path tempFilePath = Paths.get(tempFilename); if (Files.exists(tempFilePath)) Files.delete(tempFilePath); os = Files.newOutputStream(tempFilePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); long bytesWritten = ByteStreams.copy(getObjectResponse, os); if (bytesWritten != statObjectResponse.size()) { throw new IOException( tempFilename + ": unexpected data written. expected = " + statObjectResponse.size() + ", written = " + bytesWritten); } if (overwrite) { Files.move(tempFilePath, filePath, StandardCopyOption.REPLACE_EXISTING); } else { Files.move(tempFilePath, filePath); } } finally { getObjectResponse.close(); if (os != null) os.close(); } } /** * Downloads data of a SSE-C encrypted object to file. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.downloadObject(
   *     DownloadObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .ssec(ssec)
   *         .filename("my-filename")
   *         .build());
   * }
* * @param args Object of {@link DownloadObjectArgs} * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture downloadObject(DownloadObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { String filename = args.filename(); Path filePath = Paths.get(filename); if (!args.overwrite() && Files.exists(filePath)) { throw new IllegalArgumentException("Destination file " + filename + " already exists"); } return statObjectAsync(new StatObjectArgs(args)) .thenCombine( getObject(new GetObjectArgs(args)), (statObjectResponse, getObjectResponse) -> { try { downloadObject(filename, args.overwrite(), statObjectResponse, getObjectResponse); return null; } catch (IOException e) { throw new CompletionException(e); } }) .thenAccept(nullValue -> {}); } /** * Creates an object by server-side copying data from another object. * *
Example:{@code
   * // Create object "my-objectname" in bucket "my-bucketname" by copying from object
   * // "my-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-objectname")
   *                 .build())
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" by copying from object
   * // "my-source-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-source-objectname")
   *                 .build())
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" with SSE-KMS server-side
   * // encryption by copying from object "my-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-objectname")
   *                 .build())
   *         .sse(sseKms) // Replace with actual key.
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" with SSE-S3 server-side
   * // encryption by copying from object "my-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-objectname")
   *                 .build())
   *         .sse(sseS3) // Replace with actual key.
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" with SSE-C server-side encryption
   * // by copying from object "my-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-objectname")
   *                 .build())
   *         .sse(ssec) // Replace with actual key.
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" by copying from SSE-C encrypted
   * // object "my-source-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-source-objectname")
   *                 .ssec(ssec) // Replace with actual key.
   *                 .build())
   *         .build());
   *
   * // Create object "my-objectname" in bucket "my-bucketname" with custom headers conditionally
   * // by copying from object "my-objectname" in bucket "my-source-bucketname".
   * CompletableFuture future = apiAsyncClient.copyObject(
   *     CopyObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .source(
   *             CopySource.builder()
   *                 .bucket("my-source-bucketname")
   *                 .object("my-objectname")
   *                 .matchETag(etag) // Replace with actual etag.
   *                 .build())
   *         .headers(headers) // Replace with actual headers.
   *         .build());
   * }
* * @param args {@link CopyObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture copyObject(CopyObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); args.validateSse(this.baseUrl); return CompletableFuture.supplyAsync( () -> args.source().offset() != null && args.source().length() != null) .thenCompose( condition -> { if (condition) { try { return statObjectAsync(new StatObjectArgs((ObjectReadArgs) args.source())); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } } return CompletableFuture.completedFuture(null); }) .thenApply(stat -> (stat == null) ? (long) -1 : stat.size()) .thenCompose( size -> { if (args.source().offset() != null || args.source().length() != null || size > ObjectWriteArgs.MAX_PART_SIZE) { if (args.metadataDirective() != null && args.metadataDirective() == Directive.COPY) { throw new IllegalArgumentException( "COPY metadata directive is not applicable to source object size greater than" + " 5 GiB"); } if (args.taggingDirective() != null && args.taggingDirective() == Directive.COPY) { throw new IllegalArgumentException( "COPY tagging directive is not applicable to source object size greater than" + " 5 GiB"); } try { return composeObject(new ComposeObjectArgs(args)); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } } return CompletableFuture.completedFuture(null); }) .thenCompose( objectWriteResponse -> { if (objectWriteResponse != null) { return CompletableFuture.completedFuture(objectWriteResponse); } Multimap headers = args.genHeaders(); if (args.metadataDirective() != null) { headers.put("x-amz-metadata-directive", args.metadataDirective().name()); } if (args.taggingDirective() != null) { headers.put("x-amz-tagging-directive", args.taggingDirective().name()); } headers.putAll(args.source().genCopyHeaders()); try { return executePutAsync(args, headers, null, null, 0) .thenApply( response -> { try { CopyObjectResult result = Xml.unmarshal(CopyObjectResult.class, response.body().charStream()); return new ObjectWriteResponse( response.headers(), args.bucket(), args.region(), args.object(), result.etag(), response.header("x-amz-version-id")); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }); } private CompletableFuture uploadPartCopy( String bucketName, String region, String objectName, String uploadId, int partNumber, Multimap headers, Part[] parts) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { return uploadPartCopyAsync(bucketName, region, objectName, uploadId, partNumber, headers, null) .thenApply( uploadPartCopyResponse -> { parts[partNumber - 1] = new Part(partNumber, uploadPartCopyResponse.result().etag()); return parts; }); } /** * Creates an object by combining data from different source objects using server-side copy. * *
Example:{@code
   * List sourceObjectList = new ArrayList();
   *
   * sourceObjectList.add(
   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-one").build());
   * sourceObjectList.add(
   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-two").build());
   * sourceObjectList.add(
   *    ComposeSource.builder().bucket("my-job-bucket").object("my-objectname-part-three").build());
   *
   * // Create my-bucketname/my-objectname by combining source object list.
   * CompletableFuture future = apiAsyncClient.composeObject(
   *    ComposeObjectArgs.builder()
   *        .bucket("my-bucketname")
   *        .object("my-objectname")
   *        .sources(sourceObjectList)
   *        .build());
   *
   * // Create my-bucketname/my-objectname with user metadata by combining source object
   * // list.
   * Map userMetadata = new HashMap<>();
   * userMetadata.put("My-Project", "Project One");
   * CompletableFuture future = apiAsyncClient.composeObject(
   *     ComposeObjectArgs.builder()
   *        .bucket("my-bucketname")
   *        .object("my-objectname")
   *        .sources(sourceObjectList)
   *        .userMetadata(userMetadata)
   *        .build());
   *
   * // Create my-bucketname/my-objectname with user metadata and server-side encryption
   * // by combining source object list.
   * CompletableFuture future = apiAsyncClient.composeObject(
   *   ComposeObjectArgs.builder()
   *        .bucket("my-bucketname")
   *        .object("my-objectname")
   *        .sources(sourceObjectList)
   *        .userMetadata(userMetadata)
   *        .ssec(sse)
   *        .build());
   * }
* * @param args {@link ComposeObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture composeObject(ComposeObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); args.validateSse(this.baseUrl); List sources = args.sources(); int[] partCount = {0}; String[] uploadIdCopy = {null}; return calculatePartCountAsync(sources) .thenApply( count -> { partCount[0] = count; return (count == 1 && args.sources().get(0).offset() == null && args.sources().get(0).length() == null); }) .thenCompose( copyObjectFlag -> { if (copyObjectFlag) { try { return copyObject(new CopyObjectArgs(args)); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } } return CompletableFuture.completedFuture(null); }) .thenCompose( objectWriteResponse -> { if (objectWriteResponse != null) { return CompletableFuture.completedFuture(objectWriteResponse); } CompletableFuture completableFuture = CompletableFuture.supplyAsync( () -> { Multimap headers = newMultimap(args.extraHeaders()); headers.putAll(args.genHeaders()); return headers; }) .thenCompose( headers -> { try { return createMultipartUploadAsync( args.bucket(), args.region(), args.object(), headers, args.extraQueryParams()); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }) .thenApply( createMultipartUploadResponse -> { String uploadId = createMultipartUploadResponse.result().uploadId(); uploadIdCopy[0] = uploadId; return uploadId; }) .thenCompose( uploadId -> { Multimap ssecHeaders = HashMultimap.create(); if (args.sse() != null && args.sse() instanceof ServerSideEncryptionCustomerKey) { ssecHeaders.putAll(newMultimap(args.sse().headers())); } int partNumber = 0; CompletableFuture future = CompletableFuture.supplyAsync( () -> { return new Part[partCount[0]]; }); for (ComposeSource src : sources) { long size = 0; try { size = src.objectSize(); } catch (InternalException e) { throw new CompletionException(e); } if (src.length() != null) { size = src.length(); } else if (src.offset() != null) { size -= src.offset(); } long offset = 0; if (src.offset() != null) offset = src.offset(); final Multimap headers; try { headers = newMultimap(src.headers()); } catch (InternalException e) { throw new CompletionException(e); } headers.putAll(ssecHeaders); if (size <= ObjectWriteArgs.MAX_PART_SIZE) { partNumber++; if (src.length() != null) { headers.put( "x-amz-copy-source-range", "bytes=" + offset + "-" + (offset + src.length() - 1)); } else if (src.offset() != null) { headers.put( "x-amz-copy-source-range", "bytes=" + offset + "-" + (offset + size - 1)); } final int partNum = partNumber; future = future.thenCompose( parts -> { try { return uploadPartCopy( args.bucket(), args.region(), args.object(), uploadId, partNum, headers, parts); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }); continue; } while (size > 0) { partNumber++; long startBytes = offset; long endBytes = startBytes + ObjectWriteArgs.MAX_PART_SIZE; if (size < ObjectWriteArgs.MAX_PART_SIZE) endBytes = startBytes + size; Multimap headersCopy = newMultimap(headers); headersCopy.put( "x-amz-copy-source-range", "bytes=" + startBytes + "-" + endBytes); final int partNum = partNumber; future = future.thenCompose( parts -> { try { return uploadPartCopy( args.bucket(), args.region(), args.object(), uploadId, partNum, headersCopy, parts); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }); offset = startBytes; size -= (endBytes - startBytes); } } return future; }) .thenCompose( parts -> { try { return completeMultipartUploadAsync( args.bucket(), args.region(), args.object(), uploadIdCopy[0], parts, null, null); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }); completableFuture.exceptionally( e -> { if (uploadIdCopy[0] != null) { try { abortMultipartUploadAsync( args.bucket(), args.region(), args.object(), uploadIdCopy[0], null, null) .get(); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException | InterruptedException | ExecutionException ex) { throw new CompletionException(ex); } } throw new CompletionException(e); }); return completableFuture; }); } /** * Gets presigned URL of an object for HTTP method, expiry time and custom request parameters. * *
Example:{@code
   * // Get presigned URL string to delete 'my-objectname' in 'my-bucketname' and its life time
   * // is one day.
   * String url =
   *    apiAsyncClient.getPresignedObjectUrl(
   *        GetPresignedObjectUrlArgs.builder()
   *            .method(Method.DELETE)
   *            .bucket("my-bucketname")
   *            .object("my-objectname")
   *            .expiry(24 * 60 * 60)
   *            .build());
   * System.out.println(url);
   *
   * // Get presigned URL string to upload 'my-objectname' in 'my-bucketname'
   * // with response-content-type as application/json and life time as one day.
   * Map reqParams = new HashMap();
   * reqParams.put("response-content-type", "application/json");
   *
   * String url =
   *    apiAsyncClient.getPresignedObjectUrl(
   *        GetPresignedObjectUrlArgs.builder()
   *            .method(Method.PUT)
   *            .bucket("my-bucketname")
   *            .object("my-objectname")
   *            .expiry(1, TimeUnit.DAYS)
   *            .extraQueryParams(reqParams)
   *            .build());
   * System.out.println(url);
   *
   * // Get presigned URL string to download 'my-objectname' in 'my-bucketname' and its life time
   * // is 2 hours.
   * String url =
   *    apiAsyncClient.getPresignedObjectUrl(
   *        GetPresignedObjectUrlArgs.builder()
   *            .method(Method.GET)
   *            .bucket("my-bucketname")
   *            .object("my-objectname")
   *            .expiry(2, TimeUnit.HOURS)
   *            .build());
   * System.out.println(url);
   * }
* * @param args {@link GetPresignedObjectUrlArgs} object. * @return String - URL string. * @throws ErrorResponseException thrown to indicate S3 service returned an error response. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error * response. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. * @throws ServerException */ public String getPresignedObjectUrl(GetPresignedObjectUrlArgs args) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, XmlParserException, ServerException { checkArgs(args); byte[] body = (args.method() == Method.PUT || args.method() == Method.POST) ? HttpUtils.EMPTY_BODY : null; Multimap queryParams = newMultimap(args.extraQueryParams()); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); String region = null; try { region = getRegionAsync(args.bucket(), args.region()).get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } if (provider == null) { HttpUrl url = buildUrl(args.method(), args.bucket(), args.object(), region, queryParams); return url.toString(); } Credentials creds = provider.fetch(); if (creds.sessionToken() != null) queryParams.put("X-Amz-Security-Token", creds.sessionToken()); HttpUrl url = buildUrl(args.method(), args.bucket(), args.object(), region, queryParams); Request request = createRequest( url, args.method(), args.extraHeaders() == null ? null : httpHeaders(args.extraHeaders()), body, 0, creds); url = Signer.presignV4(request, region, creds.accessKey(), creds.secretKey(), args.expiry()); return url.toString(); } /** * Gets form-data of {@link PostPolicy} of an object to upload its data using POST method. * *
Example:{@code
   * // Create new post policy for 'my-bucketname' with 7 days expiry from now.
   * PostPolicy policy = new PostPolicy("my-bucketname", ZonedDateTime.now().plusDays(7));
   *
   * // Add condition that 'key' (object name) equals to 'my-objectname'.
   * policy.addEqualsCondition("key", "my-objectname");
   *
   * // Add condition that 'Content-Type' starts with 'image/'.
   * policy.addStartsWithCondition("Content-Type", "image/");
   *
   * // Add condition that 'content-length-range' is between 64kiB to 10MiB.
   * policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024);
   *
   * Map formData = apiAsyncClient.getPresignedPostFormData(policy);
   *
   * // Upload an image using POST object with form-data.
   * MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
   * multipartBuilder.setType(MultipartBody.FORM);
   * for (Map.Entry entry : formData.entrySet()) {
   *   multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
   * }
   * multipartBuilder.addFormDataPart("key", "my-objectname");
   * multipartBuilder.addFormDataPart("Content-Type", "image/png");
   *
   * // "file" must be added at last.
   * multipartBuilder.addFormDataPart(
   *     "file", "my-objectname", RequestBody.create(new File("Pictures/avatar.png"), null));
   *
   * Request request =
   *     new Request.Builder()
   *         .url("")
   *         .post(multipartBuilder.build())
   *         .build();
   * OkHttpClient httpClient = new OkHttpClient().newBuilder().build();
   * Response response = httpClient.newCall(request).execute();
   * if (response.isSuccessful()) {
   *   System.out.println("Pictures/avatar.png is uploaded successfully using POST object");
   * } else {
   *   System.out.println("Failed to upload Pictures/avatar.png");
   * }
   * }
* * @param policy Post policy of an object. * @return {@code Map} - Contains form-data to upload an object using POST method. * @throws ErrorResponseException thrown to indicate S3 service returned an error response. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error * response. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. * @see PostPolicy */ public Map getPresignedPostFormData(PostPolicy policy) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException { if (provider == null) { throw new IllegalArgumentException( "Anonymous access does not require presigned post form-data"); } String region = null; try { region = getRegionAsync(policy.bucket(), null).get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } return policy.formData(provider.fetch(), region); } /** * Removes an object. * *
Example:{@code
   * // Remove object.
   * CompletableFuture future = apiAsyncClient.removeObject(
   *     RemoveObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build());
   *
   * // Remove versioned object.
   * CompletableFuture future = apiAsyncClient.removeObject(
   *     RemoveObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-versioned-objectname")
   *         .versionId("my-versionid")
   *         .build());
   *
   * // Remove versioned object bypassing Governance mode.
   * CompletableFuture future = apiAsyncClient.removeObject(
   *     RemoveObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-versioned-objectname")
   *         .versionId("my-versionid")
   *         .bypassRetentionMode(true)
   *         .build());
   * }
* * @param args {@link RemoveObjectArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws ErrorResponseException thrown to indicate S3 service returned an error response. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error * response. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture removeObject(RemoveObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync( args, args.bypassGovernanceMode() ? newMultimap("x-amz-bypass-governance-retention", "true") : null, (args.versionId() != null) ? newMultimap("versionId", args.versionId()) : null) .thenAccept(response -> response.close()); } /** * Removes multiple objects lazily. Its required to iterate the returned Iterable to perform * removal. * *
Example:{@code
   * List objects = new LinkedList<>();
   * objects.add(new DeleteObject("my-objectname1"));
   * objects.add(new DeleteObject("my-objectname2"));
   * objects.add(new DeleteObject("my-objectname3"));
   * Iterable> results =
   *     apiAsyncClient.removeObjects(
   *         RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build());
   * for (Result result : results) {
   *   DeleteError error = errorResult.get();
   *   System.out.println(
   *       "Error in deleting object " + error.objectName() + "; " + error.message());
   * }
   * }
* * @param args {@link RemoveObjectsArgs} object. * @return {@code Iterable>} - Lazy iterator contains object removal status. */ public Iterable> removeObjects(RemoveObjectsArgs args) { checkArgs(args); return new Iterable>() { @Override public Iterator> iterator() { return new Iterator>() { private Result error = null; private Iterator errorIterator = null; private boolean completed = false; private Iterator objectIter = args.objects().iterator(); private void setError() { error = null; while (errorIterator.hasNext()) { DeleteError deleteError = errorIterator.next(); if (!"NoSuchVersion".equals(deleteError.code())) { error = new Result<>(deleteError); break; } } } private synchronized void populate() { if (completed) { return; } try { List objectList = new LinkedList<>(); while (objectIter.hasNext() && objectList.size() < 1000) { objectList.add(objectIter.next()); } completed = objectList.isEmpty(); if (completed) return; DeleteObjectsResponse response = null; try { response = deleteObjectsAsync( args.bucket(), args.region(), objectList, true, args.bypassGovernanceMode(), args.extraHeaders(), args.extraQueryParams()) .get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } if (!response.result().errorList().isEmpty()) { errorIterator = response.result().errorList().iterator(); setError(); completed = true; } } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) { error = new Result<>(e); completed = true; } } @Override public boolean hasNext() { while (error == null && errorIterator == null && !completed) { populate(); } if (error == null && errorIterator != null) setError(); if (error != null) return true; if (completed) return false; errorIterator = null; return hasNext(); } @Override public Result next() { if (!hasNext()) throw new NoSuchElementException(); if (this.error != null) { Result error = this.error; this.error = null; return error; } // This never happens. throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } /** * Restores an object asynchronously. * *
Example:{@code
   * // Restore object.
   * CompletableFuture future = apiAsyncClient.restoreObject(
   *     RestoreObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .request(new RestoreRequest(null, null, null, null, null, null))
   *         .build());
   *
   * // Restore versioned object.
   * CompletableFuture future = apiAsyncClient.restoreObject(
   *     RestoreObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-versioned-objectname")
   *         .versionId("my-versionid")
   *         .request(new RestoreRequest(null, null, null, null, null, null))
   *         .build());
   * }
* * @param args {@link RestoreObjectArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture restoreObject(RestoreObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePostAsync(args, null, newMultimap("restore", ""), args.request()) .thenAccept(response -> response.close()); } /** * Lists objects information optionally with versions of a bucket. Supports both the versions 1 * and 2 of the S3 API. By default, the version 2 API * is used.
* Version 1 * can be used by passing the optional argument {@code useVersion1} as {@code true}. * *
Example:{@code
   * // Lists objects information.
   * Iterable> results = apiAsyncClient.listObjects(
   *     ListObjectsArgs.builder().bucket("my-bucketname").build());
   *
   * // Lists objects information recursively.
   * Iterable> results = apiAsyncClient.listObjects(
   *     ListObjectsArgs.builder().bucket("my-bucketname").recursive(true).build());
   *
   * // Lists maximum 100 objects information whose names starts with 'E' and after
   * // 'ExampleGuide.pdf'.
   * Iterable> results = apiAsyncClient.listObjects(
   *     ListObjectsArgs.builder()
   *         .bucket("my-bucketname")
   *         .startAfter("ExampleGuide.pdf")
   *         .prefix("E")
   *         .maxKeys(100)
   *         .build());
   *
   * // Lists maximum 100 objects information with version whose names starts with 'E' and after
   * // 'ExampleGuide.pdf'.
   * Iterable> results = apiAsyncClient.listObjects(
   *     ListObjectsArgs.builder()
   *         .bucket("my-bucketname")
   *         .startAfter("ExampleGuide.pdf")
   *         .prefix("E")
   *         .maxKeys(100)
   *         .includeVersions(true)
   *         .build());
   * }
* * @param args Instance of {@link ListObjectsArgs} built using the builder * @return {@code Iterable>} - Lazy iterator contains object information. * @throws XmlParserException upon parsing response xml */ public Iterable> listObjects(ListObjectsArgs args) { if (args.includeVersions() || args.versionIdMarker() != null) { return listObjectVersions(args); } if (args.useApiVersion1()) { return listObjectsV1(args); } return listObjectsV2(args); } /** * Lists bucket information of all buckets. * *
Example:{@code
   * CompletableFuture> future = apiAsyncClient.listBuckets();
   * }
* * @return {@link CompletableFuture}<{@link List}<{@link Bucket}>> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture> listBuckets() throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { return listBuckets(ListBucketsArgs.builder().build()); } /** * Lists bucket information of all buckets. * *
Example:{@code
   * CompletableFuture> future =
   *     apiAsyncClient.listBuckets(ListBucketsArgs.builder().extraHeaders(headers).build());
   * }
* * @return {@link CompletableFuture}<{@link List}<{@link Bucket}>> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture> listBuckets(ListBucketsArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { return executeGetAsync(args, null, null) .thenApply( response -> { try { ListAllMyBucketsResult result = Xml.unmarshal(ListAllMyBucketsResult.class, response.body().charStream()); return result.buckets(); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Checks if a bucket exists. * *
Example:{@code
   * CompletableFuture future =
   *      apiAsyncClient.bucketExists(HeadBucketArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link HeadBucketArgs} object. * @return {@link CompletableFuture}<{@link Boolean}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture bucketExists(HeadBucketArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { return executeHeadAsync(args, null, null) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex).errorResponse().code().equals(NO_SUCH_BUCKET)) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { try { return response != null; } finally { if (response != null) response.close(); } }); } /** * Creates a bucket with region and object lock. * *
Example:{@code
   * // Create bucket with default region.
   * CompletableFuture future = apiAsyncClient.makeBucket(
   *     MakeBucketArgs.builder()
   *         .bucket("my-bucketname")
   *         .build());
   *
   * // Create bucket with specific region.
   * CompletableFuture future = apiAsyncClient.makeBucket(
   *     MakeBucketArgs.builder()
   *         .bucket("my-bucketname")
   *         .region("us-west-1")
   *         .build());
   *
   * // Create object-lock enabled bucket with specific region.
   * CompletableFuture future = apiAsyncClient.makeBucket(
   *     MakeBucketArgs.builder()
   *         .bucket("my-bucketname")
   *         .region("us-west-1")
   *         .objectLock(true)
   *         .build());
   * }
* * @param args Object with bucket name, region and lock functionality * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture makeBucket(MakeBucketArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); String region = args.region(); if (this.region != null && !this.region.isEmpty()) { // Error out if region does not match with region passed via constructor. if (region != null && !region.equals(this.region)) { throw new IllegalArgumentException( "region must be " + this.region + ", but passed " + region); } region = this.region; } if (region == null) { region = US_EAST_1; } Multimap headers = args.objectLock() ? newMultimap("x-amz-bucket-object-lock-enabled", "true") : null; final String location = region; return executeAsync( Method.PUT, args.bucket(), null, location, httpHeaders(merge(args.extraHeaders(), headers)), args.extraQueryParams(), location.equals(US_EAST_1) ? null : new CreateBucketConfiguration(location), 0) .thenAccept( response -> { regionCache.put(args.bucket(), location); response.close(); }); } /** * Sets versioning configuration of a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.putBucketVersioning(
   *     PutBucketVersioningArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link PutBucketVersioningArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putBucketVersioning(PutBucketVersioningArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("versioning", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Gets versioning configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketVersioning(
   *         GetBucketVersioningArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketVersioningArgs} object. * @return {@link CompletableFuture}<{@link VersioningConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketVersioning( GetBucketVersioningArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("versioning", "")) .thenApply( response -> { try { return Xml.unmarshal(VersioningConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets default object retention in a bucket. * *
Example:{@code
   * ObjectLockConfiguration config = new ObjectLockConfiguration(
   *     RetentionMode.COMPLIANCE, new RetentionDurationDays(100));
   * CompletableFuture future = apiAsyncClient.putObjectLockConfiguration(
   *     PutObjectLockConfigurationArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link PutObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putObjectLockConfiguration(PutObjectLockConfigurationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("object-lock", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Deletes default object retention in a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteObjectLockConfiguration(
   *     DeleteObjectLockConfigurationArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteObjectLockConfiguration( DeleteObjectLockConfigurationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync( args, null, newMultimap("object-lock", ""), new ObjectLockConfiguration(), 0) .thenAccept(response -> response.close()); } /** * Gets default object retention in a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getObjectLockConfiguration(
   *         GetObjectLockConfigurationArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetObjectLockConfigurationArgs} object. * @return {@link CompletableFuture}<{@link ObjectLockConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getObjectLockConfiguration( GetObjectLockConfigurationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("object-lock", "")) .thenApply( response -> { try { return Xml.unmarshal(ObjectLockConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets retention configuration to an object. * *
Example:{@code
   *  Retention retention = new Retention(
   *       RetentionMode.COMPLIANCE, ZonedDateTime.now().plusYears(1));
   *  CompletableFuture future = apiAsyncClient.putObjectRetention(
   *      PutObjectRetentionArgs.builder()
   *          .bucket("my-bucketname")
   *          .object("my-objectname")
   *          .config(config)
   *          .bypassGovernanceMode(true)
   *          .build());
   * }
* * @param args {@link PutObjectRetentionArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putObjectRetention(PutObjectRetentionArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("retention", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executePutAsync( args, args.bypassGovernanceMode() ? newMultimap("x-amz-bypass-governance-retention", "True") : null, queryParams, args.config(), 0) .thenAccept(response -> response.close()); } /** * Gets retention configuration of an object. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getObjectRetention(GetObjectRetentionArgs.builder()
   *        .bucket(bucketName)
   *        .object(objectName)
   *        .versionId(versionId)
   *        .build());
   * }
* * @param args {@link GetObjectRetentionArgs} object. * @return {@link CompletableFuture}<{@link Retention}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getObjectRetention(GetObjectRetentionArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("retention", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(NO_SUCH_OBJECT_LOCK_CONFIGURATION)) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return null; try { return Xml.unmarshal(Retention.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Enables legal hold on an object. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.putObjectLegalHold(
   *    PutObjectLegalHoldArgs.builder()
   *        .bucket("my-bucketname")
   *        .object("my-objectname")
   *        .versionId("object-versionId")
   *        .build());
   * }
* * @param args {@link PutObjectLegalHoldArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putObjectLegalHold(PutObjectLegalHoldArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executePutAsync(args, null, queryParams, new LegalHold(true), 0) .thenAccept(response -> response.close()); } /** * Disables legal hold on an object. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.disableObjectLegalHold(
   *    DisableObjectLegalHoldArgs.builder()
   *        .bucket("my-bucketname")
   *        .object("my-objectname")
   *        .versionId("object-versionId")
   *        .build());
   * }
* * @param args {@link DisableObjectLegalHoldArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture disableObjectLegalHold(DisableObjectLegalHoldArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executePutAsync(args, null, queryParams, new LegalHold(false), 0) .thenAccept(response -> response.close()); } /** * Returns true if legal hold is enabled on an object. * *
Example:{@code
   * CompletableFuture future =
   *     s3Client.getObjectLegalHold(
   *        GetObjectLegalHoldArgs.builder()
   *             .bucket("my-bucketname")
   *             .object("my-objectname")
   *             .versionId("object-versionId")
   *             .build());
   * }
* * @param args {@link GetObjectLegalHoldArgs} object. * @return {@link CompletableFuture}<{@link Boolean}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getObjectLegalHold(GetObjectLegalHoldArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("legal-hold", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(NO_SUCH_OBJECT_LOCK_CONFIGURATION)) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return false; try { LegalHold result = Xml.unmarshal(LegalHold.class, response.body().charStream()); return result.status(); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Removes an empty bucket using arguments * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.deleteBucket(DeleteBucketArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteBucketArgs} bucket. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucket(DeleteBucketArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, null) .thenAccept(response -> regionCache.remove(args.bucket())); } /** * Uploads data from a stream to an object. * *
Example:{@code
   * // Upload known sized input stream.
   * CompletableFuture future = apiAsyncClient.putObject(
   *     PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream(
   *             inputStream, size, -1)
   *         .contentType("video/mp4")
   *         .build());
   *
   * // Upload unknown sized input stream.
   * CompletableFuture future = apiAsyncClient.putObject(
   *     PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream(
   *             inputStream, -1, 10485760)
   *         .contentType("video/mp4")
   *         .build());
   *
   * // Create object ends with '/' (also called as folder or directory).
   * CompletableFuture future = apiAsyncClient.putObject(
   *     PutObjectArgs.builder().bucket("my-bucketname").object("path/to/").stream(
   *             new ByteArrayInputStream(new byte[] {}), 0, -1)
   *         .build());
   *
   * // Upload input stream with headers and user metadata.
   * Map headers = new HashMap<>();
   * headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY");
   * Map userMetadata = new HashMap<>();
   * userMetadata.put("My-Project", "Project One");
   * CompletableFuture future = apiAsyncClient.putObject(
   *     PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream(
   *             inputStream, size, -1)
   *         .headers(headers)
   *         .userMetadata(userMetadata)
   *         .build());
   *
   * // Upload input stream with server-side encryption.
   * CompletableFuture future = apiAsyncClient.putObject(
   *     PutObjectArgs.builder().bucket("my-bucketname").object("my-objectname").stream(
   *             inputStream, size, -1)
   *         .sse(sse)
   *         .build());
   * }
* * @param args {@link PutObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putObject(PutObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); args.validateSse(this.baseUrl); return putObjectAsync( args, args.stream(), args.objectSize(), args.partSize(), args.partCount(), args.contentType()); } /** * Uploads data from a file to an object. * *
Example:{@code
   * // Upload an JSON file.
   * CompletableFuture future = apiAsyncClient.postObject(
   *     PostObjectArgs.builder()
   *         .bucket("my-bucketname").object("my-objectname").filename("person.json").build());
   *
   * // Upload a video file.
   * CompletableFuture future = apiAsyncClient.postObject(
   *     PostObjectArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .filename("my-video.avi")
   *         .contentType("video/mp4")
   *         .build());
   * }
* * @param args {@link PostObjectArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture postObject(PostObjectArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); args.validateSse(this.baseUrl); final RandomAccessFile file = new RandomAccessFile(args.filename(), "r"); return putObjectAsync( args, file, args.objectSize(), args.partSize(), args.partCount(), args.contentType()) .exceptionally( e -> { try { file.close(); } catch (IOException ex) { throw new CompletionException(ex); } Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } throw new CompletionException(ex); }) .thenApply( objectWriteResponse -> { try { file.close(); } catch (IOException e) { throw new CompletionException(e); } return objectWriteResponse; }); } /** * Gets bucket policy configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketPolicy(
   *         GetBucketPolicyArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link String}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketPolicy(GetBucketPolicyArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("policy", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(NO_SUCH_BUCKET_POLICY)) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return ""; try { byte[] buf = new byte[MAX_BUCKET_POLICY_SIZE]; int bytesRead = 0; bytesRead = response.body().byteStream().read(buf, 0, MAX_BUCKET_POLICY_SIZE); if (bytesRead < 0) { throw new CompletionException( new IOException("unexpected EOF when reading bucket policy")); } // Read one byte extra to ensure only MAX_BUCKET_POLICY_SIZE data is sent by the // server. if (bytesRead == MAX_BUCKET_POLICY_SIZE) { int byteRead = 0; while (byteRead == 0) { byteRead = response.body().byteStream().read(); if (byteRead < 0) { break; // reached EOF which is fine. } if (byteRead > 0) { throw new CompletionException( new BucketPolicyTooLargeException(args.bucket())); } } } return new String(buf, 0, bytesRead, StandardCharsets.UTF_8); } catch (IOException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets bucket policy configuration to a bucket. * *
Example:{@code
   * // Assume policyJson contains below JSON string;
   * // {
   * //     "Statement": [
   * //         {
   * //             "Action": [
   * //                 "s3:GetBucketLocation",
   * //                 "s3:ListBucket"
   * //             ],
   * //             "Effect": "Allow",
   * //             "Principal": "*",
   * //             "Resource": "arn:aws:s3:::my-bucketname"
   * //         },
   * //         {
   * //             "Action": "s3:GetObject",
   * //             "Effect": "Allow",
   * //             "Principal": "*",
   * //             "Resource": "arn:aws:s3:::my-bucketname/myobject*"
   * //         }
   * //     ],
   * //     "Version": "2012-10-17"
   * // }
   * //
   * CompletableFuture future = apiAsyncClient.putBucketPolicy(
   *     PutBucketPolicyArgs.builder().bucket("my-bucketname").config(policyJson).build());
   * }
* * @param args {@link PutBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putBucketPolicy(PutBucketPolicyArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync( args, newMultimap("Content-Type", "application/json"), newMultimap("policy", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Deletes bucket policy configuration to a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.deleteBucketPolicy(
   *         DeleteBucketPolicyArgs.builder().bucket("my-bucketname"));
   * }
* * @param args {@link DeleteBucketPolicyArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketPolicy(DeleteBucketPolicyArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, newMultimap("policy", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(NO_SUCH_BUCKET_POLICY)) { return null; } } throw new CompletionException(ex); }) .thenAccept( response -> { if (response != null) response.close(); }); } /** * Sets lifecycle configuration to a bucket. * *
Example:{@code
   * List rules = new LinkedList<>();
   * rules.add(
   *     new LifecycleRule(
   *         Status.ENABLED,
   *         null,
   *         new Expiration((ZonedDateTime) null, 365, null),
   *         new RuleFilter("logs/"),
   *         "rule2",
   *         null,
   *         null,
   *         null));
   * LifecycleConfiguration config = new LifecycleConfiguration(rules);
   * CompletableFuture future = apiAsyncClient.putBucketLifecycle(
   *     PutBucketLifecycleArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link PutBucketLifecycleArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putBucketLifecycle(PutBucketLifecycleArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("lifecycle", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Deletes lifecycle configuration of a bucket. * *
Example:{@code
   * CompletableFuture future = deleteBucketLifecycle(
   *     DeleteBucketLifecycleArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteBucketLifecycleArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketLifecycle(DeleteBucketLifecycleArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, newMultimap("lifecycle", "")) .thenAccept(response -> response.close()); } /** * Gets lifecycle configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketLifecycle(
   *         GetBucketLifecycleArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketLifecycleArgs} object. * @return {@link LifecycleConfiguration} object. * @return {@link CompletableFuture}<{@link LifecycleConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketLifecycle(GetBucketLifecycleArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("lifecycle", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals("NoSuchLifecycleConfiguration")) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return null; try { return Xml.unmarshal(LifecycleConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Gets notification configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketNotification(
   *         GetBucketNotificationArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link NotificationConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketNotification( GetBucketNotificationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("notification", "")) .thenApply( response -> { try { return Xml.unmarshal(NotificationConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets notification configuration to a bucket. * *
Example:{@code
   * List eventList = new LinkedList<>();
   * eventList.add(EventType.OBJECT_CREATED_PUT);
   * eventList.add(EventType.OBJECT_CREATED_COPY);
   *
   * QueueConfiguration queueConfiguration = new QueueConfiguration();
   * queueConfiguration.setQueue("arn:ionos:sqs::1:webhook");
   * queueConfiguration.setEvents(eventList);
   * queueConfiguration.setPrefixRule("images");
   * queueConfiguration.setSuffixRule("pg");
   *
   * List queueConfigurationList = new LinkedList<>();
   * queueConfigurationList.add(queueConfiguration);
   *
   * NotificationConfiguration config = new NotificationConfiguration();
   * config.setQueueConfigurationList(queueConfigurationList);
   *
   * CompletableFuture future = apiAsyncClient.setBucketNotification(
   *     SetBucketNotificationArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link SetBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture setBucketNotification(SetBucketNotificationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("notification", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Deletes notification configuration of a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteBucketNotification(
   *     DeleteBucketNotificationArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteBucketNotificationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketNotification(DeleteBucketNotificationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync( args, null, newMultimap("notification", ""), new NotificationConfiguration(), 0) .thenAccept(response -> response.close()); } /** * Gets bucket replication configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketReplication(
   *         GetBucketReplicationArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link ReplicationConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketReplication( GetBucketReplicationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("replication", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals("ReplicationConfigurationNotFoundError")) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return null; try { return Xml.unmarshal(ReplicationConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets bucket replication configuration to a bucket. * *
Example:{@code
   * Map tags = new HashMap<>();
   * tags.put("key1", "value1");
   * tags.put("key2", "value2");
   *
   * ReplicationRule rule =
   *     new ReplicationRule(
   *         new DeleteMarkerReplication(Status.DISABLED),
   *         new ReplicationDestination(
   *             null, null, "REPLACE-WITH-ACTUAL-DESTINATION-BUCKET-ARN", null, null, null, null),
   *         null,
   *         new RuleFilter(new AndOperator("TaxDocs", tags)),
   *         "rule1",
   *         null,
   *         1,
   *         null,
   *         Status.ENABLED);
   *
   * List rules = new LinkedList<>();
   * rules.add(rule);
   *
   * ReplicationConfiguration config =
   *     new ReplicationConfiguration("REPLACE-WITH-ACTUAL-ROLE", rules);
   *
   * CompletableFuture future = apiAsyncClient.setBucketReplication(
   *     SetBucketReplicationArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link SetBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture setBucketReplication(SetBucketReplicationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync( args, (args.objectLockToken() != null) ? newMultimap("x-amz-bucket-object-lock-token", args.objectLockToken()) : null, newMultimap("replication", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Deletes bucket replication configuration from a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteBucketReplication(
   *     DeleteBucketReplicationArgs.builder().bucket("my-bucketname"));
   * }
* * @param args {@link DeleteBucketReplicationArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketReplication(DeleteBucketReplicationArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, newMultimap("replication", "")) .thenAccept(response -> response.close()); } /** * Listens events of object prefix and suffix of a bucket. The returned closable iterator is * lazily evaluated hence its required to iterate to get new records and must be used with * try-with-resource to release underneath network resources. * *
Example:{@code
   * String[] events = {"s3:ObjectCreated:*", "s3:ObjectAccessed:*"};
   * try (CloseableIterator> ci =
   *     apiAsyncClient.listenBucketNotification(
   *         ListenBucketNotificationArgs.builder()
   *             .bucket("bucketName")
   *             .prefix("")
   *             .suffix("")
   *             .events(events)
   *             .build())) {
   *   while (ci.hasNext()) {
   *     NotificationRecords records = ci.next().get();
   *     for (Event event : records.events()) {
   *       System.out.println("Event " + event.eventType() + " occurred at "
   *           + event.eventTime() + " for " + event.bucketName() + "/"
   *           + event.objectName());
   *     }
   *   }
   * }
   * }
* * @param args {@link ListenBucketNotificationArgs} object. * @return {@code CloseableIterator>} - Lazy closable iterator * contains event records. * @throws ErrorResponseException thrown to indicate S3 service returned an error response. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error * response. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CloseableIterator> listenBucketNotification( ListenBucketNotificationArgs args) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("prefix", args.prefix(), "suffix", args.suffix()); for (String event : args.events()) { queryParams.put("events", event); } Response response = null; try { response = executeGetAsync(args, null, queryParams).get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } NotificationResultRecords result = new NotificationResultRecords(response); return result.closeableIterator(); } /** * Selects content of an object by SQL expression. * *
Example:{@code
   * String sqlExpression = "select * from S3Object";
   * InputSerialization is =
   *     new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null,
   *         null);
   * OutputSerialization os =
   *     new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null);
   * SelectResponseStream stream =
   *     apiAsyncClient.selectObjectContent(
   *       SelectObjectContentArgs.builder()
   *       .bucket("my-bucketname")
   *       .object("my-objectname")
   *       .sqlExpression(sqlExpression)
   *       .inputSerialization(is)
   *       .outputSerialization(os)
   *       .requestProgress(true)
   *       .build());
   *
   * byte[] buf = new byte[512];
   * int bytesRead = stream.read(buf, 0, buf.length);
   * System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8));
   *
   * Stats stats = stream.stats();
   * System.out.println("bytes scanned: " + stats.bytesScanned());
   * System.out.println("bytes processed: " + stats.bytesProcessed());
   * System.out.println("bytes returned: " + stats.bytesReturned());
   *
   * stream.close();
   * }
* * @param args instance of {@link SelectObjectContentArgs} * @return {@link SelectResponseStream} - Contains filtered records and progress. * @throws ErrorResponseException thrown to indicate S3 service returned an error response. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws InvalidResponseException thrown to indicate S3 service returned invalid or no error * response. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public SelectResponseStream selectObjectContent(SelectObjectContentArgs args) throws ErrorResponseException, InsufficientDataException, InternalException, InvalidKeyException, InvalidResponseException, IOException, NoSuchAlgorithmException, ServerException, XmlParserException { checkArgs(args); args.validateSsec(this.baseUrl); Response response = null; try { response = executePostAsync( args, (args.ssec() != null) ? newMultimap(args.ssec().headers()) : null, newMultimap("select", "", "select-type", "2"), new SelectObjectContentRequest( args.sqlExpression(), args.requestProgress(), args.inputSerialization(), args.outputSerialization(), args.scanStartRange(), args.scanEndRange())) .get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throwEncapsulatedException(e); } return new SelectResponseStream(response.body().byteStream()); } /** * Sets encryption configuration of a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.putBucketEncryption(
   *     PutBucketEncryptionArgs.builder().bucket("my-bucketname").config(config).build());
   * }
* * @param args {@link PutBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putBucketEncryption(PutBucketEncryptionArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("encryption", ""), args.config(), 0) .thenAccept(response -> response.close()); } /** * Gets encryption configuration of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketEncryption(
   *         GetBucketEncryptionArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link SseConfiguration}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketEncryption(GetBucketEncryptionArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("encryption", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR)) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return new SseConfiguration(null); try { return Xml.unmarshal(SseConfiguration.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Deletes encryption configuration of a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteBucketEncryption(
   *     DeleteBucketEncryptionArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteBucketEncryptionArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketEncryption(DeleteBucketEncryptionArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, newMultimap("encryption", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex) .errorResponse() .code() .equals(SERVER_SIDE_ENCRYPTION_CONFIGURATION_NOT_FOUND_ERROR)) { return null; } } throw new CompletionException(ex); }) .thenAccept( response -> { if (response != null) response.close(); }); } /** * Gets tags of a bucket. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getBucketTagging(GetBucketTaggingArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link GetBucketTaggingArgs} object. * @return {@link CompletableFuture}<{@link Tags}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getBucketTagging(GetBucketTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeGetAsync(args, null, newMultimap("tagging", "")) .exceptionally( e -> { Throwable ex = e.getCause(); if (ex instanceof CompletionException) { ex = ((CompletionException) ex).getCause(); } if (ex instanceof ExecutionException) { ex = ((ExecutionException) ex).getCause(); } if (ex instanceof ErrorResponseException) { if (((ErrorResponseException) ex).errorResponse().code().equals("NoSuchTagSet")) { return null; } } throw new CompletionException(ex); }) .thenApply( response -> { if (response == null) return new Tags(); try { return Xml.unmarshal(Tags.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets tags to a bucket. * *
Example:{@code
   * Map map = new HashMap<>();
   * map.put("Project", "Project One");
   * map.put("User", "jsmith");
   * CompletableFuture future = apiAsyncClient.putBucketTagging(
   *     PutBucketTaggingArgs.builder().bucket("my-bucketname").tags(map).build());
   * }
* * @param args {@link PutBucketTaggingArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putBucketTagging(PutBucketTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executePutAsync(args, null, newMultimap("tagging", ""), args.tags(), 0) .thenAccept(response -> response.close()); } /** * Deletes tags of a bucket. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteBucketTagging(
   *     DeleteBucketTaggingArgs.builder().bucket("my-bucketname").build());
   * }
* * @param args {@link DeleteBucketTaggingArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteBucketTagging(DeleteBucketTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return executeDeleteAsync(args, null, newMultimap("tagging", "")) .thenAccept(response -> response.close()); } /** * Gets tags of an object. * *
Example:{@code
   * CompletableFuture future =
   *     apiAsyncClient.getObjectTagging(
   *         GetObjectTaggingArgs.builder().bucket("my-bucketname").object("my-objectname").build());
   * }
* * @param args {@link GetObjectTaggingArgs} object. * @return {@link CompletableFuture}<{@link Tags}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture getObjectTagging(GetObjectTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeGetAsync(args, null, queryParams) .thenApply( response -> { try { return Xml.unmarshal(Tags.class, response.body().charStream()); } catch (XmlParserException e) { throw new CompletionException(e); } finally { response.close(); } }); } /** * Sets tags to an object. * *
Example:{@code
   * Map map = new HashMap<>();
   * map.put("Project", "Project One");
   * map.put("User", "jsmith");
   * CompletableFuture future = apiAsyncClient.putObjectTagging(
   *     PutObjectTaggingArgs.builder()
   *         .bucket("my-bucketname")
   *         .object("my-objectname")
   *         .tags((map)
   *         .build());
   * }
* * @param args {@link PutObjectTaggingArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture putObjectTagging(PutObjectTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executePutAsync(args, null, queryParams, args.tags(), 0) .thenAccept(response -> response.close()); } /** * Deletes tags of an object. * *
Example:{@code
   * CompletableFuture future = apiAsyncClient.deleteObjectTags(
   *     DeleteObjectTagging.builder().bucket("my-bucketname").object("my-objectname").build());
   * }
* * @param args {@link DeleteObjectTaggingArgs} object. * @return {@link CompletableFuture}<{@link Void}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture deleteObjectTags(DeleteObjectTaggingArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); Multimap queryParams = newMultimap("tagging", ""); if (args.versionId() != null) queryParams.put("versionId", args.versionId()); return executeDeleteAsync(args, null, queryParams).thenAccept(response -> response.close()); } /** * Uploads multiple objects in a single put call. It is done by creating intermediate TAR file * optionally compressed which is uploaded to S3 service. * *
Example:{@code
   * // Upload snowball objects.
   * List objects = new ArrayList();
   * objects.add(
   *     new SnowballObject(
   *         "my-object-one",
   *         new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)),
   *         5,
   *         null));
   * objects.add(
   *     new SnowballObject(
   *         "my-object-two",
   *         new ByteArrayInputStream("java".getBytes(StandardCharsets.UTF_8)),
   *         4,
   *         null));
   * CompletableFuture future = apiAsyncClient.uploadSnowballObjects(
   *     UploadSnowballObjectsArgs.builder().bucket("my-bucketname").objects(objects).build());
   * }
* * @param args {@link UploadSnowballObjectsArgs} object. * @return {@link CompletableFuture}<{@link ObjectWriteResponse}> object. * @throws InsufficientDataException thrown to indicate not enough data available in InputStream. * @throws InternalException thrown to indicate internal library error. * @throws InvalidKeyException thrown to indicate missing of HMAC SHA-256 library. * @throws IOException thrown to indicate I/O error on S3 operation. * @throws NoSuchAlgorithmException thrown to indicate missing of MD5 or SHA-256 digest library. * @throws XmlParserException thrown to indicate XML parsing error. */ public CompletableFuture uploadSnowballObjects( UploadSnowballObjectsArgs args) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException { checkArgs(args); return CompletableFuture.supplyAsync( () -> { FileOutputStream fos = null; BufferedOutputStream bos = null; SnappyFramedOutputStream sos = null; ByteArrayOutputStream baos = null; TarArchiveOutputStream tarOutputStream = null; try { OutputStream os = null; if (args.stagingFilename() != null) { fos = new FileOutputStream(args.stagingFilename()); bos = new BufferedOutputStream(fos); os = bos; } else { baos = new ByteArrayOutputStream(); os = baos; } if (args.compression()) { sos = new SnappyFramedOutputStream(os); os = sos; } tarOutputStream = new TarArchiveOutputStream(os); tarOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); for (SnowballObject object : args.objects()) { if (object.filename() != null) { Path filePath = Paths.get(object.filename()); TarArchiveEntry entry = new TarArchiveEntry(filePath.toFile(), object.name()); tarOutputStream.putArchiveEntry(entry); Files.copy(filePath, tarOutputStream); } else { TarArchiveEntry entry = new TarArchiveEntry(object.name()); if (object.modificationTime() != null) { entry.setModTime(Date.from(object.modificationTime().toInstant())); } entry.setSize(object.size()); tarOutputStream.putArchiveEntry(entry); ByteStreams.copy(object.stream(), tarOutputStream); } tarOutputStream.closeArchiveEntry(); } tarOutputStream.finish(); } catch (IOException e) { throw new CompletionException(e); } finally { try { if (tarOutputStream != null) tarOutputStream.flush(); if (sos != null) sos.flush(); if (bos != null) bos.flush(); if (fos != null) fos.flush(); if (tarOutputStream != null) tarOutputStream.close(); if (sos != null) sos.close(); if (bos != null) bos.close(); if (fos != null) fos.close(); } catch (IOException e) { throw new CompletionException(e); } } return baos; }) .thenCompose( baos -> { Multimap headers = newMultimap(args.extraHeaders()); headers.putAll(args.genHeaders()); headers.put("X-Amz-Meta-Snowball-Auto-Extract", "true"); if (args.stagingFilename() == null) { byte[] data = baos.toByteArray(); try { return putObjectAsync( args.bucket(), args.region(), args.object(), data, data.length, headers, args.extraQueryParams()); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } } long length = Paths.get(args.stagingFilename()).toFile().length(); if (length > ObjectWriteArgs.MAX_OBJECT_SIZE) { throw new IllegalArgumentException( "tarball size " + length + " is more than maximum allowed 5TiB"); } try (RandomAccessFile file = new RandomAccessFile(args.stagingFilename(), "r")) { return putObjectAsync( args.bucket(), args.region(), args.object(), file, length, headers, args.extraQueryParams()); } catch (InsufficientDataException | InternalException | InvalidKeyException | IOException | NoSuchAlgorithmException | XmlParserException e) { throw new CompletionException(e); } }); } public static Builder builder() { return new Builder(); } /** Argument builder of {@link ApiClient}. */ public static final class Builder { private HttpUrl baseUrl; private String awsS3Prefix; private String awsDomainSuffix; private boolean awsDualstack; private boolean useVirtualStyle; private String region; private Provider provider; private OkHttpClient httpClient; private void setAwsInfo(String host, boolean https) { this.awsS3Prefix = null; this.awsDomainSuffix = null; this.awsDualstack = false; if (!HttpUtils.HOSTNAME_REGEX.matcher(host).find()) return; if (HttpUtils.AWS_ELB_ENDPOINT_REGEX.matcher(host).find()) { String[] tokens = host.split("\\.elb\\.amazonaws\\.com", 1)[0].split("\\."); this.region = tokens[tokens.length - 1]; return; } if (!HttpUtils.AWS_ENDPOINT_REGEX.matcher(host).find()) return; if (!HttpUtils.AWS_S3_ENDPOINT_REGEX.matcher(host).find()) { throw new IllegalArgumentException("invalid Amazon AWS host " + host); } Matcher matcher = HttpUtils.AWS_S3_PREFIX_REGEX.matcher(host); matcher.lookingAt(); int end = matcher.end(); this.awsS3Prefix = host.substring(0, end); if (this.awsS3Prefix.contains("s3-accesspoint") && !https) { throw new IllegalArgumentException("use HTTPS scheme for host " + host); } String[] tokens = host.substring(end).split("\\."); awsDualstack = "dualstack".equals(tokens[0]); if (awsDualstack) tokens = Arrays.copyOfRange(tokens, 1, tokens.length); String regionInHost = null; if (!tokens[0].equals("vpce") && !tokens[0].equals("amazonaws")) { regionInHost = tokens[0]; tokens = Arrays.copyOfRange(tokens, 1, tokens.length); } this.awsDomainSuffix = String.join(".", tokens); if (host.equals("s3-external-1.amazonaws.com")) regionInHost = "us-east-1"; if (host.equals("s3-us-gov-west-1.amazonaws.com") || host.equals("s3-fips-us-gov-west-1.amazonaws.com")) { regionInHost = "us-gov-west-1"; } if (regionInHost != null) this.region = regionInHost; } private void setBaseUrl(HttpUrl url) { this.baseUrl = url; this.setAwsInfo(url.host(), url.isHttps()); this.useVirtualStyle = this.awsDomainSuffix != null || url.host().endsWith("aliyuncs.com"); } public Builder endpoint(String endpoint) { setBaseUrl(HttpUtils.getBaseUrl(endpoint)); return this; } public Builder endpoint(String endpoint, int port, boolean secure) { HttpUrl url = HttpUtils.getBaseUrl(endpoint); if (port < 1 || port > 65535) { throw new IllegalArgumentException("port must be in range of 1 to 65535"); } url = url.newBuilder().port(port).scheme(secure ? "https" : "http").build(); setBaseUrl(url); return this; } public Builder endpoint(URL url) { HttpUtils.validateNotNull(url, "url"); return endpoint(HttpUrl.get(url)); } public Builder endpoint(HttpUrl url) { HttpUtils.validateNotNull(url, "url"); HttpUtils.validateUrl(url); setBaseUrl(url); return this; } public Builder region(String region) { if (region != null && !HttpUtils.REGION_REGEX.matcher(region).find()) { throw new IllegalArgumentException("invalid region " + region); } this.region = region; return this; } public Builder credentials(String accessKey, String secretKey) { this.provider = new StaticProvider(accessKey, secretKey, null); return this; } public Builder credentialsProvider(Provider provider) { this.provider = provider; return this; } public Builder httpClient(OkHttpClient httpClient) { HttpUtils.validateNotNull(httpClient, "http client"); this.httpClient = httpClient; return this; } public ApiAsyncClient build() { HttpUtils.validateNotNull(this.baseUrl, "endpoint"); if (this.awsDomainSuffix != null && this.awsDomainSuffix.endsWith(".cn") && !this.awsS3Prefix.endsWith("s3-accelerate.") && this.region == null) { throw new IllegalArgumentException( "Region missing in Amazon S3 China endpoint " + this.baseUrl); } if (this.httpClient == null) { this.httpClient = HttpUtils.newDefaultHttpClient( DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); } return new ApiAsyncClient( baseUrl, awsS3Prefix, awsDomainSuffix, awsDualstack, useVirtualStyle, region, provider, httpClient); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy