Intermediate checking for file upload functionality

This commit is contained in:
Vishwas Babu AJ
2012-10-10 05:19:59 +05:30
parent 9c75498ebe
commit 2e83169b99
21 changed files with 1078 additions and 2 deletions
@@ -0,0 +1,187 @@
package org.mifosng.platform.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.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.api.data.DocumentData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.infrastructure.ApiJsonSerializerService;
import org.mifosng.platform.api.infrastructure.ApiParameterHelper;
import org.mifosng.platform.documentmanagement.service.DocumentReadPlatformService;
import org.mifosng.platform.documentmanagement.service.DocumentWritePlatformService;
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;
@Path("{entityType}/{entityId}/documents")
@Component
@Scope("singleton")
public class DocumentManagementApiResource {
@Autowired
private DocumentReadPlatformService documentReadPlatformService;
@Autowired
private DocumentWritePlatformService documentWritePlatformService;
@Autowired
private ApiJsonSerializerService apiJsonSerializerService;
private static final Set<String> typicalResponseParameters = new HashSet<String>(
Arrays.asList("id", "parentEntityType", "parentEntityId", "name",
"fileName", "type", "size", "description"));
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveAllDocuments(@Context final UriInfo uriInfo,
@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId) {
Set<String> responseParameters = ApiParameterHelper
.extractFieldsForResponseIfProvided(uriInfo
.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo
.getQueryParameters());
Collection<DocumentData> documentDatas = this.documentReadPlatformService
.retrieveAllDocuments(entityType, entityId);
return this.apiJsonSerializerService.serializeDocumentDataToJson(
prettyPrint, responseParameters, documentDatas);
}
@POST
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Produces({ MediaType.APPLICATION_JSON })
public Response createDocument(@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId,
@FormDataParam("file") InputStream inputStream,
@FormDataParam("file") FormDataContentDisposition fileDetails,
@FormDataParam("file") FormDataBodyPart bodyPart,
@HeaderParam("Content-Length") Long fileSize) {
// TODO: Using Content-Length gives me size of the entire request, need
// to filter out only the size of the file
// TODO: Need to add a Fast fail for checking size of file depending on
// the size sent by client, also need to have a backup and stop reading
// from stream after size is reached to protect against malicious
// clients
DocumentCommand documentCommand = new DocumentCommand(null, null,
entityType, entityId, "test", fileDetails.getFileName(),
fileSize, bodyPart.getMediaType().toString(), "description",
null);
bodyPart.getMediaType();
Long documentId = this.documentWritePlatformService.createDocument(
documentCommand, inputStream);
return Response.ok().entity(new EntityIdentifier(documentId)).build();
}
@GET
@Path("{documentId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String getDocument(@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId,
@PathParam("documentId") Long documentId,
@Context final UriInfo uriInfo) {
Set<String> responseParameters = ApiParameterHelper
.extractFieldsForResponseIfProvided(uriInfo
.getQueryParameters());
if (responseParameters.isEmpty()) {
responseParameters.addAll(typicalResponseParameters);
}
boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo
.getQueryParameters());
DocumentData documentData = this.documentReadPlatformService
.retrieveDocument(entityType, entityId, documentId);
return this.apiJsonSerializerService.serializeDocumentDataToJson(
prettyPrint, responseParameters, documentData);
}
@GET
@Path("{documentId}/attachment")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response downloadFile(@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId,
@PathParam("documentId") Long documentId) {
DocumentData documentData = this.documentReadPlatformService
.retrieveDocument(entityType, entityId, documentId);
File file = new File(documentData.getLocation());
ResponseBuilder response = Response.ok(file);
response.header("Content-Disposition", "attachment; filename=\""
+ documentData.getFileName() + "\"");
response.header("Content-Type", documentData.getType());
return response.build();
}
@PUT
@Path("{documentId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response updateDocument(@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId,
@PathParam("documentId") Long documentId) {
// TODO
// EntityIdentifier identifier =
// this.documentWritePlatformService.updateDocument(documentCommand,
// inputStream, fileDetails)
return Response.ok().entity(null).build();
}
@DELETE
@Path("{documentId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response deleteDocument(@PathParam("entityType") String entityType,
@PathParam("entityId") Long entityId,
@PathParam("documentId") Long documentId) {
DocumentCommand documentCommand = new DocumentCommand(null, documentId,
entityType, entityId, null, null, null, null, null, null);
EntityIdentifier documentIdentifier = this.documentWritePlatformService
.deleteDocument(documentCommand);
return Response.ok(documentIdentifier).build();
}
}
@@ -0,0 +1,119 @@
package org.mifosng.platform.api.commands;
import java.util.Set;
/**
* Immutable command for creating or updating details of a client identifier.
*/
public class DocumentCommand {
private final Long id;
private final String parentEntityType;
private final Long parentEntityId;
private final String name;
private final String description;
private String fileName;
private Long size;
private String type;
private String location;
private final Set<String> modifiedParameters;
public DocumentCommand(final Set<String> modifiedParameters, final Long id,
final String parentEntityType, final Long parentEntityId,
final String name, final String fileName, Long size, String type,
final String description, String location) {
this.modifiedParameters = modifiedParameters;
this.id = id;
this.parentEntityType = parentEntityType;
this.parentEntityId = parentEntityId;
this.name = name;
this.fileName = fileName;
this.size = size;
this.type = type;
this.description = description;
this.location = location;
}
public Long getId() {
return id;
}
public String getParentEntityType() {
return parentEntityType;
}
public Long getParentEntityId() {
return parentEntityId;
}
public String getName() {
return name;
}
public String getFileName() {
return fileName;
}
public Long getSize() {
return size;
}
public String getType() {
return type;
}
public String getDescription() {
return description;
}
public String getLocation() {
return location;
}
public Set<String> getModifiedParameters() {
return modifiedParameters;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public void setSize(Long size) {
this.size = size;
}
public void setType(String type) {
this.type = type;
}
public void setLocation(String location) {
this.location = location;
}
public boolean isNameChanged() {
return this.modifiedParameters.contains("name");
}
public boolean isFileNameChanged() {
return this.modifiedParameters.contains("fileName");
}
public boolean isSizeChanged() {
return this.modifiedParameters.contains("size");
}
public boolean isFileTypeChanged() {
return this.modifiedParameters.contains("type");
}
public boolean isDescriptionChanged() {
return this.modifiedParameters.contains("description");
}
public boolean isLocationChanged() {
return this.modifiedParameters.contains("location");
}
}
@@ -0,0 +1,67 @@
package org.mifosng.platform.api.data;
public class DocumentData {
private final Long id;
private final String parentEntityType;
private final Long parentEntityId;
private final String name;
private final String fileName;
private final Long size;
private final String type;
private final String description;
private final String location;
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) {
this.id = id;
this.parentEntityType = parentEntityType;
this.parentEntityId = parentEntityId;
this.name = name;
this.fileName = fileName;
this.size = size;
this.type = type;
this.description = description;
this.location = location;
}
public Long getId() {
return id;
}
public String getParentEntityType() {
return parentEntityType;
}
public Long getParentEntityId() {
return parentEntityId;
}
public String getName() {
return name;
}
public String getFileName() {
return fileName;
}
public Long getSize() {
return size;
}
public String getType() {
return type;
}
public String getDescription() {
return description;
}
public String getLocation() {
return location;
}
}
@@ -11,6 +11,7 @@ import org.mifosng.platform.api.commands.DepositAccountWithdrawalCommand;
import org.mifosng.platform.api.commands.DepositProductCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionApprovalCommand;
import org.mifosng.platform.api.commands.DepositStateTransitionCommand;
import org.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.api.commands.FundCommand;
import org.mifosng.platform.api.commands.GroupCommand;
import org.mifosng.platform.api.commands.LoanApplicationCommand;
@@ -1467,4 +1467,5 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService {
return new ClientIdentifierCommand(modifiedParameters,resourceIdentifier,clientId,documentTypeId,documentKey,documentDescription);
}
}
@@ -15,6 +15,7 @@ import org.mifosng.platform.api.data.ConfigurationData;
import org.mifosng.platform.api.data.DatatableData;
import org.mifosng.platform.api.data.DepositAccountData;
import org.mifosng.platform.api.data.DepositProductData;
import org.mifosng.platform.api.data.DocumentData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.data.GenericResultsetData;
@@ -122,4 +123,9 @@ public interface ApiJsonSerializerService {
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<ClientIdentifierData> clientIdentifiers);
String serializeClientIdentifierDataToJson(boolean prettyPrint, Set<String> responseParameters, ClientIdentifierData clientIdentifierData);
String serializeDocumentDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<DocumentData> documentDatas);
String serializeDocumentDataToJson(boolean prettyPrint, Set<String> responseParameters, DocumentData documentData);
}
@@ -17,6 +17,7 @@ import org.mifosng.platform.api.data.ConfigurationData;
import org.mifosng.platform.api.data.DatatableData;
import org.mifosng.platform.api.data.DepositAccountData;
import org.mifosng.platform.api.data.DepositProductData;
import org.mifosng.platform.api.data.DocumentData;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.api.data.FundData;
import org.mifosng.platform.api.data.GenericResultsetData;
@@ -126,6 +127,10 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
private static final Set<String> CLIENT_IDENTIFIER_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "clientId", "documentTypeId", "documentKey",
"description", "documentTypeName" ,"allowedDocumentTypes"));
private static final Set<String> DOCUMENT_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("id", "parentEntityType", "parentEntityId", "name",
"fileName", "type", "size", "description"));
private static final Set<String> CLIENT_ACCOUNTS_DATA_PARAMETERS = new HashSet<String>(
Arrays.asList("pendingApprovalLoans", "awaitingDisbursalLoans",
@@ -652,4 +657,25 @@ public class GoogleGsonApiJsonSerializerService implements ApiJsonSerializerServ
CLIENT_IDENTIFIER_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, clientIdentifierData);
}
@Override
public String serializeDocumentDataToJson(boolean prettyPrint,
Set<String> responseParameters,
Collection<DocumentData> documentDatas) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(
DOCUMENT_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer,
documentDatas.toArray(new DocumentData[documentDatas.size()]));
}
@Override
public String serializeDocumentDataToJson(boolean prettyPrint,
Set<String> responseParameters,
DocumentData documentData) {
final Gson gsonDeserializer = helper
.createGsonBuilderWithParameterExclusionSerializationStrategy(
DOCUMENT_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, documentData);
}
}
@@ -4,4 +4,13 @@ public class ApplicationConstants {
public static final Long CLIENT_IDENTITY_CODE_ID = 9L;
/*** Entities for document Management **/
public static enum DOCUMENT_MANAGEMENT_ENTITIES {
CLIENTs, STAFF, LOANS;
@Override
public String toString() {
return name().toString().toLowerCase();
}
}
}
@@ -0,0 +1,81 @@
package org.mifosng.platform.common;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import org.mifosng.platform.infrastructure.ThreadLocalContextUtil;
public class FileUtils {
public static final String MIFOSX_BASE_DIR = System
.getProperty("user.home") + File.separator + ".mifosx";
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 Type
*
* @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
+ entityType + File.separator + entityId + File.separator
+ FileUtils.generateRandomString();
}
/**
* @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;
}
}
@@ -0,0 +1,94 @@
package org.mifosng.platform.documentmanagement.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.infrastructure.AbstractAuditableCustom;
import org.mifosng.platform.user.domain.AppUser;
@Entity
@Table(name = "m_document")
public class Document extends AbstractAuditableCustom<AppUser, Long> {
@SuppressWarnings("unused")
@Column(name = "parent_entity_type", length = 50)
private String parentEntityType;
@SuppressWarnings("unused")
@Column(name = "parent_entity_id", length = 1000)
private Long parentEntityId;
@SuppressWarnings("unused")
@Column(name = "name", length = 250)
private String name;
@SuppressWarnings("unused")
@Column(name = "file_name", length = 250)
private String fileName;
@SuppressWarnings("unused")
@Column(name = "size")
private Long size;
@SuppressWarnings("unused")
@Column(name = "type", length = 50)
private String type;
@SuppressWarnings("unused")
@Column(name = "description", length = 1000)
private String description;
@SuppressWarnings("unused")
@Column(name = "location", length = 500)
private String location;
public Document() {
}
public static Document createNew(String parentEntityType,
Long parentEntityId, String name, String fileName, Long size,
String type, String description, String location) {
return new Document(parentEntityType, parentEntityId, name, fileName,
size, type, description, location);
}
private Document(String parentEntityType, Long parentEntityId, String name,
String fileName, Long size, String type, String description,
String location) {
this.parentEntityType = StringUtils.defaultIfEmpty(parentEntityType,
null);
this.parentEntityId = parentEntityId;
this.name = StringUtils.defaultIfEmpty(name, null);
this.fileName = StringUtils.defaultIfEmpty(fileName, null);
this.size = size;
this.type = StringUtils.defaultIfEmpty(type, null);
this.description = StringUtils.defaultIfEmpty(description, null);
this.location = StringUtils.defaultIfEmpty(location, null);
}
public void update(final DocumentCommand command) {
if (command.isDescriptionChanged()) {
this.description = command.getDescription();
}
if (command.isFileNameChanged()) {
this.fileName = command.getFileName();
}
if (command.isFileTypeChanged()) {
this.type = command.getType();
}
if (command.isLocationChanged()) {
this.location = command.getLocation();
}
if (command.isNameChanged()) {
this.name = command.getName();
}
if (command.isSizeChanged()) {
this.size = command.getSize();
}
}
}
@@ -0,0 +1,9 @@
package org.mifosng.platform.documentmanagement.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface DocumentRepository extends
JpaRepository<Document, Long>, JpaSpecificationExecutor<Document> {
// no added behaviour
}
@@ -0,0 +1,84 @@
package org.mifosng.platform.documentmanagement.service;
import java.util.ArrayList;
import java.util.List;
import org.mifosng.platform.DataValidatorBuilder;
import org.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.api.data.ApiParameterError;
import org.mifosng.platform.exceptions.PlatformApiDataValidationException;
public class DocumentCommandValidator {
private final DocumentCommand command;
public DocumentCommandValidator(DocumentCommand command) {
this.command = command;
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource("document");
baseDataValidator.reset().parameter("name").value(command.getName())
.ignoreIfNull().notBlank();
/*** TODO:Validate for max length of File to be stored **/
baseDataValidator.reset().parameter("size").value(command.getSize())
.ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("fileName")
.value(command.getFileName()).ignoreIfNull().notBlank()
.integerGreaterThanZero();
baseDataValidator.reset().parameter("location")
.value(command.getLocation()).ignoreIfNull().notBlank()
.integerGreaterThanZero();
/*** TODO: Validate for permissable types ***/
baseDataValidator.reset().parameter("type").value(command.getName())
.ignoreIfNull().notBlank().notExceedingLengthOf(250);
baseDataValidator.reset().parameter("description")
.value(command.getName()).ignoreIfNull()
.notExceedingLengthOf(250);
baseDataValidator.reset().anyOfNotNull(command.getName(),
command.getFileName(), command.getDescription(),
command.getLocation(), command.getSize());
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(
"validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors);
}
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
dataValidationErrors).resource("document");
baseDataValidator.reset().parameter("parentEntityType")
.value(command.getParentEntityType()).notBlank()
.notExceedingLengthOf(50);
baseDataValidator.reset().parameter("parentEntityId")
.value(command.getParentEntityId()).integerGreaterThanZero();
baseDataValidator.reset().parameter("name").value(command.getName())
.notBlank().notExceedingLengthOf(250);
/*** TODO:Validate for max length of File to be stored **/
baseDataValidator.reset().parameter("size").value(command.getSize())
.integerGreaterThanZero();
baseDataValidator.reset().parameter("fileName")
.value(command.getFileName()).notBlank()
.notExceedingLengthOf(250);
/*** TODO: Validate for permissable types ***/
baseDataValidator.reset().parameter("type").value(command.getName())
.notBlank().notExceedingLengthOf(250);
baseDataValidator.reset().parameter("description")
.value(command.getName()).notExceedingLengthOf(250);
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(
"validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors);
}
}
}
@@ -0,0 +1,15 @@
package org.mifosng.platform.documentmanagement.service;
import java.util.Collection;
import org.mifosng.platform.api.data.DocumentData;
public interface DocumentReadPlatformService {
Collection<DocumentData> retrieveAllDocuments(String entityType,
Long entityId);
DocumentData retrieveDocument(String entityType, Long entityId,
Long documentId);
}
@@ -0,0 +1,117 @@
package org.mifosng.platform.documentmanagement.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosng.platform.api.data.DocumentData;
import org.mifosng.platform.client.service.ClientReadPlatformService;
import org.mifosng.platform.exceptions.DocumentNotFoundException;
import org.mifosng.platform.infrastructure.JdbcSupport;
import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource;
import org.mifosng.platform.loan.service.LoanReadPlatformService;
import org.mifosng.platform.organisation.service.OfficeReadPlatformService;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.staff.service.StaffReadPlatformService;
import org.mifosng.platform.user.service.AppUserReadPlatformService;
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 DocumentReadPlatformServiceImpl implements
DocumentReadPlatformService {
private final JdbcTemplate jdbcTemplate;
// TODO: use these services to check if passed n entities for document
// management exists (and ensure appropriate errors are thrown)
private final PlatformSecurityContext context;
private final OfficeReadPlatformService officeReadPlatformService;
private final ClientReadPlatformService clientReadPlatformService;
private final LoanReadPlatformService loanReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final AppUserReadPlatformService appUserReadPlatformService;
@Autowired
public DocumentReadPlatformServiceImpl(
final PlatformSecurityContext context,
final TenantAwareRoutingDataSource dataSource,
final OfficeReadPlatformService officeReadPlatformService,
final AppUserReadPlatformService appUserReadPlatformService,
final ClientReadPlatformService clientReadPlatformService,
final LoanReadPlatformService loanReadPlatformService,
final StaffReadPlatformService staffReadPlatformService) {
this.context = context;
this.officeReadPlatformService = officeReadPlatformService;
this.appUserReadPlatformService = appUserReadPlatformService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService;
this.loanReadPlatformService = loanReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
}
@Override
public Collection<DocumentData> retrieveAllDocuments(String entityType,
Long entityId) {
// TODO verify if the entities are valid and a user
// has data
// scope for the particular entities
DocumentMapper mapper = new DocumentMapper();
String sql = "select " + mapper.schema() + " order by d.id";
return this.jdbcTemplate.query(sql, mapper, new Object[] { entityType,
entityId });
}
@Override
public DocumentData retrieveDocument(String entityType, Long entityId,
Long documentId) {
try {
// TODO verify if the entities are valid and a
// user has data
// scope for the particular entities
DocumentMapper mapper = new DocumentMapper();
String sql = "select " + mapper.schema() + " and d.id=? ";
return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] {
entityType, entityId, documentId });
} catch (EmptyResultDataAccessException e) {
throw new DocumentNotFoundException(entityType, entityId,
documentId);
}
}
private static final class DocumentMapper implements
RowMapper<DocumentData> {
public DocumentMapper() {
}
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"
+ " from m_document d where d.parent_entity_type=? and d.parent_entity_id=?";
}
@Override
public DocumentData mapRow(final ResultSet rs, final int rowNum)
throws SQLException {
Long id = JdbcSupport.getLong(rs, "id");
Long parentEntityId = JdbcSupport.getLong(rs, "parentEntityId");
Long fileSize = JdbcSupport.getLong(rs, "fileSize");
String parentEntityType = rs.getString("parentEntityType");
String name = rs.getString("name");
String fileName = rs.getString("fileName");
String fileType = rs.getString("fileType");
String description = rs.getString("description");
String location = rs.getString("location");
return new DocumentData(id, parentEntityType, parentEntityId, name,
fileName, fileSize, fileType, description, location);
}
}
}
@@ -0,0 +1,21 @@
package org.mifosng.platform.documentmanagement.service;
import java.io.InputStream;
import org.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.springframework.security.access.prepost.PreAuthorize;
public interface DocumentWritePlatformService {
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
Long createDocument(DocumentCommand documentCommand, InputStream inputStream);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
EntityIdentifier updateDocument(DocumentCommand documentCommand,
InputStream inputStream);
@PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE')")
EntityIdentifier deleteDocument(DocumentCommand documentCommand);
}
@@ -0,0 +1,200 @@
package org.mifosng.platform.documentmanagement.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.mifosng.platform.api.commands.DocumentCommand;
import org.mifosng.platform.api.data.EntityIdentifier;
import org.mifosng.platform.client.domain.ClientRepository;
import org.mifosng.platform.common.ApplicationConstants;
import org.mifosng.platform.common.FileUtils;
import org.mifosng.platform.documentmanagement.domain.Document;
import org.mifosng.platform.documentmanagement.domain.DocumentRepository;
import org.mifosng.platform.exceptions.DocumentNotFoundException;
import org.mifosng.platform.exceptions.DocumentSaveException;
import org.mifosng.platform.exceptions.InvalidEntityTypeForDocumentManagementException;
import org.mifosng.platform.exceptions.PlatformDataIntegrityException;
import org.mifosng.platform.loan.domain.LoanRepository;
import org.mifosng.platform.organisation.domain.OfficeRepository;
import org.mifosng.platform.security.PlatformSecurityContext;
import org.mifosng.platform.staff.domain.StaffRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class DocumentWritePlatformServiceJpaRepositoryImpl implements
DocumentWritePlatformService {
private final static Logger logger = LoggerFactory
.getLogger(DocumentWritePlatformServiceJpaRepositoryImpl.class);
// TODO: Use these services to ensure that the appropriate entities exist,
// are active, check data scope for user etc
private final PlatformSecurityContext context;
private final ClientRepository clientRepository;
private final OfficeRepository officeRepository;
private final LoanRepository loanRepository;
private final StaffRepository staffRepository;
private final DocumentRepository documentRepository;
@Autowired
public DocumentWritePlatformServiceJpaRepositoryImpl(
final PlatformSecurityContext context,
final ClientRepository clientRepository,
final DocumentRepository documentRepository,
final OfficeRepository officeRepository,
final LoanRepository loanRepository,
final StaffRepository staffRepository) {
this.context = context;
this.clientRepository = clientRepository;
this.documentRepository = documentRepository;
this.officeRepository = officeRepository;
this.loanRepository = loanRepository;
this.staffRepository = staffRepository;
}
@Transactional
@Override
public Long createDocument(DocumentCommand documentCommand,
InputStream inputStream) {
try {
DocumentCommandValidator validator = new DocumentCommandValidator(
documentCommand);
validateParentEntityType(documentCommand);
validator.validateForCreate();
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 check if entity id is valid and within data scope for the
// user
// TODO replace file system appender with an Amazon S3 appender
String fileLocation = FileUtils.saveToFileSystem(inputStream,
fileUploadLocation, documentCommand.getFileName());
Document document = Document.createNew(
documentCommand.getParentEntityType(),
documentCommand.getParentEntityId(),
documentCommand.getName(), documentCommand.getFileName(),
documentCommand.getSize(), documentCommand.getType(),
documentCommand.getDescription(), fileLocation);
this.documentRepository.save(document);
return document.getId();
} catch (DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(
"error.msg.document.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
} catch (IOException ioe) {
logger.error(ioe.getMessage(), ioe);
throw new DocumentSaveException(documentCommand.getName());
}
}
@Transactional
@Override
public EntityIdentifier updateDocument(DocumentCommand documentCommand,
InputStream inputStream) {
try {
DocumentCommandValidator validator = new DocumentCommandValidator(
documentCommand);
validator.validateForUpdate();
// TODO check if entity id is valid and within data scope for the
// user
Document documentForUpdate = this.documentRepository
.findOne(documentCommand.getId());
if (documentForUpdate == null) {
throw new DocumentNotFoundException(
documentCommand.getParentEntityType(),
documentCommand.getParentEntityId(),
documentCommand.getId());
}
// if a new file is also passed in
if (inputStream != null) {
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 replace file system appender with an Amazon S3 appender
// TODO delete the file at the previous location
String fileLocation = FileUtils.saveToFileSystem(inputStream,
fileUploadLocation, documentCommand.getFileName());
documentCommand.setSize(documentCommand.getSize());
}
documentForUpdate.update(documentCommand);
this.documentRepository.saveAndFlush(documentForUpdate);
return new EntityIdentifier(documentForUpdate.getId());
} catch (DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException(
"error.msg.document.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
} catch (IOException ioe) {
logger.error(ioe.getMessage(), ioe);
throw new DocumentSaveException(documentCommand.getName());
}
}
@Transactional
@Override
public EntityIdentifier deleteDocument(DocumentCommand documentCommand) {
validateParentEntityType(documentCommand);
// TODO: Check document is present under this entity Id
Document document = this.documentRepository.findOne(documentCommand
.getId());
if (document == null) {
throw new DocumentNotFoundException(
documentCommand.getParentEntityType(),
documentCommand.getParentEntityId(),
documentCommand.getId());
}
this.documentRepository.delete(document);
// TODO: delete the actual file from File system/ Amazon S3
return new EntityIdentifier(document.getId());
}
private void validateParentEntityType(DocumentCommand documentCommand) {
if (!checkValidEntityType(documentCommand.getParentEntityType())) {
throw new InvalidEntityTypeForDocumentManagementException(
documentCommand.getParentEntityType());
}
}
private static boolean checkValidEntityType(String entityType) {
for (ApplicationConstants.DOCUMENT_MANAGEMENT_ENTITIES entities : ApplicationConstants.DOCUMENT_MANAGEMENT_ENTITIES
.values()) {
if (entities.name().equalsIgnoreCase(entityType)) {
return true;
}
}
return false;
}
}
@@ -6,6 +6,6 @@ package org.mifosng.platform.exceptions;
public class CodeValueNotFoundException extends AbstractPlatformResourceNotFoundException {
public CodeValueNotFoundException(Long id) {
super("error.msg.client.id.invalid", "Code value with identifier " + id + " does not exist", id);
super("error.msg.codevalue.id.invalid", "Code value with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,12 @@
package org.mifosng.platform.exceptions;
public class DocumentNotFoundException extends
AbstractPlatformResourceNotFoundException {
public DocumentNotFoundException(final String entityType,
final Long entityId, final Long id) {
super("error.msg.document.id.invalid", "Document with identifier " + id
+ " does not exist for the " + entityType + " with Identifier "
+ entityId, id);
}
}
@@ -0,0 +1,11 @@
package org.mifosng.platform.exceptions;
public class DocumentSaveException extends
AbstractPlatformResourceNotFoundException {
public DocumentSaveException(final String name) {
super("error.msg.document.id.save",
"Unable to save the document with name" + name
+ " due to a File system / Amazon S3 issue", name);
}
}
@@ -0,0 +1,16 @@
package org.mifosng.platform.exceptions;
/**
* A {@link RuntimeException} thrown when document management functionality is
* invoked for invalid Entity Types
*/
public class InvalidEntityTypeForDocumentManagementException extends
AbstractPlatformResourceNotFoundException {
public InvalidEntityTypeForDocumentManagementException(
final String entityType) {
super("error.documentmanagement.entitytype.invalid",
"Document Management is not support for the Entity Type: "
+ entityType, entityType);
}
}
@@ -5,7 +5,7 @@
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mifosplatform-tenants"
username="root"
password="mysql"
password="password"
validationQuery="select 1"
maxActive="10"
maxIdle="4"