|
26 | 26 | */
|
27 | 27 | package com.emc.object.s3;
|
28 | 28 |
|
29 |
| -import com.emc.object.s3.bean.GetObjectResult; |
30 |
| -import com.emc.object.s3.bean.MultipartPart; |
31 |
| -import com.emc.object.s3.bean.MultipartPartETag; |
32 |
| -import com.emc.object.s3.bean.Upload; |
| 29 | +import com.emc.object.s3.bean.*; |
33 | 30 | import com.emc.object.s3.jersey.S3JerseyClient;
|
34 | 31 | import com.emc.object.s3.lfu.LargeFileMultipartSource;
|
35 | 32 | import com.emc.object.s3.lfu.LargeFileUpload;
|
36 | 33 | import com.emc.object.s3.lfu.LargeFileUploaderResumeContext;
|
37 | 34 | import com.emc.object.s3.lfu.PartMismatchException;
|
38 |
| -import com.emc.object.s3.request.AbortMultipartUploadRequest; |
39 |
| -import com.emc.object.s3.request.GetObjectRequest; |
40 |
| -import com.emc.object.s3.request.ListMultipartUploadsRequest; |
41 |
| -import com.emc.object.s3.request.UploadPartRequest; |
| 35 | +import com.emc.object.s3.request.*; |
42 | 36 | import com.emc.object.util.ProgressListener;
|
43 | 37 | import com.emc.rest.util.StreamUtil;
|
44 | 38 | import com.emc.util.RandomInputStream;
|
|
52 | 46 | import java.security.DigestInputStream;
|
53 | 47 | import java.security.MessageDigest;
|
54 | 48 | import java.util.*;
|
| 49 | +import java.util.concurrent.ThreadPoolExecutor; |
55 | 50 | import java.util.concurrent.TimeUnit;
|
56 | 51 | import java.util.concurrent.TimeoutException;
|
57 | 52 | import java.util.concurrent.atomic.AtomicLong;
|
@@ -168,6 +163,126 @@ public void testLargeFileUploaderProgressListener() throws Exception {
|
168 | 163 | client.deleteObject(getTestBucket(), key);
|
169 | 164 | }
|
170 | 165 |
|
| 166 | + @Test |
| 167 | + public void testLFUCopy() throws Exception { |
| 168 | + // enable versioning on the bucket |
| 169 | + client.setBucketVersioning(getTestBucket(), new VersioningConfiguration().withStatus(VersioningConfiguration.Status.Enabled)); |
| 170 | + |
| 171 | + String srcKey = "lfu-source.bin"; |
| 172 | + String dstKey = "lfu-single-copy.bin"; |
| 173 | + byte[] data = new byte[(int) MockMultipartSource.totalSize]; |
| 174 | + new Random().nextBytes(data); |
| 175 | + String srcMD5Sum = DigestUtils.md5Hex(data); |
| 176 | + client.putObject(getTestBucket(), srcKey, data, null); |
| 177 | + String versionId0 = client.listVersions(getTestBucket(), null).getVersions().get(0).getVersionId(); |
| 178 | + S3ObjectMetadata sourceMetadata0 = client.getObjectMetadata(new GetObjectMetadataRequest(getTestBucket(), srcKey).withVersionId(versionId0)); |
| 179 | + Assert.assertEquals(MockMultipartSource.totalSize, sourceMetadata0.getContentLength().longValue()); |
| 180 | + Assert.assertEquals(srcMD5Sum, sourceMetadata0.getETag()); |
| 181 | + |
| 182 | + //Upload new Version Object |
| 183 | + new Random().nextBytes(data); |
| 184 | + String srcMD5SumNew = DigestUtils.md5Hex(data); |
| 185 | + client.putObject(getTestBucket(), srcKey, data, null); |
| 186 | + String versionId1 = client.listVersions(getTestBucket(), null).getVersions().get(0).getVersionId(); |
| 187 | + S3ObjectMetadata sourceMetadata1 = client.getObjectMetadata(new GetObjectMetadataRequest(getTestBucket(), srcKey).withVersionId(versionId1)); |
| 188 | + Assert.assertEquals(MockMultipartSource.totalSize, sourceMetadata1.getContentLength().longValue()); |
| 189 | + Assert.assertEquals(srcMD5SumNew, sourceMetadata1.getETag()); |
| 190 | + |
| 191 | + //Single Copy from old version object |
| 192 | + S3ObjectMetadata objectMetadata = new S3ObjectMetadata().addUserMetadata("key", "value"); |
| 193 | + LargeFileUploader uploader = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 194 | + .withObjectMetadata(objectMetadata) |
| 195 | + .withSourceVersionId(versionId0); |
| 196 | + uploader.upload(); |
| 197 | + Assert.assertEquals(0, uploader.getBytesTransferred()); |
| 198 | + Assert.assertEquals(srcMD5Sum, uploader.getETag()); |
| 199 | + Assert.assertEquals(MockMultipartSource.totalSize, client.getObjectMetadata(getTestBucket(), dstKey).getContentLength().longValue()); |
| 200 | + Assert.assertEquals(objectMetadata.getUserMetadata(), client.getObjectMetadata(getTestBucket(), dstKey).getUserMetadata()); |
| 201 | + |
| 202 | + //Single Copy from new version object (without providing version Id) |
| 203 | + uploader = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 204 | + .withObjectMetadata(objectMetadata); |
| 205 | + uploader.upload(); |
| 206 | + Assert.assertEquals(0, uploader.getBytesTransferred()); |
| 207 | + Assert.assertEquals(srcMD5SumNew, uploader.getETag()); |
| 208 | + Assert.assertEquals(MockMultipartSource.totalSize, client.getObjectMetadata(getTestBucket(), dstKey).getContentLength().longValue()); |
| 209 | + Assert.assertEquals(objectMetadata.getUserMetadata(), client.getObjectMetadata(getTestBucket(), dstKey).getUserMetadata()); |
| 210 | + |
| 211 | + //Multi Part Copy from old version object |
| 212 | + uploader = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 213 | + .withObjectMetadata(objectMetadata) |
| 214 | + .withPartSize(MockMultipartSource.partSize) |
| 215 | + .withMpuThreshold(MockMultipartSource.partSize) |
| 216 | + .withSourceVersionId(versionId0); |
| 217 | + uploader.upload(); |
| 218 | + Assert.assertEquals(0, uploader.getBytesTransferred()); |
| 219 | + Assert.assertTrue(uploader.getETag().contains("-")); // hyphen signifies multipart / updated object |
| 220 | + Assert.assertEquals(MockMultipartSource.totalSize, client.getObjectMetadata(getTestBucket(), dstKey).getContentLength().longValue()); |
| 221 | + Assert.assertEquals(objectMetadata.getUserMetadata(), client.getObjectMetadata(getTestBucket(), dstKey).getUserMetadata()); |
| 222 | + Assert.assertEquals(srcMD5Sum, DigestUtils.md5Hex(client.getObject(getTestBucket(), dstKey).getObject())); |
| 223 | + |
| 224 | + //Multi Part Copy from new version object |
| 225 | + uploader = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 226 | + .withObjectMetadata(objectMetadata) |
| 227 | + .withPartSize(MockMultipartSource.partSize) |
| 228 | + .withMpuThreshold(MockMultipartSource.partSize) |
| 229 | + .withSourceVersionId(versionId1); |
| 230 | + uploader.upload(); |
| 231 | + Assert.assertEquals(0, uploader.getBytesTransferred()); |
| 232 | + Assert.assertTrue(uploader.getETag().contains("-")); // hyphen signifies multipart / updated object |
| 233 | + Assert.assertEquals(MockMultipartSource.totalSize, client.getObjectMetadata(getTestBucket(), dstKey).getContentLength().longValue()); |
| 234 | + Assert.assertEquals(objectMetadata.getUserMetadata(), client.getObjectMetadata(getTestBucket(), dstKey).getUserMetadata()); |
| 235 | + Assert.assertEquals(srcMD5SumNew, DigestUtils.md5Hex(client.getObject(getTestBucket(), dstKey).getObject())); |
| 236 | + } |
| 237 | + |
| 238 | + @Test |
| 239 | + public void testLFUCopyPauseResume() throws Exception { |
| 240 | + String srcKey = "mpu-copy-pause.source"; |
| 241 | + String dstKey = "mpu-copy-pause.target"; |
| 242 | + |
| 243 | + MockMultipartSource mockMultipartSource = new MockMultipartSource(); |
| 244 | + LargeFileUploader lfu = new TestLargeFileUploader(client, getTestBucket(), srcKey, mockMultipartSource) |
| 245 | + .withPartSize(mockMultipartSource.getPartSize()).withMpuThreshold((int) mockMultipartSource.getTotalSize()); |
| 246 | + lfu.doMultipartUpload(); |
| 247 | + |
| 248 | + lfu = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 249 | + .withPartSize(mockMultipartSource.getPartSize()).withMpuThreshold((int) mockMultipartSource.getTotalSize()) |
| 250 | + .withThreads(1); |
| 251 | + LargeFileUpload upload = lfu.uploadAsync(); |
| 252 | + |
| 253 | + // wait for first a few parts to start |
| 254 | + while (lfu.getExecutorService() == null || ((ThreadPoolExecutor) lfu.getExecutorService()).getCompletedTaskCount() == 0) { |
| 255 | + Thread.sleep(100); |
| 256 | + } |
| 257 | + LargeFileUploaderResumeContext resumeContext = upload.pause(); |
| 258 | + |
| 259 | + // object should not exist |
| 260 | + try { |
| 261 | + Assert.assertNull(client.getObjectMetadata(getTestBucket(), dstKey)); |
| 262 | + } catch (S3Exception e) { |
| 263 | + Assert.assertEquals(404, e.getHttpCode()); |
| 264 | + Assert.assertEquals("NoSuchKey", e.getErrorCode()); |
| 265 | + } |
| 266 | + |
| 267 | + // check resume context accuracy |
| 268 | + Assert.assertNotNull(resumeContext.getUploadId()); |
| 269 | + Assert.assertNotNull(resumeContext.getUploadedParts()); |
| 270 | + Assert.assertFalse(resumeContext.getUploadedParts().isEmpty()); |
| 271 | + List<MultipartPart> parts = client.listParts(getTestBucket(), dstKey, resumeContext.getUploadId()).getParts(); |
| 272 | + Assert.assertNotNull(parts); |
| 273 | + |
| 274 | + lfu = new TestLargeFileUploader(client, getTestBucket(), srcKey, getTestBucket(), dstKey) |
| 275 | + .withPartSize(mockMultipartSource.getPartSize()).withMpuThreshold((int) mockMultipartSource.getTotalSize()) |
| 276 | + .withResumeContext(resumeContext); |
| 277 | + lfu.doMultipartUpload(); |
| 278 | + |
| 279 | + // Unfortunately, MPU ETag is not preserved even if copy parts matches the original multipart upload. |
| 280 | + // So the content verification is done by calculating md5sum instead of checking ETag. |
| 281 | + //Assert.assertEquals(mockMultipartSource.getMpuETag(), client.getObjectMetadata(getTestBucket(), dstKey).getETag()); |
| 282 | + Assert.assertEquals(DigestUtils.md5Hex(client.getObject(getTestBucket(), srcKey).getObject()), DigestUtils.md5Hex(client.getObject(getTestBucket(), dstKey).getObject())); |
| 283 | + Assert.assertEquals(mockMultipartSource.getTotalSize(), client.getObjectMetadata(getTestBucket(), dstKey).getContentLength().longValue()); |
| 284 | + } |
| 285 | + |
171 | 286 | @Test
|
172 | 287 | public void testLargeFileUploaderStream() {
|
173 | 288 | String key = "large-file-uploader-stream.bin";
|
|
0 commit comments