• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.transfer.s3.model;
17 
18 import java.io.ByteArrayInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.nio.charset.StandardCharsets;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.time.Instant;
26 import java.util.Objects;
27 import java.util.Optional;
28 import java.util.OptionalLong;
29 import java.util.function.Consumer;
30 import software.amazon.awssdk.annotations.SdkPublicApi;
31 import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
32 import software.amazon.awssdk.core.SdkBytes;
33 import software.amazon.awssdk.core.exception.SdkClientException;
34 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
35 import software.amazon.awssdk.transfer.s3.S3TransferManager;
36 import software.amazon.awssdk.transfer.s3.config.TransferRequestOverrideConfiguration;
37 import software.amazon.awssdk.transfer.s3.internal.serialization.ResumableFileDownloadSerializer;
38 import software.amazon.awssdk.utils.IoUtils;
39 import software.amazon.awssdk.utils.ToString;
40 import software.amazon.awssdk.utils.Validate;
41 import software.amazon.awssdk.utils.builder.CopyableBuilder;
42 import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
43 
44 /**
45  * An opaque token that holds the state and can be used to resume a paused download operation.
46  * <p>
47  * <b>Serialization: </b>When serializing this token, the following structures will not be preserved/persisted:
48  * <ul>
49  *     <li>{@link TransferRequestOverrideConfiguration}</li>
50  *     <li>{@link AwsRequestOverrideConfiguration} (from {@link GetObjectRequest})</li>
51  * </ul>
52  *
53  * @see S3TransferManager#downloadFile(DownloadFileRequest)
54  */
55 @SdkPublicApi
56 public final class ResumableFileDownload implements ResumableTransfer,
57                                                     ToCopyableBuilder<ResumableFileDownload.Builder, ResumableFileDownload> {
58 
59     private final DownloadFileRequest downloadFileRequest;
60     private final long bytesTransferred;
61     private final Instant s3ObjectLastModified;
62     private final Long totalSizeInBytes;
63     private final Instant fileLastModified;
64 
ResumableFileDownload(DefaultBuilder builder)65     private ResumableFileDownload(DefaultBuilder builder) {
66         this.downloadFileRequest = Validate.paramNotNull(builder.downloadFileRequest, "downloadFileRequest");
67         this.bytesTransferred = builder.bytesTransferred == null ? 0 : Validate.isNotNegative(builder.bytesTransferred,
68                                                                                               "bytesTransferred");
69         this.s3ObjectLastModified = builder.s3ObjectLastModified;
70         this.totalSizeInBytes = Validate.isPositiveOrNull(builder.totalSizeInBytes, "totalSizeInBytes");
71         this.fileLastModified = builder.fileLastModified;
72     }
73 
74     @Override
equals(Object o)75     public boolean equals(Object o) {
76         if (this == o) {
77             return true;
78         }
79         if (o == null || getClass() != o.getClass()) {
80             return false;
81         }
82 
83         ResumableFileDownload that = (ResumableFileDownload) o;
84 
85         if (bytesTransferred != that.bytesTransferred) {
86             return false;
87         }
88         if (!downloadFileRequest.equals(that.downloadFileRequest)) {
89             return false;
90         }
91         if (!Objects.equals(s3ObjectLastModified, that.s3ObjectLastModified)) {
92             return false;
93         }
94         if (!Objects.equals(fileLastModified, that.fileLastModified)) {
95             return false;
96         }
97         return Objects.equals(totalSizeInBytes, that.totalSizeInBytes);
98     }
99 
100     @Override
hashCode()101     public int hashCode() {
102         int result = downloadFileRequest.hashCode();
103         result = 31 * result + (int) (bytesTransferred ^ (bytesTransferred >>> 32));
104         result = 31 * result + (s3ObjectLastModified != null ? s3ObjectLastModified.hashCode() : 0);
105         result = 31 * result + (fileLastModified != null ? fileLastModified.hashCode() : 0);
106         result = 31 * result + (totalSizeInBytes != null ? totalSizeInBytes.hashCode() : 0);
107         return result;
108     }
109 
builder()110     public static Builder builder() {
111         return new DefaultBuilder();
112     }
113 
114     /**
115      * @return the {@link DownloadFileRequest} to resume
116      */
downloadFileRequest()117     public DownloadFileRequest downloadFileRequest() {
118         return downloadFileRequest;
119     }
120 
121     /**
122      * Retrieve the number of bytes that have been transferred.
123      * @return the number of bytes
124      */
bytesTransferred()125     public long bytesTransferred() {
126         return bytesTransferred;
127     }
128 
129     /**
130      * Last modified time of the S3 object since last pause, or {@link Optional#empty()} if unknown
131      */
s3ObjectLastModified()132     public Optional<Instant> s3ObjectLastModified() {
133         return Optional.ofNullable(s3ObjectLastModified);
134     }
135 
136     /**
137      * Last modified time of the file since last pause
138      */
fileLastModified()139     public Instant fileLastModified() {
140         return fileLastModified;
141     }
142 
143     /**
144      * The total size of the transfer in bytes or {@link OptionalLong#empty()} if unknown
145      *
146      * @return the optional total size of the transfer.
147      */
totalSizeInBytes()148     public OptionalLong totalSizeInBytes() {
149         return totalSizeInBytes == null ? OptionalLong.empty() : OptionalLong.of(totalSizeInBytes);
150     }
151 
152     @Override
toString()153     public String toString() {
154         return ToString.builder("ResumableFileDownload")
155                        .add("bytesTransferred", bytesTransferred)
156                        .add("fileLastModified", fileLastModified)
157                        .add("s3ObjectLastModified", s3ObjectLastModified)
158                        .add("totalSizeInBytes", totalSizeInBytes)
159                        .add("downloadFileRequest", downloadFileRequest)
160                        .build();
161     }
162 
163     /**
164      * Persists this download object to a file in Base64-encoded JSON format.
165      *
166      * @param path The path to the file to which you want to write the serialized download object.
167      */
168     @Override
serializeToFile(Path path)169     public void serializeToFile(Path path) {
170         try {
171             Files.write(path, ResumableFileDownloadSerializer.toJson(this));
172         } catch (IOException e) {
173             throw SdkClientException.create("Failed to write to " + path, e);
174         }
175     }
176 
177     /**
178      * Writes the serialized JSON data representing this object to an output stream.
179      * Note that the {@link OutputStream} is not closed or flushed after writing.
180      *
181      * @param outputStream The output stream to write the serialized object to.
182      */
183     @Override
serializeToOutputStream(OutputStream outputStream)184     public void serializeToOutputStream(OutputStream outputStream) {
185         byte[] bytes = ResumableFileDownloadSerializer.toJson(this);
186         try {
187             ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
188             IoUtils.copy(byteArrayInputStream, outputStream);
189         } catch (IOException e) {
190             throw SdkClientException.create("Failed to write this download object to the given OutputStream", e);
191         }
192     }
193 
194     /**
195      * Returns the serialized JSON data representing this object as a string.
196      */
197     @Override
serializeToString()198     public String serializeToString() {
199         return new String(ResumableFileDownloadSerializer.toJson(this), StandardCharsets.UTF_8);
200     }
201 
202     /**
203      * Returns the serialized JSON data representing this object as an {@link SdkBytes} object.
204      *
205      * @return the serialized JSON as {@link SdkBytes}
206      */
207     @Override
serializeToBytes()208     public SdkBytes serializeToBytes() {
209         return SdkBytes.fromByteArrayUnsafe(ResumableFileDownloadSerializer.toJson(this));
210     }
211 
212     /**
213      * Returns the serialized JSON data representing this object as an {@link InputStream}.
214      *
215      * @return the serialized JSON input stream
216      */
217     @Override
serializeToInputStream()218     public InputStream serializeToInputStream() {
219         return new ByteArrayInputStream(ResumableFileDownloadSerializer.toJson(this));
220     }
221 
222     /**
223      * Deserialize data at the given path into a {@link ResumableFileDownload}.
224      *
225      * @param path The {@link Path} to the file with serialized data
226      * @return the deserialized {@link ResumableFileDownload}
227      */
fromFile(Path path)228     public static ResumableFileDownload fromFile(Path path) {
229         try (InputStream stream = Files.newInputStream(path)) {
230             return ResumableFileDownloadSerializer.fromJson(stream);
231         } catch (IOException e) {
232             throw SdkClientException.create("Failed to create a ResumableFileDownload from " + path, e);
233         }
234     }
235 
236     /**
237      * Deserialize bytes with JSON data into a {@link ResumableFileDownload}.
238      *
239      * @param bytes the serialized data
240      * @return the deserialized {@link ResumableFileDownload}
241      */
fromBytes(SdkBytes bytes)242     public static ResumableFileDownload fromBytes(SdkBytes bytes) {
243         return ResumableFileDownloadSerializer.fromJson(bytes.asByteArrayUnsafe());
244     }
245 
246     /**
247      * Deserialize a string with JSON data into a {@link ResumableFileDownload}.
248      *
249      * @param contents the serialized data
250      * @return the deserialized {@link ResumableFileDownload}
251      */
fromString(String contents)252     public static ResumableFileDownload fromString(String contents) {
253         return ResumableFileDownloadSerializer.fromJson(contents);
254     }
255 
256     @Override
toBuilder()257     public Builder toBuilder() {
258         return new DefaultBuilder(this);
259     }
260 
261     public interface Builder extends CopyableBuilder<Builder, ResumableFileDownload> {
262 
263         /**
264          * Sets the download file request
265          *
266          * @param downloadFileRequest the download file request
267          * @return a reference to this object so that method calls can be chained together.
268          */
downloadFileRequest(DownloadFileRequest downloadFileRequest)269         Builder downloadFileRequest(DownloadFileRequest downloadFileRequest);
270 
271         /**
272          * The {@link DownloadFileRequest} request
273          *
274          * <p>
275          * This is a convenience method that creates an instance of the {@link DownloadFileRequest} builder avoiding the
276          * need to create one manually via {@link DownloadFileRequest#builder()}.
277          *
278          * @param downloadFileRequestBuilder the download file request builder
279          * @return a reference to this object so that method calls can be chained together.
280          * @see #downloadFileRequest(DownloadFileRequest)
281          */
downloadFileRequest(Consumer<DownloadFileRequest.Builder> downloadFileRequestBuilder)282         default ResumableFileDownload.Builder downloadFileRequest(Consumer<DownloadFileRequest.Builder>
283                                                                       downloadFileRequestBuilder) {
284             DownloadFileRequest request = DownloadFileRequest.builder()
285                                                              .applyMutation(downloadFileRequestBuilder)
286                                                              .build();
287             downloadFileRequest(request);
288             return this;
289         }
290 
291         /**
292          * Sets the number of bytes transferred
293          *
294          * @param bytesTransferred the number of bytes transferred
295          * @return a reference to this object so that method calls can be chained together.
296          */
bytesTransferred(Long bytesTransferred)297         Builder bytesTransferred(Long bytesTransferred);
298 
299         /**
300          * Sets the total transfer size in bytes
301          * @param totalSizeInBytes the transfer size in bytes
302          * @return a reference to this object so that method calls can be chained together.
303          */
totalSizeInBytes(Long totalSizeInBytes)304         Builder totalSizeInBytes(Long totalSizeInBytes);
305 
306         /**
307          * Sets the last modified time of the object
308          *
309          * @param s3ObjectLastModified the last modified time of the object
310          * @return a reference to this object so that method calls can be chained together.
311          */
s3ObjectLastModified(Instant s3ObjectLastModified)312         Builder s3ObjectLastModified(Instant s3ObjectLastModified);
313 
314         /**
315          * Sets the last modified time of the object
316          *
317          * @param lastModified the last modified time of the object
318          * @return a reference to this object so that method calls can be chained together.
319          */
fileLastModified(Instant lastModified)320         Builder fileLastModified(Instant lastModified);
321     }
322 
323     private static final class DefaultBuilder implements Builder {
324 
325         private DownloadFileRequest downloadFileRequest;
326         private Long bytesTransferred;
327         private Instant s3ObjectLastModified;
328         private Long totalSizeInBytes;
329         private Instant fileLastModified;
330 
DefaultBuilder()331         private DefaultBuilder() {
332         }
333 
DefaultBuilder(ResumableFileDownload persistableFileDownload)334         private DefaultBuilder(ResumableFileDownload persistableFileDownload) {
335             this.downloadFileRequest = persistableFileDownload.downloadFileRequest;
336             this.bytesTransferred = persistableFileDownload.bytesTransferred;
337             this.totalSizeInBytes = persistableFileDownload.totalSizeInBytes;
338             this.fileLastModified = persistableFileDownload.fileLastModified;
339             this.s3ObjectLastModified = persistableFileDownload.s3ObjectLastModified;
340         }
341 
342         @Override
downloadFileRequest(DownloadFileRequest downloadFileRequest)343         public Builder downloadFileRequest(DownloadFileRequest downloadFileRequest) {
344             this.downloadFileRequest = downloadFileRequest;
345             return this;
346         }
347 
348         @Override
bytesTransferred(Long bytesTransferred)349         public Builder bytesTransferred(Long bytesTransferred) {
350             this.bytesTransferred = bytesTransferred;
351             return this;
352         }
353 
354         @Override
totalSizeInBytes(Long totalSizeInBytes)355         public Builder totalSizeInBytes(Long totalSizeInBytes) {
356             this.totalSizeInBytes = totalSizeInBytes;
357             return this;
358         }
359 
360         @Override
s3ObjectLastModified(Instant s3ObjectLastModified)361         public Builder s3ObjectLastModified(Instant s3ObjectLastModified) {
362             this.s3ObjectLastModified = s3ObjectLastModified;
363             return this;
364         }
365 
366         @Override
fileLastModified(Instant fileLastModified)367         public Builder fileLastModified(Instant fileLastModified) {
368             this.fileLastModified = fileLastModified;
369             return this;
370         }
371 
372         @Override
build()373         public ResumableFileDownload build() {
374             return new ResumableFileDownload(this);
375         }
376     }
377 }
378