From 041225c8f5b49900d99fc1b6667a64ae7ab712c2 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 24 May 2012 14:08:48 +0100 Subject: [PATCH] move json formatting code into service class. support pretty printing and use non deprecated api. --- .../mifosng/data/ApiGlobalErrorResponse.java | 16 ++++ .../org/mifosng/data/ApiParameterError.java | 5 +- .../platform/api/OfficeApiResource.java | 84 +++++-------------- ...PlatformInternalServerExceptionMapper.java | 29 +++++++ .../ApiJSONFormattingService.java | 10 +++ .../ApiJSONFormattingServiceImpl.java | 60 +++++++++++++ .../PlatformInternalServerException.java | 29 +++++++ 7 files changed, 167 insertions(+), 66 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformInternalServerExceptionMapper.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingService.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformInternalServerException.java diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java index acf7e9e46..0cfd9f7a0 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiGlobalErrorResponse.java @@ -114,6 +114,22 @@ public class ApiGlobalErrorResponse { return globalErrorResponse; } + public static ApiGlobalErrorResponse serverSideError(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + + ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse(); + globalErrorResponse.setHttpStatusCode("500"); + globalErrorResponse.setDeveloperMessage("An unexpected error occured on the platform server."); + globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes"); + globalErrorResponse.setUserMessageGlobalisationCode("error.msg.platform.server.side.error"); + globalErrorResponse.setDefaultUserMessage("An unexpected error occured on the platform server."); + + List errors = new ArrayList(); + errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs)); + globalErrorResponse.setErrors(errors); + + return globalErrorResponse; + } + protected ApiGlobalErrorResponse() { // } diff --git a/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java b/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java index 603e484ea..ad083a08d 100644 --- a/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java +++ b/mifosng-data/src/main/java/org/mifosng/data/ApiParameterError.java @@ -37,7 +37,10 @@ public class ApiParameterError { * Arguments related to the user error message. */ private List args = new ArrayList(); - + + public static ApiParameterError generalError(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + return new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); + } public static ApiParameterError resourceIdentifierNotFound(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { return new ApiParameterError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java index ee90ee85b..1582b5408 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/OfficeApiResource.java @@ -1,10 +1,8 @@ package org.mifosng.platform.api; -import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Set; -import java.util.StringTokenizer; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -17,19 +15,13 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.ser.FilterProvider; -import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter; -import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider; import org.joda.time.LocalDate; import org.mifosng.data.EntityIdentifier; import org.mifosng.data.OfficeData; import org.mifosng.data.OfficeTemplateData; import org.mifosng.data.command.OfficeCommand; import org.mifosng.platform.api.infrastructure.ApiDataConversionService; -import org.mifosng.platform.exceptions.PlatformApiDataValidationException; +import org.mifosng.platform.api.infrastructure.ApiJSONFormattingService; import org.mifosng.platform.organisation.service.OfficeReadPlatformService; import org.mifosng.platform.organisation.service.OfficeWritePlatformService; import org.slf4j.Logger; @@ -43,8 +35,7 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class OfficeApiResource { - private final static Logger logger = LoggerFactory - .getLogger(OfficeApiResource.class); + private final static Logger logger = LoggerFactory.getLogger(OfficeApiResource.class); @Autowired private OfficeReadPlatformService readPlatformService; @@ -54,6 +45,9 @@ public class OfficeApiResource { @Autowired private ApiDataConversionService apiDataConversionService; + + @Autowired + private ApiJSONFormattingService jsonFormattingService; @GET @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) @@ -65,8 +59,11 @@ public class OfficeApiResource { Set excludeFields = new HashSet(); excludeFields.add("officeTemplateData"); - return convertDataObjectJSON(offices, fields, excludeFields); - + + // can use parameters for turning this on/off etc. + boolean prettyOutput = true; + + return this.jsonFormattingService.convertDataObjectJSON(offices, fields, excludeFields, prettyOutput); } @GET @@ -82,7 +79,11 @@ public class OfficeApiResource { Set excludeFields = new HashSet(); String includeFields = "officeTemplateData"; - return convertDataObjectJSON(officeData, includeFields, excludeFields); + + // can use parameters for turning this on/off etc. + boolean prettyOutput = true; + + return this.jsonFormattingService.convertDataObjectJSON(officeData, includeFields, excludeFields, prettyOutput); } @POST @@ -138,8 +139,10 @@ public class OfficeApiResource { excludeFields.add("officeTemplateData"); } } - - return convertDataObjectJSON(office, includeFields, excludeFields); + + boolean prettyOutput = true; + + return this.jsonFormattingService.convertDataObjectJSON(office, includeFields, excludeFields, prettyOutput); } @PUT @@ -159,53 +162,4 @@ public class OfficeApiResource { return Response.ok().entity(new EntityIdentifier(entityId)).build(); } - - private String convertDataObjectJSON(Object dataObject, String fields, - Set excludeFields) { - - if (excludeFields == null) { - throw new PlatformApiDataValidationException( - "validation.msg.excludeFields.null", - "JPW (might be wrong error msg) Parameter excludeFields is unexpectedly null.", - null); - } - - String json = ""; - String myFilter = "myFilter"; - Set includeFields = new HashSet(); - FilterProvider filters = null; - - ObjectMapper mapper = new ObjectMapper(); - if (fields == null || fields.equals("")) { - filters = new SimpleFilterProvider().addFilter(myFilter, - SimpleBeanPropertyFilter.serializeAllExcept(excludeFields)); - - } else { - - StringTokenizer st = new StringTokenizer(fields, ","); - while (st.hasMoreTokens()) { - includeFields.add(st.nextToken().trim()); - } - - filters = new SimpleFilterProvider().addFilter(myFilter, - SimpleBeanPropertyFilter.filterOutAllExcept(includeFields)); - } - - try { - json = mapper.filteredWriter(filters) - .writeValueAsString(dataObject); - } catch (JsonGenerationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (JsonMappingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - logger.info("office list json: " + json); - return json; - } - } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformInternalServerExceptionMapper.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformInternalServerExceptionMapper.java new file mode 100644 index 000000000..36cb4246d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/errorhandling/PlatformInternalServerExceptionMapper.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.api.errorhandling; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; + +import org.mifosng.data.ApiGlobalErrorResponse; +import org.mifosng.platform.exceptions.PlatformInternalServerException; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +/** + * An {@link ExceptionMapper} to map {@link PlatformInternalServerException} thrown by platform into a HTTP API friendly format. + * + * The {@link PlatformInternalServerException} is thrown when an api call results in unexpected server side exceptions. + */ +@Provider +@Component +@Scope("singleton") +public class PlatformInternalServerExceptionMapper implements ExceptionMapper { + + @Override + public Response toResponse(PlatformInternalServerException exception) { + + ApiGlobalErrorResponse notFoundErrorResponse = ApiGlobalErrorResponse.notFound(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage(), exception.getDefaultUserMessageArgs()); + return Response.status(Status.INTERNAL_SERVER_ERROR).entity(notFoundErrorResponse).build(); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingService.java new file mode 100644 index 000000000..fb3ef3aed --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingService.java @@ -0,0 +1,10 @@ +package org.mifosng.platform.api.infrastructure; + +import java.util.Set; + +public interface ApiJSONFormattingService { + + String convertDataObjectJSON(Object dataObject, String fields, + Set excludeFields, boolean prettyOutput); + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingServiceImpl.java new file mode 100644 index 000000000..24173c6a7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiJSONFormattingServiceImpl.java @@ -0,0 +1,60 @@ +package org.mifosng.platform.api.infrastructure; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; + +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.ObjectWriter; +import org.codehaus.jackson.map.ser.FilterProvider; +import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter; +import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider; +import org.mifosng.platform.exceptions.PlatformInternalServerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class ApiJSONFormattingServiceImpl implements ApiJSONFormattingService { + + private final static Logger logger = LoggerFactory.getLogger(ApiJSONFormattingServiceImpl.class); + + @Override + public String convertDataObjectJSON(Object dataObject, String fields, Set excludeFields, boolean prettyOutput) { + + try { + String json = ""; + String myFilter = "myFilter"; + Set includeFields = new HashSet(); + FilterProvider filters = null; + + if (fields == null || fields.equals("")) { + filters = new SimpleFilterProvider().addFilter(myFilter, + SimpleBeanPropertyFilter.serializeAllExcept(excludeFields)); + + } else { + + StringTokenizer st = new StringTokenizer(fields, ","); + while (st.hasMoreTokens()) { + includeFields.add(st.nextToken().trim()); + } + + filters = new SimpleFilterProvider().addFilter(myFilter, + SimpleBeanPropertyFilter.filterOutAllExcept(includeFields)); + } + + ObjectWriter jsonWriter = new ObjectMapper().writerWithDefaultPrettyPrinter(); + json = jsonWriter.withFilters(filters).writeValueAsString(dataObject); + return json; + } catch (JsonGenerationException e) { + throw new PlatformInternalServerException("error.msg.platform.json.generation", "An error occured whilst generating response from the platform server.", e.getMessage(), e.getStackTrace()); + } catch (JsonMappingException e) { + throw new PlatformInternalServerException("error.msg.platform.json.generation", "An error occured whilst generating response from the platform server.", e.getMessage(), e.getStackTrace()); + } catch (IOException e) { + throw new PlatformInternalServerException("error.msg.platform.json.generation", "An error occured whilst generating response from the platform server.", e.getMessage(), e.getStackTrace()); + } + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformInternalServerException.java b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformInternalServerException.java new file mode 100644 index 000000000..2421319cb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/exceptions/PlatformInternalServerException.java @@ -0,0 +1,29 @@ +package org.mifosng.platform.exceptions; + +/** + * A {@link RuntimeException} thrown when unexpected server side errors happen. + */ +public class PlatformInternalServerException extends RuntimeException { + + private final String globalisationMessageCode; + private final String defaultUserMessage; + private final Object[] defaultUserMessageArgs; + + public PlatformInternalServerException(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) { + this.globalisationMessageCode = globalisationMessageCode; + this.defaultUserMessage = defaultUserMessage; + this.defaultUserMessageArgs = defaultUserMessageArgs; + } + + public String getGlobalisationMessageCode() { + return globalisationMessageCode; + } + + public String getDefaultUserMessage() { + return defaultUserMessage; + } + + public Object[] getDefaultUserMessageArgs() { + return defaultUserMessageArgs; + } +} \ No newline at end of file