MIFOSX-237: use of jdbcTemplate and SqlRowSet along with refactoring as a result of these changes.

This commit is contained in:
Keith Woodlock
2013-03-11 13:54:57 +00:00
parent d310d4df3c
commit be011529de
16 changed files with 1159 additions and 1220 deletions
@@ -16,209 +16,220 @@ import javax.xml.bind.annotation.XmlElementWrapper;
*/
public class ApiGlobalErrorResponse {
/**
* A developer friendly plain English description of why the HTTP error response was returned from the API.
*/
private String developerMessage;
/**
* A link to a URL which provides more information on the details of this type of error.
*/
private String developerDocLink;
/**
* The HTTP status code of the response.
*/
private String httpStatusCode;
/**
* A user friendly plain English description of why the HTTP error response was returned from the API that can be presented to end users.
*/
private String defaultUserMessage;
/**
* A code that can be used for globalisation support by client applications of the API.
*/
private String userMessageGlobalisationCode;
/**
* A list of zero or more of the actual reasons for the HTTP error should they be needed. Typically used to express data validation errors with parameters passed to API.
*/
private List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
/**
* A developer friendly plain English description of why the HTTP error
* response was returned from the API.
*/
private String developerMessage;
public static ApiGlobalErrorResponse unAuthenticated() {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("401");
globalErrorResponse.setDeveloperMessage("Invalid authentication details were passed in api request.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated");
globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login.");
return globalErrorResponse;
}
public static ApiGlobalErrorResponse invalidTenantIdentifier() {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("401");
globalErrorResponse.setDeveloperMessage("Invalid tenant details were passed in api request.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.invalid.tenant.identifier");
globalErrorResponse.setDefaultUserMessage("Invalide tenant identifier provided with request.");
return globalErrorResponse;
}
/**
* A link to a URL which provides more information on the details of this
* type of error.
*/
private String developerDocLink;
public static ApiGlobalErrorResponse unAuthorized() {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("The user associated with credentials passed on this request does not have sufficient privileges to perform this action.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authorized");
globalErrorResponse.setDefaultUserMessage("Insufficient privileges to perform this action.");
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError("error.msg.not.authorized", "Insufficient privileges to perform this action."));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse domainRuleViolation(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("Request was understood but caused a domain rule violation.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("validation.msg.domain.rule.violation");
globalErrorResponse.setDefaultUserMessage("Errors contain reason for domain rule violation.");
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
/**
* The HTTP status code of the response.
*/
private String httpStatusCode;
public static ApiGlobalErrorResponse notFound(String globalisationMessageCode, String defaultUserMessage, Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("404");
globalErrorResponse.setDeveloperMessage("The requested resource is not available.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.resource.not.found");
globalErrorResponse.setDefaultUserMessage("The requested resource is not available.");
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.resourceIdentifierNotFound(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage, final String parameterName, final Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("The request caused a data integrity issue to be fired by the database.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
globalErrorResponse.setDefaultUserMessage(defaultUserMessage);
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, parameterName, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse badClientRequest(final String globalisationMessageCode, final String defaultUserMessage,
List<ApiParameterError> errors) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("400");
globalErrorResponse.setDeveloperMessage("The request was invalid. This typically will happen due to validation errors which are provided.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
globalErrorResponse.setDefaultUserMessage(defaultUserMessage);
globalErrorResponse.setErrors(errors);
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<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
protected ApiGlobalErrorResponse() {
//
}
/**
* A user friendly plain English description of why the HTTP error response
* was returned from the API that can be presented to end users.
*/
private String defaultUserMessage;
public ApiGlobalErrorResponse(final List<ApiParameterError> errors) {
this.errors = errors;
}
/**
* A code that can be used for globalisation support by client applications
* of the API.
*/
private String userMessageGlobalisationCode;
@XmlElementWrapper(name="errors")
@XmlElement(name="errorResponse")
public List<ApiParameterError> getErrors() {
return errors;
}
/**
* A list of zero or more of the actual reasons for the HTTP error should
* they be needed. Typically used to express data validation errors with
* parameters passed to API.
*/
private List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
public void setErrors(List<ApiParameterError> errors) {
this.errors = errors;
}
public static ApiGlobalErrorResponse unAuthenticated() {
public String getDeveloperMessage() {
return developerMessage;
}
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("401");
globalErrorResponse.setDeveloperMessage("Invalid authentication details were passed in api request.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authenticated");
globalErrorResponse.setDefaultUserMessage("Unauthenticated. Please login.");
public void setDeveloperMessage(String developerMessage) {
this.developerMessage = developerMessage;
}
return globalErrorResponse;
}
public String getHttpStatusCode() {
return httpStatusCode;
}
public static ApiGlobalErrorResponse invalidTenantIdentifier() {
public void setHttpStatusCode(String httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("401");
globalErrorResponse.setDeveloperMessage("Invalid tenant details were passed in api request.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.invalid.tenant.identifier");
globalErrorResponse.setDefaultUserMessage("Invalide tenant identifier provided with request.");
public String getDefaultUserMessage() {
return defaultUserMessage;
}
return globalErrorResponse;
}
public void setDefaultUserMessage(String defaultUserMessage) {
this.defaultUserMessage = defaultUserMessage;
}
public static ApiGlobalErrorResponse unAuthorized(final String defaultUserMessage) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse
.setDeveloperMessage("The user associated with credentials passed on this request does not have sufficient privileges to perform this action.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.not.authorized");
globalErrorResponse.setDefaultUserMessage("Insufficient privileges to perform this action.");
public String getUserMessageGlobalisationCode() {
return userMessageGlobalisationCode;
}
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError("error.msg.not.authorized", defaultUserMessage));
globalErrorResponse.setErrors(errors);
public void setUserMessageGlobalisationCode(String userMessageGlobalisationCode) {
this.userMessageGlobalisationCode = userMessageGlobalisationCode;
}
return globalErrorResponse;
}
public String getDeveloperDocLink() {
return developerDocLink;
}
public static ApiGlobalErrorResponse domainRuleViolation(final String globalisationMessageCode, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("Request was understood but caused a domain rule violation.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("validation.msg.domain.rule.violation");
globalErrorResponse.setDefaultUserMessage("Errors contain reason for domain rule violation.");
public void setDeveloperDocLink(String developerDocLink) {
this.developerDocLink = developerDocLink;
}
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse notFound(final String globalisationMessageCode, final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("404");
globalErrorResponse.setDeveloperMessage("The requested resource is not available.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode("error.msg.resource.not.found");
globalErrorResponse.setDefaultUserMessage("The requested resource is not available.");
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.resourceIdentifierNotFound(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage,
final String parameterName, final Object... defaultUserMessageArgs) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("403");
globalErrorResponse.setDeveloperMessage("The request caused a data integrity issue to be fired by the database.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
globalErrorResponse.setDefaultUserMessage(defaultUserMessage);
List<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, parameterName, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse badClientRequest(final String globalisationMessageCode, final String defaultUserMessage,
final List<ApiParameterError> errors) {
ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode("400");
globalErrorResponse
.setDeveloperMessage("The request was invalid. This typically will happen due to validation errors which are provided.");
globalErrorResponse.setDeveloperDocLink("https://github.com/keithwoodlock/mifosx/wiki/HTTP-API-Error-codes");
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
globalErrorResponse.setDefaultUserMessage(defaultUserMessage);
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse serverSideError(final String globalisationMessageCode, final String defaultUserMessage,
final 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<ApiParameterError> errors = new ArrayList<ApiParameterError>();
errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
globalErrorResponse.setErrors(errors);
return globalErrorResponse;
}
protected ApiGlobalErrorResponse() {
//
}
public ApiGlobalErrorResponse(final List<ApiParameterError> errors) {
this.errors = errors;
}
@XmlElementWrapper(name = "errors")
@XmlElement(name = "errorResponse")
public List<ApiParameterError> getErrors() {
return errors;
}
public void setErrors(final List<ApiParameterError> errors) {
this.errors = errors;
}
public String getDeveloperMessage() {
return developerMessage;
}
public void setDeveloperMessage(final String developerMessage) {
this.developerMessage = developerMessage;
}
public String getHttpStatusCode() {
return httpStatusCode;
}
public void setHttpStatusCode(final String httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
public String getDefaultUserMessage() {
return defaultUserMessage;
}
public void setDefaultUserMessage(final String defaultUserMessage) {
this.defaultUserMessage = defaultUserMessage;
}
public String getUserMessageGlobalisationCode() {
return userMessageGlobalisationCode;
}
public void setUserMessageGlobalisationCode(final String userMessageGlobalisationCode) {
this.userMessageGlobalisationCode = userMessageGlobalisationCode;
}
public String getDeveloperDocLink() {
return developerDocLink;
}
public void setDeveloperDocLink(final String developerDocLink) {
this.developerDocLink = developerDocLink;
}
}
@@ -30,9 +30,11 @@ import org.springframework.stereotype.Component;
public class AccessDeniedExceptionMapper implements ExceptionMapper<AccessDeniedException> {
@Override
public Response toResponse(@SuppressWarnings("unused") AccessDeniedException exception) {
public Response toResponse(final AccessDeniedException exception) {
// Status code 403 really reads as:
// "Authenticated - but not authorized":
return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized()).type(MediaType.APPLICATION_JSON).build();
final String defaultUserMessage = exception.getMessage();
return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized(defaultUserMessage))
.type(MediaType.APPLICATION_JSON).build();
}
}
@@ -29,9 +29,11 @@ import org.springframework.stereotype.Component;
public class NoAuthorizationExceptionMapper implements ExceptionMapper<NoAuthorizationException> {
@Override
public Response toResponse(@SuppressWarnings("unused") NoAuthorizationException exception) {
public Response toResponse(final NoAuthorizationException exception) {
// Status code 403 really reads as:
// "Authenticated - but not authorized":
return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized()).type(MediaType.APPLICATION_JSON).build();
final String defaultUserMessage = exception.getMessage();
return Response.status(Status.FORBIDDEN).entity(ApiGlobalErrorResponse.unAuthorized(defaultUserMessage))
.type(MediaType.APPLICATION_JSON).build();
}
}
@@ -386,6 +386,7 @@ public class JsonParserHelper {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer.format", "The parameter "
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid integer value for provided locale of ["
+ clientApplicationLocale.toString() + "].", parameterName, numericalValueFormatted, clientApplicationLocale);
error.setValue(numericalValueFormatted);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
@@ -408,6 +409,7 @@ public class JsonParserHelper {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.integer", "The parameter " + parameterName
+ " has value: " + numericalValueFormatted + " which is invalid integer.", parameterName, numericalValueFormatted);
error.setValue(numericalValueFormatted);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
@@ -458,6 +460,7 @@ public class JsonParserHelper {
ApiParameterError error = ApiParameterError.parameterError("validation.msg.invalid.decimal.format", "The parameter "
+ parameterName + " has value: " + numericalValueFormatted + " which is invalid decimal value for provided locale of ["
+ clientApplicationLocale.toString() + "].", parameterName, numericalValueFormatted, clientApplicationLocale);
error.setValue(numericalValueFormatted);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
@@ -53,6 +53,7 @@ public class ReportsApiResource {
this.genericDataService = genericDataService;
this.toApiJsonSerializer = toApiJsonSerializer;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON, "application/x-msdownload" })
@@ -144,10 +145,10 @@ public class ReportsApiResource {
} else {
json = this.toApiJsonSerializer.serializePretty(prettyPrint, result);
}
return Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
}
// CSV Export
Map<String, String> reportParams = getReportParams(queryParams, false);
StreamingOutput result = this.readExtraDataAndReportingService.retrieveReportCSV(reportName, parameterTypeValue, reportParams);
@@ -160,15 +161,10 @@ public class ReportsApiResource {
// Anyone can run a 'report' that is simply getting possible parameter
// (dropdown listbox) values.
if (!parameterType) {
AppUser currentUser = context.authenticatedUser();
if (currentUser.hasNotPermissionForReport(reportName)) {
// FIXME - JW - message isnt passing back the message in the
// string in
// the json just a generalised not authorised message
throw new NoAuthorizationException("Not Authorised to Run Report: " + reportName);
}
if (currentUser.hasNotPermissionForReport(reportName)) { throw new NoAuthorizationException("Not authorised to run report: "
+ reportName); }
}
}
@@ -15,7 +15,11 @@ public class DatatableData {
@SuppressWarnings("unused")
private final String registeredTableName;
public DatatableData(final String applicationTableName, final String registeredTableName) {
public static DatatableData create(final String applicationTableName, final String registeredTableName) {
return new DatatableData(applicationTableName, registeredTableName);
}
private DatatableData(final String applicationTableName, final String registeredTableName) {
this.applicationTableName = applicationTableName;
this.registeredTableName = registeredTableName;
}
@@ -13,19 +13,19 @@ import java.util.List;
*/
public class GenericResultsetData {
private final List<ResultsetColumnHeader> columnHeaders;
private final List<ResultsetDataRow> data;
private final List<ResultsetColumnHeaderData> columnHeaders;
private final List<ResultsetRowData> data;
public GenericResultsetData(final List<ResultsetColumnHeader> columnHeaders, final List<ResultsetDataRow> resultsetDataRows) {
public GenericResultsetData(final List<ResultsetColumnHeaderData> columnHeaders, final List<ResultsetRowData> resultsetDataRows) {
this.columnHeaders = columnHeaders;
this.data = resultsetDataRows;
}
public List<ResultsetColumnHeader> getColumnHeaders() {
public List<ResultsetColumnHeaderData> getColumnHeaders() {
return columnHeaders;
}
public List<ResultsetDataRow> getData() {
public List<ResultsetRowData> getData() {
return data;
}
}
@@ -1,128 +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.dataqueries.data;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
public class ResultsetColumnHeader {
private String columnName;
private String columnType;
private Long columnLength;
private String columnDisplayType;
private boolean isColumnNullable;
private boolean isColumnPrimaryKey;
private List<ResultsetColumnValue> columnValues = new ArrayList<ResultsetColumnValue>();
public ResultsetColumnHeader() {
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getColumnType() {
return columnType;
}
public void setColumnType(String columnType) {
this.columnType = columnType;
}
public Long getColumnLength() {
return columnLength;
}
public void setColumnLength(Long columnLength) {
this.columnLength = columnLength;
}
public List<ResultsetColumnValue> getColumnValues() {
return columnValues;
}
public void setColumnValues(List<ResultsetColumnValue> columnValues) {
this.columnValues = columnValues;
}
public boolean isColumnNullable() {
return isColumnNullable;
}
public void setColumnNullable(boolean isColumnNullable) {
this.isColumnNullable = isColumnNullable;
}
public boolean isColumnPrimaryKey() {
return isColumnPrimaryKey;
}
public void setColumnPrimaryKey(boolean isColumnPrimaryKey) {
this.isColumnPrimaryKey = isColumnPrimaryKey;
}
public String getColumnDisplayType() {
return columnDisplayType;
}
public void setColumnDisplayType() {
if (this.getColumnValues().size() > 0) {
if (this.getColumnType().equalsIgnoreCase("int")) {
this.columnDisplayType = "CODELOOKUP";
return;
}
if (this.getColumnType().equalsIgnoreCase("varchar")) {
this.columnDisplayType = "CODEVALUE";
return;
}
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type", "Invalid Lookup Type:" + this.getColumnType()
+ " - Column Name: " + this.getColumnName());
}
if (this.getColumnType().equalsIgnoreCase("varchar") || this.getColumnType().equalsIgnoreCase("char")) {
this.columnDisplayType = "STRING";
return;
}
if (this.getColumnType().equalsIgnoreCase("int") || this.getColumnType().equalsIgnoreCase("bigint")
|| this.getColumnType().equalsIgnoreCase("smallint") || this.getColumnType().equalsIgnoreCase("mediumint")
|| this.getColumnType().equalsIgnoreCase("tinyint")) {
this.columnDisplayType = "INTEGER";
return;
}
if (this.getColumnType().equalsIgnoreCase("date")) {
this.columnDisplayType = "DATE";
return;
}
if (this.getColumnType().equalsIgnoreCase("decimal")) {
this.columnDisplayType = "DECIMAL";
return;
}
if (this.getColumnType().equalsIgnoreCase("text") || this.getColumnType().equalsIgnoreCase("mediumtext")
|| this.getColumnType().equalsIgnoreCase("longtext") || this.getColumnType().equalsIgnoreCase("tinytext")) {
this.columnDisplayType = "TEXT";
return;
}
throw new PlatformDataIntegrityException("error.msg.unsupported.column.type", "Unsupported Column Type:" + this.getColumnType()
+ " - Column Name: " + this.getColumnName());
}
}
@@ -0,0 +1,218 @@
/**
* 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.dataqueries.data;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
/**
* Immutable data object representing a resultset column.
*/
public final class ResultsetColumnHeaderData {
private final String columnName;
private final String columnType;
@SuppressWarnings("unused")
private final Long columnLength;
private final String columnDisplayType;
private final boolean isColumnNullable;
@SuppressWarnings("unused")
private final boolean isColumnPrimaryKey;
private final List<ResultsetColumnValueData> columnValues;
public static ResultsetColumnHeaderData basic(final String columnName, final String columnType) {
final Long columnLength = null;
final boolean columnNullable = false;
final boolean columnIsPrimaryKey = false;
final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues);
}
public static ResultsetColumnHeaderData detailed(final String columnName, final String columnType, final Long columnLength,
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues) {
return new ResultsetColumnHeaderData(columnName, columnType, columnLength, columnNullable, columnIsPrimaryKey, columnValues);
}
private ResultsetColumnHeaderData(final String columnName, final String columnType, final Long columnLength,
final boolean columnNullable, final boolean columnIsPrimaryKey, final List<ResultsetColumnValueData> columnValues) {
this.columnName = columnName;
this.columnType = columnType;
this.columnLength = columnLength;
this.isColumnNullable = columnNullable;
this.isColumnPrimaryKey = columnIsPrimaryKey;
this.columnValues = columnValues;
String displayType = null;
if (this.columnValues.isEmpty()) {
if (isString()) {
displayType = "STRING";
} else if (isAnyInteger()) {
displayType = "INTEGER";
} else if (isDate()) {
displayType = "DATE";
} else if (isDecimal()) {
displayType = "DECIMAL";
} else if (isAnyText()) {
displayType = "TEXT";
} else {
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type", "Invalid Lookup Type:" + this.columnType
+ " - Column Name: " + this.columnName);
}
} else {
if (isInt()) {
displayType = "CODELOOKUP";
} else if (isVarchar()) {
displayType = "CODEVALUE";
} else {
throw new PlatformDataIntegrityException("error.msg.invalid.lookup.type", "Invalid Lookup Type:" + this.columnType
+ " - Column Name: " + this.columnName);
}
}
this.columnDisplayType = displayType;
}
private boolean isAnyText() {
return isText() || isTinyText() || isMediumText() || isLongText();
}
private boolean isText() {
return "text".equalsIgnoreCase(this.columnType);
}
private boolean isTinyText() {
return "tinytext".equalsIgnoreCase(this.columnType);
}
private boolean isMediumText() {
return "mediumtext".equalsIgnoreCase(this.columnType);
}
private boolean isLongText() {
return "longtext".equalsIgnoreCase(this.columnType);
}
private boolean isDecimal() {
return "decimal".equalsIgnoreCase(this.columnType);
}
private boolean isDate() {
return "date".equalsIgnoreCase(this.columnType);
}
private boolean isString() {
return isVarchar() || isChar();
}
private boolean isChar() {
return "char".equalsIgnoreCase(this.columnType);
}
private boolean isVarchar() {
return "varchar".equalsIgnoreCase(this.columnType);
}
private boolean isAnyInteger() {
return isInt() || isSmallInt() || isTinyInt() || isMediumInt() || isBigInt();
}
private boolean isInt() {
return "int".equalsIgnoreCase(this.columnType);
}
private boolean isSmallInt() {
return "smallint".equalsIgnoreCase(this.columnType);
}
private boolean isTinyInt() {
return "tinyint".equalsIgnoreCase(this.columnType);
}
private boolean isMediumInt() {
return "mediumint".equalsIgnoreCase(this.columnType);
}
private boolean isBigInt() {
return "bigint".equalsIgnoreCase(this.columnType);
}
public String getColumnName() {
return this.columnName;
}
public String getColumnType() {
return this.columnType;
}
public String getColumnDisplayType() {
return this.columnDisplayType;
}
public boolean isDateDisplayType() {
return "DATE".equalsIgnoreCase(this.columnDisplayType);
}
public boolean isIntegerDisplayType() {
return "INTEGER".equalsIgnoreCase(this.columnDisplayType);
}
public boolean isDecimalDisplayType() {
return "DECIMAL".equalsIgnoreCase(this.columnDisplayType);
}
public boolean isCodeValueDisplayType() {
return "CODEVALUE".equalsIgnoreCase(this.columnDisplayType);
}
public boolean isCodeLookupDisplayType() {
return "CODELOOKUP".equalsIgnoreCase(this.columnDisplayType);
}
public boolean isMandatory() {
return !isOptional();
}
public boolean isOptional() {
return this.isColumnNullable;
}
public boolean hasColumnValues() {
return !this.columnValues.isEmpty();
}
public boolean isColumnValueAllowed(final String match) {
boolean allowed = false;
for (ResultsetColumnValueData allowedValue : this.columnValues) {
if (allowedValue.matches(match)) {
allowed = true;
}
}
return allowed;
}
public boolean isColumnValueNotAllowed(final String match) {
return !isColumnValueAllowed(match);
}
public boolean isColumnCodeNotAllowed(final Integer match) {
return !isColumnCodeAllowed(match);
}
public boolean isColumnCodeAllowed(final Integer match) {
boolean allowed = false;
for (ResultsetColumnValueData allowedValue : this.columnValues) {
if (allowedValue.codeMatches(match)) {
allowed = true;
}
}
return allowed;
}
}
@@ -1,33 +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.dataqueries.data;
public class ResultsetColumnValue {
private int id;
private String value;
public ResultsetColumnValue(int id, String value) {
this.id = id;
this.value = value;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@@ -0,0 +1,29 @@
/**
* 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.dataqueries.data;
/**
* Immutable data object representing a possible value for a given resultset
* column.
*/
public class ResultsetColumnValueData {
private final int id;
private final String value;
public ResultsetColumnValueData(final int id, final String value) {
this.id = id;
this.value = value;
}
public boolean matches(final String match) {
return match.equalsIgnoreCase(this.value);
}
public boolean codeMatches(final Integer match) {
return match.intValue() == this.id;
}
}
@@ -5,22 +5,21 @@
*/
package org.mifosplatform.infrastructure.dataqueries.data;
import java.util.ArrayList;
import java.util.List;
public class ResultsetDataRow {
public class ResultsetRowData {
private List<String> row = new ArrayList<String>();
private final List<String> row;
public ResultsetDataRow() {
public static ResultsetRowData create(final List<String> rowValues) {
return new ResultsetRowData(rowValues);
}
private ResultsetRowData(final List<String> rowValues) {
this.row = rowValues;
}
public List<String> getRow() {
return row;
}
public void setRow(List<String> row) {
this.row = row;
}
}
@@ -5,16 +5,13 @@
*/
package org.mifosplatform.infrastructure.dataqueries.service;
import javax.sql.rowset.CachedRowSet;
import java.util.List;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
public interface GenericDataService {
CachedRowSet getCachedResultSet(String sql, String errorMsg);
void updateSQL(String sql, String sqlErrorMsg);
GenericResultsetData fillGenericResultSet(final String sql);
String generateJsonFromGenericResultsetData(GenericResultsetData grs);
@@ -25,4 +22,5 @@ public interface GenericDataService {
String getDatabaseName();
List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(String datatable);
}
@@ -5,128 +5,70 @@
*/
package org.mifosplatform.infrastructure.dataqueries.service;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import javax.sql.rowset.CachedRowSet;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeader;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetDataRow;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnValueData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
import org.mifosplatform.infrastructure.dataqueries.exception.DataTableNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
import org.springframework.stereotype.Service;
import com.sun.rowset.CachedRowSetImpl;
//TODO - JW - Performance Item - most items (code values etc) can be cached but not doing that yet
@Service
public class GenericDataServiceImpl implements GenericDataService {
private final JdbcTemplate jdbcTemplate;
private final DataSource dataSource;
@Autowired
public GenericDataServiceImpl(final TenantAwareRoutingDataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public CachedRowSet getCachedResultSet(final String sql, final String sqlErrorMsg) {
// FIXME - KW - Need to reimplement this away from Sun library - could
// be mixture of Lists and jdbcTemplate.query
// long startTime = System.currentTimeMillis();
Connection db_connection = null;
Statement db_statement = null;
CachedRowSet crs = null;
try {
db_connection = dataSource.getConnection();
db_statement = db_connection.createStatement();
ResultSet rs = db_statement.executeQuery(sql);
crs = new CachedRowSetImpl();
crs.populate(rs);
// logger.info("RS Size: " + crs.size() +
// " getCachedResultSet sql: " + sql);
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), sqlErrorMsg);
} finally {
dbClose(db_statement, db_connection);
}
// long elapsed = System.currentTimeMillis() - startTime;
// logger.info("Elapsed Time: " + elapsed + " SQL: " + sql);
return crs;
}
@Override
public void updateSQL(final String sql, final String sqlErrorMsg) {
// long startTime = System.currentTimeMillis();
Connection db_connection = null;
Statement db_statement = null;
try {
db_connection = dataSource.getConnection();
db_statement = db_connection.createStatement();
db_statement.executeUpdate(sql);
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), sqlErrorMsg);
} finally {
dbClose(db_statement, db_connection);
}
// long elapsed = System.currentTimeMillis() - startTime;
// logger.info("Elapsed Time: " + elapsed + " SQL: " + sql);
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
}
@Override
public GenericResultsetData fillGenericResultSet(final String sql) {
String sqlErrorMsg = "Sql: " + sql;
CachedRowSet rs = getCachedResultSet(sql, sqlErrorMsg);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
List<ResultsetColumnHeader> columnHeaders = new ArrayList<ResultsetColumnHeader>();
List<ResultsetDataRow> resultsetDataRows = new ArrayList<ResultsetDataRow>();
final List<ResultsetColumnHeaderData> columnHeaders = new ArrayList<ResultsetColumnHeaderData>();
final List<ResultsetRowData> resultsetDataRows = new ArrayList<ResultsetRowData>();
try {
final SqlRowSetMetaData rsmd = rs.getMetaData();
ResultSetMetaData rsmd = rs.getMetaData();
String columnName = null;
String columnValue = null;
for (int i = 0; i < rsmd.getColumnCount(); i++) {
ResultsetColumnHeader rsch = new ResultsetColumnHeader();
rsch.setColumnName(rsmd.getColumnName(i + 1));
rsch.setColumnType(rsmd.getColumnTypeName(i + 1));
columnHeaders.add(rsch);
}
for (int i = 0; i < rsmd.getColumnCount(); i++) {
ResultsetDataRow resultsetDataRow;
while (rs.next()) {
resultsetDataRow = new ResultsetDataRow();
List<String> columnValues = new ArrayList<String>();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
columnName = rsmd.getColumnName(i + 1);
columnValue = rs.getString(columnName);
columnValues.add(columnValue);
}
resultsetDataRow.setRow(columnValues);
resultsetDataRows.add(resultsetDataRow);
}
final String columnName = rsmd.getColumnName(i + 1);
final String columnType = rsmd.getColumnTypeName(i + 1);
return new GenericResultsetData(columnHeaders, resultsetDataRows);
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), sqlErrorMsg);
final ResultsetColumnHeaderData columnHeader = ResultsetColumnHeaderData.basic(columnName, columnType);
columnHeaders.add(columnHeader);
}
while (rs.next()) {
final List<String> columnValues = new ArrayList<String>();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
final String columnName = rsmd.getColumnName(i + 1);
final String columnValue = rs.getString(columnName);
columnValues.add(columnValue);
}
final ResultsetRowData resultsetDataRow = ResultsetRowData.create(columnValues);
resultsetDataRows.add(resultsetDataRow);
}
return new GenericResultsetData(columnHeaders, resultsetDataRows);
}
@Override
@@ -165,17 +107,16 @@ public class GenericDataServiceImpl implements GenericDataService {
writer.append("[");
List<ResultsetColumnHeader> columnHeaders = grs.getColumnHeaders();
// logger.info("NO. of Columns: " + columnHeaders.size());
final List<ResultsetColumnHeaderData> columnHeaders = grs.getColumnHeaders();
List<ResultsetDataRow> data = grs.getData();
List<ResultsetRowData> data = grs.getData();
List<String> row;
Integer rSize;
String doubleQuote = "\"";
String slashDoubleQuote = "\\\"";
String currColType;
String currVal;
// logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
writer.append("\n{");
@@ -205,8 +146,8 @@ public class GenericDataServiceImpl implements GenericDataService {
writer.append("[" + localDate.getYear() + ", " + localDate.getMonthOfYear() + ", " + localDate.getDayOfMonth()
+ "]");
} else {
writer.append(doubleQuote + replace(currVal, doubleQuote,slashDoubleQuote) + doubleQuote);
writer.append(doubleQuote + replace(currVal, doubleQuote, slashDoubleQuote) + doubleQuote);
}
}
} else {
@@ -217,8 +158,7 @@ public class GenericDataServiceImpl implements GenericDataService {
if (i < (data.size() - 1)) {
writer.append("},");
}
else {
} else {
writer.append("}");
}
}
@@ -227,24 +167,6 @@ public class GenericDataServiceImpl implements GenericDataService {
return writer.toString();
}
private void dbClose(Statement db_statement, Connection db_connection) {
// logger.debug("dbClose");
try {
if (db_statement != null) {
db_statement.close();
// parameter assignment in this case is ok.
db_statement = null;
}
if (db_connection != null) {
db_connection.close();
// parameter assignment in this case is ok.
db_connection = null;
}
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), "Error closing database connection");
}
}
@Override
public String getDatabaseName() {
@@ -254,4 +176,79 @@ public class GenericDataServiceImpl implements GenericDataService {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), "Error Accessing Database Name");
}
}
@Override
public List<ResultsetColumnHeaderData> fillResultsetColumnHeaders(final String datatable) {
final SqlRowSet columnDefinitions = getDatatableMetaData(datatable);
final List<ResultsetColumnHeaderData> columnHeaders = new ArrayList<ResultsetColumnHeaderData>();
columnDefinitions.beforeFirst();
while (columnDefinitions.next()) {
final String columnName = columnDefinitions.getString("COLUMN_NAME");
final String isNullable = columnDefinitions.getString("IS_NULLABLE");
final String isPrimaryKey = columnDefinitions.getString("COLUMN_KEY");
final String columnType = columnDefinitions.getString("DATA_TYPE");
final Long columnLength = columnDefinitions.getLong("CHARACTER_MAXIMUM_LENGTH");
boolean columnNullable = "YES".equalsIgnoreCase(isNullable);
boolean columnIsPrimaryKey = "PRI".equalsIgnoreCase(isPrimaryKey);
List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
if ("varchar".equalsIgnoreCase(columnType)) {
columnValues = retreiveColumnValues(columnName, "_cv");
} else if ("int".equalsIgnoreCase(columnType)) {
columnValues = retreiveColumnValues(columnName, "_cd");
}
final ResultsetColumnHeaderData rsch = ResultsetColumnHeaderData.detailed(columnName, columnType, columnLength, columnNullable,
columnIsPrimaryKey, columnValues);
columnHeaders.add(rsch);
}
return columnHeaders;
}
/*
* Candidate for using caching there to get allowed 'column values' from
* code/codevalue tables
*/
private List<ResultsetColumnValueData> retreiveColumnValues(final String columnName, final String code_suffix) {
final List<ResultsetColumnValueData> columnValues = new ArrayList<ResultsetColumnValueData>();
int codePosition = columnName.indexOf(code_suffix);
if (codePosition > 0) {
final String codeName = columnName.substring(0, codePosition);
final String sql = "select v.id, v.code_value from m_code m " + " join m_code_value v on v.code_id = m.id "
+ " where m.code_name = '" + codeName + "' order by v.order_position, v.id";
final SqlRowSet rsValues = this.jdbcTemplate.queryForRowSet(sql);
rsValues.beforeFirst();
while (rsValues.next()) {
final Integer id = rsValues.getInt("id");
final String codeValue = rsValues.getString("code_value");
columnValues.add(new ResultsetColumnValueData(id, codeValue));
}
}
return columnValues;
}
private SqlRowSet getDatatableMetaData(final String datatable) {
final String sql = "select COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY"
+ " from INFORMATION_SCHEMA.COLUMNS " + " where TABLE_SCHEMA = schema() and TABLE_NAME = '" + datatable
+ "'order by ORDINAL_POSITION";
final SqlRowSet columnDefinitions = this.jdbcTemplate.queryForRowSet(sql);
if (columnDefinitions.next()) { return columnDefinitions; }
throw new DataTableNotFoundException(datatable);
}
}
@@ -13,13 +13,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import javax.sql.rowset.CachedRowSet;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
@@ -28,8 +26,8 @@ import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityExce
import org.mifosplatform.infrastructure.core.service.FileUtils;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeader;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetDataRow;
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.security.service.PlatformSecurityContext;
import org.mifosplatform.useradministration.domain.AppUser;
@@ -49,6 +47,8 @@ import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Service;
import com.lowagie.text.Document;
@@ -59,467 +59,373 @@ import com.lowagie.text.pdf.PdfWriter;
@Service
public class ReadReportingServiceImpl implements ReadReportingService {
private final DataSource dataSource;
private final PlatformSecurityContext context;
private final static Logger logger = LoggerFactory
.getLogger(ReadReportingServiceImpl.class);
private Boolean noPentaho = false;
@Autowired
public ReadReportingServiceImpl(final PlatformSecurityContext context,
final TenantAwareRoutingDataSource dataSource) {
// kick off pentaho reports server
ClassicEngineBoot.getInstance().start();
noPentaho = false;
this.context = context;
this.dataSource = dataSource;
}
@Autowired
private GenericDataService genericDataService;
@Override
public StreamingOutput retrieveReportCSV(final String name,
final String type, final Map<String, String> queryParams) {
return new StreamingOutput() {
@Override
public void write(final OutputStream out) {
try {
GenericResultsetData result = retrieveGenericResultset(
name, type, queryParams);
StringBuffer sb = generateCsvFileBuffer(result);
InputStream in = new ByteArrayInputStream(sb.toString()
.getBytes("UTF-8"));
byte[] outputByte = new byte[4096];
Integer readLen = in.read(outputByte, 0, 4096);
while (readLen != -1) {
out.write(outputByte, 0, readLen);
readLen = in.read(outputByte, 0, 4096);
}
// in.close();
// out.flush();
// out.close();
} catch (Exception e) {
throw new PlatformDataIntegrityException(
"error.msg.exception.error", e.getMessage());
}
}
};
}
private StringBuffer generateCsvFileBuffer(final GenericResultsetData result) {
StringBuffer writer = new StringBuffer();
List<ResultsetColumnHeader> columnHeaders = result.getColumnHeaders();
logger.info("NO. of Columns: " + columnHeaders.size());
Integer chSize = columnHeaders.size();
for (int i = 0; i < chSize; i++) {
writer.append('"' + columnHeaders.get(i).getColumnName() + '"');
if (i < (chSize - 1))
writer.append(",");
}
writer.append('\n');
List<ResultsetDataRow> data = result.getData();
List<String> row;
Integer rSize;
// String currCol;
String currColType;
String currVal;
String doubleQuote = "\"";
String twoDoubleQuotes = doubleQuote + doubleQuote;
logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
row = data.get(i).getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
// currCol = columnHeaders.get(j).getColumnName();
currColType = columnHeaders.get(j).getColumnType();
currVal = row.get(j);
if (currVal != null) {
if (currColType.equals("DECIMAL")
|| currColType.equals("DOUBLE")
|| currColType.equals("BIGINT")
|| currColType.equals("SMALLINT")
|| currColType.equals("INT"))
writer.append(currVal);
else
writer.append('"' + genericDataService.replace(currVal,
doubleQuote, twoDoubleQuotes) + '"');
}
if (j < (rSize - 1))
writer.append(",");
}
writer.append('\n');
}
return writer;
}
@Override
public GenericResultsetData retrieveGenericResultset(final String name,
final String type, final Map<String, String> queryParams) {
long startTime = System.currentTimeMillis();
logger.info("STARTING REPORT: " + name + " Type: " + type);
String sql;
if (name.equals(".")) {
// this is to support api /reports - which isn't an important
// call. It isn't used in the default reporting UI. But there is a
// need to provide an api that does bring back 'permitted' reports
// PERMITTED REPORTS SQL
sql = "select r.report_id, r.report_name, r.report_type, r.report_subtype, r.report_category,"
+ " rp.parameter_id, rp.report_parameter_name, p.parameter_name"
+ " from stretchy_report r"
+ " left join stretchy_report_parameter rp on rp.report_id = r.report_id"
+ " left join stretchy_parameter p on p.parameter_id = rp.parameter_id"
+ " where exists"
+ " (select 'f'"
+ " from m_appuser_role ur "
+ " join m_role r on r.id = ur.role_id"
+ " left join m_role_permission rp on rp.role_id = r.id"
+ " left join m_permission p on p.id = rp.permission_id"
+ " where ur.appuser_id = "
+ context.authenticatedUser().getId()
+ " and (p.code in ('ALL_FUNCTIONS', 'ALL_FUNCTIONS_READ') or p.code = concat('READ_', r.report_name))) "
+ " order by r.report_name, rp.parameter_id";
} else {
sql = getSQLtoRun(name, type, queryParams);
}
GenericResultsetData result = genericDataService
.fillGenericResultSet(sql);
long elapsed = System.currentTimeMillis() - startTime;
logger.info("FINISHING Report/Request Name: " + name + " - " + type
+ " Elapsed Time: " + elapsed);
return result;
}
private String getSQLtoRun(final String name, final String type,
final Map<String, String> queryParams) {
String sql = getSql(name, type);
Set<String> keys = queryParams.keySet();
for (String key : keys) {
String pValue = queryParams.get(key);
// logger.info("(" + key + " : " + pValue + ")");
sql = genericDataService.replace(sql, key, pValue);
}
AppUser currentUser = context.authenticatedUser();
// Allows sql query to restrict data by office hierarchy if required
sql = genericDataService.replace(sql, "${currentUserHierarchy}",
currentUser.getOffice().getHierarchy());
// Allows sql query to restrict data by current user Id if required
// (typically used to return report lists containing only reports
// permitted to be run by the user
sql = genericDataService.replace(sql, "${currentUserId}", currentUser
.getId().toString());
sql = genericDataService.wrapSQL(sql);
return sql;
}
private String getSql(final String name, final String type) {
String inputSql = "select " + type + "_sql as the_sql from stretchy_"
+ type + " where " + type + "_name = '" + name + "'";
inputSql = genericDataService.wrapSQL(inputSql);
String sqlErrorMsg = "Sql: " + inputSql;
CachedRowSet rs = genericDataService.getCachedResultSet(inputSql,
sqlErrorMsg);
try {
if (rs.next()) {
return rs.getString("the_sql");
}
throw new ReportNotFoundException(inputSql);
} catch (SQLException e) {
logger.info("msg: " + e.getMessage() + " sql: " + inputSql);
throw new PlatformDataIntegrityException("error.msg.sql.error",
e.getMessage(), sqlErrorMsg);
}
}
@Override
public String getReportType(final String reportName) {
String sql = "SELECT ifnull(report_type,'') as report_type FROM `stretchy_report` where report_name = '"
+ reportName + "'";
sql = genericDataService.wrapSQL(sql);
String sqlErrorMsg = "Report Name: " + reportName + " Sql: " + sql;
CachedRowSet rs = genericDataService.getCachedResultSet(sql,
sqlErrorMsg);
try {
if (rs.next()) {
return rs.getString("report_type");
}
throw new ReportNotFoundException(sql);
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error",
e.getMessage(), sqlErrorMsg);
}
}
@Override
public Response processPentahoRequest(final String reportName,
final String outputTypeParam, final Map<String, String> queryParams) {
String outputType = "HTML";
if (StringUtils.isNotBlank(outputTypeParam))
outputType = outputTypeParam;
if (!(outputType.equalsIgnoreCase("HTML")
|| outputType.equalsIgnoreCase("PDF")
|| outputType.equalsIgnoreCase("XLS") || outputType
.equalsIgnoreCase("CSV")))
throw new PlatformDataIntegrityException(
"error.msg.invalid.outputType", "No matching Output Type: "
+ outputType);
if (noPentaho)
throw new PlatformDataIntegrityException("error.msg.no.pentaho",
"Pentaho is not enabled", "Pentaho is not enabled");
// TODO - JW - use pentaho location finder like Pawel does in Mifos
// String reportPath =
// "C:\\dev\\apache-tomcat-7.0.25\\webapps\\ROOT\\PentahoReports\\"
// + reportName + ".prpt";
/*String reportPath = FileUtils.MIFOSX_BASE_DIR
+ File.separator
+ ThreadLocalContextUtil.getTenant().getName()
.replaceAll(" ", "").trim() + File.separator
+ "reports" + File.separator + reportName + ".prpt";*/
String reportPath = FileUtils.MIFOSX_BASE_DIR
+ File.separator
+ "reports" + File.separator + reportName + ".prpt";
logger.info("Report path: " + reportPath);
// load report definition
ResourceManager manager = new ResourceManager();
manager.registerDefaults();
Resource res;
try {
res = manager.createDirectly(reportPath, MasterReport.class);
MasterReport masterReport = (MasterReport) res.getResource();
addParametersToReport(masterReport, queryParams);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if ("PDF".equalsIgnoreCase(outputType)) {
PdfReportUtil.createPDF(masterReport, baos);
return Response.ok().entity(baos.toByteArray())
.type("application/pdf").build();
}
if ("XLS".equalsIgnoreCase(outputType)) {
ExcelReportUtil.createXLS(masterReport, baos);
return Response
.ok()
.entity(baos.toByteArray())
.type("application/vnd.ms-excel")
.header("Content-Disposition",
"attachment;filename="
+ reportName.replaceAll(" ", "")
+ ".xls").build();
}
if ("CSV".equalsIgnoreCase(outputType)) {
CSVReportUtil.createCSV(masterReport, baos, "UTF-8");
return Response
.ok()
.entity(baos.toByteArray())
.type("application/x-msdownload")
.header("Content-Disposition",
"attachment;filename="
+ reportName.replaceAll(" ", "")
+ ".csv").build();
}
if ("HTML".equalsIgnoreCase(outputType)) {
HtmlReportUtil.createStreamHTML(masterReport, baos);
return Response.ok().entity(baos.toByteArray())
.type("text/html").build();
}
} catch (ResourceException e) {
throw new PlatformDataIntegrityException(
"error.msg.reporting.error", e.getMessage());
} catch (ReportProcessingException e) {
throw new PlatformDataIntegrityException(
"error.msg.reporting.error", e.getMessage());
} catch (IOException e) {
throw new PlatformDataIntegrityException(
"error.msg.reporting.error", e.getMessage());
}
throw new PlatformDataIntegrityException(
"error.msg.invalid.outputType", "No matching Output Type: "
+ outputType);
}
private void addParametersToReport(final MasterReport report,
final Map<String, String> queryParams) {
AppUser currentUser = context.authenticatedUser();
try {
logger.info("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
ReportParameterValues rptParamValues = report.getParameterValues();
ReportParameterDefinition paramsDefinition = report
.getParameterDefinition();
/*
* only allow integer, long, date and string parameter types and
* assume all mandatory - could go more detailed like Pawel did in
* Mifos later and could match incoming and pentaho parameters
* better... currently assuming they come in ok... and if not an
* error
*/
for (ParameterDefinitionEntry paramDefEntry : paramsDefinition
.getParameterDefinitions()) {
String paramName = paramDefEntry.getName();
if (!((paramName.equals("tenantdb")) || (paramName.equals("userhierarchy")))) {
logger.info("paramName:" + paramName);
String pValue = queryParams.get(paramName);
if (StringUtils.isBlank(pValue))
throw new PlatformDataIntegrityException(
"error.msg.reporting.error",
"Pentaho Parameter: " + paramName
+ " - not Provided");
Class<?> clazz = paramDefEntry.getValueType();
logger.info("addParametersToReport(" + paramName + " : "
+ pValue + " : " + clazz.getCanonicalName() + ")");
if (clazz.getCanonicalName().equalsIgnoreCase(
"java.lang.Integer"))
rptParamValues.put(paramName, Integer.parseInt(pValue));
else if (clazz.getCanonicalName().equalsIgnoreCase(
"java.lang.Long"))
rptParamValues.put(paramName, Long.parseLong(pValue));
else if (clazz.getCanonicalName().equalsIgnoreCase(
"java.sql.Date"))
rptParamValues.put(paramName, Date.valueOf(pValue));
else
rptParamValues.put(paramName, pValue);
}
// tenant database name and current user's office hierarchy
// passed as parameters to allow multitenant penaho reporting and
// data scoping
String tenantdb = dataSource.getConnection().getCatalog();
String userhierarchy = currentUser.getOffice().getHierarchy();
logger.info("db name:" + tenantdb + " userhierarchy:" + userhierarchy);
rptParamValues.put("tenantdb", tenantdb);
rptParamValues.put("userhierarchy", userhierarchy);
}
} catch (Exception e) {
logger.info("error.msg.reporting.error:" + e.getMessage());
throw new PlatformDataIntegrityException(
"error.msg.reporting.error", e.getMessage());
}
}
@Override
public String retrieveReportPDF(final String reportName, final String type,
final Map<String, String> queryParams) {
String fileLocation = FileUtils.MIFOSX_BASE_DIR + File.separator + "";
if (!new File(fileLocation).isDirectory()) {
new File(fileLocation).mkdirs();
}
String genaratePdf = fileLocation + File.separator + reportName
+ ".pdf";
try {
GenericResultsetData result = retrieveGenericResultset(reportName,
type, queryParams);
List<ResultsetColumnHeader> columnHeaders = result
.getColumnHeaders();
List<ResultsetDataRow> data = result.getData();
List<String> row;
logger.info("NO. of Columns: " + columnHeaders.size());
Integer chSize = columnHeaders.size();
Document document = new Document(PageSize.B0.rotate());
PdfWriter.getInstance(document, new FileOutputStream(new File(
fileLocation + reportName + ".pdf")));
document.open();
PdfPTable table = new PdfPTable(chSize);
table.setWidthPercentage(100);
for (int i = 0; i < chSize; i++) {
table.addCell(columnHeaders.get(i).getColumnName());
}
table.completeRow();
Integer rSize;
String currColType;
String currVal;
logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
row = data.get(i).getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
currColType = columnHeaders.get(j).getColumnType();
currVal = row.get(j);
if (currVal != null) {
if (currColType.equals("DECIMAL")
|| currColType.equals("DOUBLE")
|| currColType.equals("BIGINT")
|| currColType.equals("SMALLINT")
|| currColType.equals("INT")) {
table.addCell(currVal.toString());
} else {
table.addCell(currVal.toString());
}
}
}
}
table.completeRow();
document.add(table);
document.close();
return genaratePdf;
} catch (Exception e) {
throw new PlatformDataIntegrityException(
"error.msg.exception.error", e.getMessage());
}
}
private final static Logger logger = LoggerFactory.getLogger(ReadReportingServiceImpl.class);
private final JdbcTemplate jdbcTemplate;
private final DataSource dataSource;
private final PlatformSecurityContext context;
private final GenericDataService genericDataService;
private boolean noPentaho = false;
@Autowired
public ReadReportingServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource,
final GenericDataService genericDataService) {
// kick off pentaho reports server
ClassicEngineBoot.getInstance().start();
noPentaho = false;
this.context = context;
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.genericDataService = genericDataService;
}
@Override
public StreamingOutput retrieveReportCSV(final String name, final String type, final Map<String, String> queryParams) {
return new StreamingOutput() {
@Override
public void write(final OutputStream out) {
try {
GenericResultsetData result = retrieveGenericResultset(name, type, queryParams);
StringBuffer sb = generateCsvFileBuffer(result);
InputStream in = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
byte[] outputByte = new byte[4096];
Integer readLen = in.read(outputByte, 0, 4096);
while (readLen != -1) {
out.write(outputByte, 0, readLen);
readLen = in.read(outputByte, 0, 4096);
}
// in.close();
// out.flush();
// out.close();
} catch (Exception e) {
throw new PlatformDataIntegrityException("error.msg.exception.error", e.getMessage());
}
}
};
}
private StringBuffer generateCsvFileBuffer(final GenericResultsetData result) {
StringBuffer writer = new StringBuffer();
List<ResultsetColumnHeaderData> columnHeaders = result.getColumnHeaders();
logger.info("NO. of Columns: " + columnHeaders.size());
Integer chSize = columnHeaders.size();
for (int i = 0; i < chSize; i++) {
writer.append('"' + columnHeaders.get(i).getColumnName() + '"');
if (i < (chSize - 1)) writer.append(",");
}
writer.append('\n');
List<ResultsetRowData> data = result.getData();
List<String> row;
Integer rSize;
// String currCol;
String currColType;
String currVal;
String doubleQuote = "\"";
String twoDoubleQuotes = doubleQuote + doubleQuote;
logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
row = data.get(i).getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
// currCol = columnHeaders.get(j).getColumnName();
currColType = columnHeaders.get(j).getColumnType();
currVal = row.get(j);
if (currVal != null) {
if (currColType.equals("DECIMAL") || currColType.equals("DOUBLE") || currColType.equals("BIGINT")
|| currColType.equals("SMALLINT") || currColType.equals("INT"))
writer.append(currVal);
else
writer.append('"' + genericDataService.replace(currVal, doubleQuote, twoDoubleQuotes) + '"');
}
if (j < (rSize - 1)) writer.append(",");
}
writer.append('\n');
}
return writer;
}
@Override
public GenericResultsetData retrieveGenericResultset(final String name, final String type, final Map<String, String> queryParams) {
long startTime = System.currentTimeMillis();
logger.info("STARTING REPORT: " + name + " Type: " + type);
String sql;
if (name.equals(".")) {
// this is to support api /reports - which isn't an important
// call. It isn't used in the default reporting UI. But there is a
// need to provide an api that does bring back 'permitted' reports
// PERMITTED REPORTS SQL
sql = "select r.report_id, r.report_name, r.report_type, r.report_subtype, r.report_category,"
+ " rp.parameter_id, rp.report_parameter_name, p.parameter_name" + " from stretchy_report r"
+ " left join stretchy_report_parameter rp on rp.report_id = r.report_id"
+ " left join stretchy_parameter p on p.parameter_id = rp.parameter_id" + " where exists" + " (select 'f'"
+ " from m_appuser_role ur " + " join m_role r on r.id = ur.role_id"
+ " left join m_role_permission rp on rp.role_id = r.id" + " left join m_permission p on p.id = rp.permission_id"
+ " where ur.appuser_id = " + context.authenticatedUser().getId()
+ " and (p.code in ('ALL_FUNCTIONS', 'ALL_FUNCTIONS_READ') or p.code = concat('READ_', r.report_name))) "
+ " order by r.report_name, rp.parameter_id";
} else {
sql = getSQLtoRun(name, type, queryParams);
}
GenericResultsetData result = genericDataService.fillGenericResultSet(sql);
long elapsed = System.currentTimeMillis() - startTime;
logger.info("FINISHING Report/Request Name: " + name + " - " + type + " Elapsed Time: " + elapsed);
return result;
}
private String getSQLtoRun(final String name, final String type, final Map<String, String> queryParams) {
String sql = getSql(name, type);
Set<String> keys = queryParams.keySet();
for (String key : keys) {
String pValue = queryParams.get(key);
// logger.info("(" + key + " : " + pValue + ")");
sql = genericDataService.replace(sql, key, pValue);
}
AppUser currentUser = context.authenticatedUser();
// Allows sql query to restrict data by office hierarchy if required
sql = genericDataService.replace(sql, "${currentUserHierarchy}", currentUser.getOffice().getHierarchy());
// Allows sql query to restrict data by current user Id if required
// (typically used to return report lists containing only reports
// permitted to be run by the user
sql = genericDataService.replace(sql, "${currentUserId}", currentUser.getId().toString());
sql = genericDataService.wrapSQL(sql);
return sql;
}
private String getSql(final String name, final String type) {
final String inputSql = "select " + type + "_sql as the_sql from stretchy_" + type + " where " + type + "_name = '" + name + "'";
final String inputSqlWrapped = genericDataService.wrapSQL(inputSql);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(inputSqlWrapped);
if (rs.next()) { return rs.getString("the_sql"); }
throw new ReportNotFoundException(inputSql);
}
@Override
public String getReportType(final String reportName) {
final String sql = "SELECT ifnull(report_type,'') as report_type FROM `stretchy_report` where report_name = '" + reportName + "'";
final String sqlWrapped = genericDataService.wrapSQL(sql);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped);
if (rs.next()) { return rs.getString("report_type"); }
throw new ReportNotFoundException(sql);
}
@Override
public Response processPentahoRequest(final String reportName, final String outputTypeParam, final Map<String, String> queryParams) {
String outputType = "HTML";
if (StringUtils.isNotBlank(outputTypeParam)) outputType = outputTypeParam;
if (!(outputType.equalsIgnoreCase("HTML") || outputType.equalsIgnoreCase("PDF") || outputType.equalsIgnoreCase("XLS") || outputType
.equalsIgnoreCase("CSV")))
throw new PlatformDataIntegrityException("error.msg.invalid.outputType", "No matching Output Type: " + outputType);
if (noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
"Pentaho is not enabled"); }
// TODO - JW - use pentaho location finder like Pawel does in Mifos
// String reportPath =
// "C:\\dev\\apache-tomcat-7.0.25\\webapps\\ROOT\\PentahoReports\\"
// + reportName + ".prpt";
/*
* String reportPath = FileUtils.MIFOSX_BASE_DIR + File.separator +
* ThreadLocalContextUtil.getTenant().getName() .replaceAll(" ",
* "").trim() + File.separator + "reports" + File.separator + reportName
* + ".prpt";
*/
final String reportPath = FileUtils.MIFOSX_BASE_DIR + File.separator + "reports" + File.separator + reportName + ".prpt";
logger.info("Report path: " + reportPath);
// load report definition
ResourceManager manager = new ResourceManager();
manager.registerDefaults();
Resource res;
try {
res = manager.createDirectly(reportPath, MasterReport.class);
MasterReport masterReport = (MasterReport) res.getResource();
addParametersToReport(masterReport, queryParams);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if ("PDF".equalsIgnoreCase(outputType)) {
PdfReportUtil.createPDF(masterReport, baos);
return Response.ok().entity(baos.toByteArray()).type("application/pdf").build();
}
if ("XLS".equalsIgnoreCase(outputType)) {
ExcelReportUtil.createXLS(masterReport, baos);
return Response.ok().entity(baos.toByteArray()).type("application/vnd.ms-excel")
.header("Content-Disposition", "attachment;filename=" + reportName.replaceAll(" ", "") + ".xls").build();
}
if ("CSV".equalsIgnoreCase(outputType)) {
CSVReportUtil.createCSV(masterReport, baos, "UTF-8");
return Response.ok().entity(baos.toByteArray()).type("application/x-msdownload")
.header("Content-Disposition", "attachment;filename=" + reportName.replaceAll(" ", "") + ".csv").build();
}
if ("HTML".equalsIgnoreCase(outputType)) {
HtmlReportUtil.createStreamHTML(masterReport, baos);
return Response.ok().entity(baos.toByteArray()).type("text/html").build();
}
} catch (ResourceException e) {
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
} catch (ReportProcessingException e) {
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
} catch (IOException e) {
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
}
throw new PlatformDataIntegrityException("error.msg.invalid.outputType", "No matching Output Type: " + outputType);
}
private void addParametersToReport(final MasterReport report, final Map<String, String> queryParams) {
AppUser currentUser = context.authenticatedUser();
try {
logger.info("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
ReportParameterValues rptParamValues = report.getParameterValues();
ReportParameterDefinition paramsDefinition = report.getParameterDefinition();
/*
* only allow integer, long, date and string parameter types and
* assume all mandatory - could go more detailed like Pawel did in
* Mifos later and could match incoming and pentaho parameters
* better... currently assuming they come in ok... and if not an
* error
*/
for (ParameterDefinitionEntry paramDefEntry : paramsDefinition.getParameterDefinitions()) {
String paramName = paramDefEntry.getName();
if (!((paramName.equals("tenantdb")) || (paramName.equals("userhierarchy")))) {
logger.info("paramName:" + paramName);
String pValue = queryParams.get(paramName);
if (StringUtils.isBlank(pValue))
throw new PlatformDataIntegrityException("error.msg.reporting.error", "Pentaho Parameter: " + paramName
+ " - not Provided");
Class<?> clazz = paramDefEntry.getValueType();
logger.info("addParametersToReport(" + paramName + " : " + pValue + " : " + clazz.getCanonicalName() + ")");
if (clazz.getCanonicalName().equalsIgnoreCase("java.lang.Integer"))
rptParamValues.put(paramName, Integer.parseInt(pValue));
else if (clazz.getCanonicalName().equalsIgnoreCase("java.lang.Long"))
rptParamValues.put(paramName, Long.parseLong(pValue));
else if (clazz.getCanonicalName().equalsIgnoreCase("java.sql.Date"))
rptParamValues.put(paramName, Date.valueOf(pValue));
else
rptParamValues.put(paramName, pValue);
}
// tenant database name and current user's office hierarchy
// passed as parameters to allow multitenant penaho reporting
// and
// data scoping
String tenantdb = dataSource.getConnection().getCatalog();
String userhierarchy = currentUser.getOffice().getHierarchy();
logger.info("db name:" + tenantdb + " userhierarchy:" + userhierarchy);
rptParamValues.put("tenantdb", tenantdb);
rptParamValues.put("userhierarchy", userhierarchy);
}
} catch (Exception e) {
logger.error("error.msg.reporting.error:" + e.getMessage());
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
}
}
@Override
public String retrieveReportPDF(final String reportName, final String type, final Map<String, String> queryParams) {
String fileLocation = FileUtils.MIFOSX_BASE_DIR + File.separator + "";
if (!new File(fileLocation).isDirectory()) {
new File(fileLocation).mkdirs();
}
String genaratePdf = fileLocation + File.separator + reportName + ".pdf";
try {
GenericResultsetData result = retrieveGenericResultset(reportName, type, queryParams);
List<ResultsetColumnHeaderData> columnHeaders = result.getColumnHeaders();
List<ResultsetRowData> data = result.getData();
List<String> row;
logger.info("NO. of Columns: " + columnHeaders.size());
Integer chSize = columnHeaders.size();
Document document = new Document(PageSize.B0.rotate());
PdfWriter.getInstance(document, new FileOutputStream(new File(fileLocation + reportName + ".pdf")));
document.open();
PdfPTable table = new PdfPTable(chSize);
table.setWidthPercentage(100);
for (int i = 0; i < chSize; i++) {
table.addCell(columnHeaders.get(i).getColumnName());
}
table.completeRow();
Integer rSize;
String currColType;
String currVal;
logger.info("NO. of Rows: " + data.size());
for (int i = 0; i < data.size(); i++) {
row = data.get(i).getRow();
rSize = row.size();
for (int j = 0; j < rSize; j++) {
currColType = columnHeaders.get(j).getColumnType();
currVal = row.get(j);
if (currVal != null) {
if (currColType.equals("DECIMAL") || currColType.equals("DOUBLE") || currColType.equals("BIGINT")
|| currColType.equals("SMALLINT") || currColType.equals("INT")) {
table.addCell(currVal.toString());
} else {
table.addCell(currVal.toString());
}
}
}
}
table.completeRow();
document.add(table);
document.close();
return genaratePdf;
} catch (Exception e) {
logger.error("error.msg.reporting.error:" + e.getMessage());
throw new PlatformDataIntegrityException("error.msg.exception.error", e.getMessage());
}
}
}
@@ -7,8 +7,6 @@ package org.mifosplatform.infrastructure.dataqueries.service;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -16,7 +14,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.sql.rowset.CachedRowSet;
import javax.sql.DataSource;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
@@ -26,17 +24,20 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.infrastructure.dataqueries.data.DatatableData;
import org.mifosplatform.infrastructure.dataqueries.data.GenericResultsetData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeader;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnValue;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetDataRow;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetColumnHeaderData;
import org.mifosplatform.infrastructure.dataqueries.data.ResultsetRowData;
import org.mifosplatform.infrastructure.dataqueries.exception.DataTableNotFoundException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
import org.springframework.stereotype.Service;
import com.google.gson.reflect.TypeToken;
@@ -46,18 +47,24 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class);
private final JdbcTemplate jdbcTemplate;
private final DataSource dataSource;
private final PlatformSecurityContext context;
private final FromJsonHelper fromJsonHelper;
private final JsonParserHelper helper;
private final GenericDataService genericDataService;
@Autowired
public ReadWriteNonCoreDataServiceImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper) {
public ReadWriteNonCoreDataServiceImpl(final TenantAwareRoutingDataSource dataSource, final PlatformSecurityContext context,
final FromJsonHelper fromJsonHelper, final GenericDataService genericDataService) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
this.context = context;
this.fromJsonHelper = fromJsonHelper;
this.helper = new JsonParserHelper();
this.genericDataService = genericDataService;
}
@Autowired
private GenericDataService genericDataService;
@Override
public List<DatatableData> retrieveDatatableNames(final String appTable) {
@@ -67,25 +74,23 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} else {
andClause = " and application_table_name = '" + appTable + "'";
}
// PERMITTED datatables
String sql = "select application_table_name, registered_table_name" + " from x_registered_table " + " where exists"
final String sql = "select application_table_name, registered_table_name" + " from x_registered_table " + " where exists"
+ " (select 'f'" + " from m_appuser_role ur " + " join m_role r on r.id = ur.role_id"
+ " left join m_role_permission rp on rp.role_id = r.id" + " left join m_permission p on p.id = rp.permission_id"
+ " where ur.appuser_id = " + context.authenticatedUser().getId()
+ " and (p.code in ('ALL_FUNCTIONS', 'ALL_FUNCTIONS_READ') or p.code = concat('READ_', registered_table_name))) "
+ andClause + " order by application_table_name, registered_table_name";
String sqlErrorMsg = "Application Table Name: " + appTable + " sql: " + sql;
CachedRowSet rs = genericDataService.getCachedResultSet(sql, sqlErrorMsg);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
List<DatatableData> datatables = new ArrayList<DatatableData>();
try {
while (rs.next()) {
datatables.add(new DatatableData(rs.getString("application_table_name"), rs.getString("registered_table_name")));
}
final List<DatatableData> datatables = new ArrayList<DatatableData>();
while (rs.next()) {
final String appTableName = rs.getString("application_table_name");
final String registeredDatatableName = rs.getString("registered_table_name");
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), sqlErrorMsg);
datatables.add(DatatableData.create(appTableName, registeredDatatableName));
}
return datatables;
@@ -96,63 +101,89 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
validateAppTable(appTable);
String createPermission = "'CREATE_" + datatable + "'";
String createPermissionChecker = "'CREATE_" + datatable + "_CHECKER'";
String readPermission = "'READ_" + datatable + "'";
String updatePermission = "'UPDATE_" + datatable + "'";
String updatePermissionChecker = "'UPDATE_" + datatable + "_CHECKER'";
String deletePermission = "'DELETE_" + datatable + "'";
String deletePermissionChecker = "'DELETE_" + datatable + "_CHECKER'";
// TODO - JW - put in batch command later
String sql = "insert into x_registered_table (registered_table_name, application_table_name) values ('" + datatable + "', '"
+ appTable + "')";
final String createPermission = "'CREATE_" + datatable + "'";
final String createPermissionChecker = "'CREATE_" + datatable + "_CHECKER'";
final String readPermission = "'READ_" + datatable + "'";
final String updatePermission = "'UPDATE_" + datatable + "'";
final String updatePermissionChecker = "'UPDATE_" + datatable + "_CHECKER'";
final String deletePermission = "'DELETE_" + datatable + "'";
final String deletePermissionChecker = "'DELETE_" + datatable + "_CHECKER'";
genericDataService.updateSQL(sql, "SQL: " + sql);
/*
* add all permissions, including checker permissions for maintenance
* tasks
*/
sql = "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values " + "('datatable', "
+ createPermission + ", 'CREATE', '" + datatable + "', true)," + "('datatable', " + createPermissionChecker
+ ", 'CREATE', '" + datatable + "', false)," + "('datatable', " + readPermission + ", 'READ', '" + datatable + "', false),"
+ "('datatable', " + updatePermission + ", 'UPDATE', '" + datatable + "', true)," + "('datatable', "
+ updatePermissionChecker + ", 'UPDATE', '" + datatable + "', false)," + "('datatable', " + deletePermission
+ ", 'DELETE', '" + datatable + "', true)," + "('datatable', " + deletePermissionChecker + ", 'DELETE', '" + datatable
// TODO - JW - put in batch command later
final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('"
+ datatable + "', '" + appTable + "')";
this.jdbcTemplate.update(registerDatatableSql);
final String permissionsSql = "insert into m_permission (grouping, code, action_name, entity_name, can_maker_checker) values "
+ "('datatable', "
+ createPermission
+ ", 'CREATE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ createPermissionChecker
+ ", 'CREATE', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ readPermission
+ ", 'READ', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ updatePermission
+ ", 'UPDATE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ updatePermissionChecker
+ ", 'UPDATE', '"
+ datatable
+ "', false),"
+ "('datatable', "
+ deletePermission
+ ", 'DELETE', '"
+ datatable
+ "', true),"
+ "('datatable', "
+ deletePermissionChecker
+ ", 'DELETE', '"
+ datatable
+ "', false)";
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(permissionsSql);
}
@Override
public void deregisterDatatable(final String datatable) {
// TODO - JW - put in batch command later
String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_"
final String permissionList = "('CREATE_" + datatable + "', 'CREATE_" + datatable + "_CHECKER', 'READ_" + datatable + "', 'UPDATE_"
+ datatable + "', 'UPDATE_" + datatable + "_CHECKER', 'DELETE_" + datatable + "', 'DELETE_" + datatable + "_CHECKER')";
String sql = "delete from m_role_permission where m_role_permission.permission_id in (select id from m_permission where code in "
+ permissionList + ")";
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
sql = "delete from m_permission where code in " + permissionList;
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
sql = "delete from x_registered_table where registered_table_name = '" + datatable + "'";
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
}
@Override
public void newDatatableEntry(final String datatable, final Long appTableId, final JsonCommand command) {
String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
List<ResultsetColumnHeader> columnHeaders = getDatatableResultsetColumnHeaders(datatable);
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(datatable);
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
String sql = getAddSql(columnHeaders, datatable, getFKField(appTable), appTableId, dataParams);
final String sql = getAddSql(columnHeaders, datatable, getFKField(appTable), appTableId, dataParams);
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
}
@Override
@@ -166,7 +197,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
throw new PlatformDataIntegrityException("error.msg.attempting.multiple.update", "Application Table: " + datatable
+ " Foreign Key Id: " + appTableId);
String fkName = getFKField(getApplicationTableName(datatable));
String fkName = getFKField(queryForApplicationTableName(datatable));
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
@@ -175,10 +206,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (changes.size() == 0) return changes;
String sql = getUpdateSql(datatable, fkName, appTableId, changes);
final String sql = getUpdateSql(datatable, fkName, appTableId, changes);
if (sql != null) {
genericDataService.updateSQL(sql, "SQL: " + sql);
if (StringUtils.isNotBlank(sql)) {
this.jdbcTemplate.update(sql);
} else {
logger.info("No Changes");
}
@@ -190,21 +221,21 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
public Map<String, Object> updateDatatableEntryOneToMany(final String datatable, final Long appTableId, final Long datatableId,
final JsonCommand command) {
GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, datatableId);
final GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, datatableId);
if (grs.getData().size() == 0) throw new DataTableNotFoundException(datatable, appTableId);
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
final Map<String, String> dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json());
final Map<String, Object> changes = getAffectedAndChangedColumns(grs, dataParams, "id");
if (changes.size() == 0) return changes;
String sql = getUpdateSql(datatable, "id", datatableId, changes);
final String sql = getUpdateSql(datatable, "id", datatableId, changes);
if (sql != null) {
genericDataService.updateSQL(sql, "SQL: " + sql);
if (StringUtils.isNotBlank(sql)) {
this.jdbcTemplate.update(sql);
} else {
logger.info("No Changes");
}
@@ -215,11 +246,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
@Override
public void deleteDatatableEntries(final String datatable, final Long appTableId) {
String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
final String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
String sql = getDeleteEntriesSql(datatable, getFKField(appTable), appTableId);
final String sql = getDeleteEntriesSql(datatable, getFKField(appTable), appTableId);
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
}
@Override
@@ -227,18 +258,18 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
getWithinScopeApplicationTableName(datatable, appTableId);
String sql = getDeleteEntrySql(datatable, datatableId);
final String sql = getDeleteEntrySql(datatable, datatableId);
genericDataService.updateSQL(sql, "SQL: " + sql);
this.jdbcTemplate.update(sql);
}
@Override
public GenericResultsetData retrieveDataTableGenericResultSet(final String datatable, final Long appTableId, final String order,
final Long id) {
String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
final String appTable = getWithinScopeApplicationTableName(datatable, appTableId);
List<ResultsetColumnHeader> columnHeaders = getDatatableResultsetColumnHeaders(datatable);
final List<ResultsetColumnHeaderData> columnHeaders = this.genericDataService.fillResultsetColumnHeaders(datatable);
String sql = "";
@@ -252,20 +283,20 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (order != null) sql = sql + " order by " + order;
List<ResultsetDataRow> result = fillDatatableResultSetDataRows(sql);
List<ResultsetRowData> result = fillDatatableResultSetDataRows(sql);
return new GenericResultsetData(columnHeaders, result);
}
private void checkMainResourceExistsWithinScope(final String appTable, final Long appTableId) {
String unscopedSql = "select t.id from `" + appTable + "` t ${dataScopeCriteria} where t.id = " + appTableId;
final String unscopedSql = "select t.id from `" + appTable + "` t ${dataScopeCriteria} where t.id = " + appTableId;
String sql = dataScopedSQL(unscopedSql, appTable);
final String sql = dataScopedSQL(unscopedSql, appTable);
CachedRowSet rs = genericDataService.getCachedResultSet(sql, "SQL : " + sql);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
if (rs.size() == 0) throw new DataTableNotFoundException(appTable, appTableId);
if (!rs.next()) { throw new DataTableNotFoundException(appTable, appTableId); }
}
private String dataScopedSQL(final String unscopedSQL, final String appTable) {
@@ -303,74 +334,50 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
throw new PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid Application Table: " + appTable);
}
private List<ResultsetDataRow> fillDatatableResultSetDataRows(final String sql) {
private List<ResultsetRowData> fillDatatableResultSetDataRows(final String sql) {
String sqlErrorMsg = "Sql: " + sql;
CachedRowSet rs = genericDataService.getCachedResultSet(sql, sqlErrorMsg);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
List<ResultsetDataRow> resultsetDataRows = new ArrayList<ResultsetDataRow>();
final List<ResultsetRowData> resultsetDataRows = new ArrayList<ResultsetRowData>();
try {
final SqlRowSetMetaData rsmd = rs.getMetaData();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
ResultsetDataRow resultsetDataRow;
String columnName = null;
String columnValue = null;
while (rs.next()) {
resultsetDataRow = new ResultsetDataRow();
List<String> columnValues = new ArrayList<String>();
for (int i = 0; i < columnCount; i++) {
columnName = rsmd.getColumnName(i + 1);
columnValue = rs.getString(columnName);
columnValues.add(columnValue);
}
resultsetDataRow.setRow(columnValues);
resultsetDataRows.add(resultsetDataRow);
while (rs.next()) {
final List<String> columnValues = new ArrayList<String>();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
final String columnName = rsmd.getColumnName(i + 1);
final String columnValue = rs.getString(columnName);
columnValues.add(columnValue);
}
return resultsetDataRows;
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage(), sqlErrorMsg);
final ResultsetRowData resultsetDataRow = ResultsetRowData.create(columnValues);
resultsetDataRows.add(resultsetDataRow);
}
return resultsetDataRows;
}
private String getWithinScopeApplicationTableName(final String datatable, final Long appTableId) {
String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = '" + datatable + "'";
CachedRowSet rs = genericDataService.getCachedResultSet(sql, "SQL : " + sql);
final String applicationTableName = queryForApplicationTableName(datatable);
checkMainResourceExistsWithinScope(applicationTableName, appTableId);
if (rs.size() == 0) throw new DataTableNotFoundException(datatable);
try {
rs.next();
String appTable = rs.getString("application_table_name");
checkMainResourceExistsWithinScope(appTable, appTableId);
return appTable;
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage());
}
return applicationTableName;
}
private String getApplicationTableName(final String datatable) {
// TODO - JW - only used for update... can probably remove this after as
// its
// a reread
String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = '" + datatable + "'";
private String queryForApplicationTableName(final String datatable) {
final String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = '" + datatable + "'";
CachedRowSet rs = genericDataService.getCachedResultSet(sql, "SQL : " + sql);
final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
if (rs.size() == 0) throw new DataTableNotFoundException(datatable);
try {
rs.next();
return rs.getString("application_table_name");
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage());
String applicationTableName = null;
if (rs.next()) {
applicationTableName = rs.getString("application_table_name");
} else {
throw new DataTableNotFoundException(datatable);
}
return applicationTableName;
}
private String getFKField(final String applicationTableName) {
@@ -378,89 +385,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return applicationTableName.substring(2) + "_id";
}
private CachedRowSet getDatatableMetaData(final String datatable) {
String sql = "select COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY"
+ " from INFORMATION_SCHEMA.COLUMNS " + " where TABLE_SCHEMA = schema() and TABLE_NAME = '" + datatable
+ "'order by ORDINAL_POSITION";
CachedRowSet columnDefinitions = genericDataService.getCachedResultSet(sql, "SQL: " + sql);
if (columnDefinitions.size() > 0) return columnDefinitions;
throw new DataTableNotFoundException(datatable);
}
private List<ResultsetColumnHeader> getDatatableResultsetColumnHeaders(final String datatable) {
CachedRowSet columnDefinitions = getDatatableMetaData(datatable);
List<ResultsetColumnHeader> columnHeaders = new ArrayList<ResultsetColumnHeader>();
try {
while (columnDefinitions.next()) {
ResultsetColumnHeader rsch = new ResultsetColumnHeader();
rsch.setColumnName(columnDefinitions.getString("COLUMN_NAME"));
String isNullable = columnDefinitions.getString("IS_NULLABLE");
if (isNullable.equalsIgnoreCase("YES"))
rsch.setColumnNullable(true);
else
rsch.setColumnNullable(false);
String isPrimaryKey = columnDefinitions.getString("COLUMN_KEY");
if (isPrimaryKey.equalsIgnoreCase("PRI"))
rsch.setColumnPrimaryKey(true);
else
rsch.setColumnPrimaryKey(false);
Long columnLength = columnDefinitions.getLong("CHARACTER_MAXIMUM_LENGTH");
if (columnLength > 0) rsch.setColumnLength(columnDefinitions.getLong("CHARACTER_MAXIMUM_LENGTH"));
rsch.setColumnType(columnDefinitions.getString("DATA_TYPE"));
/* look for codes */
if (rsch.getColumnType().equalsIgnoreCase("varchar")) addCodesValueIfNecessary(rsch, "_cv");
if (rsch.getColumnType().equalsIgnoreCase("int")) addCodesValueIfNecessary(rsch, "_cd");
rsch.setColumnDisplayType();
columnHeaders.add(rsch);
}
;
return columnHeaders;
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage());
}
}
private void addCodesValueIfNecessary(final ResultsetColumnHeader rsch, final String code_suffix) {
int codePosition = rsch.getColumnName().indexOf(code_suffix);
if (codePosition > 0) {
String codeName = rsch.getColumnName().substring(0, codePosition);
String sql = "select v.id, v.code_value from m_code m " + " join m_code_value v on v.code_id = m.id "
+ " where m.code_name = '" + codeName + "' order by v.order_position, v.id";
CachedRowSet rsValues = genericDataService.getCachedResultSet(sql, "SQL: " + sql);
try {
while (rsValues.next()) {
rsch.getColumnValues().add(new ResultsetColumnValue(rsValues.getInt("id"), rsValues.getString("code_value")));
}
} catch (SQLException e) {
throw new PlatformDataIntegrityException("error.msg.sql.error", e.getMessage());
}
}
}
private String getAddSql(final List<ResultsetColumnHeader> columnHeaders, final String datatable, final String fkName,
private String getAddSql(final List<ResultsetColumnHeaderData> columnHeaders, final String datatable, final String fkName,
final Long appTableId, final Map<String, String> queryParams) {
final Map<String, String> affectedColumns = getAffectedColumns(columnHeaders, queryParams, fkName);
@@ -573,8 +498,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
throw new PlatformDataIntegrityException("error.msg.invalid.columnName", "Parameter Column Name: " + key + " not found");
}
private Map<String, String> getAffectedColumns(final List<ResultsetColumnHeader> columnHeaders, final Map<String, String> queryParams,
final String keyFieldName) {
private Map<String, String> getAffectedColumns(final List<ResultsetColumnHeaderData> columnHeaders,
final Map<String, String> queryParams, final String keyFieldName) {
String dateFormat = queryParams.get("dateFormat");
Locale clientApplicationLocale = null;
@@ -600,7 +525,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
// matches incoming fields with and without underscores (spaces
// and underscores considered the same)
queryParamColumnUnderscored = genericDataService.replace(key, space, underscore);
for (ResultsetColumnHeader columnHeader : columnHeaders) {
for (ResultsetColumnHeaderData columnHeader : columnHeaders) {
if (notFound) {
columnHeaderUnderscored = genericDataService.replace(columnHeader.getColumnName(), space, underscore);
if (queryParamColumnUnderscored.equalsIgnoreCase(columnHeaderUnderscored)) {
@@ -618,74 +543,84 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return affectedColumns;
}
private String validateColumn(final ResultsetColumnHeader columnHeader, final String pValue, final String dateFormat,
private String validateColumn(final ResultsetColumnHeaderData columnHeader, final String pValue, final String dateFormat,
final Locale clientApplicationLocale) {
String paramValue = pValue;
if ((columnHeader.getColumnDisplayType().equals("DATE")) || (columnHeader.getColumnDisplayType().equals("INTEGER"))
|| (columnHeader.getColumnDisplayType().equals("DECIMAL"))) paramValue = paramValue.trim();
if (columnHeader.isDateDisplayType() || columnHeader.isIntegerDisplayType() || columnHeader.isDecimalDisplayType()) {
paramValue = paramValue.trim();
}
if ((StringUtils.isEmpty(paramValue)) && (!(columnHeader.isColumnNullable()))) {
if (StringUtils.isEmpty(paramValue) && columnHeader.isMandatory()) {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("error.msg.column.mandatory", "Mandatory",
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final ApiParameterError error = ApiParameterError.parameterError("error.msg.column.mandatory", "Mandatory",
columnHeader.getColumnName());
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
if (!StringUtils.isEmpty(paramValue)) {
if (StringUtils.isNotEmpty(paramValue)) {
if (columnHeader.getColumnValues().size() > 0) {
// match code value or id
List<ResultsetColumnValue> allowedValues = columnHeader.getColumnValues();
if (columnHeader.getColumnDisplayType().equals("CODEVALUE")) {
for (ResultsetColumnValue allowedValue : allowedValues) {
if (paramValue.equalsIgnoreCase(allowedValue.getValue())) return paramValue;
if (columnHeader.hasColumnValues()) {
if (columnHeader.isCodeValueDisplayType()) {
if (columnHeader.isColumnValueNotAllowed(paramValue)) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
"Value not found in Allowed Value list", columnHeader.getColumnName(), paramValue);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
"Value not found in Allowed Value list", columnHeader.getColumnName(), paramValue);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
if (columnHeader.getColumnDisplayType().equals("CODELOOKUP")) {
for (ResultsetColumnValue allowedValue : allowedValues) {
if (paramValue.equals(Integer.toString(allowedValue.getId()))) return paramValue;
return paramValue;
} else if (columnHeader.isCodeLookupDisplayType()) {
final Integer codeLookup = Integer.valueOf(paramValue);
if (columnHeader.isColumnCodeNotAllowed(codeLookup)) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
"Value not found in Allowed Value list", columnHeader.getColumnName(), paramValue);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
ApiParameterError error = ApiParameterError.parameterError("error.msg.invalid.columnValue",
"Value not found in Allowed Value list", columnHeader.getColumnName(), paramValue);
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
throw new PlatformDataIntegrityException("error.msg.invalid.columnType.", "Code: " + columnHeader.getColumnName()
+ " - Invalid Type " + columnHeader.getColumnType() + " (neither varchar nor int)");
return paramValue;
} else {
throw new PlatformDataIntegrityException("error.msg.invalid.columnType.", "Code: " + columnHeader.getColumnName()
+ " - Invalid Type " + columnHeader.getColumnType() + " (neither varchar nor int)");
}
}
JsonParserHelper helper = new JsonParserHelper();
if (columnHeader.getColumnDisplayType().equals("DATE")) {
LocalDate tmpDate = helper.convertFrom(paramValue, columnHeader.getColumnName(), dateFormat, clientApplicationLocale);
if (tmpDate == null) return null;
return tmpDate.toString();
if (columnHeader.isDateDisplayType()) {
final LocalDate tmpDate = helper.convertFrom(paramValue, columnHeader.getColumnName(), dateFormat, clientApplicationLocale);
if (tmpDate == null) {
paramValue = null;
} else {
paramValue = tmpDate.toString();
}
}
if (columnHeader.getColumnDisplayType().equals("INTEGER")) {
if (columnHeader.isIntegerDisplayType()) {
Integer tmpInt = helper.convertToInteger(paramValue, columnHeader.getColumnName(), clientApplicationLocale);
if (tmpInt == null) return null;
return tmpInt.toString();
}
if (columnHeader.getColumnDisplayType().equals("DECIMAL")) {
BigDecimal tmpDecimal = helper.convertFrom(paramValue, columnHeader.getColumnName(), clientApplicationLocale);
if (tmpDecimal == null) return null;
return tmpDecimal.toString();
if (tmpInt == null) {
paramValue = null;
} else {
paramValue = tmpInt.toString();
}
}
if (columnHeader.isDecimalDisplayType()) {
BigDecimal tmpDecimal = helper.convertFrom(paramValue, columnHeader.getColumnName(), clientApplicationLocale);
if (tmpDecimal == null) {
paramValue = null;
} else {
paramValue = tmpDecimal.toString();
}
}
}
return paramValue;