• 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.PutObjectRequest;
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.ResumableFileUploadSerializer;
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  * POJO class that holds the state and can be used to resume a paused upload file 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 PutObjectRequest})</li>
51  * </ul>
52  *
53  * @see S3TransferManager#uploadFile(UploadFileRequest)
54  * @see S3TransferManager#resumeUploadFile(ResumableFileUpload)
55  */
56 @SdkPublicApi
57 public final class ResumableFileUpload implements ResumableTransfer,
58                                                   ToCopyableBuilder<ResumableFileUpload.Builder, ResumableFileUpload> {
59 
60     private final UploadFileRequest uploadFileRequest;
61     private final Instant fileLastModified;
62     private final String multipartUploadId;
63     private final Long partSizeInBytes;
64     private final Long totalParts;
65     private final long fileLength;
66     private final Long transferredParts;
67 
ResumableFileUpload(DefaultBuilder builder)68     private ResumableFileUpload(DefaultBuilder builder) {
69         this.uploadFileRequest = Validate.paramNotNull(builder.uploadFileRequest, "uploadFileRequest");
70         this.fileLastModified = Validate.paramNotNull(builder.fileLastModified, "fileLastModified");
71         this.fileLength = Validate.paramNotNull(builder.fileLength, "fileLength");
72         this.multipartUploadId = builder.multipartUploadId;
73         this.totalParts = builder.totalParts;
74         this.partSizeInBytes = builder.partSizeInBytes;
75         this.transferredParts = builder.transferredParts;
76     }
77 
78     @Override
equals(Object o)79     public boolean equals(Object o) {
80         if (this == o) {
81             return true;
82         }
83         if (o == null || getClass() != o.getClass()) {
84             return false;
85         }
86 
87         ResumableFileUpload that = (ResumableFileUpload) o;
88 
89         if (fileLength != that.fileLength) {
90             return false;
91         }
92         if (!uploadFileRequest.equals(that.uploadFileRequest)) {
93             return false;
94         }
95         if (!fileLastModified.equals(that.fileLastModified)) {
96             return false;
97         }
98         if (!Objects.equals(multipartUploadId, that.multipartUploadId)) {
99             return false;
100         }
101         if (!Objects.equals(partSizeInBytes, that.partSizeInBytes)) {
102             return false;
103         }
104 
105         if (!Objects.equals(transferredParts, that.transferredParts)) {
106             return false;
107         }
108 
109         return Objects.equals(totalParts, that.totalParts);
110     }
111 
112     @Override
hashCode()113     public int hashCode() {
114         int result = uploadFileRequest.hashCode();
115         result = 31 * result + fileLastModified.hashCode();
116         result = 31 * result + (multipartUploadId != null ? multipartUploadId.hashCode() : 0);
117         result = 31 * result + (partSizeInBytes != null ? partSizeInBytes.hashCode() : 0);
118         result = 31 * result + (totalParts != null ? totalParts.hashCode() : 0);
119         result = 31 * result + (transferredParts != null ? transferredParts.hashCode() : 0);
120         result = 31 * result + (int) (fileLength ^ (fileLength >>> 32));
121         return result;
122     }
123 
builder()124     public static Builder builder() {
125         return new DefaultBuilder();
126     }
127 
128     /**
129      * @return the {@link UploadFileRequest} to resume
130      */
uploadFileRequest()131     public UploadFileRequest uploadFileRequest() {
132         return uploadFileRequest;
133     }
134 
135     /**
136      * Last modified time of the file since last pause
137      */
fileLastModified()138     public Instant fileLastModified() {
139         return fileLastModified;
140     }
141 
142     /**
143      * File length since last pause
144      */
fileLength()145     public long fileLength() {
146         return fileLength;
147     }
148 
149     /**
150      * Return the part size in bytes or {@link OptionalLong#empty()} if unknown
151      */
partSizeInBytes()152     public OptionalLong partSizeInBytes() {
153         return partSizeInBytes == null ? OptionalLong.empty() : OptionalLong.of(partSizeInBytes);
154     }
155 
156     /**
157      * Return the total number of parts associated with this transfer or {@link OptionalLong#empty()} if unknown
158      */
totalParts()159     public OptionalLong totalParts() {
160         return totalParts == null ? OptionalLong.empty() : OptionalLong.of(totalParts);
161     }
162 
163     /**
164      * The multipart upload ID, or {@link Optional#empty()} if unknown
165      *
166      * @return the optional total size of the transfer.
167      */
multipartUploadId()168     public Optional<String> multipartUploadId() {
169         return Optional.ofNullable(multipartUploadId);
170     }
171 
172     /**
173      * Return the total number of parts completed with this transfer or {@link OptionalLong#empty()} if unknown
174      */
transferredParts()175     public OptionalLong transferredParts() {
176         return transferredParts == null ? OptionalLong.empty() : OptionalLong.of(transferredParts);
177     }
178 
179     @Override
serializeToFile(Path path)180     public void serializeToFile(Path path) {
181         try {
182             Files.write(path, ResumableFileUploadSerializer.toJson(this));
183         } catch (IOException e) {
184             throw SdkClientException.create("Failed to write to " + path, e);
185         }
186     }
187 
188     @Override
serializeToOutputStream(OutputStream outputStream)189     public void serializeToOutputStream(OutputStream outputStream) {
190         byte[] bytes = ResumableFileUploadSerializer.toJson(this);
191         try {
192             ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
193             IoUtils.copy(byteArrayInputStream, outputStream);
194         } catch (IOException e) {
195             throw SdkClientException.create("Failed to write this download object to the given OutputStream", e);
196         }
197     }
198 
199     @Override
serializeToString()200     public String serializeToString() {
201         return new String(ResumableFileUploadSerializer.toJson(this), StandardCharsets.UTF_8);
202     }
203 
204     @Override
serializeToBytes()205     public SdkBytes serializeToBytes() {
206         return SdkBytes.fromByteArrayUnsafe(ResumableFileUploadSerializer.toJson(this));
207     }
208 
209     @Override
serializeToInputStream()210     public InputStream serializeToInputStream() {
211         return new ByteArrayInputStream(ResumableFileUploadSerializer.toJson(this));
212     }
213 
214     /**
215      * Deserializes data at the given path into a {@link ResumableFileUpload}.
216      *
217      * @param path The {@link Path} to the file with serialized data
218      * @return the deserialized {@link ResumableFileUpload}
219      */
fromFile(Path path)220     public static ResumableFileUpload fromFile(Path path) {
221         try (InputStream stream = Files.newInputStream(path)) {
222             return ResumableFileUploadSerializer.fromJson(stream);
223         } catch (IOException e) {
224             throw SdkClientException.create("Failed to create a ResumableFileUpload from " + path, e);
225         }
226     }
227 
228     /**
229      * Deserializes bytes with JSON data into a {@link ResumableFileUpload}.
230      *
231      * @param bytes the serialized data
232      * @return the deserialized {@link ResumableFileUpload}
233      */
fromBytes(SdkBytes bytes)234     public static ResumableFileUpload fromBytes(SdkBytes bytes) {
235         return ResumableFileUploadSerializer.fromJson(bytes.asByteArrayUnsafe());
236     }
237 
238     /**
239      * Deserializes a string with JSON data into a {@link ResumableFileUpload}.
240      *
241      * @param contents the serialized data
242      * @return the deserialized {@link ResumableFileUpload}
243      */
fromString(String contents)244     public static ResumableFileUpload fromString(String contents) {
245         return ResumableFileUploadSerializer.fromJson(contents);
246     }
247 
248 
249     @Override
toString()250     public String toString() {
251         return ToString.builder("ResumableFileUpload")
252                        .add("fileLastModified", fileLastModified)
253                        .add("multipartUploadId", multipartUploadId)
254                        .add("uploadFileRequest", uploadFileRequest)
255                        .add("fileLength", fileLength)
256                        .add("totalParts", totalParts)
257                        .add("partSizeInBytes", partSizeInBytes)
258                        .add("transferredParts", transferredParts)
259                        .build();
260     }
261 
262     @Override
toBuilder()263     public Builder toBuilder() {
264         return new DefaultBuilder(this);
265     }
266 
267     public interface Builder extends CopyableBuilder<Builder, ResumableFileUpload> {
268 
269         /**
270          * Sets the upload file request
271          *
272          * @param uploadFileRequest the upload file request
273          * @return a reference to this object so that method calls can be chained together.
274          */
uploadFileRequest(UploadFileRequest uploadFileRequest)275         Builder uploadFileRequest(UploadFileRequest uploadFileRequest);
276 
277         /**
278          * The {@link UploadFileRequest} request
279          *
280          * <p>
281          * This is a convenience method that creates an instance of the {@link UploadFileRequest} builder avoiding the
282          * need to create one manually via {@link UploadFileRequest#builder()}.
283          *
284          * @param uploadFileRequestBuilder the upload file request builder
285          * @return a reference to this object so that method calls can be chained together.
286          * @see #uploadFileRequest(UploadFileRequest)
287          */
uploadFileRequest(Consumer<UploadFileRequest.Builder> uploadFileRequestBuilder)288         default ResumableFileUpload.Builder uploadFileRequest(Consumer<UploadFileRequest.Builder>
289                                                                       uploadFileRequestBuilder) {
290             UploadFileRequest request = UploadFileRequest.builder()
291                                                              .applyMutation(uploadFileRequestBuilder)
292                                                              .build();
293             uploadFileRequest(request);
294             return this;
295         }
296 
297         /**
298          * Sets multipart ID associated with this transfer
299          *
300          * @param multipartUploadId the multipart ID
301          * @return a reference to this object so that method calls can be chained together.
302          */
multipartUploadId(String multipartUploadId)303         Builder multipartUploadId(String multipartUploadId);
304 
305         /**
306          * Sets the last modified time of the object
307          *
308          * @param fileLastModified the last modified time of the file
309          * @return a reference to this object so that method calls can be chained together.
310          */
fileLastModified(Instant fileLastModified)311         Builder fileLastModified(Instant fileLastModified);
312 
313         /**
314          * Sets the file length
315          *
316          * @param fileLength the last modified time of the object
317          * @return a reference to this object so that method calls can be chained together.
318          */
fileLength(Long fileLength)319         Builder fileLength(Long fileLength);
320 
321         /**
322          * Sets the total number of parts
323          *
324          * @param totalParts the total number of parts
325          * @return a reference to this object so that method calls can be chained together.
326          */
totalParts(Long totalParts)327         Builder totalParts(Long totalParts);
328 
329         /**
330          * Set the total number of parts transferred
331          *
332          * @param transferredParts the number of parts completed
333          * @return a reference to this object so that method calls can be chained together.
334          */
transferredParts(Long transferredParts)335         Builder transferredParts(Long transferredParts);
336 
337         /**
338          * The part size associated with this transfer
339          * @param partSizeInBytes the part size in bytes
340          * @return a reference to this object so that method calls can be chained together.
341          */
partSizeInBytes(Long partSizeInBytes)342         Builder partSizeInBytes(Long partSizeInBytes);
343     }
344 
345     private static final class DefaultBuilder implements Builder {
346 
347         private String multipartUploadId;
348         private UploadFileRequest uploadFileRequest;
349         private Long partSizeInBytes;
350         private Long totalParts;
351         private Instant fileLastModified;
352         private Long fileLength;
353 
354         private Long transferredParts;
355 
DefaultBuilder()356         private DefaultBuilder() {
357         }
358 
DefaultBuilder(ResumableFileUpload persistableFileUpload)359         private DefaultBuilder(ResumableFileUpload persistableFileUpload) {
360             this.multipartUploadId = persistableFileUpload.multipartUploadId;
361             this.uploadFileRequest = persistableFileUpload.uploadFileRequest;
362             this.partSizeInBytes = persistableFileUpload.partSizeInBytes;
363             this.fileLastModified = persistableFileUpload.fileLastModified;
364             this.totalParts = persistableFileUpload.totalParts;
365             this.fileLength = persistableFileUpload.fileLength;
366             this.transferredParts = persistableFileUpload.transferredParts;
367         }
368 
369         @Override
uploadFileRequest(UploadFileRequest uploadFileRequest)370         public Builder uploadFileRequest(UploadFileRequest uploadFileRequest) {
371             this.uploadFileRequest = uploadFileRequest;
372             return this;
373         }
374 
375         @Override
multipartUploadId(String mutipartUploadId)376         public Builder multipartUploadId(String mutipartUploadId) {
377             this.multipartUploadId = mutipartUploadId;
378             return this;
379         }
380 
381 
382         @Override
fileLastModified(Instant fileLastModified)383         public Builder fileLastModified(Instant fileLastModified) {
384             this.fileLastModified = fileLastModified;
385             return this;
386         }
387 
388         @Override
fileLength(Long fileLength)389         public Builder fileLength(Long fileLength) {
390             this.fileLength = fileLength;
391             return this;
392         }
393 
394         @Override
totalParts(Long totalParts)395         public Builder totalParts(Long totalParts) {
396             this.totalParts = totalParts;
397             return this;
398         }
399 
400         @Override
transferredParts(Long transferredParts)401         public Builder transferredParts(Long transferredParts) {
402             this.transferredParts = transferredParts;
403             return this;
404         }
405 
406         @Override
partSizeInBytes(Long partSizeInBytes)407         public Builder partSizeInBytes(Long partSizeInBytes) {
408             this.partSizeInBytes = partSizeInBytes;
409             return this;
410         }
411 
412         @Override
build()413         public ResumableFileUpload build() {
414             return new ResumableFileUpload(this);
415         }
416     }
417 }
418