From 9c1f9cd912ea58b42f07da23c963c50292c05a06 Mon Sep 17 00:00:00 2001 From: Harshac Date: Thu, 25 Apr 2013 18:31:38 +0530 Subject: [PATCH 1/2] Adding ability to upload files on s3. To enable file upload on S3 you need to enable the configuration setting in c_configuration table for s3. The S3 credentials (access_key, secret_key, bucket_name) in a m_external_services table. m_client table is normalised into m_client and m_image table. The relation between m_client and m_image is one-to-one for now. To persist the storage type(s3 or filesystem) a column had been added to m_document. MISOFX-69 Adding db migrations for s3 support MIFOSX-69 Adding method to return boolean value of S3 config MIFOSX-69 - Priti/Harsha - Refactoring Code, extracted interface for Documents upload MIFOSX-69 Refactored to add S3 Support for file upload. Modified saveDocs to throw Document Management Exception instead of IO exception MIFOSX-69 Adding S3 support for file upload MIFOSX-69 - Adding S3 support for Image upload MIFOSX-69 Adding factory to get instance of data store as per s3 configuration MIFOSX-69 Changing table s3_details to generic tabe for external services, adding column to store the storage type in m_documents table MIFOSX-69 Fetching S3 details from database MIFOSX-69 Adding document storage type while creating a document MIFOSX-69 Normalising client table to create an image table Refactoring ClientImage code to save client image in m_image table Refactored code to get document as file MIFOSX-69 Retreiving documents from S3 MIFOSX-69 Refactored ClientData to store ImageData MIFOSX-69 Retrieving images from S3, Modifying Base64 to encode from byte array instead of file in CLientImagesApiResource MIFOSX-69 Refactored code to delete client image MIFOSX-69 Deleting Images from S3 Refactored DocumentStore to return StoreType from string value MIFOSX-69 Refactored to move document deletion responsibility to storageType MIFOSX-69 Giving more appropriate name to getInstance methods of DocumentStoreFactory as per the instance creation criteria MIFOSX-69 Deleting Documents from S3 MIFOSX-69 Removing unnecessary s3_param from migration MIFOSX-69 Adding ability to update document and update doc store type MIFOSX-69 Renaming method to follow java conventions MIFOSX-69 Adding filename to error log for s3 file retrieval Adding data migrations and changing the version for migration file --- .../core_db/V40__adding_S3_Support.sql | 28 +++ .../domain/ConfigurationDomainService.java | 1 + .../domain/ConfigurationDomainServiceJpa.java | 5 + .../domain/ExternalServiceProperty.java | 37 +++ .../domain/ExternalServiceRepository.java | 10 + .../domain/ExternalServicesDomainService.java | 18 ++ .../core/service/DocumentStore.java | 52 ++++ .../core/service/DocumentStoreFactory.java | 45 ++++ .../core/service/DocumentStoreType.java | 15 ++ .../core/service/FileSystemDocumentStore.java | 160 ++++++++++++ .../core/service/FileUtils.java | 234 ------------------ .../core/service/ImageUtils.java | 105 ++++++++ .../core/service/RandomStringGenerator.java | 33 +++ .../core/service/S3ClientFactory.java | 24 ++ .../core/service/S3DocumentStore.java | 201 +++++++++++++++ .../service/ReadReportingServiceImpl.java | 6 +- .../api/DocumentManagementApiResource.java | 59 ++--- .../command/DocumentCommand.java | 10 + .../documentmanagement/data/DocumentData.java | 24 +- .../documentmanagement/data/FileData.java | 56 +++++ .../documentmanagement/domain/Document.java | 28 ++- .../service/DocumentReadPlatformService.java | 8 +- .../DocumentReadPlatformServiceImpl.java | 57 +++-- ...WritePlatformServiceJpaRepositoryImpl.java | 65 ++--- .../client/api/ClientImagesApiResource.java | 78 ++---- .../portfolio/client/data/ClientData.java | 55 ++-- .../portfolio/client/data/ImageData.java | 78 ++++++ .../portfolio/client/domain/Client.java | 39 +-- .../portfolio/client/domain/Image.java | 62 +++++ .../client/domain/ImageRepository.java | 13 + .../service/ClientReadPlatformService.java | 1 + .../ClientReadPlatformServiceImpl.java | 43 +++- .../service/ClientWritePlatformService.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 92 +++---- .../ConfigurationDomainServiceJpaTest.java | 46 ++++ .../service/DocumentStoreFactoryTest.java | 55 ++++ .../core/service/FileDocumentStoreTest.java | 31 +++ .../core/service/S3DocumentStoreTest.java | 135 ++++++++++ 38 files changed, 1517 insertions(+), 494 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V40__adding_S3_Support.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileUtils.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java create mode 100644 mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpaTest.java create mode 100644 mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java create mode 100644 mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java create mode 100644 mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java diff --git a/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql b/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql new file mode 100644 index 000000000..ff1e63dec --- /dev/null +++ b/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql @@ -0,0 +1,28 @@ +INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0); + + +CREATE TABLE IF NOT EXISTS m_external_services ( + name VARCHAR(150) NOT NULL, + value VARCHAR(250), + UNIQUE(name) +); + +INSERT INTO m_external_services (name) VALUES ('s3_bucket_name'); +INSERT INTO m_external_services (name) VALUES ('s3_access_key'); +INSERT INTO m_external_services (name) VALUES ('s3_secret_key'); + +ALTER TABLE m_document ADD COLUMN storage_type varchar(50); + +CREATE TABLE IF NOT EXISTS `m_image`( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `client_id` BIGINT NOT NULL, + `key` varchar(500), + `storage_type` varchar(50), + FOREIGN KEY (`client_id`) REFERENCES m_client(`id`), + PRIMARY KEY (`id`), + UNIQUE(client_id) +); + +UPDATE m_document set storage_type='file_system'; + +insert into m_image (`client_id`,`key`,`storage_type`) select id,`image_key`,'file_system' from m_client where `image_key` IS NOT NULL; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java index 1c63e5f35..81c4c9b17 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -8,4 +8,5 @@ package org.mifosplatform.infrastructure.configuration.domain; public interface ConfigurationDomainService { boolean isMakerCheckerEnabledForTask(String taskPermissionCode); + boolean isAmazonS3Enabled(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 110c66b42..32107eca2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -39,4 +39,9 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService return thisTask.hasMakerCheckerEnabled() && property.isEnabled(); } + + @Override + public boolean isAmazonS3Enabled(){ + return this.globalConfigurationRepository.findOneByName("amazon-S3").isEnabled(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java new file mode 100644 index 000000000..e9c7ef86f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java @@ -0,0 +1,37 @@ +package org.mifosplatform.infrastructure.configuration.domain; +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import org.springframework.data.jpa.domain.AbstractPersistable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +@Entity +@Table(name = "m_external_services", uniqueConstraints = { @UniqueConstraint(columnNames = ("name"))}) +public class ExternalServiceProperty extends AbstractPersistable { + + @SuppressWarnings("unused") + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "value", nullable = false) + private String value; + + public ExternalServiceProperty(final String name, final String value) { + this.name = name; + this.value = value; + } + + public ExternalServiceProperty() { + } + + public String getValue(){ + return this.value; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java new file mode 100644 index 000000000..8acc015d6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java @@ -0,0 +1,10 @@ +package org.mifosplatform.infrastructure.configuration.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ExternalServiceRepository extends JpaRepository, + JpaSpecificationExecutor { + + ExternalServiceProperty findOneByName(String name); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java new file mode 100644 index 000000000..e562c9ee0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java @@ -0,0 +1,18 @@ +package org.mifosplatform.infrastructure.configuration.domain; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ExternalServicesDomainService { + private ExternalServiceRepository externalServiceRepository; + + @Autowired + public ExternalServicesDomainService(final ExternalServiceRepository externalServiceRepository) { + this.externalServiceRepository = externalServiceRepository; + } + + public String getValue(String name){ + return externalServiceRepository.findOneByName(name).getValue(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java new file mode 100644 index 000000000..0722fd042 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java @@ -0,0 +1,52 @@ +package org.mifosplatform.infrastructure.core.service; + +import org.mifosplatform.infrastructure.core.api.ApiConstants; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; +import org.mifosplatform.portfolio.client.data.ImageData; + +import java.io.InputStream; + +public abstract class DocumentStore { + protected DocumentStoreType type; + + protected Integer maxFileSize = ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB; + protected Integer maxImageSize = ApiConstants.MAX_IMAGE_UPLOAD_SIZE_IN_MB; + + public abstract String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException; + public abstract void deleteDocument(String documentName, String documentPath) throws DocumentManagementException; + public abstract FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException; + + public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException; + public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException; + public abstract void deleteImage(final Long resourceId, final String location); + public abstract ImageData retrieveImage(ImageData imageData); + + public abstract DocumentStoreType getType(); + + protected void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) { + /** + * Using Content-Length gives me size of the entire request, which is + * good enough for now for a fast fail as the length of the rest of the + * content i.e name and description while compared to the uploaded file + * size is negligible + **/ + if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) { + throw new DocumentManagementException(name, fileSize, + maxFileSize); + } + } + + public static DocumentStoreType getDocumentStoreType(String documentStoreType){ + if (documentStoreType.equals(DocumentStoreType.FILE_SYSTEM.getValue())) { + return DocumentStoreType.FILE_SYSTEM; + } else { + return DocumentStoreType.S3; + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java new file mode 100644 index 000000000..32b4b05bd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java @@ -0,0 +1,45 @@ +package org.mifosplatform.infrastructure.core.service; + +import com.amazonaws.services.s3.AmazonS3Client; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +@Component +public class DocumentStoreFactory { + + private ApplicationContext applicationContext; + + @Autowired + public DocumentStoreFactory(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + public DocumentStore getInstanceFromConfiguration(){ + ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class); + if(configurationDomainServiceJpa.isAmazonS3Enabled()){ + return createS3DocumentStore(); + } + else { + return new FileSystemDocumentStore(); + } + } + + public DocumentStore getInstanceFromStorageType(DocumentStoreType documentStoreType){ + if(documentStoreType == DocumentStoreType.FILE_SYSTEM){ + return new FileSystemDocumentStore(); + } + else { + return createS3DocumentStore(); + } + } + private DocumentStore createS3DocumentStore() { + AmazonS3Client s3Client = applicationContext.getBean("s3ClientFactory", S3ClientFactory.class).getS3Client(); + ExternalServicesDomainService externalServicesDomainService = applicationContext.getBean("externalServicesDomainService", ExternalServicesDomainService.class); + return new S3DocumentStore(externalServicesDomainService.getValue("s3_bucket_name"), s3Client); + } + + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java new file mode 100644 index 000000000..ac6a34c01 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java @@ -0,0 +1,15 @@ +package org.mifosplatform.infrastructure.core.service; + +public enum DocumentStoreType { + FILE_SYSTEM("file_system"), S3("s3"); + + private String value; + + DocumentStoreType(String value){ + this.value = value; + } + + public String getValue() { + return value; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java new file mode 100644 index 000000000..6e1a51c9f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java @@ -0,0 +1,160 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.core.service; + +import com.lowagie.text.pdf.codec.Base64; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; +import org.mifosplatform.portfolio.client.data.ImageData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; + +public class FileSystemDocumentStore extends DocumentStore { + + private final static Logger logger = LoggerFactory.getLogger(FileSystemDocumentStore.class); + + public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx"; + + + @Override + public String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException { + String documentName = documentCommand.getFileName(); + String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId()); + + validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize); + makeDirectories(uploadDocumentLocation); + + String fileLocation = uploadDocumentLocation + File.separator + documentName; + + writeFileToFileSystem(uploadedInputStream, fileLocation); + return fileLocation; + } + + + @Override + public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException { + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + + validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize); + makeDirectories(uploadImageLocation); + + String fileLocation = uploadImageLocation + File.separator + imageName; + + writeFileToFileSystem(uploadedInputStream, fileLocation); + return fileLocation; + } + + @Override + public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException { + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + + makeDirectories(uploadImageLocation); + + String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension(); + try { + OutputStream out = new FileOutputStream(new File(fileLocation)); + byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString()); + out.write(imgBytes); + out.flush(); + out.close(); + }catch (IOException ioe){ + throw new DocumentManagementException(ioe.getMessage()); + } + return fileLocation; + } + + @Override + public void deleteImage(final Long resourceId, final String location) { + final boolean fileDeleted = deleteFile(location); + if (!fileDeleted) { + // no need to throw an Error, simply log a warning + logger.warn("Unable to delete image associated with clients with Id " + resourceId); + } + } + + @Override + public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException{ + final boolean fileDeleted = deleteFile(documentPath); + if (!fileDeleted) { throw new DocumentManagementException(documentName); } + } + + private boolean deleteFile(String documentPath) { + final File fileToBeDeleted = new File(documentPath); + return fileToBeDeleted.delete(); + } + + @Override + public DocumentStoreType getType() { + return DocumentStoreType.FILE_SYSTEM; + } + + @Override + public FileData retrieveDocument(DocumentData documentData){ + File file = new File(documentData.fileLocation()); + return new FileData(file, documentData.fileName(), documentData.contentType()); + } + + @Override + public ImageData retrieveImage(ImageData imageData) { + File file = new File(imageData.imageKey()); + imageData.setContent(file); + return imageData; + } + + /** + * Generate the directory path for storing the new document + * + * @param entityType + * @param entityId + * @return + */ + private String generateFileParentDirectory(String entityType, Long entityId) { + return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + + File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator + + RandomStringGenerator.generateRandomString(); + } + + /** + * Generate directory path for storing new Image + */ + private String generateClientImageParentDirectory(final Long resourceId) { + return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + + File.separator + "images" + File.separator + "clients" + File.separator + resourceId; + } + + /** + * Recursively create the directory if it does not exist * + */ + private void makeDirectories(String uploadDocumentLocation) { + if (!new File(uploadDocumentLocation).isDirectory()) { + new File(uploadDocumentLocation).mkdirs(); + } + } + + + private void writeFileToFileSystem(InputStream uploadedInputStream, String fileLocation) throws DocumentManagementException { + + try { + OutputStream out = new FileOutputStream(new File(fileLocation)); + int read = 0; + byte[] bytes = new byte[1024]; + + while ((read = uploadedInputStream.read(bytes)) != -1) { + out.write(bytes, 0, read); + } + out.flush(); + out.close(); + } catch (IOException ioException) { + throw new DocumentManagementException("IO Exception caught while writing to FileSystem" + ioException.getMessage()); + } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileUtils.java deleted file mode 100644 index 5f464a226..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileUtils.java +++ /dev/null @@ -1,234 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.infrastructure.core.service; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import org.apache.commons.lang.StringUtils; -import org.mifosplatform.infrastructure.core.data.ApiParameterError; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; -import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException; -import org.mifosplatform.infrastructure.core.exception.ImageUploadException; -import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.lowagie.text.pdf.codec.Base64; - -public class FileUtils { - - private final static Logger logger = LoggerFactory.getLogger(FileUtils.class); - - public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx"; - - public static enum IMAGE_MIME_TYPE { - GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png"); - - private final String value; - - private IMAGE_MIME_TYPE(final String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - }; - - public static enum IMAGE_FILE_EXTENSION { - GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png"); - - private final String value; - - private IMAGE_FILE_EXTENSION(final String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - }; - - public static enum IMAGE_DATA_URI_SUFFIX { - GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG( - "data:" + IMAGE_MIME_TYPE.PNG.getValue() + ";base64,"); - - private final String value; - - private IMAGE_DATA_URI_SUFFIX(final String value) { - this.value = value; - } - - public String getValue() { - return this.value; - } - }; - - public static Random random = new Random(); - - /** - * Generate a random String - * - * @return - */ - public static String generateRandomString() { - String characters = "abcdefghijklmnopqrstuvwxyz123456789"; - int length = generateRandomNumber(); - char[] text = new char[length]; - for (int i = 0; i < length; i++) { - text[i] = characters.charAt(random.nextInt(characters.length())); - } - return new String(text); - } - - /** - * Generate a random number between 5 to 16 - * - * @return - */ - public static int generateRandomNumber() { - Random randomGenerator = new Random(); - return randomGenerator.nextInt(11) + 5; - } - - /** - * Generate the directory path for storing the new document - * - * @param entityType - * @param entityId - * @return - */ - public static String generateFileParentDirectory(String entityType, Long entityId) { - return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() - + File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator - + FileUtils.generateRandomString(); - } - - /** - * Generate directory path for storing new Image - */ - public static String generateClientImageParentDirectory(final Long resourceId) { - return FileUtils.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() - + File.separator + "images" + File.separator + "clients" + File.separator + resourceId; - } - - /** - * @param uploadedInputStream - * @param uploadedFileLocation - * @return - * @throws IOException - */ - public static String saveToFileSystem(InputStream uploadedInputStream, String uploadedFileLocation, String fileName) throws IOException { - String fileLocation = uploadedFileLocation + File.separator + fileName; - OutputStream out = new FileOutputStream(new File(fileLocation)); - int read = 0; - byte[] bytes = new byte[1024]; - - while ((read = uploadedInputStream.read(bytes)) != -1) { - out.write(bytes, 0, read); - } - out.flush(); - out.close(); - return fileLocation; - } - - /** - * @param base64EncodedImage - * @param uploadedFileLocation - * @param fileName - * @return - * @throws IOException - */ - public static String saveToFileSystem(Base64EncodedImage base64EncodedImage, String uploadedFileLocation, String fileName) - throws IOException { - String fileLocation = uploadedFileLocation + File.separator + fileName + base64EncodedImage.getFileExtension(); - OutputStream out = new FileOutputStream(new File(fileLocation)); - byte[] imgBytes = Base64.decode(base64EncodedImage.getBase64EncodedString()); - out.write(imgBytes); - out.flush(); - out.close(); - return fileLocation; - } - - /** - * @param fileSize - * @param name - */ - public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) { - /** - * Using Content-Length gives me size of the entire request, which is - * good enough for now for a fast fail as the length of the rest of the - * content i.e name and description while compared to the uploaded file - * size is negligible - **/ - if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) { throw new DocumentManagementException(name, fileSize, - maxFileSize); } - } - - /** - * Validates that passed in Mime type maps to known image mime types - * - * @param mimeType - */ - public static void validateImageMimeType(String mimeType) { - if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType - .equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { throw new ImageUploadException(); } - } - - /** - * Extracts Image from a Data URL - * - * @param mimeType - */ - public static Base64EncodedImage extractImageFromDataURL(String dataURL) { - String fileExtension = ""; - String base64EncodedString = null; - if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.GIF.getValue())) { - base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.GIF.getValue(), ""); - fileExtension = IMAGE_FILE_EXTENSION.GIF.getValue(); - } else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.PNG.getValue())) { - base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.PNG.getValue(), ""); - fileExtension = IMAGE_FILE_EXTENSION.PNG.getValue(); - } else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.JPEG.getValue())) { - base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.JPEG.getValue(), ""); - fileExtension = IMAGE_FILE_EXTENSION.JPEG.getValue(); - } else { - throw new ImageDataURLNotValidException(); - } - - return new Base64EncodedImage(base64EncodedString, fileExtension); - } - - public static void validateClientImageNotEmpty(String imageFileName) { - List dataValidationErrors = new ArrayList(); - if (imageFileName == null) { - StringBuilder validationErrorCode = new StringBuilder("validation.msg.clientImage.cannot.be.blank"); - StringBuilder defaultEnglishMessage = new StringBuilder("The parameter image cannot be blank."); - ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), - "image"); - dataValidationErrors.add(error); - throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", - dataValidationErrors); - } - } - - public static void deleteClientImage(final Long resourceId, final String location) { - File fileToBeDeleted = new File(location); - boolean fileDeleted = fileToBeDeleted.delete(); - if (!fileDeleted) { - // no need to throw an Error, simply log a warning - logger.warn("Unable to delete image associated with clients with Id " + resourceId); - } - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java new file mode 100644 index 000000000..5c55495f9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java @@ -0,0 +1,105 @@ +package org.mifosplatform.infrastructure.core.service; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException; +import org.mifosplatform.infrastructure.core.exception.ImageUploadException; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +import java.util.ArrayList; +import java.util.List; + +public class ImageUtils { + + public static enum IMAGE_MIME_TYPE { + GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png"); + + private final String value; + + private IMAGE_MIME_TYPE(final String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + + public static enum IMAGE_FILE_EXTENSION { + GIF(".gif"), JPEG(".jpeg"), JPG(".jpg"), PNG(".png"); + + private final String value; + + private IMAGE_FILE_EXTENSION(final String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + + public static enum IMAGE_DATA_URI_SUFFIX { + GIF("data:" + IMAGE_MIME_TYPE.GIF.getValue() + ";base64,"), JPEG("data:" + IMAGE_MIME_TYPE.JPEG.getValue() + ";base64,"), PNG( + "data:" + IMAGE_MIME_TYPE.PNG.getValue() + ";base64,"); + + private final String value; + + private IMAGE_DATA_URI_SUFFIX(final String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + /** + * Validates that passed in Mime type maps to known image mime types + * + * @param mimeType + */ + public static void validateImageMimeType(String mimeType) { + if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType + .equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { + throw new ImageUploadException(); + } + } + + /** + * Extracts Image from a Data URL + * + * @param mimeType + */ + public static Base64EncodedImage extractImageFromDataURL(String dataURL) { + String fileExtension = ""; + String base64EncodedString = null; + if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.GIF.getValue())) { + base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.GIF.getValue(), ""); + fileExtension = IMAGE_FILE_EXTENSION.GIF.getValue(); + } else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.PNG.getValue())) { + base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.PNG.getValue(), ""); + fileExtension = IMAGE_FILE_EXTENSION.PNG.getValue(); + } else if (StringUtils.startsWith(dataURL, IMAGE_DATA_URI_SUFFIX.JPEG.getValue())) { + base64EncodedString = dataURL.replaceAll(IMAGE_DATA_URI_SUFFIX.JPEG.getValue(), ""); + fileExtension = IMAGE_FILE_EXTENSION.JPEG.getValue(); + } else { + throw new ImageDataURLNotValidException(); + } + + return new Base64EncodedImage(base64EncodedString, fileExtension); + } + + public static void validateClientImageNotEmpty(String imageFileName) { + List dataValidationErrors = new ArrayList(); + if (imageFileName == null) { + StringBuilder validationErrorCode = new StringBuilder("validation.msg.clientImage.cannot.be.blank"); + StringBuilder defaultEnglishMessage = new StringBuilder("The parameter image cannot be blank."); + ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(), + "image"); + dataValidationErrors.add(error); + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", + dataValidationErrors); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java new file mode 100644 index 000000000..31f1ecfd0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java @@ -0,0 +1,33 @@ +package org.mifosplatform.infrastructure.core.service; + +import java.util.Random; + +public class RandomStringGenerator { + public static Random random = new Random(); + + /** + * Generate a random String + * + * @return + */ + public static String generateRandomString() { + String characters = "abcdefghijklmnopqrstuvwxyz123456789"; + int length = generateRandomNumber(); + char[] text = new char[length]; + for (int i = 0; i < length; i++) { + text[i] = characters.charAt(random.nextInt(characters.length())); + } + return new String(text); + } + + /** + * Generate a random number between 5 to 16 + * + * @return + */ + public static int generateRandomNumber() { + Random randomGenerator = new Random(); + return randomGenerator.nextInt(11) + 5; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java new file mode 100644 index 000000000..5274274f2 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java @@ -0,0 +1,24 @@ +package org.mifosplatform.infrastructure.core.service; + +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class S3ClientFactory { + + private ExternalServicesDomainService ExternalServicesDomainService; + + @Autowired + public S3ClientFactory(final ExternalServicesDomainService ExternalServicesDomainService) { + this.ExternalServicesDomainService = ExternalServicesDomainService; + } + + public AmazonS3Client getS3Client() { + String s3_access_key = ExternalServicesDomainService.getValue("s3_access_key"); + String s3_secret_key = ExternalServicesDomainService.getValue("s3_secret_key"); + return new AmazonS3Client(new BasicAWSCredentials(s3_access_key,s3_secret_key)); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java new file mode 100644 index 000000000..9781ef9c9 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java @@ -0,0 +1,201 @@ +package org.mifosplatform.infrastructure.core.service; + + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.*; +import com.lowagie.text.pdf.codec.Base64; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; +import org.mifosplatform.portfolio.client.data.ImageData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; + +public class S3DocumentStore extends DocumentStore { + + private final static Logger logger = LoggerFactory.getLogger(S3DocumentStore.class); + + private final String s3BucketName; + private AmazonS3 s3Client; + + public S3DocumentStore(String s3BucketName, AmazonS3 s3Client) { + this.s3BucketName = s3BucketName; + this.s3Client = s3Client; + } + + @Override + public String saveDocument(InputStream toUpload, DocumentCommand documentCommand) throws DocumentManagementException { + String documentName = documentCommand.getFileName(); + validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize); + + String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId()); + String uploadDocFullPath = uploadDocFolder + File.separator + documentName; + + uploadDocument(toUpload, uploadDocFullPath); + return uploadDocFullPath; + } + + @Override + public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException { + try{ + deleteObjectFromS3(documentPath); + }catch (AmazonServiceException ase){ + deleteObjectAmazonServiceExceptionMessage(ase); + throw new DocumentManagementException(documentName); + }catch (AmazonClientException ace){ + deleteObjectAmazonClientExceptionMessage(ace); + throw new DocumentManagementException(documentName); + } + } + + @Override + public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException { + validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize); + + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + String fileLocation = uploadImageLocation + File.separator + imageName; + + uploadDocument(toUploadInputStream, fileLocation); + return fileLocation; + } + + @Override + public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException { + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension(); + InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString())); + + uploadDocument(toUploadInputStream, fileLocation); + return fileLocation; + } + + @Override + public void deleteImage(Long resourceId, String location) { + try { + deleteObjectFromS3(location); + } catch (AmazonServiceException ase) { + deleteObjectAmazonServiceExceptionMessage(ase); + logger.warn("Unable to delete image associated with clients with Id " + resourceId); + } catch (AmazonClientException ace) { + deleteObjectAmazonClientExceptionMessage(ace); + logger.warn("Unable to delete image associated with clients with Id " + resourceId); + } + } + + private void deleteObjectAmazonClientExceptionMessage(AmazonClientException ace) { + String message = "Caught an AmazonClientException." + + "Error Message: " + ace.getMessage(); + logger.error(message); + } + + private void deleteObjectAmazonServiceExceptionMessage(AmazonServiceException ase) { + String message = "Caught an AmazonServiceException." + + "Error Message: " + ase.getMessage() + + "HTTP Status Code: " + ase.getStatusCode() + + "AWS Error Code: " + ase.getErrorCode() + + "Error Type: " + ase.getErrorType() + + "Request ID: " + ase.getRequestId(); + logger.error(message); + } + + private void deleteObjectFromS3(String location) { + s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location)); + } + + @Override + public DocumentStoreType getType() { + return DocumentStoreType.S3; + } + + @Override + public FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException { + FileData fileData = null; + String fileName = documentData.fileName(); + try { + logger.info("Downloading an object"); + S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, documentData.fileLocation())); + fileData = new FileData(s3object.getObjectContent(), fileName, documentData.contentType()); + } catch (AmazonServiceException ase) { + getObjectAmazonServiceExceptionMessage(ase,fileName); + throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId()); + } catch (AmazonClientException ace) { + getObjectAmazonClientExceptionMessage(ace,fileName); + throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId()); + } + return fileData; + } + + @Override + public ImageData retrieveImage(ImageData imageData) { + S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.imageKey())); + imageData.setContent(s3object.getObjectContent()); + return imageData; + } + + String generateFileParentDirectory(String entityType, Long entityId) { + return "documents" + File.separator + entityType + File.separator + entityId + File.separator + RandomStringGenerator.generateRandomString(); + } + + String generateClientImageParentDirectory(Long resourceId) { + return "images" + File.separator + "clients" + File.separator + resourceId; + } + + private void uploadDocument(InputStream inputStream, String s3UploadLocation) throws DocumentManagementException { + try { + logger.info("Uploading a new object to S3 from a file to " + s3UploadLocation); + s3Client.putObject(new PutObjectRequest(this.s3BucketName, s3UploadLocation, inputStream, new ObjectMetadata())); + + } catch (AmazonServiceException ase) { + String message = "Caught an AmazonServiceException, which " + "means your request made it " + "to Amazon S3, but was rejected with an error response" + + " for some reason. \n Error Message: " + ase.getMessage() + + "HTTP Status Code: " + ase.getStatusCode() + + "AWS Error Code: " + ase.getErrorCode() + + "Error Type: " + ase.getErrorType() + + "Request ID: " + ase.getRequestId(); + + logger.error(message); + throw new DocumentManagementException(message); + } catch (AmazonClientException ace) { + String message = "Caught an AmazonClientException, which " + + "means the client encountered " + + "an internal error while trying to " + + "communicate with S3, " + + "such as not being able to access the network." + + "Error Message: " + ace.getMessage(); + logger.error(message); + throw new DocumentManagementException(message); + } + } + + private void getObjectAmazonClientExceptionMessage(AmazonClientException ace, String filename) { + String message = "Caught an AmazonClientException, which means the client encountered " + + "an internal error while trying to communicate with S3, " + + "such as not being able to access the network." + + "Error Message: " + ace.getMessage() + + "FileName: " + filename; + + logger.error(message); + } + + private void getObjectAmazonServiceExceptionMessage(AmazonServiceException ase, String filename) { + String message = "Caught an AmazonServiceException, which means your request made it " + + "to Amazon S3, but was rejected with an error response" + + " for some reason." + + "Error Message: " + ase.getMessage() + + "HTTP Status Code: " + ase.getStatusCode() + + "AWS Error Code: " + ase.getErrorCode() + + "Error Type: " + ase.getErrorType() + + "Request ID: " + ase.getRequestId() + + "FileName: " + filename; + logger.error(message); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java index 7d4ca2b08..3e573af66 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java @@ -29,7 +29,7 @@ import javax.ws.rs.core.StreamingOutput; import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.FileUtils; +import org.mifosplatform.infrastructure.core.service.FileSystemDocumentStore; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData; import org.mifosplatform.infrastructure.dataqueries.data.ReportData; @@ -243,7 +243,7 @@ public class ReadReportingServiceImpl implements ReadReportingService { if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled", "Pentaho is not enabled"); } - final String reportPath = FileUtils.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt"; + final String reportPath = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt"; logger.info("Report path: " + reportPath); // load report definition @@ -357,7 +357,7 @@ public class ReadReportingServiceImpl implements ReadReportingService { @Override public String retrieveReportPDF(final String reportName, final String type, final Map queryParams) { - String fileLocation = FileUtils.MIFOSX_BASE_DIR + File.separator + ""; + String fileLocation = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ""; if (!new File(fileLocation).isDirectory()) { new File(fileLocation).mkdirs(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java index b12ae5054..ff48aa1e8 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java @@ -5,36 +5,16 @@ */ package org.mifosplatform.infrastructure.documentmanagement.api; -import java.io.File; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; -import javax.ws.rs.core.UriInfo; - -import org.mifosplatform.infrastructure.core.api.ApiConstants; +import com.sun.jersey.core.header.FormDataContentDisposition; +import com.sun.jersey.multipart.FormDataBodyPart; +import com.sun.jersey.multipart.FormDataParam; import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer; -import org.mifosplatform.infrastructure.core.service.FileUtils; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; import org.mifosplatform.infrastructure.documentmanagement.service.DocumentReadPlatformService; import org.mifosplatform.infrastructure.documentmanagement.service.DocumentWritePlatformService; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -42,9 +22,17 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import com.sun.jersey.core.header.FormDataContentDisposition; -import com.sun.jersey.multipart.FormDataBodyPart; -import com.sun.jersey.multipart.FormDataParam; +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.UriInfo; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; @Path("{entityType}/{entityId}/documents") @Component @@ -64,8 +52,8 @@ public class DocumentManagementApiResource { @Autowired public DocumentManagementApiResource(final PlatformSecurityContext context, - final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService, - final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer toApiJsonSerializer) { + final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer toApiJsonSerializer) { this.context = context; this.documentReadPlatformService = documentReadPlatformService; this.documentWritePlatformService = documentWritePlatformService; @@ -95,8 +83,6 @@ public class DocumentManagementApiResource { @FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart, @FormDataParam("name") final String name, @FormDataParam("description") final String description) { - FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB); - /** * TODO: also need to have a backup and stop reading from stream after * max size is reached to protect against malicious clients @@ -124,8 +110,6 @@ public class DocumentManagementApiResource { @FormDataParam("file") final FormDataBodyPart bodyPart, @FormDataParam("name") final String name, @FormDataParam("description") final String description) { - FileUtils.validateFileSizeWithinPermissibleRange(fileSize, name, ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB); - final Set modifiedParams = new HashSet(); modifiedParams.add("name"); modifiedParams.add("description"); @@ -182,11 +166,10 @@ public class DocumentManagementApiResource { this.context.authenticatedUser().validateHasReadPermission(this.SystemEntityType); - final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId); - final File file = new File(documentData.fileLocation()); - final ResponseBuilder response = Response.ok(file); - response.header("Content-Disposition", "attachment; filename=\"" + documentData.fileName() + "\""); - response.header("Content-Type", documentData.contentType()); + final FileData fileData = this.documentReadPlatformService.retrieveDocumentAsFile(entityType, entityId, documentId); + final ResponseBuilder response = Response.ok(fileData.file()); + response.header("Content-Disposition", "attachment; filename=\"" + fileData.name() + "\""); + response.header("Content-Type", fileData.contentType()); return response.build(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java index d138ad51f..2c6e636c0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java @@ -5,6 +5,8 @@ */ package org.mifosplatform.infrastructure.documentmanagement.command; +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; + import java.util.Set; /** @@ -24,6 +26,7 @@ public class DocumentCommand { private String location; private final Set modifiedParameters; + private DocumentStoreType storageType; public DocumentCommand(final Set modifiedParameters, final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size, final String type, final String description, final String location) { @@ -119,4 +122,11 @@ public class DocumentCommand { return this.modifiedParameters.contains("location"); } + public void setStorageType(DocumentStoreType storageType) { + this.storageType = storageType; + } + + public String getStorageType(){ + return storageType.getValue(); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java index 25cdcb931..6cf339bef 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java @@ -5,6 +5,9 @@ */ package org.mifosplatform.infrastructure.documentmanagement.data; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; + /** * Immutable data object represent document being managed on platform. */ @@ -26,8 +29,10 @@ public class DocumentData { private final String description; private String location; + private String storageType; + public DocumentData(final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName, - final Long size, final String type, final String description, final String location) { + final Long size, final String type, final String description, final String location, String storageType) { this.id = id; this.parentEntityType = parentEntityType; this.parentEntityId = parentEntityId; @@ -37,6 +42,7 @@ public class DocumentData { this.type = type; this.description = description; this.location = location; + this.storageType = storageType; } public String contentType() { @@ -55,4 +61,20 @@ public class DocumentData { this.location = location; } + public DocumentStoreType storeType(){ + return DocumentStore.getDocumentStoreType(storageType); + } + + public String getParentEntityType(){ + return this.parentEntityType; + } + + public Long getParentEntityId(){ + return this.parentEntityId; + } + + public Long getId(){ + return this.id; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java new file mode 100644 index 000000000..bbaad808f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java @@ -0,0 +1,56 @@ +package org.mifosplatform.infrastructure.documentmanagement.data; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class FileData { + private static final Logger logger = LoggerFactory.getLogger(FileData.class); + + private final File file; + private final String fileName; + private final String contentType; + private final InputStream inputStream; + + public FileData(File file, String fileName, String contentType) { + this.file = file; + this.fileName = fileName; + this.contentType = contentType; + this.inputStream = null; + } + + public FileData(InputStream inputStream, String fileName, String contentType) { + this.file = null; + this.inputStream = inputStream; + this.fileName = fileName; + this.contentType = contentType; + } + + public String contentType() { + return contentType; + } + + public String name() { + return fileName; + } + + public InputStream file(){ + try{ + if(inputStream == null){ + return new FileInputStream(file); + } + else { + return inputStream; + } + + } catch (FileNotFoundException e){ + logger.error(e.toString()); + return null; + } + } +} + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java index 467503668..33e0747d2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java @@ -5,14 +5,16 @@ */ package org.mifosplatform.infrastructure.documentmanagement.domain; +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.springframework.data.jpa.domain.AbstractPersistable; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; -import org.apache.commons.lang.StringUtils; -import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; -import org.springframework.data.jpa.domain.AbstractPersistable; - @Entity @Table(name = "m_document") public class Document extends AbstractPersistable { @@ -41,15 +43,19 @@ public class Document extends AbstractPersistable { @Column(name = "location", length = 500) private String location; - public Document() {} + @Column(name = "storage_type", length = 50) + private String storageType; + + public Document() { + } public static Document createNew(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, - final Long size, final String type, final String description, final String location) { - return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location); + final Long size, final String type, final String description, final String location, final DocumentStoreType storageType) { + return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location, storageType); } private Document(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size, - final String type, final String description, final String location) { + final String type, final String description, final String location, final DocumentStoreType storageType) { this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null); this.parentEntityId = parentEntityId; this.name = StringUtils.defaultIfEmpty(name, null); @@ -58,6 +64,7 @@ public class Document extends AbstractPersistable { this.type = StringUtils.defaultIfEmpty(type, null); this.description = StringUtils.defaultIfEmpty(description, null); this.location = StringUtils.defaultIfEmpty(location, null); + this.storageType = storageType.getValue(); } public void update(final DocumentCommand command) { @@ -79,7 +86,7 @@ public class Document extends AbstractPersistable { if (command.isSizeChanged()) { this.size = command.getSize(); } - + this.storageType = command.getStorageType(); } public String getParentEntityType() { @@ -146,4 +153,7 @@ public class Document extends AbstractPersistable { this.location = location; } + public DocumentStoreType storageType(){ + return DocumentStore.getDocumentStoreType(this.storageType); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java index f6b3f6692..af99a5f70 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java @@ -5,13 +5,17 @@ */ package org.mifosplatform.infrastructure.documentmanagement.service; -import java.util.Collection; - import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; + +import java.util.Collection; public interface DocumentReadPlatformService { Collection retrieveAllDocuments(String entityType, Long entityId); + FileData retrieveDocumentAsFile(String entityType, Long entityId, Long documentId); + DocumentData retrieveDocument(String entityType, Long entityId, Long documentId); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java index 15fd7dcf9..c531eacdc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java @@ -5,13 +5,12 @@ */ package org.mifosplatform.infrastructure.documentmanagement.service; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collection; - import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.springframework.beans.factory.annotation.Autowired; @@ -20,16 +19,23 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + @Service public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformService { private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; + private final DocumentStoreFactory documentStoreFactory; @Autowired - public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) { + public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, + final DocumentStoreFactory documentStoreFactory) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.documentStoreFactory = documentStoreFactory; } @Override @@ -46,17 +52,22 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ } @Override - public DocumentData retrieveDocument(final String entityType, final Long entityId, final Long documentId) { - + public FileData retrieveDocumentAsFile(final String entityType, final Long entityId, final Long documentId) { try { - this.context.authenticatedUser(); + DocumentData documentData = getDocumentData(entityType, entityId, documentId); + DocumentStore instanceForRead = documentStoreFactory.getInstanceFromStorageType(documentData.storeType()); + return instanceForRead.retrieveDocument(documentData); + } catch (final EmptyResultDataAccessException e) { + throw new DocumentNotFoundException(entityType, entityId, documentId); + } catch (final DocumentNotFoundException d) { + throw d; + } + } - // TODO verify if the entities are valid and a - // user has data - // scope for the particular entities - final DocumentMapper mapper = new DocumentMapper(false); - final String sql = "select " + mapper.schema() + " and d.id=? "; - return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { entityType, entityId, documentId }); + @Override + public DocumentData retrieveDocument(String entityType, Long entityId, Long documentId) { + try { + return getDocumentData(entityType, entityId, documentId); } catch (final EmptyResultDataAccessException e) { throw new DocumentNotFoundException(entityType, entityId, documentId); } @@ -73,7 +84,8 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ public String schema() { return "d.id as id, d.parent_entity_type as parentEntityType, d.parent_entity_id as parentEntityId, d.name as name, " + " d.file_name as fileName, d.size as fileSize, d.type as fileType, " - + " d.description as description, d.location as location" + + " d.description as description, d.location as location," + + " d.storage_type as storageType" + " from m_document d where d.parent_entity_type=? and d.parent_entity_id=?"; } @@ -92,8 +104,21 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ if (!this.hideLocation) { location = rs.getString("location"); } + final String storageType = rs.getString("storageType"); - return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location); + return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location, storageType); } } + + + private DocumentData getDocumentData(String entityType, Long entityId, Long documentId) { + this.context.authenticatedUser(); + + // TODO verify if the entities are valid and a + // user has data + // scope for the particular entities + final DocumentMapper mapper = new DocumentMapper(false); + final String sql = "select " + mapper.schema() + " and d.id=? "; + return this.jdbcTemplate.queryForObject(sql, mapper, new Object[]{entityType, entityId, documentId}); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java index 84a1ca41e..83bff1a3c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java @@ -5,13 +5,11 @@ */ package org.mifosplatform.infrastructure.documentmanagement.service; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.FileUtils; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommandValidator; import org.mifosplatform.infrastructure.documentmanagement.domain.Document; @@ -27,6 +25,8 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.InputStream; + @Service public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWritePlatformService { @@ -34,11 +34,14 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr private final PlatformSecurityContext context; private final DocumentRepository documentRepository; + private final DocumentStoreFactory documentStoreFactory; @Autowired - public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository) { + public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository, + final DocumentStoreFactory documentStoreFactory) { this.context = context; this.documentRepository = documentRepository; + this.documentStoreFactory = documentStoreFactory; } @Transactional @@ -53,19 +56,13 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr validator.validateForCreate(); - final String fileUploadLocation = FileUtils.generateFileParentDirectory(documentCommand.getParentEntityType(), - documentCommand.getParentEntityId()); + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); - /** Recursively create the directory if it does not exist **/ - if (!new File(fileUploadLocation).isDirectory()) { - new File(fileUploadLocation).mkdirs(); - } - - final String fileLocation = FileUtils.saveToFileSystem(inputStream, fileUploadLocation, documentCommand.getFileName()); + final String fileLocation = documentStore.saveDocument(inputStream, documentCommand); final Document document = Document.createNew(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getName(), documentCommand.getFileName(), documentCommand.getSize(), documentCommand.getType(), - documentCommand.getDescription(), fileLocation); + documentCommand.getDescription(), fileLocation, documentStore.getType()); this.documentRepository.save(document); @@ -74,8 +71,8 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); - } catch (final IOException ioe) { - logger.error(ioe.getMessage(), ioe); + } catch (final DocumentManagementException dme) { + logger.error(dme.getMessage(), dme); throw new DocumentManagementException(documentCommand.getName()); } } @@ -92,30 +89,21 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr // TODO check if entity id is valid and within data scope for the // user final Document documentForUpdate = this.documentRepository.findOne(documentCommand.getId()); + DocumentStoreType documentStoreType = documentForUpdate.storageType(); if (documentForUpdate == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getId()); } oldLocation = documentForUpdate.getLocation(); - // if a new file is also passed in if (inputStream != null && documentCommand.isFileNameChanged()) { - final String fileUploadLocation = FileUtils.generateFileParentDirectory(documentCommand.getParentEntityType(), - documentCommand.getParentEntityId()); - - /** Recursively create the directory if it does not exist **/ - if (!new File(fileUploadLocation).isDirectory()) { - new File(fileUploadLocation).mkdirs(); - } - - // TODO provide switch to toggle between file system appender - // and Amazon S3 appender - final String fileLocation = FileUtils.saveToFileSystem(inputStream, fileUploadLocation, documentCommand.getFileName()); - documentCommand.setLocation(fileLocation); + DocumentStore instanceFromConfiguration = this.documentStoreFactory.getInstanceFromConfiguration(); + documentCommand.setLocation(instanceFromConfiguration.saveDocument(inputStream, documentCommand)); + documentCommand.setStorageType(instanceFromConfiguration.getType()); } documentForUpdate.update(documentCommand); if (inputStream != null && documentCommand.isFileNameChanged()) { - // delete previous file - deleteFile(documentCommand.getName(), oldLocation); + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(documentStoreType); + documentStore.deleteDocument(documentCommand.getName(), oldLocation); } this.documentRepository.saveAndFlush(documentForUpdate); @@ -125,8 +113,8 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); - } catch (final IOException ioe) { - logger.error(ioe.getMessage(), ioe); + } catch (final DocumentManagementException dme) { + logger.error(dme.getMessage(), dme); throw new DocumentManagementException(documentCommand.getName()); } } @@ -139,20 +127,15 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr validateParentEntityType(documentCommand); // TODO: Check document is present under this entity Id final Document document = this.documentRepository.findOne(documentCommand.getId()); + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(document.storageType()); if (document == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getId()); } this.documentRepository.delete(document); - deleteFile(document.getName(), document.getLocation()); + documentStore.deleteDocument(document.getName(), document.getLocation()); return new CommandProcessingResult(document.getId()); } - private void deleteFile(final String documentName, final String location) { - final File fileToBeDeleted = new File(location); - final boolean fileDeleted = fileToBeDeleted.delete(); - if (!fileDeleted) { throw new DocumentManagementException(documentName); } - } - private void validateParentEntityType(final DocumentCommand documentCommand) { if (!checkValidEntityType(documentCommand.getParentEntityType())) { throw new InvalidEntityTypeForDocumentManagementException( documentCommand.getParentEntityType()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java index dccf49f26..087c08196 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java @@ -5,44 +5,31 @@ */ package org.mifosplatform.portfolio.client.api; -import java.io.File; -import java.io.InputStream; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; - +import com.lowagie.text.pdf.codec.Base64; +import com.sun.jersey.core.header.FormDataContentDisposition; +import com.sun.jersey.multipart.FormDataBodyPart; +import com.sun.jersey.multipart.FormDataParam; import org.apache.commons.lang.StringUtils; -import org.mifosplatform.infrastructure.core.api.ApiConstants; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; -import org.mifosplatform.infrastructure.core.service.FileUtils; -import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_DATA_URI_SUFFIX; -import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_FILE_EXTENSION; -import org.mifosplatform.infrastructure.core.service.FileUtils.IMAGE_MIME_TYPE; +import org.mifosplatform.infrastructure.core.service.ImageUtils; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.exception.ImageNotFoundException; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; import org.mifosplatform.portfolio.client.service.ClientWritePlatformService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import com.lowagie.text.pdf.codec.Base64; -import com.sun.jersey.core.header.FormDataContentDisposition; -import com.sun.jersey.multipart.FormDataBodyPart; -import com.sun.jersey.multipart.FormDataParam; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; +import java.io.InputStream; @Path("/clients/{clientId}/images") @Component @@ -53,10 +40,11 @@ public class ClientImagesApiResource { private final ClientReadPlatformService clientReadPlatformService; private final ClientWritePlatformService clientWritePlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; + private final static Logger logger = LoggerFactory.getLogger(ClientImagesApiResource.class); @Autowired public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, - final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer toApiJsonSerializer) { + final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer toApiJsonSerializer) { this.context = context; this.clientReadPlatformService = readPlatformService; this.clientWritePlatformService = clientWritePlatformService; @@ -76,12 +64,11 @@ public class ClientImagesApiResource { // TODO: vishwas might need more advances validation (like reading magic // number) for handling malicious clients // and clients not setting mime type - FileUtils.validateClientImageNotEmpty(fileDetails.getFileName()); - FileUtils.validateImageMimeType(bodyPart.getMediaType().toString()); - FileUtils.validateFileSizeWithinPermissibleRange(fileSize, fileDetails.getFileName(), ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB); + ImageUtils.validateClientImageNotEmpty(fileDetails.getFileName()); + ImageUtils.validateImageMimeType(bodyPart.getMediaType().toString()); final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(), - inputStream); + inputStream, fileSize); return this.toApiJsonSerializer.serialize(result); } @@ -94,7 +81,7 @@ public class ClientImagesApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) { - final Base64EncodedImage base64EncodedImage = FileUtils.extractImageFromDataURL(jsonRequestBody); + final Base64EncodedImage base64EncodedImage = ImageUtils.extractImageFromDataURL(jsonRequestBody); final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage); @@ -116,15 +103,14 @@ public class ClientImagesApiResource { if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } // TODO: Need a better way of determining image type - String imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.JPEG.getValue(); - if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.GIF.getValue())) { - imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.GIF.getValue(); - } else if (StringUtils.endsWith(clientData.imageKey(), IMAGE_FILE_EXTENSION.PNG.getValue())) { - imageDataURISuffix = IMAGE_DATA_URI_SUFFIX.PNG.getValue(); + String imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue(); + if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { + imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue(); + } else if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { + imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue(); } - String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeFromFile(clientData.imageKey()); - + String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(clientData.image()); return clientImageAsBase64Text; } @@ -138,22 +124,12 @@ public class ClientImagesApiResource { if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } - File image = new File(clientData.imageKey()); - String imageName = image.getName(); - ResponseBuilder response = Response.ok(image); - response.header("Content-Disposition", "attachment; filename=\"" + imageName + "\""); + ResponseBuilder response = Response.ok(clientData.image()); + response.header("Content-Disposition", "attachment; filename=\"" + clientData.imageName() + "\""); // TODO: Need a better way of determining image type - // determine image type - String contentType = IMAGE_MIME_TYPE.JPEG.getValue(); - if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.GIF.getValue())) { - contentType = IMAGE_MIME_TYPE.GIF.getValue(); - } else if (StringUtils.endsWith(imageName, IMAGE_FILE_EXTENSION.PNG.getValue())) { - contentType = IMAGE_MIME_TYPE.PNG.getValue(); - } - - response.header("Content-Type", contentType); + response.header("Content-Type", clientData.imageContentType()); return response.build(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index d4ee1e225..d72f85f80 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -5,9 +5,6 @@ */ package org.mifosplatform.portfolio.client.data; -import java.util.Collection; -import java.util.List; - import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.EqualsBuilder; @@ -16,12 +13,21 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; /** * Immutable data object representing client data. */ final public class ClientData implements Comparable { + private final static Logger logger = LoggerFactory.getLogger(ClientData.class); + + private final Long id; private final String accountNo; private final String externalId; @@ -40,7 +46,8 @@ final public class ClientData implements Comparable { private final Long officeId; private final String officeName; - private final String imageKey; +// private String imageData; + private ImageData imageData; @SuppressWarnings("unused") private final Boolean imagePresent; @@ -58,13 +65,13 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, - clientData.externalId, clientData.activationDate, clientData.imageKey, allowedOffices, clientData.groups); + clientData.externalId, clientData.activationDate, clientData.imageData, allowedOffices, clientData.groups); } public static ClientData setParentGroups(final ClientData clientData, final Collection parentGroups) { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, - clientData.externalId, clientData.activationDate, clientData.imageKey, clientData.officeOptions, parentGroups); + clientData.externalId, clientData.activationDate, clientData.imageData, clientData.officeOptions, parentGroups); } public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname, @@ -81,14 +88,14 @@ final public class ClientData implements Comparable { public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, - final String displayName, final String externalId, final LocalDate activationDate, final String imageKey) { + final String displayName, final String externalId, final LocalDate activationDate, final ImageData imageData) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageKey, null, null); + externalId, activationDate, imageData, null, null); } private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, - final String externalId, final LocalDate activationDate, final String imageKey, final Collection allowedOffices, + final String externalId, final LocalDate activationDate, final ImageData imageKey, final Collection allowedOffices, final Collection groups) { this.accountNo = accountNo; this.status = status; @@ -107,7 +114,7 @@ final public class ClientData implements Comparable { this.displayName = StringUtils.defaultIfEmpty(displayName, null); this.externalId = StringUtils.defaultIfEmpty(externalId, null); this.activationDate = activationDate; - this.imageKey = imageKey; + this.imageData = imageKey; if (imageKey != null) { this.imagePresent = Boolean.TRUE; } else { @@ -138,17 +145,16 @@ final public class ClientData implements Comparable { } public String imageKey() { - return this.imageKey; + return this.imageData.imageKey(); } public boolean imageKeyDoesNotExist() { - return !imageKeyExists(); + return imageData.equals(null) && !imageKeyExists(); } private boolean imageKeyExists() { - return StringUtils.isNotBlank(this.imageKey); + return StringUtils.isNotBlank(this.imageData.imageKey()); } - @Override public int compareTo(final ClientData obj) { if (obj == null) { return -1; } @@ -194,4 +200,25 @@ final public class ClientData implements Comparable { public LocalDate getActivationDate() { return this.activationDate; } + + public void setImageData(ImageData imageData){ + this.imageData = imageData; + } + + public String imageName() { + return imageData.name(); + } + + public String imageContentType() { + return imageData.contentType(); + } + + public byte[] image(){ + try{ + return imageData.getContent(); + }catch (IOException ioe){ + logger.error("Error in fetching client image"); + return null; + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java new file mode 100644 index 000000000..95ede014c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java @@ -0,0 +1,78 @@ +package org.mifosplatform.portfolio.client.data; + +import org.apache.commons.lang.StringUtils; +import org.apache.poi.util.IOUtils; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; +import org.mifosplatform.infrastructure.core.service.ImageUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ImageData { + + private final Long clientId; + private final String key; + // private final DocumentStoreType storeType; + private final String storeType; + private File file; + private String contentType; + private String name; + private InputStream inputStream; + + + public ImageData(Long clientId, String key, String storeType) { + this.clientId = clientId; + this.key = key; + this.storeType = storeType; + } + + public String imageKey() { + return this.key; + } + + public byte[] getContent() throws IOException { + if(inputStream == null){ + FileInputStream fileInputStream = new FileInputStream(file); + return IOUtils.toByteArray(fileInputStream); + } + else + { + return IOUtils.toByteArray(inputStream); + } + } + + private String setImageContentType() { + String fileName = file.getName(); + String contentType = ImageUtils.IMAGE_MIME_TYPE.JPEG.getValue(); + if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { + contentType = ImageUtils.IMAGE_MIME_TYPE.GIF.getValue(); + } else if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { + contentType = ImageUtils.IMAGE_MIME_TYPE.PNG.getValue(); + } + return contentType; + } + + public String contentType(){ + return this.contentType; + } + + public DocumentStoreType storeType() { + return DocumentStore.getDocumentStoreType(storeType); + } + + public String name(){ + return this.file.getName(); + } + + + public void setContent(InputStream objectContent) { + this.inputStream = objectContent; + } + + public void setContent(File file) { + this.file = file; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 4a1ebff7f..f9400bbd2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -5,26 +5,6 @@ */ package org.mifosplatform.portfolio.client.domain; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; - import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; @@ -39,6 +19,9 @@ import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.group.domain.Group; import org.springframework.data.jpa.domain.AbstractPersistable; +import javax.persistence.*; +import java.util.*; + @Entity @Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") }) public final class Client extends AbstractPersistable { @@ -79,9 +62,6 @@ public final class Client extends AbstractPersistable { @Column(name = "external_id", length = 100, nullable = true, unique = true) private String externalId; - @Column(name = "image_key", length = 500, nullable = true) - private String imageKey; - @ManyToMany @JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id")) private Set groups; @@ -385,13 +365,12 @@ public final class Client extends AbstractPersistable { return this.office.identifiedBy(officeId); } - public String imageKey() { - return imageKey; - } - - public void updateImageKey(final String imageKey) { - this.imageKey = imageKey; - } +// public String imageKey() { +// } +// +// public void updateImageKey(final String imageKey) { +// this.image.setKey(imageKey); +// } public Long officeId() { return this.office.getId(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java new file mode 100644 index 000000000..d186f5b26 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java @@ -0,0 +1,62 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.client.domain; + +import org.mifosplatform.infrastructure.core.service.DocumentStoreType; +import org.springframework.data.jpa.domain.AbstractPersistable; + +import javax.persistence.*; + +@Entity +@Table(name = "m_image") +public final class Image extends AbstractPersistable { + + // @Column(name = "`client_id`") + @OneToOne() + @JoinColumn(name = "`client_id`") + private Client client; + + @Column(name = "`key`", length = 500) + private String key; + + @Column(name = "`storage_type`", length = 50) + private String storeType; + + public Image(Client client, String image_key, DocumentStoreType storeType) { + this.client = client; + this.key = image_key; + this.storeType = storeType.getValue(); + } + + public Image() { + + } + + + public Image(Client client) { + this.client = client; + this.key = null; + this.storeType = null; + } + + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String updateKey(String key) { + setKey(key); + return this.key; + } + + public void updateStorageType(String documentStoreType) { + this.storeType = documentStoreType; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java new file mode 100644 index 000000000..ed0fe6cc1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.client.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ImageRepository extends JpaRepository, JpaSpecificationExecutor { + Image findOneByClient(Client client); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java index fc87e6cdf..33d6808f0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.client.service; + import java.util.Collection; import org.mifosplatform.infrastructure.core.service.Page; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index 714f29a36..3fb4b72a1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -16,6 +16,7 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; @@ -25,6 +26,7 @@ import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientData; +import org.mifosplatform.portfolio.client.data.ImageData; import org.mifosplatform.portfolio.client.domain.ClientEnumerations; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.group.data.GroupGeneralData; @@ -38,12 +40,14 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; -@Service(value = "clientReadPlatformService") +@Service public class ClientReadPlatformServiceImpl implements ClientReadPlatformService { + private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final OfficeReadPlatformService officeReadPlatformService; + private final DocumentStoreFactory documentStoreFactory; // data mappers private final PaginationHelper paginationHelper = new PaginationHelper(); @@ -54,9 +58,10 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Autowired public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final OfficeReadPlatformService officeReadPlatformService) { + final OfficeReadPlatformService officeReadPlatformService, final DocumentStoreFactory documentStoreFactory) { this.context = context; this.officeReadPlatformService = officeReadPlatformService; + this.documentStoreFactory = documentStoreFactory; this.jdbcTemplate = new JdbcTemplate(dataSource); } @@ -92,7 +97,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService if (searchParameters.isOrderByRequested()) { sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); - + if (searchParameters.isSortOrderProvided()) { sqlBuilder.append(' ').append(searchParameters.getSortOrder()); } @@ -109,7 +114,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), new Object[] { hierarchySearchString }, this.clientMapper); } - + private String buildSqlStringFromClientCriteria(final SearchParameters searchParameters) { final String sqlSearch = searchParameters.getSqlSearch(); @@ -159,7 +164,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Override public ClientData retrieveOne(final Long clientId) { - try { AppUser currentUser = context.authenticatedUser(); String hierarchy = currentUser.getOffice().getHierarchy(); @@ -173,7 +177,15 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService Collection parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper, new Object[] { clientId }); + + String clientImageSql = "select * from m_image where client_id =" + clientId; + + try { + ImageData imageData = this.jdbcTemplate.queryForObject(clientImageSql, new ImageMapper()); + clientData.setImageData(documentStoreFactory.getInstanceFromStorageType(imageData.storeType()).retrieveImage(imageData)); + } catch (EmptyResultDataAccessException e) {} return ClientData.setParentGroups(clientData, parentGroups); + } catch (EmptyResultDataAccessException e) { throw new ClientNotFoundException(clientId); } @@ -206,8 +218,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String hierarchy = currentUser.getOffice().getHierarchy(); final String hierarchySearchString = hierarchy + "%"; - final String sql = "select " + this.membersOfGroupMapper.schema() - + " where o.hierarchy like ? and pgc.group_id = ?"; + final String sql = "select " + this.membersOfGroupMapper.schema() + " where o.hierarchy like ? and pgc.group_id = ?"; return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, new Object[] { hierarchySearchString, groupId }); } @@ -255,7 +266,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String officeName = rs.getString("officeName"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageKey); + externalId, activationDate, null); } } @@ -299,11 +310,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String displayName = rs.getString("displayName"); final String externalId = rs.getString("externalId"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); - final String imageKey = rs.getString("imageKey"); + // final String imageKey = rs.getString("imageKey"); final String officeName = rs.getString("officeName"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageKey); + externalId, activationDate, null); } } @@ -325,6 +336,18 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } } + private static final class ImageMapper implements RowMapper { + + @Override + public ImageData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long clientId = rs.getLong("client_id"); + final String imageKey = rs.getString("key"); + final String storeType = rs.getString("storage_type"); + return new ImageData(clientId, imageKey, storeType); + } + + } + private static final class ClientLookupMapper implements RowMapper { private final String schema; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java index 9f8d5b43e..578b74c10 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java @@ -23,7 +23,7 @@ public interface ClientWritePlatformService { CommandProcessingResult deleteClient(Long clientId); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") - CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream); + CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream, Long fileSize); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 0bc4fb35f..3eb007ecb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -5,13 +5,6 @@ */ package org.mifosplatform.portfolio.client.service; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.Locale; -import java.util.Map; - import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; @@ -21,7 +14,8 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.FileUtils; +import org.mifosplatform.infrastructure.core.service.DocumentStore; +import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.domain.Office; @@ -29,10 +23,7 @@ import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.data.ClientDataValidator; -import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; -import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; -import org.mifosplatform.portfolio.client.domain.Client; -import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.domain.*; import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.group.domain.Group; @@ -47,6 +38,11 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.InputStream; +import java.util.List; +import java.util.Locale; +import java.util.Map; + @Service public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWritePlatformService { @@ -59,12 +55,14 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final GroupRepository groupRepository; private final ClientDataValidator fromApiJsonDeserializer; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; + private final DocumentStoreFactory documentStoreFactory; + private final ImageRepository imageRepository; @Autowired public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, - final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository, - final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, - final GroupRepository groupRepository) { + final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository, + final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, + final GroupRepository groupRepository, DocumentStoreFactory documentStoreFactory, ImageRepository imageRepository) { this.context = context; this.clientRepository = clientRepository; this.officeRepository = officeRepository; @@ -72,6 +70,8 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; this.groupRepository = groupRepository; + this.documentStoreFactory = documentStoreFactory; + this.imageRepository = imageRepository; } @Transactional @@ -223,16 +223,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP @Transactional @Override - public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream) { + public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream, Long fileSize) { try { final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); - String imageUploadLocation = setupForClientImageUpdate(clientId, client); + deletePreviousClientImage(clientId, client); - String imageLocation = FileUtils.saveToFileSystem(inputStream, imageUploadLocation, imageName); - - return updateClientImage(clientId, client, imageLocation); - } catch (IOException ioe) { - logger.error(ioe.getMessage(), ioe); + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); + String imageLocation = documentStore.saveImage(inputStream, clientId, imageName, fileSize); + return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue()); + } catch (DocumentManagementException dme) { + logger.error(dme.getMessage(), dme); throw new DocumentManagementException(imageName); } } @@ -243,48 +243,52 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); + Image image = imageRepository.findOneByClient(client); + String imageKey = image.getKey(); // delete image from the file system - if (StringUtils.isNotEmpty(client.imageKey())) { - FileUtils.deleteClientImage(clientId, client.imageKey()); + if (StringUtils.isNotEmpty((imageKey))) { + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); + documentStore.deleteImage(clientId, imageKey); + this.imageRepository.delete(image); } - return updateClientImage(clientId, client, null); + + return new CommandProcessingResult(clientId) ; } @Override public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) { try { final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); - final String imageUploadLocation = setupForClientImageUpdate(clientId, client); + deletePreviousClientImage(clientId, client); - final String imageLocation = FileUtils.saveToFileSystem(encodedImage, imageUploadLocation, "image"); + DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); + final String imageLocation = documentStore.saveImage(encodedImage, clientId, "image"); - return updateClientImage(clientId, client, imageLocation); - } catch (IOException ioe) { - logger.error(ioe.getMessage(), ioe); + return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue()); + } catch (DocumentManagementException dme) { + logger.error(dme.getMessage(), dme); throw new DocumentManagementException("image"); } } - private String setupForClientImageUpdate(final Long clientId, final Client client) { + private void deletePreviousClientImage(final Long clientId, final Client client) { if (client == null) { throw new ClientNotFoundException(clientId); } - final String imageUploadLocation = FileUtils.generateClientImageParentDirectory(clientId); - // delete previous image from the file system - if (StringUtils.isNotEmpty(client.imageKey())) { - FileUtils.deleteClientImage(clientId, client.imageKey()); + Image image = imageRepository.findOneByClient(client); + if (image != null){ + this.documentStoreFactory.getInstanceFromConfiguration().deleteImage(clientId, image.getKey()); } - - /** Recursively create the directory if it does not exist **/ - if (!new File(imageUploadLocation).isDirectory()) { - new File(imageUploadLocation).mkdirs(); - } - return imageUploadLocation; } - private CommandProcessingResult updateClientImage(final Long clientId, final Client client, final String imageLocation) { - client.updateImageKey(imageLocation); - this.clientRepository.save(client); + private CommandProcessingResult updateClientImage(final Long clientId, final Client client, final String imageLocation, final String documentStoreType) { + Image image = imageRepository.findOneByClient(client); + if(image == null){ + image = new Image(client); + } + image.updateKey(imageLocation); + image.updateStorageType(documentStoreType); + this.imageRepository.save(image); return new CommandProcessingResult(clientId); } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpaTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpaTest.java new file mode 100644 index 000000000..5fe094e14 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpaTest.java @@ -0,0 +1,46 @@ +package org.mifosplatform.infrastructure.configuration.domain; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mifosplatform.useradministration.domain.Permission; +import org.mifosplatform.useradministration.domain.PermissionRepository; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unused") +@RunWith(MockitoJUnitRunner.class) +public class ConfigurationDomainServiceJpaTest { + + private final String S3 = "amazon-S3"; + private PermissionRepository permissionRepositoryMock = mock(PermissionRepository.class);; + private GlobalConfigurationRepository globalConfigurationRepositoryMock = mock(GlobalConfigurationRepository.class); + + @Test + public void shouldReturnTrueIfAmazonS3ConfigurationIsEnabled(){ + when(globalConfigurationRepositoryMock.findOneByName(S3)).thenReturn(new GlobalConfigurationProperty(S3, true)); + ConfigurationDomainServiceJpa configurationDomainServiceJpa = new ConfigurationDomainServiceJpa(permissionRepositoryMock, globalConfigurationRepositoryMock); + + boolean actualIsS3Enabled = configurationDomainServiceJpa.isAmazonS3Enabled(); + + assertTrue(actualIsS3Enabled); + } + + @Test + public void shouldReturnFalseIfAmazonS3ConfigurationIsDisabled(){ + when(globalConfigurationRepositoryMock.findOneByName(S3)).thenReturn(new GlobalConfigurationProperty(S3, false)); + ConfigurationDomainServiceJpa configurationDomainServiceJpa = new ConfigurationDomainServiceJpa(permissionRepositoryMock, globalConfigurationRepositoryMock); + + boolean actualIsS3Enabled = configurationDomainServiceJpa.isAmazonS3Enabled(); + + assertFalse(actualIsS3Enabled); + } +} diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java new file mode 100644 index 000000000..8592df1f9 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java @@ -0,0 +1,55 @@ +package org.mifosplatform.infrastructure.core.service; + + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.context.ApplicationContext; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unused") + +@RunWith(MockitoJUnitRunner.class) +public class DocumentStoreFactoryTest { + + @Test + public void shouldReturnS3DocumentStoreObjectIfS3Enabled(){ + S3ClientFactory s3ClientFactoryMock = mock(S3ClientFactory.class); + when(s3ClientFactoryMock.getS3Client()).thenReturn(null); + + ExternalServicesDomainService externalServicesDomainServiceMock = mock(ExternalServicesDomainService.class); + when(externalServicesDomainServiceMock.getValue("s3_bucket_name")).thenReturn("bucket_name"); + + ConfigurationDomainService configurationDomainServiceMock = mock(ConfigurationDomainService.class); + when(configurationDomainServiceMock.isAmazonS3Enabled()).thenReturn(true); + + ApplicationContext applicationContextMock = mock(ApplicationContext.class); + + when(applicationContextMock.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainServiceMock); + when(applicationContextMock.getBean("s3ClientFactory", S3ClientFactory.class)).thenReturn(s3ClientFactoryMock); + when(applicationContextMock.getBean("externalServicesDomainService", ExternalServicesDomainService.class)).thenReturn(externalServicesDomainServiceMock); + + + DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContextMock); + assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),S3DocumentStore.class); + } + + @Test + public void shouldReturnFileSystemDocumentStoreObjectIfS3IsDisabled(){ + ConfigurationDomainService configurationDomainService = mock(ConfigurationDomainService.class); + when(configurationDomainService.isAmazonS3Enabled()).thenReturn(false); + + ApplicationContext applicationContext = mock(ApplicationContext.class); + when(applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainService); + + DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContext); + assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),FileSystemDocumentStore.class); + } + + +} diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java new file mode 100644 index 000000000..2f70fe84c --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java @@ -0,0 +1,31 @@ +package org.mifosplatform.infrastructure.core.service; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mockito.runners.MockitoJUnitRunner; + +import static junit.framework.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unused") + +@RunWith(MockitoJUnitRunner.class) +public class FileDocumentStoreTest { + + @Test + public void shouldRetrieveDocument(){ + FileSystemDocumentStore fileSystemDocumentStore = new FileSystemDocumentStore(); + DocumentData documentDataMock = mock(DocumentData.class); + when(documentDataMock.fileName()).thenReturn("some_file"); + when(documentDataMock.fileName()).thenReturn("content_type"); + when(documentDataMock.fileLocation()).thenReturn("abcd"); + + FileData fileData = fileSystemDocumentStore.retrieveDocument(documentDataMock); + + assertEquals(fileData.name(), documentDataMock.fileName()); + assertEquals(fileData.contentType(), documentDataMock.contentType()); + } +} diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java new file mode 100644 index 000000000..3307d7d96 --- /dev/null +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java @@ -0,0 +1,135 @@ +package org.mifosplatform.infrastructure.core.service; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; +import org.mockito.Matchers; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.IOException; +import java.io.InputStream; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + +@SuppressWarnings("unused") + +@RunWith(MockitoJUnitRunner.class) +public class S3DocumentStoreTest { + + InputStream inputStream; + AmazonS3Client s3ClientMock; + DocumentCommand documentCommand; + DocumentData documentDataMock; + + @Test + public void shouldGenerateParentDirectoryFromEntityDetails(){ + S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null); + String actualParentDirectory = s3DocumentStore.generateFileParentDirectory("entity_type", 1234L); + assertTrue(actualParentDirectory.startsWith("documents/entity_type/1234")); + } + + @Test + public void shouldGenerateClientImageDirectoryFromResourceId(){ + S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null); + String actualParentDirectory = s3DocumentStore.generateClientImageParentDirectory(1234L); + assertTrue(actualParentDirectory.startsWith("images/clients/1234")); + } + + @Test + public void shouldSaveADocumentToS3() throws IOException { + initUpload(); + when(s3ClientMock.putObject(Matchers.anyObject())).thenReturn(new PutObjectResult()); + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + + s3DocumentStore.saveDocument(inputStream, documentCommand); + + verify(s3ClientMock, times(1)).putObject(Matchers.anyObject()); + } + + @Test(expected = DocumentManagementException.class) + public void shouldCatchAmazonServiceExceptionWhenUnableToUploadDocumentToS3() throws IOException { + initUpload(); + when(s3ClientMock.putObject(Matchers.anyObject())).thenThrow(new AmazonServiceException("serviceException")); + + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + + s3DocumentStore.saveDocument(inputStream, documentCommand); + } + + @Test(expected = DocumentManagementException.class) + public void shouldCatchAmazonClientExceptionWhenUnableToUploadDocumentToS3() throws IOException { + initUpload(); + when(s3ClientMock.putObject(Matchers.anyObject())).thenThrow(new AmazonClientException("clientException")); + + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + + s3DocumentStore.saveDocument(inputStream, documentCommand); + } + + @Test + public void shouldRetrieveDocumentFromS3() throws IOException { + initRetrieve(); + when(s3ClientMock.getObject(Matchers.anyObject())).thenReturn(new S3Object()); + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + + FileData fileData = s3DocumentStore.retrieveDocument(documentDataMock); + + verify(s3ClientMock, times(1)).getObject(Matchers.anyObject()); + assertEquals(fileData.name(), "name"); + assertEquals(fileData.contentType(), "type"); + } + + @Test(expected = DocumentNotFoundException.class) + public void shouldCatchAmazonServiceExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException { + initRetrieve(); + when(s3ClientMock.getObject(Matchers.anyObject())).thenThrow(new AmazonServiceException("serviceException")); + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + + s3DocumentStore.retrieveDocument(documentDataMock); + } + + @Test(expected = DocumentNotFoundException.class) + public void shouldCatchAmazonClientExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException { + initRetrieve(); + when(s3ClientMock.getObject(Matchers.anyObject())).thenThrow(new AmazonClientException("clientException")); + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + + s3DocumentStore.retrieveDocument(documentDataMock); + } + + @Test + public void shouldDeleteImageFromS3() throws IOException { + AmazonS3Client s3ClientMock = mock(AmazonS3Client.class); + S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + + s3DocumentStore.deleteImage(123L,"imagekey"); + + verify(s3ClientMock, times(1)).deleteObject(Matchers.anyObject()); + } + + private void initRetrieve() { + s3ClientMock = mock(AmazonS3Client.class); + + documentDataMock = mock(DocumentData.class); + when(documentDataMock.fileLocation()).thenReturn("path"); + when(documentDataMock.fileName()).thenReturn("name"); + when(documentDataMock.contentType()).thenReturn("type"); + } + + + private void initUpload(){ + inputStream = S3DocumentStoreTest.class.getResourceAsStream("DummyString"); + s3ClientMock = mock(AmazonS3Client.class); + documentCommand = new DocumentCommand(null, -1l, "parentEntityType", 1l, null, "someFileName", 3l, null, null, null); + } +} From 15311f6425e2770acb6d8f3ff9700c0183ab991f Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Mon, 3 Jun 2013 02:05:43 +0530 Subject: [PATCH 2/2] Refactoring document and image management --- .../core_db/V40__adding_S3_Support.sql | 28 --- .../core_db/V48__adding-S3-Support.sql | 70 ++++++ .../configuration/data/S3CredentialsData.java | 27 +++ ...viceProperty.java => ExternalService.java} | 16 +- .../domain/ExternalServiceRepository.java | 5 +- .../domain/ExternalServicesDomainService.java | 18 -- .../service/ExternalServicesConstants.java | 9 + .../ExternalServicesReadPlatformService.java | 9 + ...ternalServicesReadPlatformServiceImpl.java | 51 +++++ .../infrastructure/core/api/ApiConstants.java | 15 -- .../DefaultPlatformPasswordEncoder.java | 3 +- .../core/service/DocumentStore.java | 52 ----- .../core/service/DocumentStoreFactory.java | 45 ---- .../core/service/DocumentStoreType.java | 15 -- .../core/service/RandomStringGenerator.java | 33 --- .../core/service/S3ClientFactory.java | 24 --- .../core/service/S3DocumentStore.java | 201 ------------------ .../service/ReadReportingServiceImpl.java | 6 +- .../api/DocumentManagementApiResource.java | 9 +- .../api/ImagesApiResource.java} | 113 +++++----- .../command/DocumentCommand.java | 19 +- .../contentrepository/ContentRepository.java | 38 ++++ .../ContentRepositoryFactory.java | 41 ++++ .../ContentRepositoryUtils.java} | 57 ++++- .../FileSystemContentRepository.java} | 84 ++++---- .../S3ContentRepository.java | 157 ++++++++++++++ .../documentmanagement/data/DocumentData.java | 27 +-- .../documentmanagement/data/FileData.java | 17 +- .../documentmanagement/data/ImageData.java | 90 ++++++++ .../documentmanagement/domain/Document.java | 26 +-- .../documentmanagement/domain/Image.java | 49 +++++ .../domain/ImageRepository.java | 7 +- .../domain/StorageType.java | 30 +++ ...n.java => ContentManagementException.java} | 9 +- .../service/DocumentReadPlatformService.java | 2 +- .../DocumentReadPlatformServiceImpl.java | 66 +++--- ...WritePlatformServiceJpaRepositoryImpl.java | 55 +++-- .../service/ImageReadPlatformService.java | 14 ++ .../service/ImageReadPlatformServiceImpl.java | 79 +++++++ .../service/ImageWritePlatformService.java | 24 +++ ...WritePlatformServiceJpaRepositoryImpl.java | 103 +++++++++ .../portfolio/client/data/ClientData.java | 62 ++---- .../portfolio/client/data/ImageData.java | 78 ------- .../portfolio/client/domain/Client.java | 30 ++- .../portfolio/client/domain/Image.java | 62 ------ .../ClientReadPlatformServiceImpl.java | 38 +--- .../service/ClientWritePlatformService.java | 12 -- ...WritePlatformServiceJpaRepositoryImpl.java | 101 +-------- .../service/DocumentStoreFactoryTest.java | 43 ++-- .../core/service/FileDocumentStoreTest.java | 18 +- .../core/service/S3DocumentStoreTest.java | 91 ++++---- 51 files changed, 1204 insertions(+), 1074 deletions(-) delete mode 100644 mifosng-db/migrations/core_db/V40__adding_S3_Support.sql create mode 100755 mifosng-db/migrations/core_db/V48__adding-S3-Support.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/S3CredentialsData.java rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/{ExternalServiceProperty.java => ExternalService.java} (69%) delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesConstants.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiConstants.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java rename mifosng-provider/src/main/java/org/mifosplatform/{portfolio/client/api/ClientImagesApiResource.java => infrastructure/documentmanagement/api/ImagesApiResource.java} (62%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryFactory.java rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/{core/service/ImageUtils.java => documentmanagement/contentrepository/ContentRepositoryUtils.java} (70%) rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/{core/service/FileSystemDocumentStore.java => documentmanagement/contentrepository/FileSystemContentRepository.java} (59%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/S3ContentRepository.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Image.java rename mifosng-provider/src/main/java/org/mifosplatform/{portfolio/client => infrastructure/documentmanagement}/domain/ImageRepository.java (71%) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/StorageType.java rename mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/{DocumentManagementException.java => ContentManagementException.java} (59%) create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java diff --git a/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql b/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql deleted file mode 100644 index ff1e63dec..000000000 --- a/mifosng-db/migrations/core_db/V40__adding_S3_Support.sql +++ /dev/null @@ -1,28 +0,0 @@ -INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0); - - -CREATE TABLE IF NOT EXISTS m_external_services ( - name VARCHAR(150) NOT NULL, - value VARCHAR(250), - UNIQUE(name) -); - -INSERT INTO m_external_services (name) VALUES ('s3_bucket_name'); -INSERT INTO m_external_services (name) VALUES ('s3_access_key'); -INSERT INTO m_external_services (name) VALUES ('s3_secret_key'); - -ALTER TABLE m_document ADD COLUMN storage_type varchar(50); - -CREATE TABLE IF NOT EXISTS `m_image`( - `id` BIGINT NOT NULL AUTO_INCREMENT, - `client_id` BIGINT NOT NULL, - `key` varchar(500), - `storage_type` varchar(50), - FOREIGN KEY (`client_id`) REFERENCES m_client(`id`), - PRIMARY KEY (`id`), - UNIQUE(client_id) -); - -UPDATE m_document set storage_type='file_system'; - -insert into m_image (`client_id`,`key`,`storage_type`) select id,`image_key`,'file_system' from m_client where `image_key` IS NOT NULL; diff --git a/mifosng-db/migrations/core_db/V48__adding-S3-Support.sql b/mifosng-db/migrations/core_db/V48__adding-S3-Support.sql new file mode 100755 index 000000000..9fab76c5a --- /dev/null +++ b/mifosng-db/migrations/core_db/V48__adding-S3-Support.sql @@ -0,0 +1,70 @@ +INSERT INTO c_configuration (name,enabled) VALUES ('amazon-S3',0); + +/*New table for storing details of external services used*/ +CREATE TABLE `c_external_service` ( + `name` VARCHAR(150) NOT NULL, + `value` VARCHAR(250) NULL DEFAULT NULL, + UNIQUE INDEX `name` (`name`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +INSERT INTO c_external_service (name) VALUES ('s3_bucket_name'); +INSERT INTO c_external_service (name) VALUES ('s3_access_key'); +INSERT INTO c_external_service (name) VALUES ('s3_secret_key'); + + + +/*Image tables stores details of all images*/ +CREATE TABLE IF NOT EXISTS `m_image`( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `location` varchar(500), + `storage_type_enum` SMALLINT(5), + PRIMARY KEY (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +/*Client points to image table*/ +ALTER TABLE `m_client` + ADD COLUMN `image_id` BIGINT(20) NULL DEFAULT NULL AFTER `display_name`, + ADD CONSTRAINT `FK_m_client_m_image` FOREIGN KEY (`image_id`) REFERENCES `m_image` (`id`); + +/*Move existing image locations to new table*/ +drop procedure if exists migrate_customer_image_data; + +delimiter # +create procedure migrate_customer_image_data() +begin +declare v_counter int unsigned default 0; +declare num_of_clients INT DEFAULT 0; +declare curr_client INT DEFAULT 0; +declare curr_image INT DEFAULT 0; +declare prev_image INT DEFAULT 0; +SELECT COUNT(*) FROM m_client INTO num_of_clients; +while v_counter < num_of_clients do + INSERT INTO m_image(`location`,`storage_type_enum`) select image_key, 1 FROM m_client where image_key is not null LIMIT v_counter,1; + select ifnull(max(`id`),0) from m_image INTO curr_image; + select id FROM m_client where image_key is not null limit v_counter,1 INTO curr_client; + IF (prev_image != curr_image) THEN UPDATE m_client set image_id = curr_image where id=curr_client; + END IF; + set prev_image=curr_image; + set v_counter=v_counter+1; +end while; +delete from m_image where `location` is null; +end# + +delimiter ; +call migrate_customer_image_data(); + +/*Now drop the procedure*/ +drop procedure if exists migrate_customer_image_data; + +/*Delete image key from client*/ +ALTER TABLE `m_client` DROP COLUMN `image_key`; + + +/*Add storage type for m_document table and update existing documents to file storage*/ +ALTER TABLE m_document ADD COLUMN storage_type_enum SMALLINT(5); +UPDATE m_document set storage_type_enum=1; + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/S3CredentialsData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/S3CredentialsData.java new file mode 100755 index 000000000..914fe2474 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/S3CredentialsData.java @@ -0,0 +1,27 @@ +package org.mifosplatform.infrastructure.configuration.data; + +public class S3CredentialsData { + + private final String bucketName; + private final String accessKey; + private final String secretKey; + + public S3CredentialsData(String bucketName, String accessKey, String secretKey) { + this.bucketName = bucketName; + this.accessKey = accessKey; + this.secretKey = secretKey; + } + + public String getBucketName() { + return this.bucketName; + } + + public String getAccessKey() { + return this.accessKey; + } + + public String getSecretKey() { + return this.secretKey; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java similarity index 69% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java index e9c7ef86f..1fca5d62d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceProperty.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalService.java @@ -1,20 +1,21 @@ package org.mifosplatform.infrastructure.configuration.domain; + /** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -import org.springframework.data.jpa.domain.AbstractPersistable; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.UniqueConstraint; +import org.springframework.data.jpa.domain.AbstractPersistable; + @Entity -@Table(name = "m_external_services", uniqueConstraints = { @UniqueConstraint(columnNames = ("name"))}) -public class ExternalServiceProperty extends AbstractPersistable { +@Table(name = "c_external_service", uniqueConstraints = { @UniqueConstraint(columnNames = ("name")) }) +public class ExternalService extends AbstractPersistable { @SuppressWarnings("unused") @Column(name = "name", nullable = false) @@ -23,15 +24,14 @@ public class ExternalServiceProperty extends AbstractPersistable { @Column(name = "value", nullable = false) private String value; - public ExternalServiceProperty(final String name, final String value) { + public ExternalService(final String name, final String value) { this.name = name; this.value = value; } - public ExternalServiceProperty() { - } + protected ExternalService() {} - public String getValue(){ + public String getValue() { return this.value; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java index 8acc015d6..f950a9c59 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServiceRepository.java @@ -3,8 +3,7 @@ package org.mifosplatform.infrastructure.configuration.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface ExternalServiceRepository extends JpaRepository, - JpaSpecificationExecutor { +public interface ExternalServiceRepository extends JpaRepository, JpaSpecificationExecutor { - ExternalServiceProperty findOneByName(String name); + ExternalService findOneByName(String name); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java deleted file mode 100644 index e562c9ee0..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ExternalServicesDomainService.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.mifosplatform.infrastructure.configuration.domain; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class ExternalServicesDomainService { - private ExternalServiceRepository externalServiceRepository; - - @Autowired - public ExternalServicesDomainService(final ExternalServiceRepository externalServiceRepository) { - this.externalServiceRepository = externalServiceRepository; - } - - public String getValue(String name){ - return externalServiceRepository.findOneByName(name).getValue(); - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesConstants.java new file mode 100755 index 000000000..5d6d2127d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesConstants.java @@ -0,0 +1,9 @@ +package org.mifosplatform.infrastructure.configuration.service; + +public class ExternalServicesConstants { + + public static final String S3_BUCKET_NAME = "s3_bucket_name"; + public static final String S3_ACCESS_KEY = "s3_access_key"; + public static final String S3_SECRET_KEY = "s3_secret_key"; + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformService.java new file mode 100755 index 000000000..91ec373d4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformService.java @@ -0,0 +1,9 @@ +package org.mifosplatform.infrastructure.configuration.service; + +import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData; + +public interface ExternalServicesReadPlatformService { + + S3CredentialsData getS3Credentials(); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java new file mode 100644 index 000000000..83ec84d65 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java @@ -0,0 +1,51 @@ +package org.mifosplatform.infrastructure.configuration.service; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData; +import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.stereotype.Service; + +@Service +public class ExternalServicesReadPlatformServiceImpl implements ExternalServicesReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public ExternalServicesReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class S3CredentialsDataExtractor implements ResultSetExtractor { + + @Override + public S3CredentialsData extractData(ResultSet rs) throws SQLException, DataAccessException { + String accessKey = null; + String bucketName = null; + String secretKey = null; + while (rs.next()) { + if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_ACCESS_KEY)) { + accessKey = rs.getString("value"); + } else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_BUCKET_NAME)) { + bucketName = rs.getString("value"); + } else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.S3_SECRET_KEY)) { + secretKey = rs.getString("value"); + } + } + return new S3CredentialsData(bucketName, accessKey, secretKey); + } + } + + @Override + public S3CredentialsData getS3Credentials() { + final ResultSetExtractor resultSetExtractor = new S3CredentialsDataExtractor(); + final String sql = "SELECT es.name, es.value FROM c_external_service es where es.name in('s3_bucket_name','s3_access_key','s3_secret_key')"; + final S3CredentialsData s3CredentialsData = this.jdbcTemplate.query(sql, resultSetExtractor, new Object[] {}); + return s3CredentialsData; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiConstants.java deleted file mode 100644 index 266e57211..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/api/ApiConstants.java +++ /dev/null @@ -1,15 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.infrastructure.core.api; - -public class ApiConstants { - - // TODO:Vishwas Need to move these settings to the Database - public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5; - - // TODO:Vishwas Need to move these settings to the Database - public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1; -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/DefaultPlatformPasswordEncoder.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/DefaultPlatformPasswordEncoder.java index 846d90a82..e2ca920a0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/DefaultPlatformPasswordEncoder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/domain/DefaultPlatformPasswordEncoder.java @@ -13,12 +13,13 @@ import org.springframework.security.authentication.dao.SaltSource; import org.springframework.security.authentication.encoding.PasswordEncoder; import org.springframework.stereotype.Service; +@SuppressWarnings("deprecation") @Service(value = "applicationPasswordEncoder") @Scope("singleton") public class DefaultPlatformPasswordEncoder implements PlatformPasswordEncoder { private final PasswordEncoder passwordEncoder; - private final SaltSource saltSource; + private final SaltSource saltSource; @Autowired public DefaultPlatformPasswordEncoder(final PasswordEncoder passwordEncoder, final SaltSource saltSource) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java deleted file mode 100644 index 0722fd042..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStore.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - -import org.mifosplatform.infrastructure.core.api.ApiConstants; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; -import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; -import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; -import org.mifosplatform.infrastructure.documentmanagement.data.FileData; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; -import org.mifosplatform.portfolio.client.data.ImageData; - -import java.io.InputStream; - -public abstract class DocumentStore { - protected DocumentStoreType type; - - protected Integer maxFileSize = ApiConstants.MAX_FILE_UPLOAD_SIZE_IN_MB; - protected Integer maxImageSize = ApiConstants.MAX_IMAGE_UPLOAD_SIZE_IN_MB; - - public abstract String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException; - public abstract void deleteDocument(String documentName, String documentPath) throws DocumentManagementException; - public abstract FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException; - - public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException; - public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException; - public abstract void deleteImage(final Long resourceId, final String location); - public abstract ImageData retrieveImage(ImageData imageData); - - public abstract DocumentStoreType getType(); - - protected void validateFileSizeWithinPermissibleRange(Long fileSize, String name, int maxFileSize) { - /** - * Using Content-Length gives me size of the entire request, which is - * good enough for now for a fast fail as the length of the rest of the - * content i.e name and description while compared to the uploaded file - * size is negligible - **/ - if (fileSize != null && ((fileSize / (1024 * 1024)) > maxFileSize)) { - throw new DocumentManagementException(name, fileSize, - maxFileSize); - } - } - - public static DocumentStoreType getDocumentStoreType(String documentStoreType){ - if (documentStoreType.equals(DocumentStoreType.FILE_SYSTEM.getValue())) { - return DocumentStoreType.FILE_SYSTEM; - } else { - return DocumentStoreType.S3; - } - } - -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java deleted file mode 100644 index 32b4b05bd..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - -import com.amazonaws.services.s3.AmazonS3Client; -import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; -import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -@Component -public class DocumentStoreFactory { - - private ApplicationContext applicationContext; - - @Autowired - public DocumentStoreFactory(ApplicationContext applicationContext) { - this.applicationContext = applicationContext; - } - - public DocumentStore getInstanceFromConfiguration(){ - ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class); - if(configurationDomainServiceJpa.isAmazonS3Enabled()){ - return createS3DocumentStore(); - } - else { - return new FileSystemDocumentStore(); - } - } - - public DocumentStore getInstanceFromStorageType(DocumentStoreType documentStoreType){ - if(documentStoreType == DocumentStoreType.FILE_SYSTEM){ - return new FileSystemDocumentStore(); - } - else { - return createS3DocumentStore(); - } - } - private DocumentStore createS3DocumentStore() { - AmazonS3Client s3Client = applicationContext.getBean("s3ClientFactory", S3ClientFactory.class).getS3Client(); - ExternalServicesDomainService externalServicesDomainService = applicationContext.getBean("externalServicesDomainService", ExternalServicesDomainService.class); - return new S3DocumentStore(externalServicesDomainService.getValue("s3_bucket_name"), s3Client); - } - - -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java deleted file mode 100644 index ac6a34c01..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DocumentStoreType.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - -public enum DocumentStoreType { - FILE_SYSTEM("file_system"), S3("s3"); - - private String value; - - DocumentStoreType(String value){ - this.value = value; - } - - public String getValue() { - return value; - } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java deleted file mode 100644 index 31f1ecfd0..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/RandomStringGenerator.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - -import java.util.Random; - -public class RandomStringGenerator { - public static Random random = new Random(); - - /** - * Generate a random String - * - * @return - */ - public static String generateRandomString() { - String characters = "abcdefghijklmnopqrstuvwxyz123456789"; - int length = generateRandomNumber(); - char[] text = new char[length]; - for (int i = 0; i < length; i++) { - text[i] = characters.charAt(random.nextInt(characters.length())); - } - return new String(text); - } - - /** - * Generate a random number between 5 to 16 - * - * @return - */ - public static int generateRandomNumber() { - Random randomGenerator = new Random(); - return randomGenerator.nextInt(11) + 5; - } - -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java deleted file mode 100644 index 5274274f2..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3ClientFactory.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.services.s3.AmazonS3Client; -import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class S3ClientFactory { - - private ExternalServicesDomainService ExternalServicesDomainService; - - @Autowired - public S3ClientFactory(final ExternalServicesDomainService ExternalServicesDomainService) { - this.ExternalServicesDomainService = ExternalServicesDomainService; - } - - public AmazonS3Client getS3Client() { - String s3_access_key = ExternalServicesDomainService.getValue("s3_access_key"); - String s3_secret_key = ExternalServicesDomainService.getValue("s3_secret_key"); - return new AmazonS3Client(new BasicAWSCredentials(s3_access_key,s3_secret_key)); - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java deleted file mode 100644 index 9781ef9c9..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/S3DocumentStore.java +++ /dev/null @@ -1,201 +0,0 @@ -package org.mifosplatform.infrastructure.core.service; - - -import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.*; -import com.lowagie.text.pdf.codec.Base64; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; -import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; -import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; -import org.mifosplatform.infrastructure.documentmanagement.data.FileData; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; -import org.mifosplatform.portfolio.client.data.ImageData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; - -public class S3DocumentStore extends DocumentStore { - - private final static Logger logger = LoggerFactory.getLogger(S3DocumentStore.class); - - private final String s3BucketName; - private AmazonS3 s3Client; - - public S3DocumentStore(String s3BucketName, AmazonS3 s3Client) { - this.s3BucketName = s3BucketName; - this.s3Client = s3Client; - } - - @Override - public String saveDocument(InputStream toUpload, DocumentCommand documentCommand) throws DocumentManagementException { - String documentName = documentCommand.getFileName(); - validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize); - - String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId()); - String uploadDocFullPath = uploadDocFolder + File.separator + documentName; - - uploadDocument(toUpload, uploadDocFullPath); - return uploadDocFullPath; - } - - @Override - public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException { - try{ - deleteObjectFromS3(documentPath); - }catch (AmazonServiceException ase){ - deleteObjectAmazonServiceExceptionMessage(ase); - throw new DocumentManagementException(documentName); - }catch (AmazonClientException ace){ - deleteObjectAmazonClientExceptionMessage(ace); - throw new DocumentManagementException(documentName); - } - } - - @Override - public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException { - validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize); - - String uploadImageLocation = generateClientImageParentDirectory(resourceId); - String fileLocation = uploadImageLocation + File.separator + imageName; - - uploadDocument(toUploadInputStream, fileLocation); - return fileLocation; - } - - @Override - public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException { - String uploadImageLocation = generateClientImageParentDirectory(resourceId); - String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension(); - InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString())); - - uploadDocument(toUploadInputStream, fileLocation); - return fileLocation; - } - - @Override - public void deleteImage(Long resourceId, String location) { - try { - deleteObjectFromS3(location); - } catch (AmazonServiceException ase) { - deleteObjectAmazonServiceExceptionMessage(ase); - logger.warn("Unable to delete image associated with clients with Id " + resourceId); - } catch (AmazonClientException ace) { - deleteObjectAmazonClientExceptionMessage(ace); - logger.warn("Unable to delete image associated with clients with Id " + resourceId); - } - } - - private void deleteObjectAmazonClientExceptionMessage(AmazonClientException ace) { - String message = "Caught an AmazonClientException." + - "Error Message: " + ace.getMessage(); - logger.error(message); - } - - private void deleteObjectAmazonServiceExceptionMessage(AmazonServiceException ase) { - String message = "Caught an AmazonServiceException." + - "Error Message: " + ase.getMessage() + - "HTTP Status Code: " + ase.getStatusCode() + - "AWS Error Code: " + ase.getErrorCode() + - "Error Type: " + ase.getErrorType() + - "Request ID: " + ase.getRequestId(); - logger.error(message); - } - - private void deleteObjectFromS3(String location) { - s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location)); - } - - @Override - public DocumentStoreType getType() { - return DocumentStoreType.S3; - } - - @Override - public FileData retrieveDocument(DocumentData documentData) throws DocumentNotFoundException { - FileData fileData = null; - String fileName = documentData.fileName(); - try { - logger.info("Downloading an object"); - S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, documentData.fileLocation())); - fileData = new FileData(s3object.getObjectContent(), fileName, documentData.contentType()); - } catch (AmazonServiceException ase) { - getObjectAmazonServiceExceptionMessage(ase,fileName); - throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId()); - } catch (AmazonClientException ace) { - getObjectAmazonClientExceptionMessage(ace,fileName); - throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId()); - } - return fileData; - } - - @Override - public ImageData retrieveImage(ImageData imageData) { - S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.imageKey())); - imageData.setContent(s3object.getObjectContent()); - return imageData; - } - - String generateFileParentDirectory(String entityType, Long entityId) { - return "documents" + File.separator + entityType + File.separator + entityId + File.separator + RandomStringGenerator.generateRandomString(); - } - - String generateClientImageParentDirectory(Long resourceId) { - return "images" + File.separator + "clients" + File.separator + resourceId; - } - - private void uploadDocument(InputStream inputStream, String s3UploadLocation) throws DocumentManagementException { - try { - logger.info("Uploading a new object to S3 from a file to " + s3UploadLocation); - s3Client.putObject(new PutObjectRequest(this.s3BucketName, s3UploadLocation, inputStream, new ObjectMetadata())); - - } catch (AmazonServiceException ase) { - String message = "Caught an AmazonServiceException, which " + "means your request made it " + "to Amazon S3, but was rejected with an error response" + - " for some reason. \n Error Message: " + ase.getMessage() + - "HTTP Status Code: " + ase.getStatusCode() + - "AWS Error Code: " + ase.getErrorCode() + - "Error Type: " + ase.getErrorType() + - "Request ID: " + ase.getRequestId(); - - logger.error(message); - throw new DocumentManagementException(message); - } catch (AmazonClientException ace) { - String message = "Caught an AmazonClientException, which " + - "means the client encountered " + - "an internal error while trying to " + - "communicate with S3, " + - "such as not being able to access the network." + - "Error Message: " + ace.getMessage(); - logger.error(message); - throw new DocumentManagementException(message); - } - } - - private void getObjectAmazonClientExceptionMessage(AmazonClientException ace, String filename) { - String message = "Caught an AmazonClientException, which means the client encountered " + - "an internal error while trying to communicate with S3, " + - "such as not being able to access the network." + - "Error Message: " + ace.getMessage() + - "FileName: " + filename; - - logger.error(message); - } - - private void getObjectAmazonServiceExceptionMessage(AmazonServiceException ase, String filename) { - String message = "Caught an AmazonServiceException, which means your request made it " + - "to Amazon S3, but was rejected with an error response" + - " for some reason." + - "Error Message: " + ase.getMessage() + - "HTTP Status Code: " + ase.getStatusCode() + - "AWS Error Code: " + ase.getErrorCode() + - "Error Type: " + ase.getErrorType() + - "Request ID: " + ase.getRequestId() + - "FileName: " + filename; - logger.error(message); - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java index 3e573af66..cace21ef3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadReportingServiceImpl.java @@ -29,7 +29,6 @@ import javax.ws.rs.core.StreamingOutput; import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.FileSystemDocumentStore; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData; import org.mifosplatform.infrastructure.dataqueries.data.ReportData; @@ -38,6 +37,7 @@ import org.mifosplatform.infrastructure.dataqueries.data.ReportParameterJoinData import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData; import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData; import org.mifosplatform.infrastructure.dataqueries.exception.ReportNotFoundException; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.useradministration.domain.AppUser; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; @@ -243,7 +243,7 @@ public class ReadReportingServiceImpl implements ReadReportingService { if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled", "Pentaho is not enabled"); } - final String reportPath = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt"; + final String reportPath = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + "pentahoReports" + File.separator + reportName + ".prpt"; logger.info("Report path: " + reportPath); // load report definition @@ -357,7 +357,7 @@ public class ReadReportingServiceImpl implements ReadReportingService { @Override public String retrieveReportPDF(final String reportName, final String type, final Map queryParams) { - String fileLocation = FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ""; + String fileLocation = FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + ""; if (!new File(fileLocation).isDirectory()) { new File(fileLocation).mkdirs(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java index ff48aa1e8..dbf103a2b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/DocumentManagementApiResource.java @@ -52,8 +52,8 @@ public class DocumentManagementApiResource { @Autowired public DocumentManagementApiResource(final PlatformSecurityContext context, - final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService, - final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer toApiJsonSerializer) { + final DocumentReadPlatformService documentReadPlatformService, final DocumentWritePlatformService documentWritePlatformService, + final ApiRequestParameterHelper apiRequestParameterHelper, final ToApiJsonSerializer toApiJsonSerializer) { this.context = context; this.documentReadPlatformService = documentReadPlatformService; this.documentWritePlatformService = documentWritePlatformService; @@ -150,9 +150,6 @@ public class DocumentManagementApiResource { final DocumentData documentData = this.documentReadPlatformService.retrieveDocument(entityType, entityId, documentId); - // we do not want to send document location as a part of the response - documentData.setLocation(null); - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, documentData, this.RESPONSE_DATA_PARAMETERS); } @@ -166,7 +163,7 @@ public class DocumentManagementApiResource { this.context.authenticatedUser().validateHasReadPermission(this.SystemEntityType); - final FileData fileData = this.documentReadPlatformService.retrieveDocumentAsFile(entityType, entityId, documentId); + final FileData fileData = this.documentReadPlatformService.retrieveFileData(entityType, entityId, documentId); final ResponseBuilder response = Response.ok(fileData.file()); response.header("Content-Disposition", "attachment; filename=\"" + fileData.name() + "\""); response.header("Content-Type", fileData.contentType()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java similarity index 62% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java index 087c08196..dbd576106 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientImagesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java @@ -3,51 +3,63 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.client.api; +package org.mifosplatform.infrastructure.documentmanagement.api; + +import java.io.InputStream; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils.IMAGE_FILE_EXTENSION; +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.service.ImageReadPlatformService; +import org.mifosplatform.infrastructure.documentmanagement.service.ImageWritePlatformService; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.portfolio.client.data.ClientData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; import com.lowagie.text.pdf.codec.Base64; import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataBodyPart; import com.sun.jersey.multipart.FormDataParam; -import org.apache.commons.lang.StringUtils; -import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; -import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; -import org.mifosplatform.infrastructure.core.service.ImageUtils; -import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; -import org.mifosplatform.portfolio.client.data.ClientData; -import org.mifosplatform.portfolio.client.exception.ImageNotFoundException; -import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; -import org.mifosplatform.portfolio.client.service.ClientWritePlatformService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; -import java.io.InputStream; - -@Path("/clients/{clientId}/images") +/** + * TODO Vishwas need to change the url to entity/entityId/images to make the + * image management API's generic for other entities like staff etc + **/ +@Path("{clients}/{clientId}/images") @Component @Scope("singleton") -public class ClientImagesApiResource { +public class ImagesApiResource { private final PlatformSecurityContext context; - private final ClientReadPlatformService clientReadPlatformService; - private final ClientWritePlatformService clientWritePlatformService; + private final ImageReadPlatformService imageReadPlatformService; + private final ImageWritePlatformService imageWritePlatformService; private final DefaultToApiJsonSerializer toApiJsonSerializer; - private final static Logger logger = LoggerFactory.getLogger(ClientImagesApiResource.class); @Autowired - public ClientImagesApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, - final ClientWritePlatformService clientWritePlatformService, final DefaultToApiJsonSerializer toApiJsonSerializer) { + public ImagesApiResource(final PlatformSecurityContext context, final ImageReadPlatformService readPlatformService, + final ImageWritePlatformService imageWritePlatformService, final DefaultToApiJsonSerializer toApiJsonSerializer) { this.context = context; - this.clientReadPlatformService = readPlatformService; - this.clientWritePlatformService = clientWritePlatformService; + this.imageReadPlatformService = readPlatformService; + this.imageWritePlatformService = imageWritePlatformService; this.toApiJsonSerializer = toApiJsonSerializer; } @@ -64,10 +76,10 @@ public class ClientImagesApiResource { // TODO: vishwas might need more advances validation (like reading magic // number) for handling malicious clients // and clients not setting mime type - ImageUtils.validateClientImageNotEmpty(fileDetails.getFileName()); - ImageUtils.validateImageMimeType(bodyPart.getMediaType().toString()); + ContentRepositoryUtils.validateClientImageNotEmpty(fileDetails.getFileName()); + ContentRepositoryUtils.validateImageMimeType(bodyPart.getMediaType().toString()); - final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(), + final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, fileDetails.getFileName(), inputStream, fileSize); return this.toApiJsonSerializer.serialize(result); @@ -81,9 +93,9 @@ public class ClientImagesApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String addNewClientImage(@PathParam("clientId") final Long clientId, final String jsonRequestBody) { - final Base64EncodedImage base64EncodedImage = ImageUtils.extractImageFromDataURL(jsonRequestBody); + final Base64EncodedImage base64EncodedImage = ContentRepositoryUtils.extractImageFromDataURL(jsonRequestBody); - final CommandProcessingResult result = this.clientWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage); + final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateClientImage(clientId, base64EncodedImage); return this.toApiJsonSerializer.serialize(result); } @@ -98,19 +110,17 @@ public class ClientImagesApiResource { context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); - final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId); - - if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } + final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId); // TODO: Need a better way of determining image type - String imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue(); - if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { - imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue(); - } else if (StringUtils.endsWith(clientData.imageKey(), ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { - imageDataURISuffix = ImageUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue(); + String imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue(); + if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { + imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.GIF.getValue(); + } else if (StringUtils.endsWith(imageData.location(), ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { + imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.PNG.getValue(); } - String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(clientData.image()); + String clientImageAsBase64Text = imageDataURISuffix + Base64.encodeBytes(imageData.getContent()); return clientImageAsBase64Text; } @@ -120,16 +130,15 @@ public class ClientImagesApiResource { public Response downloadClientImage(@PathParam("clientId") final Long clientId) { context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); - final ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId); + final ImageData imageData = this.imageReadPlatformService.retrieveClientImage(clientId); - if (clientData.imageKeyDoesNotExist()) { throw new ImageNotFoundException("clients", clientId); } - - ResponseBuilder response = Response.ok(clientData.image()); - response.header("Content-Disposition", "attachment; filename=\"" + clientData.imageName() + "\""); + ResponseBuilder response = Response.ok(imageData.getContent()); + response.header("Content-Disposition", "attachment; filename=\"" + imageData.getEntityDisplayName() + IMAGE_FILE_EXTENSION.JPEG + + "\""); // TODO: Need a better way of determining image type - response.header("Content-Type", clientData.imageContentType()); + response.header("Content-Type", imageData.contentType()); return response.build(); } @@ -163,7 +172,7 @@ public class ClientImagesApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) public String deleteClientImage(@PathParam("clientId") final Long clientId) { - this.clientWritePlatformService.deleteClientImage(clientId); + this.imageWritePlatformService.deleteClientImage(clientId); return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId)); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java index 2c6e636c0..1706f7f8e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/command/DocumentCommand.java @@ -5,8 +5,6 @@ */ package org.mifosplatform.infrastructure.documentmanagement.command; -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; - import java.util.Set; /** @@ -24,9 +22,9 @@ public class DocumentCommand { private Long size; private String type; private String location; + private Integer storageType; private final Set modifiedParameters; - private DocumentStoreType storageType; public DocumentCommand(final Set modifiedParameters, final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size, final String type, final String description, final String location) { @@ -98,6 +96,14 @@ public class DocumentCommand { this.location = location; } + public Integer getStorageType() { + return this.storageType; + } + + public void setStorageType(Integer storageType) { + this.storageType = storageType; + } + public boolean isNameChanged() { return this.modifiedParameters.contains("name"); } @@ -122,11 +128,4 @@ public class DocumentCommand { return this.modifiedParameters.contains("location"); } - public void setStorageType(DocumentStoreType storageType) { - this.storageType = storageType; - } - - public String getStorageType(){ - return storageType.getValue(); - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepository.java new file mode 100644 index 000000000..96c10cc65 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepository.java @@ -0,0 +1,38 @@ +package org.mifosplatform.infrastructure.documentmanagement.contentrepository; + +import java.io.InputStream; + +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; + +public interface ContentRepository { + + public StorageType type = null; + + // TODO:Vishwas Need to move these settings to the Database + public static final Integer MAX_FILE_UPLOAD_SIZE_IN_MB = 5; + + // TODO:Vishwas Need to move these settings to the Database + public static final Integer MAX_IMAGE_UPLOAD_SIZE_IN_MB = 1; + + public abstract String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand); + + public abstract void deleteFile(String fileName, String documentPath); + + public abstract FileData fetchFile(DocumentData documentData); + + public abstract String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize); + + public abstract String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName); + + public abstract void deleteImage(final Long resourceId, final String location); + + public abstract ImageData fetchImage(ImageData imageData); + + public abstract StorageType getStorageType(); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryFactory.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryFactory.java new file mode 100644 index 000000000..837ad7dfc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryFactory.java @@ -0,0 +1,41 @@ +package org.mifosplatform.infrastructure.documentmanagement.contentrepository; + +import org.mifosplatform.infrastructure.configuration.data.S3CredentialsData; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.mifosplatform.infrastructure.configuration.service.ExternalServicesReadPlatformService; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +@Component +public class ContentRepositoryFactory { + + private ApplicationContext applicationContext; + private ExternalServicesReadPlatformService externalServicesReadPlatformService; + + @Autowired + public ContentRepositoryFactory(final ApplicationContext applicationContext, + final ExternalServicesReadPlatformService externalServicesReadPlatformService) { + this.applicationContext = applicationContext; + this.externalServicesReadPlatformService = externalServicesReadPlatformService; + } + + public ContentRepository getRepository() { + ConfigurationDomainService configurationDomainServiceJpa = applicationContext.getBean("configurationDomainServiceJpa", + ConfigurationDomainService.class); + if (configurationDomainServiceJpa.isAmazonS3Enabled()) { return createS3DocumentStore(); } + return new FileSystemContentRepository(); + } + + public ContentRepository getRepository(StorageType documentStoreType) { + if (documentStoreType == StorageType.FILE_SYSTEM) { return new FileSystemContentRepository(); } + return createS3DocumentStore(); + } + + private ContentRepository createS3DocumentStore() { + S3CredentialsData s3CredentialsData = externalServicesReadPlatformService.getS3Credentials(); + return new S3ContentRepository(s3CredentialsData.getBucketName(), s3CredentialsData.getSecretKey(), + s3CredentialsData.getAccessKey()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java similarity index 70% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java index 5c55495f9..730bf9986 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/ImageUtils.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/ContentRepositoryUtils.java @@ -1,4 +1,8 @@ -package org.mifosplatform.infrastructure.core.service; +package org.mifosplatform.infrastructure.documentmanagement.contentrepository; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.core.data.ApiParameterError; @@ -6,11 +10,11 @@ import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; import org.mifosplatform.infrastructure.core.exception.ImageDataURLNotValidException; import org.mifosplatform.infrastructure.core.exception.ImageUploadException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException; -import java.util.ArrayList; -import java.util.List; +public class ContentRepositoryUtils { -public class ImageUtils { + public static Random random = new Random(); public static enum IMAGE_MIME_TYPE { GIF("image/gif"), JPEG("image/jpeg"), PNG("image/png"); @@ -54,21 +58,20 @@ public class ImageUtils { return this.value; } } + /** * Validates that passed in Mime type maps to known image mime types - * + * * @param mimeType */ public static void validateImageMimeType(String mimeType) { if (!(mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.GIF.getValue()) || mimeType.equalsIgnoreCase(IMAGE_MIME_TYPE.JPEG.getValue()) || mimeType - .equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { - throw new ImageUploadException(); - } + .equalsIgnoreCase(IMAGE_MIME_TYPE.PNG.getValue()))) { throw new ImageUploadException(); } } /** * Extracts Image from a Data URL - * + * * @param mimeType */ public static Base64EncodedImage extractImageFromDataURL(String dataURL) { @@ -90,6 +93,17 @@ public class ImageUtils { return new Base64EncodedImage(base64EncodedString, fileExtension); } + public static void validateFileSizeWithinPermissibleRange(Long fileSize, String name) { + /** + * Using Content-Length gives me size of the entire request, which is + * good enough for now for a fast fail as the length of the rest of the + * content i.e name and description while compared to the uploaded file + * size is negligible + **/ + if (fileSize != null && ((fileSize / (1024 * 1024)) > ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB)) { throw new ContentManagementException( + name, fileSize, ContentRepository.MAX_FILE_UPLOAD_SIZE_IN_MB); } + } + public static void validateClientImageNotEmpty(String imageFileName) { List dataValidationErrors = new ArrayList(); if (imageFileName == null) { @@ -102,4 +116,29 @@ public class ImageUtils { dataValidationErrors); } } + + /** + * Generate a random String + * + * @return + */ + public static String generateRandomString() { + String characters = "abcdefghijklmnopqrstuvwxyz123456789"; + int length = generateRandomNumber(); + char[] text = new char[length]; + for (int i = 0; i < length; i++) { + text[i] = characters.charAt(random.nextInt(characters.length())); + } + return new String(text); + } + + /** + * Generate a random number between 5 to 16 + * + * @return + */ + public static int generateRandomNumber() { + Random randomGenerator = new Random(); + return randomGenerator.nextInt(11) + 5; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/FileSystemContentRepository.java similarity index 59% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/FileSystemContentRepository.java index 6e1a51c9f..0f065d925 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/FileSystemDocumentStore.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/FileSystemContentRepository.java @@ -3,57 +3,63 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.infrastructure.core.service; +package org.mifosplatform.infrastructure.documentmanagement.contentrepository; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; -import com.lowagie.text.pdf.codec.Base64; import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; import org.mifosplatform.infrastructure.documentmanagement.data.FileData; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; -import org.mifosplatform.portfolio.client.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; +import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.*; +import com.lowagie.text.pdf.codec.Base64; -public class FileSystemDocumentStore extends DocumentStore { +public class FileSystemContentRepository implements ContentRepository { - private final static Logger logger = LoggerFactory.getLogger(FileSystemDocumentStore.class); + private final static Logger logger = LoggerFactory.getLogger(FileSystemContentRepository.class); public static final String MIFOSX_BASE_DIR = System.getProperty("user.home") + File.separator + ".mifosx"; - @Override - public String saveDocument(InputStream uploadedInputStream, DocumentCommand documentCommand) throws DocumentManagementException { - String documentName = documentCommand.getFileName(); - String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId()); + public String saveFile(InputStream uploadedInputStream, DocumentCommand documentCommand) { + String fileName = documentCommand.getFileName(); + String uploadDocumentLocation = generateFileParentDirectory(documentCommand.getParentEntityType(), + documentCommand.getParentEntityId()); - validateFileSizeWithinPermissibleRange(documentCommand.getSize(), documentName, maxFileSize); + ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName); makeDirectories(uploadDocumentLocation); - String fileLocation = uploadDocumentLocation + File.separator + documentName; + String fileLocation = uploadDocumentLocation + File.separator + fileName; - writeFileToFileSystem(uploadedInputStream, fileLocation); + writeFileToFileSystem(fileName, uploadedInputStream, fileLocation); return fileLocation; } - @Override - public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) throws DocumentManagementException { + public String saveImage(InputStream uploadedInputStream, Long resourceId, String imageName, Long fileSize) { String uploadImageLocation = generateClientImageParentDirectory(resourceId); - validateFileSizeWithinPermissibleRange(fileSize, imageName, maxImageSize); + ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName); makeDirectories(uploadImageLocation); String fileLocation = uploadImageLocation + File.separator + imageName; - writeFileToFileSystem(uploadedInputStream, fileLocation); + writeFileToFileSystem(imageName, uploadedInputStream, fileLocation); return fileLocation; } @Override - public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) throws DocumentManagementException { + public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) { String uploadImageLocation = generateClientImageParentDirectory(resourceId); makeDirectories(uploadImageLocation); @@ -65,8 +71,8 @@ public class FileSystemDocumentStore extends DocumentStore { out.write(imgBytes); out.flush(); out.close(); - }catch (IOException ioe){ - throw new DocumentManagementException(ioe.getMessage()); + } catch (IOException ioe) { + throw new ContentManagementException(imageName, ioe.getMessage()); } return fileLocation; } @@ -81,9 +87,9 @@ public class FileSystemDocumentStore extends DocumentStore { } @Override - public void deleteDocument(String documentName, String documentPath) throws DocumentManagementException{ + public void deleteFile(String fileName, String documentPath) { final boolean fileDeleted = deleteFile(documentPath); - if (!fileDeleted) { throw new DocumentManagementException(documentName); } + if (!fileDeleted) { throw new ContentManagementException(fileName, null); } } private boolean deleteFile(String documentPath) { @@ -92,42 +98,43 @@ public class FileSystemDocumentStore extends DocumentStore { } @Override - public DocumentStoreType getType() { - return DocumentStoreType.FILE_SYSTEM; + public StorageType getStorageType() { + return StorageType.FILE_SYSTEM; } @Override - public FileData retrieveDocument(DocumentData documentData){ + public FileData fetchFile(DocumentData documentData) { File file = new File(documentData.fileLocation()); return new FileData(file, documentData.fileName(), documentData.contentType()); } @Override - public ImageData retrieveImage(ImageData imageData) { - File file = new File(imageData.imageKey()); - imageData.setContent(file); + public ImageData fetchImage(ImageData imageData) { + File file = new File(imageData.location()); + imageData.updateContent(file); return imageData; } /** * Generate the directory path for storing the new document - * + * * @param entityType * @param entityId * @return */ private String generateFileParentDirectory(String entityType, Long entityId) { - return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() - + File.separator + "documents" + File.separator + entityType + File.separator + entityId + File.separator - + RandomStringGenerator.generateRandomString(); + return FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + File.separator + "documents" + File.separator + + entityType + File.separator + entityId + File.separator + ContentRepositoryUtils.generateRandomString(); } /** * Generate directory path for storing new Image */ private String generateClientImageParentDirectory(final Long resourceId) { - return FileSystemDocumentStore.MIFOSX_BASE_DIR + File.separator + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() - + File.separator + "images" + File.separator + "clients" + File.separator + resourceId; + return FileSystemContentRepository.MIFOSX_BASE_DIR + File.separator + + ThreadLocalContextUtil.getTenant().getName().replaceAll(" ", "").trim() + File.separator + "images" + File.separator + + "clients" + File.separator + resourceId; } /** @@ -139,9 +146,7 @@ public class FileSystemDocumentStore extends DocumentStore { } } - - private void writeFileToFileSystem(InputStream uploadedInputStream, String fileLocation) throws DocumentManagementException { - + private void writeFileToFileSystem(String fileName, InputStream uploadedInputStream, String fileLocation) { try { OutputStream out = new FileOutputStream(new File(fileLocation)); int read = 0; @@ -153,8 +158,7 @@ public class FileSystemDocumentStore extends DocumentStore { out.flush(); out.close(); } catch (IOException ioException) { - throw new DocumentManagementException("IO Exception caught while writing to FileSystem" + ioException.getMessage()); + throw new ContentManagementException(fileName, ioException.getMessage()); } } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/S3ContentRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/S3ContentRepository.java new file mode 100644 index 000000000..fb80d9784 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/contentrepository/S3ContentRepository.java @@ -0,0 +1,157 @@ +package org.mifosplatform.infrastructure.documentmanagement.contentrepository; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; + +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; +import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.S3Object; +import com.lowagie.text.pdf.codec.Base64; + +public class S3ContentRepository implements ContentRepository { + + private final static Logger logger = LoggerFactory.getLogger(S3ContentRepository.class); + + private final String s3BucketName; + private AmazonS3 s3Client; + + public S3ContentRepository(String bucketName, String secretKey, String accessKey) { + this.s3BucketName = bucketName; + this.s3Client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey)); + } + + @Override + public String saveFile(InputStream toUpload, DocumentCommand documentCommand) { + String fileName = documentCommand.getFileName(); + ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(documentCommand.getSize(), fileName); + + String uploadDocFolder = generateFileParentDirectory(documentCommand.getParentEntityType(), documentCommand.getParentEntityId()); + String uploadDocFullPath = uploadDocFolder + File.separator + fileName; + + uploadDocument(fileName, toUpload, uploadDocFullPath); + return uploadDocFullPath; + } + + @Override + public void deleteFile(String documentName, String documentPath) { + try { + deleteObjectFromS3(documentPath); + } catch (AmazonClientException ace) { + throw new ContentManagementException(documentName, ace.getMessage()); + } + } + + @Override + public String saveImage(InputStream toUploadInputStream, Long resourceId, String imageName, Long fileSize) { + ContentRepositoryUtils.validateFileSizeWithinPermissibleRange(fileSize, imageName); + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + String fileLocation = uploadImageLocation + File.separator + imageName; + + uploadDocument(imageName, toUploadInputStream, fileLocation); + return fileLocation; + } + + @Override + public String saveImage(Base64EncodedImage base64EncodedImage, Long resourceId, String imageName) { + String uploadImageLocation = generateClientImageParentDirectory(resourceId); + String fileLocation = uploadImageLocation + File.separator + imageName + base64EncodedImage.getFileExtension(); + InputStream toUploadInputStream = new ByteArrayInputStream(Base64.decode(base64EncodedImage.getBase64EncodedString())); + + uploadDocument(imageName, toUploadInputStream, fileLocation); + return fileLocation; + } + + @Override + public void deleteImage(Long resourceId, String location) { + try { + deleteObjectFromS3(location); + } catch (AmazonServiceException ase) { + deleteObjectAmazonServiceExceptionMessage(ase); + logger.warn("Unable to delete image associated with clients with Id " + resourceId); + } catch (AmazonClientException ace) { + deleteObjectAmazonClientExceptionMessage(ace); + logger.warn("Unable to delete image associated with clients with Id " + resourceId); + } + } + + @Override + public StorageType getStorageType() { + return StorageType.S3; + } + + @Override + public FileData fetchFile(DocumentData documentData) throws DocumentNotFoundException { + FileData fileData = null; + String fileName = documentData.fileName(); + try { + logger.info("Downloading an object"); + S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, documentData.fileLocation())); + fileData = new FileData(s3object.getObjectContent(), fileName, documentData.contentType()); + } catch (AmazonClientException ace) { + logger.error(ace.getMessage()); + throw new DocumentNotFoundException(documentData.getParentEntityType(), documentData.getParentEntityId(), documentData.getId()); + } + return fileData; + } + + @Override + public ImageData fetchImage(ImageData imageData) { + S3Object s3object = s3Client.getObject(new GetObjectRequest(s3BucketName, imageData.location())); + imageData.updateContent(s3object.getObjectContent()); + return imageData; + } + + private void deleteObjectAmazonClientExceptionMessage(AmazonClientException ace) { + String message = "Caught an AmazonClientException." + "Error Message: " + ace.getMessage(); + logger.error(message); + } + + private void deleteObjectAmazonServiceExceptionMessage(AmazonServiceException ase) { + String message = "Caught an AmazonServiceException." + "Error Message: " + ase.getMessage() + "HTTP Status Code: " + + ase.getStatusCode() + "AWS Error Code: " + ase.getErrorCode() + "Error Type: " + ase.getErrorType() + + "Request ID: " + ase.getRequestId(); + logger.error(message); + } + + private String generateFileParentDirectory(String entityType, Long entityId) { + return "documents" + File.separator + entityType + File.separator + entityId + File.separator + + ContentRepositoryUtils.generateRandomString(); + } + + private String generateClientImageParentDirectory(Long resourceId) { + return "images" + File.separator + "clients" + File.separator + resourceId; + } + + private void deleteObjectFromS3(String location) { + s3Client.deleteObject(new DeleteObjectRequest(s3BucketName, location)); + } + + private void uploadDocument(String filename, InputStream inputStream, String s3UploadLocation) throws ContentManagementException { + try { + logger.info("Uploading a new object to S3 from a file to " + s3UploadLocation); + s3Client.putObject(new PutObjectRequest(this.s3BucketName, s3UploadLocation, inputStream, new ObjectMetadata())); + } catch (AmazonClientException ace) { + String message = ace.getMessage(); + throw new ContentManagementException(filename, message); + } + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java index 6cf339bef..25d155473 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/DocumentData.java @@ -5,19 +5,15 @@ */ package org.mifosplatform.infrastructure.documentmanagement.data; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; /** * Immutable data object represent document being managed on platform. */ public class DocumentData { - @SuppressWarnings("unused") private final Long id; - @SuppressWarnings("unused") private final String parentEntityType; - @SuppressWarnings("unused") private final Long parentEntityId; @SuppressWarnings("unused") private final String name; @@ -27,12 +23,11 @@ public class DocumentData { private final String type; @SuppressWarnings("unused") private final String description; - private String location; - - private String storageType; + private final String location; + private final Integer storageType; public DocumentData(final Long id, final String parentEntityType, final Long parentEntityId, final String name, final String fileName, - final Long size, final String type, final String description, final String location, String storageType) { + final Long size, final String type, final String description, final String location, Integer storageType) { this.id = id; this.parentEntityType = parentEntityType; this.parentEntityId = parentEntityId; @@ -57,23 +52,19 @@ public class DocumentData { return this.location; } - public void setLocation(final String location) { - this.location = location; + public StorageType storageType() { + return StorageType.fromInt(storageType); } - public DocumentStoreType storeType(){ - return DocumentStore.getDocumentStoreType(storageType); - } - - public String getParentEntityType(){ + public String getParentEntityType() { return this.parentEntityType; } - public Long getParentEntityId(){ + public Long getParentEntityId() { return this.parentEntityId; } - public Long getId(){ + public Long getId() { return this.id; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java index bbaad808f..6c7761154 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/FileData.java @@ -9,6 +9,7 @@ import java.io.FileNotFoundException; import java.io.InputStream; public class FileData { + private static final Logger logger = LoggerFactory.getLogger(FileData.class); private final File file; @@ -38,19 +39,13 @@ public class FileData { return fileName; } - public InputStream file(){ - try{ - if(inputStream == null){ - return new FileInputStream(file); - } - else { - return inputStream; - } - - } catch (FileNotFoundException e){ + public InputStream file() { + try { + if (inputStream == null) { return new FileInputStream(file); } + return inputStream; + } catch (FileNotFoundException e) { logger.error(e.toString()); return null; } } } - diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java new file mode 100644 index 000000000..7a25e895d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/data/ImageData.java @@ -0,0 +1,90 @@ +package org.mifosplatform.infrastructure.documentmanagement.data; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.lang.StringUtils; +import org.apache.poi.util.IOUtils; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; + +public class ImageData { + + @SuppressWarnings("unused") + private final Long imageId; + private final String location; + private final Integer storageType; + private final String entityDisplayName; + + private File file; + private String contentType; + private InputStream inputStream; + + public ImageData(Long imageId, String location, Integer storageType, String entityDisplayName) { + this.imageId = imageId; + this.location = location; + this.storageType = storageType; + this.entityDisplayName = entityDisplayName; + } + + public byte[] getContent() { + // TODO Vishwas Fix error handling + try { + if (inputStream == null) { + FileInputStream fileInputStream = new FileInputStream(file); + return IOUtils.toByteArray(fileInputStream); + } + + return IOUtils.toByteArray(inputStream); + } catch (IOException e) { + return null; + } + } + + private String setImageContentType() { + String contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.JPEG.getValue(); + + if (file != null) { + String fileName = file.getName(); + + if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { + contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.GIF.getValue(); + } else if (StringUtils.endsWith(fileName, ContentRepositoryUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { + contentType = ContentRepositoryUtils.IMAGE_MIME_TYPE.PNG.getValue(); + } + } + return contentType; + } + + public String contentType() { + return this.contentType; + } + + public StorageType storageType() { + return StorageType.fromInt(storageType); + } + + public String name() { + return this.file.getName(); + } + + public String location() { + return this.location; + } + + public void updateContent(InputStream objectContent) { + this.inputStream = objectContent; + } + + public void updateContent(File file) { + this.file = file; + setImageContentType(); + } + + public String getEntityDisplayName() { + return this.entityDisplayName; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java index 33e0747d2..5ae3e0a73 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Document.java @@ -5,16 +5,14 @@ */ package org.mifosplatform.infrastructure.documentmanagement.domain; -import org.apache.commons.lang.StringUtils; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; -import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; -import org.springframework.data.jpa.domain.AbstractPersistable; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.springframework.data.jpa.domain.AbstractPersistable; + @Entity @Table(name = "m_document") public class Document extends AbstractPersistable { @@ -43,19 +41,18 @@ public class Document extends AbstractPersistable { @Column(name = "location", length = 500) private String location; - @Column(name = "storage_type", length = 50) - private String storageType; + @Column(name = "storage_type_enum") + private Integer storageType; - public Document() { - } + public Document() {} public static Document createNew(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, - final Long size, final String type, final String description, final String location, final DocumentStoreType storageType) { + final Long size, final String type, final String description, final String location, final StorageType storageType) { return new Document(parentEntityType, parentEntityId, name, fileName, size, type, description, location, storageType); } private Document(final String parentEntityType, final Long parentEntityId, final String name, final String fileName, final Long size, - final String type, final String description, final String location, final DocumentStoreType storageType) { + final String type, final String description, final String location, final StorageType storageType) { this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType, null); this.parentEntityId = parentEntityId; this.name = StringUtils.defaultIfEmpty(name, null); @@ -86,7 +83,6 @@ public class Document extends AbstractPersistable { if (command.isSizeChanged()) { this.size = command.getSize(); } - this.storageType = command.getStorageType(); } public String getParentEntityType() { @@ -153,7 +149,7 @@ public class Document extends AbstractPersistable { this.location = location; } - public DocumentStoreType storageType(){ - return DocumentStore.getDocumentStoreType(this.storageType); + public StorageType storageType() { + return StorageType.fromInt(this.storageType); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Image.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Image.java new file mode 100644 index 000000000..c4b21c373 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/Image.java @@ -0,0 +1,49 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.domain; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_image") +public final class Image extends AbstractPersistable { + + @Column(name = "location", length = 500) + private String location; + + @Column(name = "storage_type_enum") + private Integer storageType; + + public Image(String location, StorageType storageType) { + this.location = location; + this.storageType = storageType.getValue(); + } + + protected Image() { + + } + + public String getLocation() { + return this.location; + } + + public Integer getStorageType() { + return this.storageType; + } + + public void setLocation(String location) { + this.location = location; + } + + public void setStorageType(Integer storageType) { + this.storageType = storageType; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/ImageRepository.java similarity index 71% rename from mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/ImageRepository.java index ed0fe6cc1..946cf2eae 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ImageRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/ImageRepository.java @@ -3,11 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.mifosplatform.portfolio.client.domain; +package org.mifosplatform.infrastructure.documentmanagement.domain; +import org.mifosplatform.portfolio.client.domain.Client; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface ImageRepository extends JpaRepository, JpaSpecificationExecutor { - Image findOneByClient(Client client); -} \ No newline at end of file +public interface ImageRepository extends JpaRepository, JpaSpecificationExecutor {} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/StorageType.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/StorageType.java new file mode 100644 index 000000000..2cd78d59f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/domain/StorageType.java @@ -0,0 +1,30 @@ +package org.mifosplatform.infrastructure.documentmanagement.domain; + +import java.util.HashMap; +import java.util.Map; + +public enum StorageType { + FILE_SYSTEM(1), S3(2); + + private Integer value; + + StorageType(Integer value) { + this.value = value; + } + + public Integer getValue() { + return value; + } + + private static final Map intToEnumMap = new HashMap(); + static { + for (final StorageType type : StorageType.values()) { + intToEnumMap.put(type.value, type); + } + } + + public static StorageType fromInt(final int i) { + final StorageType type = intToEnumMap.get(Integer.valueOf(i)); + return type; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/DocumentManagementException.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/ContentManagementException.java similarity index 59% rename from mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/DocumentManagementException.java rename to mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/ContentManagementException.java index 4c1f864e1..a45d00e64 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/DocumentManagementException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/ContentManagementException.java @@ -7,13 +7,14 @@ package org.mifosplatform.infrastructure.documentmanagement.exception; import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; -public class DocumentManagementException extends AbstractPlatformDomainRuleException { +public class ContentManagementException extends AbstractPlatformDomainRuleException { - public DocumentManagementException(final String name) { - super("error.msg.document.save", "Error while manipulating file " + name + " due to a File system / Amazon S3 issue", name); + public ContentManagementException(final String filename, final String message) { + super("error.msg.document.save", + "Error while manipulating file " + filename + " due to a File system / Amazon S3 issue " + message, filename, message); } - public DocumentManagementException(final String name, final Long fileSize, final int maxFileSize) { + public ContentManagementException(final String name, final Long fileSize, final int maxFileSize) { super("error.msg.document.file.too.big", "Unable to save the document with name" + name + " since its file Size of " + fileSize / (1024 * 1024) + " MB exceeds the max permissable file size of " + maxFileSize + " MB", name, fileSize); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java index af99a5f70..20a803cc2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformService.java @@ -14,7 +14,7 @@ public interface DocumentReadPlatformService { Collection retrieveAllDocuments(String entityType, Long entityId); - FileData retrieveDocumentAsFile(String entityType, Long entityId, Long documentId); + FileData retrieveFileData(String entityType, Long entityId, Long documentId); DocumentData retrieveDocument(String entityType, Long entityId, Long documentId); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java index c531eacdc..dfedcce55 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentReadPlatformServiceImpl.java @@ -6,9 +6,9 @@ package org.mifosplatform.infrastructure.documentmanagement.service; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; import org.mifosplatform.infrastructure.documentmanagement.data.FileData; import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; @@ -28,14 +28,14 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; - private final DocumentStoreFactory documentStoreFactory; + private final ContentRepositoryFactory contentRepositoryFactory; @Autowired public DocumentReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final DocumentStoreFactory documentStoreFactory) { + final ContentRepositoryFactory documentStoreFactory) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); - this.documentStoreFactory = documentStoreFactory; + this.contentRepositoryFactory = documentStoreFactory; } @Override @@ -46,46 +46,60 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ // TODO verify if the entities are valid and a user // has data // scope for the particular entities - final DocumentMapper mapper = new DocumentMapper(true); + final DocumentMapper mapper = new DocumentMapper(true, true); final String sql = "select " + mapper.schema() + " order by d.id"; return this.jdbcTemplate.query(sql, mapper, new Object[] { entityType, entityId }); } @Override - public FileData retrieveDocumentAsFile(final String entityType, final Long entityId, final Long documentId) { + public FileData retrieveFileData(final String entityType, final Long entityId, final Long documentId) { try { - DocumentData documentData = getDocumentData(entityType, entityId, documentId); - DocumentStore instanceForRead = documentStoreFactory.getInstanceFromStorageType(documentData.storeType()); - return instanceForRead.retrieveDocument(documentData); + final DocumentMapper mapper = new DocumentMapper(false, false); + DocumentData documentData = fetchDocumentDetails(entityType, entityId, documentId, mapper); + ContentRepository contentRepository = contentRepositoryFactory.getRepository(documentData.storageType()); + return contentRepository.fetchFile(documentData); } catch (final EmptyResultDataAccessException e) { throw new DocumentNotFoundException(entityType, entityId, documentId); - } catch (final DocumentNotFoundException d) { - throw d; } } @Override public DocumentData retrieveDocument(String entityType, Long entityId, Long documentId) { try { - return getDocumentData(entityType, entityId, documentId); + final DocumentMapper mapper = new DocumentMapper(true, true); + return fetchDocumentDetails(entityType, entityId, documentId, mapper); } catch (final EmptyResultDataAccessException e) { throw new DocumentNotFoundException(entityType, entityId, documentId); } } + /** + * @param entityType + * @param entityId + * @param documentId + * @param mapper + * @return + */ + private DocumentData fetchDocumentDetails(final String entityType, final Long entityId, final Long documentId, + final DocumentMapper mapper) { + final String sql = "select " + mapper.schema() + " and d.id=? "; + return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { entityType, entityId, documentId }); + } + private static final class DocumentMapper implements RowMapper { private final boolean hideLocation; + private final boolean hideStorageType; - public DocumentMapper(final boolean hideLocation) { + public DocumentMapper(final boolean hideLocation, final boolean hideStorageType) { this.hideLocation = hideLocation; + this.hideStorageType = hideStorageType; } public String schema() { return "d.id as id, d.parent_entity_type as parentEntityType, d.parent_entity_id as parentEntityId, d.name as name, " + " d.file_name as fileName, d.size as fileSize, d.type as fileType, " - + " d.description as description, d.location as location," - + " d.storage_type as storageType" + + " d.description as description, d.location as location," + " d.storage_type_enum as storageType" + " from m_document d where d.parent_entity_type=? and d.parent_entity_id=?"; } @@ -101,24 +115,16 @@ public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformServ final String fileType = rs.getString("fileType"); final String description = rs.getString("description"); String location = null; + Integer storageType = null; if (!this.hideLocation) { location = rs.getString("location"); } - final String storageType = rs.getString("storageType"); - - return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location, storageType); + if (!this.hideStorageType) { + storageType = rs.getInt("storageType"); + } + return new DocumentData(id, parentEntityType, parentEntityId, name, fileName, fileSize, fileType, description, location, + storageType); } } - - private DocumentData getDocumentData(String entityType, Long entityId, Long documentId) { - this.context.authenticatedUser(); - - // TODO verify if the entities are valid and a - // user has data - // scope for the particular entities - final DocumentMapper mapper = new DocumentMapper(false); - final String sql = "select " + mapper.schema() + " and d.id=? "; - return this.jdbcTemplate.queryForObject(sql, mapper, new Object[]{entityType, entityId, documentId}); - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java index 83bff1a3c..e7734dc9e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/DocumentWritePlatformServiceJpaRepositoryImpl.java @@ -5,16 +5,18 @@ */ package org.mifosplatform.infrastructure.documentmanagement.service; +import java.io.InputStream; + import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommandValidator; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; import org.mifosplatform.infrastructure.documentmanagement.domain.Document; import org.mifosplatform.infrastructure.documentmanagement.domain.DocumentRepository; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; +import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException; import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; import org.mifosplatform.infrastructure.documentmanagement.exception.InvalidEntityTypeForDocumentManagementException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -25,8 +27,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.InputStream; - @Service public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWritePlatformService { @@ -34,14 +34,14 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr private final PlatformSecurityContext context; private final DocumentRepository documentRepository; - private final DocumentStoreFactory documentStoreFactory; + private final ContentRepositoryFactory contentRepositoryFactory; @Autowired - public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final DocumentRepository documentRepository, - final DocumentStoreFactory documentStoreFactory) { + public DocumentWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final DocumentRepository documentRepository, final ContentRepositoryFactory documentStoreFactory) { this.context = context; this.documentRepository = documentRepository; - this.documentStoreFactory = documentStoreFactory; + this.contentRepositoryFactory = documentStoreFactory; } @Transactional @@ -56,13 +56,13 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr validator.validateForCreate(); - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(); - final String fileLocation = documentStore.saveDocument(inputStream, documentCommand); + final String fileLocation = contentRepository.saveFile(inputStream, documentCommand); final Document document = Document.createNew(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getName(), documentCommand.getFileName(), documentCommand.getSize(), documentCommand.getType(), - documentCommand.getDescription(), fileLocation, documentStore.getType()); + documentCommand.getDescription(), fileLocation, contentRepository.getStorageType()); this.documentRepository.save(document); @@ -71,9 +71,6 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); - } catch (final DocumentManagementException dme) { - logger.error(dme.getMessage(), dme); - throw new DocumentManagementException(documentCommand.getName()); } } @@ -89,21 +86,22 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr // TODO check if entity id is valid and within data scope for the // user final Document documentForUpdate = this.documentRepository.findOne(documentCommand.getId()); - DocumentStoreType documentStoreType = documentForUpdate.storageType(); if (documentForUpdate == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getId()); } + + StorageType documentStoreType = documentForUpdate.storageType(); oldLocation = documentForUpdate.getLocation(); if (inputStream != null && documentCommand.isFileNameChanged()) { - DocumentStore instanceFromConfiguration = this.documentStoreFactory.getInstanceFromConfiguration(); - documentCommand.setLocation(instanceFromConfiguration.saveDocument(inputStream, documentCommand)); - documentCommand.setStorageType(instanceFromConfiguration.getType()); + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(); + documentCommand.setLocation(contentRepository.saveFile(inputStream, documentCommand)); + documentCommand.setStorageType(contentRepository.getStorageType().getValue()); } documentForUpdate.update(documentCommand); if (inputStream != null && documentCommand.isFileNameChanged()) { - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(documentStoreType); - documentStore.deleteDocument(documentCommand.getName(), oldLocation); + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(documentStoreType); + contentRepository.deleteFile(documentCommand.getName(), oldLocation); } this.documentRepository.saveAndFlush(documentForUpdate); @@ -113,9 +111,9 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr logger.error(dve.getMessage(), dve); throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); - } catch (final DocumentManagementException dme) { - logger.error(dme.getMessage(), dme); - throw new DocumentManagementException(documentCommand.getName()); + } catch (final ContentManagementException cme) { + logger.error(cme.getMessage(), cme); + throw new ContentManagementException(documentCommand.getName(), cme.getMessage()); } } @@ -127,12 +125,12 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr validateParentEntityType(documentCommand); // TODO: Check document is present under this entity Id final Document document = this.documentRepository.findOne(documentCommand.getId()); - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromStorageType(document.storageType()); if (document == null) { throw new DocumentNotFoundException(documentCommand.getParentEntityType(), documentCommand.getParentEntityId(), documentCommand.getId()); } - this.documentRepository.delete(document); - documentStore.deleteDocument(document.getName(), document.getLocation()); + + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(document.storageType()); + contentRepository.deleteFile(document.getName(), document.getLocation()); return new CommandProcessingResult(document.getId()); } @@ -142,7 +140,6 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr } private static boolean checkValidEntityType(final String entityType) { - for (final DOCUMENT_MANAGEMENT_ENTITY entities : DOCUMENT_MANAGEMENT_ENTITY.values()) { if (entities.name().equalsIgnoreCase(entityType)) { return true; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java new file mode 100755 index 000000000..eca7a90c6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java @@ -0,0 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.service; + +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; + +public interface ImageReadPlatformService { + + ImageData retrieveClientImage(Long clientId); + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java new file mode 100755 index 000000000..c4e560658 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java @@ -0,0 +1,79 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.service; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; +import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.exception.ImageNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class ImageReadPlatformServiceImpl implements ImageReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final ContentRepositoryFactory contentRepositoryFactory; + private final ClientRepositoryWrapper clientRepositoryWrapper; + + @Autowired + public ImageReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource, final ContentRepositoryFactory documentStoreFactory, + final ClientRepositoryWrapper clientRepositoryWrapper) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.contentRepositoryFactory = documentStoreFactory; + this.clientRepositoryWrapper = clientRepositoryWrapper; + } + + private static final class ImageMapper implements RowMapper { + + private final String entityDisplayName; + + public ImageMapper(final String entityDisplayName) { + this.entityDisplayName = entityDisplayName; + } + + public String schema() { + return " image.id as id, image.location as location, image.storage_type_enum as storageType " + + " from m_image image , m_client client " + " where client.image_id = image.id and client.id=?"; + } + + @Override + public ImageData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = JdbcSupport.getLong(rs, "id"); + final String location = rs.getString("location"); + final Integer storageType = JdbcSupport.getInteger(rs, "storageType"); + return new ImageData(id, location, storageType, entityDisplayName); + } + } + + @Override + public ImageData retrieveClientImage(Long clientId) { + try { + Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); + + final ImageMapper imageMapper = new ImageMapper(client.getDisplayName()); + + final String sql = "select " + imageMapper.schema(); + + ImageData imageData = this.jdbcTemplate.queryForObject(sql, imageMapper, new Object[] { clientId }); + ContentRepository contentRepository = contentRepositoryFactory.getRepository(imageData.storageType()); + return contentRepository.fetchImage(imageData); + } catch (EmptyResultDataAccessException e) { + throw new ImageNotFoundException("clients", clientId); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java new file mode 100755 index 000000000..950fbe126 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java @@ -0,0 +1,24 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.service; + +import java.io.InputStream; + +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface ImageWritePlatformService { + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") + CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream, Long fileSize); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") + CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage); + + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')") + CommandProcessingResult deleteClientImage(Long clientId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java new file mode 100755 index 000000000..cb5a1b3ce --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,103 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.service; + +import java.io.InputStream; + +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; +import org.mifosplatform.infrastructure.documentmanagement.domain.Image; +import org.mifosplatform.infrastructure.documentmanagement.domain.ImageRepository; +import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePlatformService { + + private final ContentRepositoryFactory contentRepositoryFactory; + private final ClientRepositoryWrapper clientRepositoryWrapper; + private final ImageRepository imageRepository; + + @Autowired + public ImageWritePlatformServiceJpaRepositoryImpl(final ContentRepositoryFactory documentStoreFactory, + final ClientRepositoryWrapper clientRepositoryWrapper, final ImageRepository imageRepository) { + this.contentRepositoryFactory = documentStoreFactory; + this.clientRepositoryWrapper = clientRepositoryWrapper; + this.imageRepository = imageRepository; + } + + @Transactional + @Override + public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream, + Long fileSize) { + final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); + deletePreviousClientImage(client); + + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(); + String imageLocation = contentRepository.saveImage(inputStream, clientId, imageName, fileSize); + return updateClientImage(client, imageLocation, contentRepository.getStorageType()); + } + + @Transactional + @Override + public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) { + final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); + deletePreviousClientImage(client); + + ContentRepository contenRepository = this.contentRepositoryFactory.getRepository(); + final String imageLocation = contenRepository.saveImage(encodedImage, clientId, "image"); + + return updateClientImage(client, imageLocation, contenRepository.getStorageType()); + } + + @Transactional + @Override + public CommandProcessingResult deleteClientImage(final Long clientId) { + + final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); + + Image image = client.getImage(); + // delete image from the file system + if (image != null) { + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(StorageType.fromInt(image.getStorageType())); + contentRepository.deleteImage(clientId, image.getLocation()); + client.setImage(null); + this.imageRepository.delete(image); + this.clientRepositoryWrapper.save(client); + } + + return new CommandProcessingResult(clientId); + } + + private void deletePreviousClientImage(final Client client) { + Image image = client.getImage(); + if (image != null) { + ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(StorageType.fromInt(image.getStorageType())); + contentRepository.deleteImage(client.getId(), image.getLocation()); + } + } + + private CommandProcessingResult updateClientImage(final Client client, final String imageLocation, final StorageType storageType) { + Image image = client.getImage(); + if (image == null) { + image = new Image(imageLocation, storageType); + } else { + image.setLocation(imageLocation); + image.setStorageType(storageType.getValue()); + } + this.imageRepository.save(image); + client.setImage(image); + this.clientRepositoryWrapper.save(client); + return new CommandProcessingResult(client.getId()); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index d72f85f80..1463f0473 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -5,6 +5,9 @@ */ package org.mifosplatform.portfolio.client.data; +import java.util.Collection; +import java.util.List; + import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.EqualsBuilder; @@ -13,20 +16,12 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Collection; -import java.util.List; /** * Immutable data object representing client data. */ final public class ClientData implements Comparable { - private final static Logger logger = LoggerFactory.getLogger(ClientData.class); - private final Long id; private final String accountNo; @@ -46,9 +41,7 @@ final public class ClientData implements Comparable { private final Long officeId; private final String officeName; -// private String imageData; - private ImageData imageData; - @SuppressWarnings("unused") + private final Long imageId; private final Boolean imagePresent; // associations @@ -65,13 +58,13 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, - clientData.externalId, clientData.activationDate, clientData.imageData, allowedOffices, clientData.groups); + clientData.externalId, clientData.activationDate, clientData.imageId, allowedOffices, clientData.groups); } public static ClientData setParentGroups(final ClientData clientData, final Collection parentGroups) { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, - clientData.externalId, clientData.activationDate, clientData.imageData, clientData.officeOptions, parentGroups); + clientData.externalId, clientData.activationDate, clientData.imageId, clientData.officeOptions, parentGroups); } public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname, @@ -88,14 +81,14 @@ final public class ClientData implements Comparable { public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, - final String displayName, final String externalId, final LocalDate activationDate, final ImageData imageData) { + final String displayName, final String externalId, final LocalDate activationDate, final Long imageId) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageData, null, null); + externalId, activationDate, imageId, null, null); } private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, - final String externalId, final LocalDate activationDate, final ImageData imageKey, final Collection allowedOffices, + final String externalId, final LocalDate activationDate, final Long imageId, final Collection allowedOffices, final Collection groups) { this.accountNo = accountNo; this.status = status; @@ -114,11 +107,11 @@ final public class ClientData implements Comparable { this.displayName = StringUtils.defaultIfEmpty(displayName, null); this.externalId = StringUtils.defaultIfEmpty(externalId, null); this.activationDate = activationDate; - this.imageData = imageKey; - if (imageKey != null) { + this.imageId = imageId; + if (imageId != null) { this.imagePresent = Boolean.TRUE; } else { - this.imagePresent = null; + this.imagePresent = Boolean.FALSE; } // associations @@ -144,17 +137,14 @@ final public class ClientData implements Comparable { return this.officeName; } - public String imageKey() { - return this.imageData.imageKey(); + public Long getImageId() { + return this.imageId; } - public boolean imageKeyDoesNotExist() { - return imageData.equals(null) && !imageKeyExists(); + public Boolean getImagePresent() { + return this.imagePresent; } - private boolean imageKeyExists() { - return StringUtils.isNotBlank(this.imageData.imageKey()); - } @Override public int compareTo(final ClientData obj) { if (obj == null) { return -1; } @@ -201,24 +191,4 @@ final public class ClientData implements Comparable { return this.activationDate; } - public void setImageData(ImageData imageData){ - this.imageData = imageData; - } - - public String imageName() { - return imageData.name(); - } - - public String imageContentType() { - return imageData.contentType(); - } - - public byte[] image(){ - try{ - return imageData.getContent(); - }catch (IOException ioe){ - logger.error("Error in fetching client image"); - return null; - } - } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java deleted file mode 100644 index 95ede014c..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ImageData.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.mifosplatform.portfolio.client.data; - -import org.apache.commons.lang.StringUtils; -import org.apache.poi.util.IOUtils; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; -import org.mifosplatform.infrastructure.core.service.ImageUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class ImageData { - - private final Long clientId; - private final String key; - // private final DocumentStoreType storeType; - private final String storeType; - private File file; - private String contentType; - private String name; - private InputStream inputStream; - - - public ImageData(Long clientId, String key, String storeType) { - this.clientId = clientId; - this.key = key; - this.storeType = storeType; - } - - public String imageKey() { - return this.key; - } - - public byte[] getContent() throws IOException { - if(inputStream == null){ - FileInputStream fileInputStream = new FileInputStream(file); - return IOUtils.toByteArray(fileInputStream); - } - else - { - return IOUtils.toByteArray(inputStream); - } - } - - private String setImageContentType() { - String fileName = file.getName(); - String contentType = ImageUtils.IMAGE_MIME_TYPE.JPEG.getValue(); - if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.GIF.getValue())) { - contentType = ImageUtils.IMAGE_MIME_TYPE.GIF.getValue(); - } else if (StringUtils.endsWith(fileName, ImageUtils.IMAGE_FILE_EXTENSION.PNG.getValue())) { - contentType = ImageUtils.IMAGE_MIME_TYPE.PNG.getValue(); - } - return contentType; - } - - public String contentType(){ - return this.contentType; - } - - public DocumentStoreType storeType() { - return DocumentStore.getDocumentStoreType(storeType); - } - - public String name(){ - return this.file.getName(); - } - - - public void setContent(InputStream objectContent) { - this.inputStream = objectContent; - } - - public void setContent(File file) { - this.file = file; - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index f9400bbd2..b8408c84a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -13,6 +13,7 @@ import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.service.DateUtils; +import org.mifosplatform.infrastructure.documentmanagement.domain.Image; import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.portfolio.client.api.ClientApiConstants; @@ -20,6 +21,7 @@ import org.mifosplatform.portfolio.group.domain.Group; import org.springframework.data.jpa.domain.AbstractPersistable; import javax.persistence.*; + import java.util.*; @Entity @@ -33,6 +35,10 @@ public final class Client extends AbstractPersistable { @JoinColumn(name = "office_id", nullable = false) private Office office; + @OneToOne(optional = true) + @JoinColumn(name = "image_id", nullable = true) + private Image image; + /** * A value from {@link ClientStatus}. */ @@ -55,7 +61,6 @@ public final class Client extends AbstractPersistable { @Column(name = "fullname", length = 100) private String fullname; - @SuppressWarnings("unused") @Column(name = "display_name", length = 100, nullable = false) private String displayName; @@ -365,15 +370,24 @@ public final class Client extends AbstractPersistable { return this.office.identifiedBy(officeId); } -// public String imageKey() { -// } -// -// public void updateImageKey(final String imageKey) { -// this.image.setKey(imageKey); -// } - public Long officeId() { return this.office.getId(); } + public void setImage(Image image) { + this.image = image; + } + + public Image getImage() { + return this.image; + } + + public String getDisplayName() { + return this.displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java deleted file mode 100644 index d186f5b26..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Image.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package org.mifosplatform.portfolio.client.domain; - -import org.mifosplatform.infrastructure.core.service.DocumentStoreType; -import org.springframework.data.jpa.domain.AbstractPersistable; - -import javax.persistence.*; - -@Entity -@Table(name = "m_image") -public final class Image extends AbstractPersistable { - - // @Column(name = "`client_id`") - @OneToOne() - @JoinColumn(name = "`client_id`") - private Client client; - - @Column(name = "`key`", length = 500) - private String key; - - @Column(name = "`storage_type`", length = 50) - private String storeType; - - public Image(Client client, String image_key, DocumentStoreType storeType) { - this.client = client; - this.key = image_key; - this.storeType = storeType.getValue(); - } - - public Image() { - - } - - - public Image(Client client) { - this.client = client; - this.key = null; - this.storeType = null; - } - - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String updateKey(String key) { - setKey(key); - return this.key; - } - - public void updateStorageType(String documentStoreType) { - this.storeType = documentStoreType; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index 3fb4b72a1..9b817efed 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -16,7 +16,6 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; -import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.core.service.PaginationHelper; import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; @@ -26,7 +25,6 @@ import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientData; -import org.mifosplatform.portfolio.client.data.ImageData; import org.mifosplatform.portfolio.client.domain.ClientEnumerations; import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.group.data.GroupGeneralData; @@ -43,11 +41,9 @@ import org.springframework.stereotype.Service; @Service public class ClientReadPlatformServiceImpl implements ClientReadPlatformService { - private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final OfficeReadPlatformService officeReadPlatformService; - private final DocumentStoreFactory documentStoreFactory; // data mappers private final PaginationHelper paginationHelper = new PaginationHelper(); @@ -58,10 +54,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Autowired public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final OfficeReadPlatformService officeReadPlatformService, final DocumentStoreFactory documentStoreFactory) { + final OfficeReadPlatformService officeReadPlatformService) { this.context = context; this.officeReadPlatformService = officeReadPlatformService; - this.documentStoreFactory = documentStoreFactory; this.jdbcTemplate = new JdbcTemplate(dataSource); } @@ -178,12 +173,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService Collection parentGroups = this.jdbcTemplate.query(clientGroupsSql, this.clientGroupsMapper, new Object[] { clientId }); - String clientImageSql = "select * from m_image where client_id =" + clientId; - - try { - ImageData imageData = this.jdbcTemplate.queryForObject(clientImageSql, new ImageMapper()); - clientData.setImageData(documentStoreFactory.getInstanceFromStorageType(imageData.storeType()).retrieveImage(imageData)); - } catch (EmptyResultDataAccessException e) {} return ClientData.setParentGroups(clientData, parentGroups); } catch (EmptyResultDataAccessException e) { @@ -234,7 +223,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("c.office_id as officeId, o.name as officeName, "); sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, "); - sqlBuilder.append("c.activation_date as activationDate, c.image_key as imagekey "); + sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId"); sqlBuilder.append("from m_client c "); sqlBuilder.append("join m_office o on o.id = c.office_id "); sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id"); @@ -262,11 +251,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String displayName = rs.getString("displayName"); final String externalId = rs.getString("externalId"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); - final String imageKey = rs.getString("imageKey"); + final Long imageId = JdbcSupport.getLong(rs, "imageId"); final String officeName = rs.getString("officeName"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, null); + externalId, activationDate, imageId); } } @@ -282,7 +271,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("c.office_id as officeId, o.name as officeName, "); builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); builder.append("c.fullname as fullname, c.display_name as displayName, "); - builder.append("c.activation_date as activationDate, c.image_key as imagekey "); + builder.append("c.activation_date as activationDate, c.image_id as imageId "); builder.append("from m_client c "); builder.append("join m_office o on o.id = c.office_id "); @@ -310,13 +299,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String displayName = rs.getString("displayName"); final String externalId = rs.getString("externalId"); final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); - // final String imageKey = rs.getString("imageKey"); final String officeName = rs.getString("officeName"); + final Long imageId = JdbcSupport.getLong(rs, "imageId"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, null); + externalId, activationDate, imageId); } - } private static final class ParentGroupsMapper implements RowMapper { @@ -336,18 +324,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } } - private static final class ImageMapper implements RowMapper { - - @Override - public ImageData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - final Long clientId = rs.getLong("client_id"); - final String imageKey = rs.getString("key"); - final String storeType = rs.getString("storage_type"); - return new ImageData(clientId, imageKey, storeType); - } - - } - private static final class ClientLookupMapper implements RowMapper { private final String schema; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java index 578b74c10..bdad04f22 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java @@ -5,12 +5,8 @@ */ package org.mifosplatform.portfolio.client.service; -import java.io.InputStream; - import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; -import org.springframework.security.access.prepost.PreAuthorize; public interface ClientWritePlatformService { @@ -22,12 +18,4 @@ public interface ClientWritePlatformService { CommandProcessingResult deleteClient(Long clientId); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") - CommandProcessingResult saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream, Long fileSize); - - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") - CommandProcessingResult saveOrUpdateClientImage(Long clientId, Base64EncodedImage encodedImage); - - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')") - CommandProcessingResult deleteClientImage(Long clientId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 3eb007ecb..52cf5ad4b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -5,27 +5,28 @@ */ package org.mifosplatform.portfolio.client.service; -import org.apache.commons.lang.StringUtils; +import java.util.List; +import java.util.Locale; +import java.util.Map; + import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; -import org.mifosplatform.infrastructure.core.service.DocumentStore; -import org.mifosplatform.infrastructure.core.service.DocumentStoreFactory; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.data.ClientDataValidator; -import org.mifosplatform.portfolio.client.domain.*; +import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; +import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; +import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException; -import org.mifosplatform.portfolio.client.exception.ClientNotFoundException; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepository; import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; @@ -38,11 +39,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.InputStream; -import java.util.List; -import java.util.Locale; -import java.util.Map; - @Service public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWritePlatformService { @@ -55,14 +51,12 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final GroupRepository groupRepository; private final ClientDataValidator fromApiJsonDeserializer; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; - private final DocumentStoreFactory documentStoreFactory; - private final ImageRepository imageRepository; @Autowired public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, - final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository, - final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, - final GroupRepository groupRepository, DocumentStoreFactory documentStoreFactory, ImageRepository imageRepository) { + final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository, + final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, + final GroupRepository groupRepository) { this.context = context; this.clientRepository = clientRepository; this.officeRepository = officeRepository; @@ -70,8 +64,6 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; this.groupRepository = groupRepository; - this.documentStoreFactory = documentStoreFactory; - this.imageRepository = imageRepository; } @Transactional @@ -221,77 +213,6 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } } - @Transactional - @Override - public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final String imageName, final InputStream inputStream, Long fileSize) { - try { - final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); - deletePreviousClientImage(clientId, client); - - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); - String imageLocation = documentStore.saveImage(inputStream, clientId, imageName, fileSize); - return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue()); - } catch (DocumentManagementException dme) { - logger.error(dme.getMessage(), dme); - throw new DocumentManagementException(imageName); - } - } - - @Transactional - @Override - public CommandProcessingResult deleteClientImage(final Long clientId) { - - final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); - - Image image = imageRepository.findOneByClient(client); - String imageKey = image.getKey(); - // delete image from the file system - if (StringUtils.isNotEmpty((imageKey))) { - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); - documentStore.deleteImage(clientId, imageKey); - this.imageRepository.delete(image); - } - - return new CommandProcessingResult(clientId) ; - } - - @Override - public CommandProcessingResult saveOrUpdateClientImage(final Long clientId, final Base64EncodedImage encodedImage) { - try { - final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); - deletePreviousClientImage(clientId, client); - - DocumentStore documentStore = this.documentStoreFactory.getInstanceFromConfiguration(); - final String imageLocation = documentStore.saveImage(encodedImage, clientId, "image"); - - return updateClientImage(clientId, client, imageLocation, documentStore.getType().getValue()); - } catch (DocumentManagementException dme) { - logger.error(dme.getMessage(), dme); - throw new DocumentManagementException("image"); - } - } - - private void deletePreviousClientImage(final Long clientId, final Client client) { - if (client == null) { throw new ClientNotFoundException(clientId); } - - Image image = imageRepository.findOneByClient(client); - if (image != null){ - this.documentStoreFactory.getInstanceFromConfiguration().deleteImage(clientId, image.getKey()); - } - } - - private CommandProcessingResult updateClientImage(final Long clientId, final Client client, final String imageLocation, final String documentStoreType) { - Image image = imageRepository.findOneByClient(client); - if(image == null){ - image = new Image(client); - } - image.updateKey(imageLocation); - image.updateStorageType(documentStoreType); - - this.imageRepository.save(image); - return new CommandProcessingResult(clientId); - } - private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { logger.error(dve.getMessage(), dve); } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java index 8592df1f9..29a85f473 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/DocumentStoreFactoryTest.java @@ -1,28 +1,28 @@ package org.mifosplatform.infrastructure.core.service; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.junit.Test; import org.junit.runner.RunWith; import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; -import org.mifosplatform.infrastructure.configuration.domain.ExternalServicesDomainService; +import org.mifosplatform.infrastructure.configuration.service.ExternalServicesReadPlatformServiceImpl; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.S3ContentRepository; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.context.ApplicationContext; -import static junit.framework.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@SuppressWarnings("unused") - @RunWith(MockitoJUnitRunner.class) public class DocumentStoreFactoryTest { - +/** @Test - public void shouldReturnS3DocumentStoreObjectIfS3Enabled(){ + public void shouldReturnS3DocumentStoreObjectIfS3Enabled() { S3ClientFactory s3ClientFactoryMock = mock(S3ClientFactory.class); when(s3ClientFactoryMock.getS3Client()).thenReturn(null); - ExternalServicesDomainService externalServicesDomainServiceMock = mock(ExternalServicesDomainService.class); + ExternalServicesReadPlatformServiceImpl externalServicesDomainServiceMock = mock(ExternalServicesReadPlatformServiceImpl.class); when(externalServicesDomainServiceMock.getValue("s3_bucket_name")).thenReturn("bucket_name"); ConfigurationDomainService configurationDomainServiceMock = mock(ConfigurationDomainService.class); @@ -30,26 +30,27 @@ public class DocumentStoreFactoryTest { ApplicationContext applicationContextMock = mock(ApplicationContext.class); - when(applicationContextMock.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainServiceMock); + when(applicationContextMock.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn( + configurationDomainServiceMock); when(applicationContextMock.getBean("s3ClientFactory", S3ClientFactory.class)).thenReturn(s3ClientFactoryMock); - when(applicationContextMock.getBean("externalServicesDomainService", ExternalServicesDomainService.class)).thenReturn(externalServicesDomainServiceMock); + when(applicationContextMock.getBean("externalServicesDomainService", ExternalServicesReadPlatformServiceImpl.class)).thenReturn( + externalServicesDomainServiceMock); - - DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContextMock); - assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),S3DocumentStore.class); + ContentRepositoryFactory documentStoreFactory = new ContentRepositoryFactory(applicationContextMock); + assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(), S3ContentRepository.class); } @Test - public void shouldReturnFileSystemDocumentStoreObjectIfS3IsDisabled(){ + public void shouldReturnFileSystemDocumentStoreObjectIfS3IsDisabled() { ConfigurationDomainService configurationDomainService = mock(ConfigurationDomainService.class); when(configurationDomainService.isAmazonS3Enabled()).thenReturn(false); ApplicationContext applicationContext = mock(ApplicationContext.class); - when(applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn(configurationDomainService); + when(applicationContext.getBean("configurationDomainServiceJpa", ConfigurationDomainService.class)).thenReturn( + configurationDomainService); - DocumentStoreFactory documentStoreFactory = new DocumentStoreFactory(applicationContext); - assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(),FileSystemDocumentStore.class); + ContentRepositoryFactory documentStoreFactory = new ContentRepositoryFactory(applicationContext); + assertEquals(documentStoreFactory.getInstanceFromConfiguration().getClass(), FileSystemContentRepository.class); } - - +**/ } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java index 2f70fe84c..22764aa94 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/FileDocumentStoreTest.java @@ -1,23 +1,22 @@ package org.mifosplatform.infrastructure.core.service; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import org.junit.Test; import org.junit.runner.RunWith; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository; import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; import org.mifosplatform.infrastructure.documentmanagement.data.FileData; import org.mockito.runners.MockitoJUnitRunner; -import static junit.framework.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@SuppressWarnings("unused") - @RunWith(MockitoJUnitRunner.class) public class FileDocumentStoreTest { - +/** @Test - public void shouldRetrieveDocument(){ - FileSystemDocumentStore fileSystemDocumentStore = new FileSystemDocumentStore(); + public void shouldRetrieveDocument() { + FileSystemContentRepository fileSystemDocumentStore = new FileSystemContentRepository(); DocumentData documentDataMock = mock(DocumentData.class); when(documentDataMock.fileName()).thenReturn("some_file"); when(documentDataMock.fileName()).thenReturn("content_type"); @@ -28,4 +27,5 @@ public class FileDocumentStoreTest { assertEquals(fileData.name(), documentDataMock.fileName()); assertEquals(fileData.contentType(), documentDataMock.contentType()); } +**/ } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java index 3307d7d96..212f8c49a 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/infrastructure/core/service/S3DocumentStoreTest.java @@ -1,27 +1,34 @@ package org.mifosplatform.infrastructure.core.service; -import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.*; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; -import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; -import org.mifosplatform.infrastructure.documentmanagement.data.FileData; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentManagementException; -import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; -import org.mockito.Matchers; -import org.mockito.runners.MockitoJUnitRunner; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.io.IOException; import java.io.InputStream; -import static junit.framework.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mifosplatform.infrastructure.documentmanagement.command.DocumentCommand; +import org.mifosplatform.infrastructure.documentmanagement.contentrepository.S3ContentRepository; +import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData; +import org.mifosplatform.infrastructure.documentmanagement.data.FileData; +import org.mifosplatform.infrastructure.documentmanagement.exception.ContentManagementException; +import org.mifosplatform.infrastructure.documentmanagement.exception.DocumentNotFoundException; +import org.mockito.Matchers; +import org.mockito.runners.MockitoJUnitRunner; -@SuppressWarnings("unused") +import com.amazonaws.AmazonClientException; +import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3Object; @RunWith(MockitoJUnitRunner.class) public class S3DocumentStoreTest { @@ -30,17 +37,17 @@ public class S3DocumentStoreTest { AmazonS3Client s3ClientMock; DocumentCommand documentCommand; DocumentData documentDataMock; - +/** @Test - public void shouldGenerateParentDirectoryFromEntityDetails(){ - S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null); + public void shouldGenerateParentDirectoryFromEntityDetails() { + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", null); String actualParentDirectory = s3DocumentStore.generateFileParentDirectory("entity_type", 1234L); assertTrue(actualParentDirectory.startsWith("documents/entity_type/1234")); } @Test - public void shouldGenerateClientImageDirectoryFromResourceId(){ - S3DocumentStore s3DocumentStore=new S3DocumentStore("bucketName", null); + public void shouldGenerateClientImageDirectoryFromResourceId() { + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", null); String actualParentDirectory = s3DocumentStore.generateClientImageParentDirectory(1234L); assertTrue(actualParentDirectory.startsWith("images/clients/1234")); } @@ -48,20 +55,20 @@ public class S3DocumentStoreTest { @Test public void shouldSaveADocumentToS3() throws IOException { initUpload(); - when(s3ClientMock.putObject(Matchers.anyObject())).thenReturn(new PutObjectResult()); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + when(s3ClientMock.putObject(Matchers. anyObject())).thenReturn(new PutObjectResult()); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); s3DocumentStore.saveDocument(inputStream, documentCommand); - verify(s3ClientMock, times(1)).putObject(Matchers.anyObject()); + verify(s3ClientMock, times(1)).putObject(Matchers. anyObject()); } @Test(expected = DocumentManagementException.class) public void shouldCatchAmazonServiceExceptionWhenUnableToUploadDocumentToS3() throws IOException { initUpload(); - when(s3ClientMock.putObject(Matchers.anyObject())).thenThrow(new AmazonServiceException("serviceException")); + when(s3ClientMock.putObject(Matchers. anyObject())).thenThrow(new AmazonServiceException("serviceException")); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); s3DocumentStore.saveDocument(inputStream, documentCommand); } @@ -69,9 +76,9 @@ public class S3DocumentStoreTest { @Test(expected = DocumentManagementException.class) public void shouldCatchAmazonClientExceptionWhenUnableToUploadDocumentToS3() throws IOException { initUpload(); - when(s3ClientMock.putObject(Matchers.anyObject())).thenThrow(new AmazonClientException("clientException")); + when(s3ClientMock.putObject(Matchers. anyObject())).thenThrow(new AmazonClientException("clientException")); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName", s3ClientMock); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); s3DocumentStore.saveDocument(inputStream, documentCommand); } @@ -79,12 +86,12 @@ public class S3DocumentStoreTest { @Test public void shouldRetrieveDocumentFromS3() throws IOException { initRetrieve(); - when(s3ClientMock.getObject(Matchers.anyObject())).thenReturn(new S3Object()); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + when(s3ClientMock.getObject(Matchers. anyObject())).thenReturn(new S3Object()); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); FileData fileData = s3DocumentStore.retrieveDocument(documentDataMock); - verify(s3ClientMock, times(1)).getObject(Matchers.anyObject()); + verify(s3ClientMock, times(1)).getObject(Matchers. anyObject()); assertEquals(fileData.name(), "name"); assertEquals(fileData.contentType(), "type"); } @@ -92,8 +99,8 @@ public class S3DocumentStoreTest { @Test(expected = DocumentNotFoundException.class) public void shouldCatchAmazonServiceExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException { initRetrieve(); - when(s3ClientMock.getObject(Matchers.anyObject())).thenThrow(new AmazonServiceException("serviceException")); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + when(s3ClientMock.getObject(Matchers. anyObject())).thenThrow(new AmazonServiceException("serviceException")); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); s3DocumentStore.retrieveDocument(documentDataMock); } @@ -101,8 +108,8 @@ public class S3DocumentStoreTest { @Test(expected = DocumentNotFoundException.class) public void shouldCatchAmazonClientExceptionWhenUnableToRetrieveDocumentFromS3() throws IOException { initRetrieve(); - when(s3ClientMock.getObject(Matchers.anyObject())).thenThrow(new AmazonClientException("clientException")); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + when(s3ClientMock.getObject(Matchers. anyObject())).thenThrow(new AmazonClientException("clientException")); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); s3DocumentStore.retrieveDocument(documentDataMock); } @@ -110,11 +117,11 @@ public class S3DocumentStoreTest { @Test public void shouldDeleteImageFromS3() throws IOException { AmazonS3Client s3ClientMock = mock(AmazonS3Client.class); - S3DocumentStore s3DocumentStore = new S3DocumentStore("bucketName",s3ClientMock); + S3ContentRepository s3DocumentStore = new S3ContentRepository("bucketName", s3ClientMock); - s3DocumentStore.deleteImage(123L,"imagekey"); + s3DocumentStore.deleteImage(123L, "imagekey"); - verify(s3ClientMock, times(1)).deleteObject(Matchers.anyObject()); + verify(s3ClientMock, times(1)).deleteObject(Matchers. anyObject()); } private void initRetrieve() { @@ -126,10 +133,10 @@ public class S3DocumentStoreTest { when(documentDataMock.contentType()).thenReturn("type"); } - - private void initUpload(){ + private void initUpload() { inputStream = S3DocumentStoreTest.class.getResourceAsStream("DummyString"); s3ClientMock = mock(AmazonS3Client.class); - documentCommand = new DocumentCommand(null, -1l, "parentEntityType", 1l, null, "someFileName", 3l, null, null, null); + documentCommand = new DocumentCommand(null, -1l, "parentEntityType", 1l, null, "someFileName", 3l, null, null, null); } + **/ }