001/** 002 * Copyright 2005-2017 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kew.notes.service.impl; 017 018import java.io.File; 019import java.net.URLEncoder; 020import java.util.UUID; 021 022import org.apache.commons.lang3.StringUtils; 023import org.kuali.rice.kew.notes.Attachment; 024import org.kuali.rice.kew.notes.service.AttachmentService; 025import org.springframework.beans.factory.InitializingBean; 026import org.springframework.beans.factory.annotation.Autowired; 027import org.springframework.beans.factory.annotation.Required; 028import org.springframework.core.io.Resource; 029import org.springframework.core.io.ResourceLoader; 030 031import com.amazonaws.services.s3.AmazonS3; 032import com.amazonaws.services.s3.model.DeleteObjectRequest; 033import com.amazonaws.services.s3.model.ObjectMetadata; 034import com.amazonaws.services.s3.transfer.TransferManager; 035import com.amazonaws.services.s3.transfer.Upload; 036 037/** 038 * An {@link AttachmentService} implementation which utilizes AWS Simple Storage Service. 039 * 040 * <p>Must inject a ResourceLoader that is capable of resolving "s3://" urls as well as the name of the bucket this 041 * service should use. Also an instance of the AmazonS3 client api should be injected. 042 * 043 * The generated id of the attachment will be used as the object key is S3, so the bucket should exist only for storing 044 * the attachments managed by this service. 045 * 046 */ 047public class AmazonS3AttachmentServiceImpl implements AttachmentService, InitializingBean { 048 049 private ResourceLoader resourceLoader; 050 private String bucketName; 051 private String folderName; 052 private AmazonS3 amazonS3; 053 054 @Override 055 public void afterPropertiesSet() { 056 if (StringUtils.isBlank(folderName)) { 057 throw new IllegalStateException("S3 attachment service must be configured with a non-blank folder name"); 058 } 059 if (StringUtils.isBlank(bucketName)) { 060 throw new IllegalStateException("S3 attachment service must be configured with a non-blank bucket name"); 061 } 062 } 063 064 @Override 065 public void persistAttachedFileAndSetAttachmentBusinessObjectValue(Attachment attachment) throws Exception { 066 if (attachment.getFileLoc() == null) { 067 String s3Url = generateS3Url(attachment); 068 attachment.setFileLoc(s3Url); 069 } 070 TransferManager manager = new TransferManager(this.amazonS3); 071 ObjectMetadata metadata = new ObjectMetadata(); 072 if (attachment.getMimeType() != null) { 073 metadata.setContentType(attachment.getMimeType()); 074 } 075 if (attachment.getFileName() != null) { 076 metadata.setContentDisposition("attachment; filename=" + URLEncoder.encode(attachment.getFileName(), "UTF-8")); 077 } 078 Upload upload = manager.upload(this.bucketName, parseObjectKey(attachment.getFileLoc()), attachment.getAttachedObject(), metadata); 079 upload.waitForCompletion(); 080 } 081 082 @Override 083 public File findAttachedFile(Attachment attachment) throws Exception { 084 throw new UnsupportedOperationException("S3 Attachment Service implementation cannot provide a file, please you \"findAttachedResource\" instead."); 085 } 086 087 @Override 088 public Resource findAttachedResource(Attachment attachment) { 089 if (attachment == null) { 090 throw new IllegalArgumentException("Given attachment was null"); 091 } 092 if (StringUtils.isBlank(attachment.getFileLoc())) { 093 throw new IllegalArgumentException("Given attachment has an empty file location"); 094 } 095 return this.resourceLoader.getResource(attachment.getFileLoc()); 096 } 097 098 @Override 099 public void deleteAttachedFile(Attachment attachment) throws Exception { 100 amazonS3.deleteObject(new DeleteObjectRequest(this.bucketName, parseObjectKey(attachment.getFileLoc()))); 101 } 102 103 private String generateS3Url(Attachment attachment) { 104 return generateS3Prefix() + folderName + "/" + UUID.randomUUID(); 105 } 106 107 private String generateS3Prefix() { 108 return "s3://" + this.bucketName + "/"; 109 } 110 111 private String parseObjectKey(String s3Url) { 112 String prefix = generateS3Prefix(); 113 String objectKey = s3Url.substring(prefix.length()); 114 return objectKey; 115 } 116 117 @Required 118 @Autowired 119 public void setResourceLoader(ResourceLoader resourceLoader) { 120 this.resourceLoader = resourceLoader; 121 } 122 123 @Required 124 public void setFolderName(String folderName) { 125 this.folderName = folderName; 126 } 127 128 @Required 129 public void setBucketName(String bucketName) { 130 this.bucketName = bucketName; 131 } 132 133 @Required 134 @Autowired 135 public void setAmazonS3(AmazonS3 amazonS3) { 136 this.amazonS3 = amazonS3; 137 } 138 139}