S3StorageProvider extends StorageProviderBase { public String addContent(String spaceId, String contentId, String contentMimeType, Map<String, String> userProperties, long contentSize, String contentChecksum, InputStream content) { log.debug("addContent(" + spaceId + ", " + contentId + ", " + contentMimeType + ", " + contentSize + ", " + contentChecksum + ")"); String bucketName = getBucketName(spaceId); ChecksumInputStream wrappedContent = new ChecksumInputStream(content, contentChecksum); String contentEncoding = removeContentEncoding(userProperties); userProperties = removeCalculatedProperties(userProperties); if (contentMimeType == null || contentMimeType.equals("")) { contentMimeType = DEFAULT_MIMETYPE; } ObjectMetadata objMetadata = new ObjectMetadata(); objMetadata.setContentType(contentMimeType); if (contentSize > 0) { objMetadata.setContentLength(contentSize); } if (null != contentChecksum && !contentChecksum.isEmpty()) { String encodedChecksum = ChecksumUtil.convertToBase64Encoding(contentChecksum); objMetadata.setContentMD5(encodedChecksum); } if (contentEncoding != null) { objMetadata.setContentEncoding(contentEncoding); } if (userProperties != null) { for (String key : userProperties.keySet()) { String value = userProperties.get(key); if (log.isDebugEnabled()) { log.debug("[" + key + "|" + value + "]"); } objMetadata.addUserMetadata(getSpaceFree(encodeHeaderKey(key)), encodeHeaderValue(value)); } } PutObjectRequest putRequest = new PutObjectRequest(bucketName, contentId, wrappedContent, objMetadata); putRequest.setStorageClass(DEFAULT_STORAGE_CLASS); putRequest.setCannedAcl(CannedAccessControlList.Private); String etag; try { PutObjectResult putResult = s3Client.putObject(putRequest); etag = putResult.getETag(); } catch (AmazonClientException e) { if (e instanceof AmazonS3Exception) { AmazonS3Exception s3Ex = (AmazonS3Exception) e; String errorCode = s3Ex.getErrorCode(); Integer statusCode = s3Ex.getStatusCode(); String message = MessageFormat.format("exception putting object {0} into {1}: errorCode={2}," + " statusCode={3}, errorMessage={4}", contentId, bucketName, errorCode, statusCode, e.getMessage()); if (errorCode.equals("InvalidDigest") || errorCode.equals("BadDigest")) { log.error(message, e); String err = "Checksum mismatch detected attempting to add " + "content " + contentId + " to S3 bucket " + bucketName + ". Content was not added."; throw new ChecksumMismatchException(err, e, NO_RETRY); } else if (errorCode.equals("IncompleteBody")) { log.error(message, e); throw new StorageException("The content body was incomplete for " + contentId + " to S3 bucket " + bucketName + ". Content was not added.", e, NO_RETRY); } else if (!statusCode.equals(HttpStatus.SC_SERVICE_UNAVAILABLE) && !statusCode.equals(HttpStatus.SC_NOT_FOUND)) { log.error(message, e); } else { log.warn(message, e); } } else { String err = MessageFormat.format("exception putting object {0} into {1}: {2}", contentId, bucketName, e.getMessage()); log.error(err, e); } etag = doesContentExistWithExpectedChecksum(bucketName, contentId, contentChecksum); if (null == etag) { String err = "Could not add content " + contentId + " with type " + contentMimeType + " and size " + contentSize + " to S3 bucket " + bucketName + " due to error: " + e.getMessage(); throw new StorageException(err, e, NO_RETRY); } } String providerChecksum = getETagValue(etag); String checksum = wrappedContent.getMD5(); StorageProviderUtil.compareChecksum(providerChecksum, spaceId, contentId, checksum); return providerChecksum; } S3StorageProvider(String accessKey, String secretKey);  S3StorageProvider(String accessKey,
                             String secretKey,
                             Map<String, String> options);  S3StorageProvider(AmazonS3 s3Client,
                             String accessKey,
                             Map<String, String> options); @Override StorageProviderType getStorageProviderType(); Iterator<String> getSpaces(); Iterator<String> getSpaceContents(String spaceId,
                                             String prefix); List<String> getSpaceContentsChunked(String spaceId,
                                                String prefix,
                                                long maxResults,
                                                String marker); void createSpace(String spaceId); String createHiddenSpace(String spaceId, int expirationInDays); void setSpaceLifecycle(String bucketName,
                                  BucketLifecycleConfiguration config); void removeSpace(String spaceId); String addHiddenContent(String spaceId,
                                   String contentId,
                                   String contentMimeType,
                                   InputStream content); String addContent(String spaceId,
                             String contentId,
                             String contentMimeType,
                             Map<String, String> userProperties,
                             long contentSize,
                             String contentChecksum,
                             InputStream content); @Override String copyContent(String sourceSpaceId,
                              String sourceContentId,
                              String destSpaceId,
                              String destContentId); RetrievedContent getContent(String spaceId, String contentId); RetrievedContent getContent(String spaceId, String contentId, String range); void deleteContent(String spaceId, String contentId); void setContentProperties(String spaceId,
                                     String contentId,
                                     Map<String, String> contentProperties); Map<String, String> getContentProperties(String spaceId,
                                                    String contentId); @Override Map<String, String> getSpaceProperties(String spaceId); String getBucketName(String spaceId); static final String HIDDEN_SPACE_PREFIX; }
@Test public void testAddContent() { Capture<PutObjectRequest> capturedRequest = createS3ClientAddContent(hexChecksum); S3StorageProvider provider = new S3StorageProvider(s3Client, accessKey, new HashMap<String, String>()); String content = "hello"; contentStream = createStream(content); String mimetype = "mimetype"; String userMetaName = "user-metadata-name"; String userMetaValue = "user-metadata-value"; Map<String, String> userMeta = new HashMap<>(); userMeta.put(userMetaName, userMetaValue); String resultChecksum = provider.addContent(spaceId, "contentId", mimetype, userMeta, content.length(), hexChecksum, contentStream); Assert.assertEquals(hexChecksum, resultChecksum); PutObjectRequest request = capturedRequest.getValue(); Assert.assertNotNull(request); ObjectMetadata requestMetadata = request.getMetadata(); Assert.assertEquals(base64Checksum, requestMetadata.getContentMD5()); Assert.assertEquals(mimetype, requestMetadata.getContentType()); Assert.assertEquals(provider.encodeHeaderValue(userMetaValue), requestMetadata.getUserMetadata().get(provider.encodeHeaderKey(userMetaName))); }
@Test public void testAddContentInvalidMimetypeResponse() { String content = "hello"; String contentId = "contentId"; Capture<PutObjectRequest> capturedRequest = createS3ClientAddContentInvalidChecksum(contentId); S3StorageProvider provider = new S3StorageProvider(s3Client, accessKey, new HashMap<String, String>()); contentStream = createStream(content); String mimetype = "mimetype"; Map<String, String> userMeta = new HashMap<>(); try { String resultChecksum = provider.addContent(spaceId, contentId, mimetype, userMeta, content.length(), hexChecksum, contentStream); fail("Checksum mismatch exception expected. Instead checksum '" + resultChecksum + "' was returned"); } catch (ChecksumMismatchException e) { assertNotNull(e); } }