This is an automated email from the ASF dual-hosted git repository.

ptuomola pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new b960616  Improve error handling if reporting service implementation is 
missing [FINERACT-1173]
b960616 is described below

commit b96061613b2987af14af1723ca362f9868ad5b0b
Author: Michael Vorburger <[email protected]>
AuthorDate: Sun Oct 4 19:25:59 2020 +0200

    Improve error handling if reporting service implementation is missing 
[FINERACT-1173]
---
 .../dataqueries/api/ReportsApiResource.java        |  11 ++-
 .../dataqueries/api/RunreportsApiResource.java     | 110 +++------------------
 .../service/DatatableReportingProcessService.java  | 110 +++++++++++++++++++++
 .../dataqueries/service/ReadReportingService.java  |  25 +++--
 .../service/ReadReportingServiceImpl.java          |  82 +++++----------
 .../service/ReportWritePlatformServiceImpl.java    |  11 ++-
 .../report/annotation/ReportService.java           |   2 +-
 .../provider/ReportingProcessServiceProvider.java  |  60 ++++-------
 .../report/service/ReportingProcessService.java    |  17 ++++
 .../ReportMailingJobWritePlatformServiceImpl.java  |   2 +-
 .../integrationtests/client/ReportsTest.java       |   2 +-
 11 files changed, 214 insertions(+), 218 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
index 990aef8..b4df23c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/ReportsApiResource.java
@@ -51,6 +51,7 @@ import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSeria
 import 
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
 import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
 import 
org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
+import 
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
@@ -71,17 +72,19 @@ public class ReportsApiResource {
     private final ReadReportingService readReportingService;
     private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
     private final ApiRequestParameterHelper apiRequestParameterHelper;
+    private final ReportingProcessServiceProvider 
reportingProcessServiceProvider;
 
     @Autowired
     public ReportsApiResource(final PlatformSecurityContext context, final 
ReadReportingService readReportingService,
             final ToApiJsonSerializer<ReportData> toApiJsonSerializer,
             final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService,
-            final ApiRequestParameterHelper apiRequestParameterHelper) {
+            final ApiRequestParameterHelper apiRequestParameterHelper, 
ReportingProcessServiceProvider reportingProcessServiceProvider) {
         this.context = context;
         this.readReportingService = readReportingService;
         this.toApiJsonSerializer = toApiJsonSerializer;
         this.commandsSourceWritePlatformService = 
commandsSourceWritePlatformService;
         this.apiRequestParameterHelper = apiRequestParameterHelper;
+        this.reportingProcessServiceProvider = reportingProcessServiceProvider;
     }
 
     @GET
@@ -117,7 +120,8 @@ public class ReportsApiResource {
         final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
 
         if (settings.isTemplate()) {
-            
result.appendedTemplate(this.readReportingService.getAllowedParameters(), 
this.readReportingService.getAllowedReportTypes());
+            
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
+                    
this.reportingProcessServiceProvider.findAllReportingTypes());
         }
         return this.toApiJsonSerializer.serialize(settings, result, 
this.responseDataParameters);
     }
@@ -135,7 +139,8 @@ public class ReportsApiResource {
         
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
 
         final ReportData result = new ReportData();
-        
result.appendedTemplate(this.readReportingService.getAllowedParameters(), 
this.readReportingService.getAllowedReportTypes());
+        
result.appendedTemplate(this.readReportingService.getAllowedParameters(),
+                this.reportingProcessServiceProvider.findAllReportingTypes());
 
         final ApiRequestJsonSerializationSettings settings = 
this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
         return this.toApiJsonSerializer.serialize(settings, result, 
this.responseDataParameters);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
index d257103..0f0c4c5 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java
@@ -25,10 +25,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.responses.ApiResponses;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
@@ -40,20 +36,14 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.core.UriInfo;
 import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
-import 
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
-import 
org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
-import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
-import 
org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import 
org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
 import 
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
 import 
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
 import 
org.apache.fineract.infrastructure.security.exception.NoAuthorizationException;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
-import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
@@ -65,20 +55,17 @@ import org.springframework.stereotype.Component;
 @Tag(name = "Run Reports", description = "")
 public class RunreportsApiResource {
 
+    public static final String IS_SELF_SERVICE_USER_REPORT_PARAMETER = 
"isSelfServiceUserReport";
+
     private final PlatformSecurityContext context;
-    private final ToApiJsonSerializer<ReportData> toApiJsonSerializer;
     private final ReadReportingService readExtraDataAndReportingService;
-    private final GenericDataService genericDataService;
     private final ReportingProcessServiceProvider 
reportingProcessServiceProvider;
 
     @Autowired
     public RunreportsApiResource(final PlatformSecurityContext context, final 
ReadReportingService readExtraDataAndReportingService,
-            final GenericDataService genericDataService, final 
ToApiJsonSerializer<ReportData> toApiJsonSerializer,
             final ReportingProcessServiceProvider 
reportingProcessServiceProvider) {
         this.context = context;
         this.readExtraDataAndReportingService = 
readExtraDataAndReportingService;
-        this.genericDataService = genericDataService;
-        this.toApiJsonSerializer = toApiJsonSerializer;
         this.reportingProcessServiceProvider = reportingProcessServiceProvider;
     }
 
@@ -108,79 +95,26 @@ public class RunreportsApiResource {
             @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
RunreportsApiResourceSwagger.RunReportsResponse.class))) })
     public Response runReport(@PathParam("reportName") @Parameter(description 
= "reportName") final String reportName,
             @Context final UriInfo uriInfo,
-            @DefaultValue("false") @QueryParam("isSelfServiceUserReport") 
@Parameter(description = "isSelfServiceUserReport") final boolean 
isSelfServiceUserReport) {
+            @DefaultValue("false") 
@QueryParam(IS_SELF_SERVICE_USER_REPORT_PARAMETER) @Parameter(description = 
IS_SELF_SERVICE_USER_REPORT_PARAMETER) final boolean isSelfServiceUserReport) {
 
         final MultivaluedMap<String, String> queryParams = 
uriInfo.getQueryParameters();
-
-        final boolean prettyPrint = 
ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
-        final boolean exportCsv = 
ApiParameterHelper.exportCsv(uriInfo.getQueryParameters());
-        final boolean parameterType = 
ApiParameterHelper.parameterType(uriInfo.getQueryParameters());
-        final boolean exportPdf = 
ApiParameterHelper.exportPdf(uriInfo.getQueryParameters());
+        final boolean parameterType = 
ApiParameterHelper.parameterType(queryParams);
 
         checkUserPermissionForReport(reportName, parameterType);
 
-        String parameterTypeValue = null;
-        if (!parameterType) {
-            parameterTypeValue = "report";
-            String reportType = 
this.readExtraDataAndReportingService.getReportType(reportName, 
isSelfServiceUserReport);
-            ReportingProcessService reportingProcessService = 
this.reportingProcessServiceProvider.findReportingProcessService(reportType);
-            if (reportingProcessService != null) {
-                return reportingProcessService.processRequest(reportName, 
queryParams);
-            }
-        } else {
-            parameterTypeValue = "parameter";
-        }
-
-        // PDF format
-
-        if (exportPdf) {
-            final Map<String, String> reportParams = 
getReportParams(queryParams);
-            final String pdfFileName = 
this.readExtraDataAndReportingService.retrieveReportPDF(reportName, 
parameterTypeValue, reportParams,
-                    isSelfServiceUserReport);
-
-            final File file = new File(pdfFileName);
-
-            final ResponseBuilder response = Response.ok(file);
-            response.header("Content-Disposition", "attachment; filename=\"" + 
pdfFileName + "\"");
-            response.header("content-Type", "application/pdf");
-
-            return response.build();
+        // Pass through isSelfServiceUserReport so that 
ReportingProcessService implementations can use it
+        queryParams.putSingle(IS_SELF_SERVICE_USER_REPORT_PARAMETER, 
Boolean.toString(isSelfServiceUserReport));
 
+        String reportType = 
this.readExtraDataAndReportingService.getReportType(reportName, 
isSelfServiceUserReport);
+        ReportingProcessService reportingProcessService = 
this.reportingProcessServiceProvider.findReportingProcessService(reportType);
+        if (reportingProcessService == null) {
+            throw new 
PlatformServiceUnavailableException("err.msg.report.service.implementation.missing",
+                    ReportingProcessServiceProvider.SERVICE_MISSING + 
reportType, reportType);
         }
-
-        if (!exportCsv) {
-            final Map<String, String> reportParams = 
getReportParams(queryParams);
-
-            final GenericResultsetData result = 
this.readExtraDataAndReportingService.retrieveGenericResultset(reportName,
-                    parameterTypeValue, reportParams, isSelfServiceUserReport);
-
-            String json = "";
-            final boolean genericResultSetIsPassed = 
ApiParameterHelper.genericResultSetPassed(uriInfo.getQueryParameters());
-            final boolean genericResultSet = 
ApiParameterHelper.genericResultSet(uriInfo.getQueryParameters());
-            if (genericResultSetIsPassed) {
-                if (genericResultSet) {
-                    json = 
this.toApiJsonSerializer.serializePretty(prettyPrint, result);
-                } else {
-                    json = 
this.genericDataService.generateJsonFromGenericResultsetData(result);
-                }
-            } else {
-                json = this.toApiJsonSerializer.serializePretty(prettyPrint, 
result);
-            }
-
-            return 
Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
-        }
-
-        // CSV Export
-        final Map<String, String> reportParams = getReportParams(queryParams);
-        final StreamingOutput result = 
this.readExtraDataAndReportingService.retrieveReportCSV(reportName, 
parameterTypeValue, reportParams,
-                isSelfServiceUserReport);
-
-        return Response.ok().entity(result).type("text/csv")
-                .header("Content-Disposition", "attachment;filename=" + 
reportName.replaceAll(" ", "") + ".csv").build();
+        return reportingProcessService.processRequest(reportName, queryParams);
     }
 
     private void checkUserPermissionForReport(final String reportName, final 
boolean parameterType) {
-
         // Anyone can run a 'report' that is simply getting possible parameter
         // (dropdown listbox) values.
         if (!parameterType) {
@@ -190,22 +124,4 @@ public class RunreportsApiResource {
             }
         }
     }
-
-    private Map<String, String> getReportParams(final MultivaluedMap<String, 
String> queryParams) {
-
-        final Map<String, String> reportParams = new HashMap<>();
-        final Set<String> keys = queryParams.keySet();
-        String pKey;
-        String pValue;
-        for (final String k : keys) {
-
-            if (k.startsWith("R_")) {
-                pKey = "${" + k.substring(2) + "}";
-                pValue = queryParams.get(k).get(0);
-                SQLInjectionValidator.validateSQLInput(pValue);
-                reportParams.put(pKey, pValue);
-            }
-        }
-        return reportParams;
-    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
new file mode 100644
index 0000000..e7cb156
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/DatatableReportingProcessService.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.dataqueries.service;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.StreamingOutput;
+import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
+import 
org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import 
org.apache.fineract.infrastructure.dataqueries.api.RunreportsApiResource;
+import 
org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
+import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
+import org.apache.fineract.infrastructure.report.annotation.ReportService;
+import 
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@ReportService(type = { "Table", "Chart", "SMS" })
+public class DatatableReportingProcessService implements 
ReportingProcessService {
+
+    private final ReadReportingService readExtraDataAndReportingService;
+    private final ToApiJsonSerializer<ReportData> toApiJsonSerializer;
+    private final GenericDataService genericDataService;
+
+    @Autowired
+    public DatatableReportingProcessService(final ReadReportingService 
readExtraDataAndReportingService,
+            final GenericDataService genericDataService, final 
ToApiJsonSerializer<ReportData> toApiJsonSerializer) {
+        this.readExtraDataAndReportingService = 
readExtraDataAndReportingService;
+        this.toApiJsonSerializer = toApiJsonSerializer;
+        this.genericDataService = genericDataService;
+    }
+
+    @Override
+    public Response processRequest(String reportName, MultivaluedMap<String, 
String> queryParams) {
+        boolean isSelfServiceUserReport = Boolean.parseBoolean(
+                
queryParams.getOrDefault(RunreportsApiResource.IS_SELF_SERVICE_USER_REPORT_PARAMETER,
 List.of("false")).get(0));
+        final boolean prettyPrint = 
ApiParameterHelper.prettyPrint(queryParams);
+        final boolean exportCsv = ApiParameterHelper.exportCsv(queryParams);
+        final boolean exportPdf = ApiParameterHelper.exportPdf(queryParams);
+        final String parameterTypeValue = 
ApiParameterHelper.parameterType(queryParams) ? "parameter" : "report";
+
+        // PDF format
+        if (exportPdf) {
+            final Map<String, String> reportParams = 
getReportParams(queryParams);
+            final String pdfFileName = 
this.readExtraDataAndReportingService.retrieveReportPDF(reportName, 
parameterTypeValue, reportParams,
+                    isSelfServiceUserReport);
+
+            final File file = new File(pdfFileName);
+
+            final ResponseBuilder response = Response.ok(file);
+            response.header("Content-Disposition", "attachment; filename=\"" + 
pdfFileName + "\"");
+            response.header("content-Type", "application/pdf");
+
+            return response.build();
+        }
+
+        // JSON format
+        if (!exportCsv) {
+            final Map<String, String> reportParams = 
getReportParams(queryParams);
+
+            final GenericResultsetData result = 
this.readExtraDataAndReportingService.retrieveGenericResultset(reportName,
+                    parameterTypeValue, reportParams, isSelfServiceUserReport);
+
+            String json;
+            final boolean genericResultSetIsPassed = 
ApiParameterHelper.genericResultSetPassed(queryParams);
+            final boolean genericResultSet = 
ApiParameterHelper.genericResultSet(queryParams);
+            if (genericResultSetIsPassed) {
+                if (genericResultSet) {
+                    json = 
this.toApiJsonSerializer.serializePretty(prettyPrint, result);
+                } else {
+                    json = 
this.genericDataService.generateJsonFromGenericResultsetData(result);
+                }
+            } else {
+                json = this.toApiJsonSerializer.serializePretty(prettyPrint, 
result);
+            }
+
+            return 
Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
+        }
+
+        // CSV format
+        final Map<String, String> reportParams = getReportParams(queryParams);
+        final StreamingOutput result = 
this.readExtraDataAndReportingService.retrieveReportCSV(reportName, 
parameterTypeValue, reportParams,
+                isSelfServiceUserReport);
+
+        return Response.ok().entity(result).type("text/csv")
+                .header("Content-Disposition", "attachment;filename=" + 
reportName.replaceAll(" ", "") + ".csv").build();
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
index dd0ef88..cf4b383 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java
@@ -30,29 +30,28 @@ import 
org.apache.fineract.useradministration.domain.AppUser;
 
 public interface ReadReportingService {
 
-    StreamingOutput retrieveReportCSV(String name, String type, Map<String, 
String> extractedQueryParams, boolean isSelfServiceUserReport);
-
-    GenericResultsetData retrieveGenericResultset(String name, String type, 
Map<String, String> extractedQueryParams,
-            boolean isSelfServiceUserReport);
+    Collection<ReportData> retrieveReportList();
 
-    String retrieveReportPDF(String name, String type, Map<String, String> 
extractedQueryParams, boolean isSelfServiceUserReport);
+    ReportData retrieveReport(Long id);
 
     String getReportType(String reportName, boolean isSelfServiceUserReport);
 
-    Collection<ReportData> retrieveReportList();
-
     Collection<ReportParameterData> getAllowedParameters();
 
-    ReportData retrieveReport(Long id);
+    // TODO Move the following x3 methods into the (new; FINERACT-1173) 
DatatableReportingProcessService?
 
-    Collection<String> getAllowedReportTypes();
+    String retrieveReportPDF(String name, String type, Map<String, String> 
extractedQueryParams, boolean isSelfServiceUserReport);
 
-    // needed for smsCampaign and emailCampaign jobs where securityContext is
-    // null
-    GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String 
name, String type, Map<String, String> extractedQueryParams);
+    StreamingOutput retrieveReportCSV(String name, String type, Map<String, 
String> extractedQueryParams, boolean isSelfServiceUserReport);
 
-    String sqlToRunForSmsEmailCampaign(String name, String type, Map<String, 
String> queryParams);
+    GenericResultsetData retrieveGenericResultset(String name, String type, 
Map<String, String> extractedQueryParams,
+            boolean isSelfServiceUserReport);
+
+    // TODO This is weird, could they not be using the 
retrieveGenericResultset() above after all?
+    // needed for smsCampaign and emailCampaign jobs where securityContext is 
null
+    GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String 
name, String type, Map<String, String> extractedQueryParams);
 
+    // TODO kill this when tackling 
https://issues.apache.org/jira/browse/FINERACT-1264
     ByteArrayOutputStream generatePentahoReportAsOutputStream(String 
reportName, String outputTypeParam, Map<String, String> queryParams,
             Locale locale, AppUser runReportAsUser, StringBuilder errorLog);
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
index 7688caa..43584f3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java
@@ -27,7 +27,6 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -51,7 +50,6 @@ import 
org.apache.fineract.infrastructure.dataqueries.data.ResultsetColumnHeader
 import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
 import 
org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
 import 
org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
-import 
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
 import org.apache.fineract.infrastructure.security.utils.SQLInjectionException;
@@ -74,53 +72,43 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
     private final DataSource dataSource;
     private final PlatformSecurityContext context;
     private final GenericDataService genericDataService;
-    private final ReportingProcessServiceProvider 
reportingProcessServiceProvider;
     private final ColumnValidator columnValidator;
 
     @Autowired
     public ReadReportingServiceImpl(final PlatformSecurityContext context, 
final RoutingDataSource dataSource,
-            final GenericDataService genericDataService, final 
ReportingProcessServiceProvider reportingProcessServiceProvider,
-            final ColumnValidator columnValidator) {
-
+            final GenericDataService genericDataService, final ColumnValidator 
columnValidator) {
         this.context = context;
         this.dataSource = dataSource;
         this.jdbcTemplate = new JdbcTemplate(this.dataSource);
         this.genericDataService = genericDataService;
-        this.reportingProcessServiceProvider = reportingProcessServiceProvider;
         this.columnValidator = columnValidator;
     }
 
     @Override
     public StreamingOutput retrieveReportCSV(final String name, final String 
type, final Map<String, String> queryParams,
             final boolean isSelfServiceUserReport) {
+        return out -> {
+            try {
 
-        return new StreamingOutput() {
-
-            @Override
-            public void write(final OutputStream out) {
-                try {
-
-                    final GenericResultsetData result = 
retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
-                    final StringBuilder sb = generateCsvFileBuffer(result);
+                final GenericResultsetData result = 
retrieveGenericResultset(name, type, queryParams, isSelfServiceUserReport);
+                final StringBuilder sb = generateCsvFileBuffer(result);
 
-                    final InputStream in = new 
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
+                final InputStream in = new 
ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8));
 
-                    final byte[] outputByte = new byte[4096];
-                    Integer readLen = in.read(outputByte, 0, 4096);
+                final 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 (final Exception e) {
-                    throw new 
PlatformDataIntegrityException("error.msg.exception.error", e.getMessage(), e);
+                while (readLen != -1) {
+                    out.write(outputByte, 0, readLen);
+                    readLen = in.read(outputByte, 0, 4096);
                 }
+                // in.close();
+                // out.flush();
+                // out.close();
+            } catch (final Exception e) {
+                throw new 
PlatformDataIntegrityException("error.msg.exception.error", e.getMessage(), e);
             }
         };
-
     }
 
     private StringBuilder generateCsvFileBuffer(final GenericResultsetData 
result) {
@@ -146,8 +134,8 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
         final String doubleQuote = "\"";
         final String twoDoubleQuotes = doubleQuote + doubleQuote;
         LOG.info("NO. of Rows: {}", data.size());
-        for (int i = 0; i < data.size(); i++) {
-            row = data.get(i).getRow();
+        for (ResultsetRowData element : data) {
+            row = element.getRow();
             rSize = row.size();
             for (int j = 0; j < rSize; j++) {
                 // currCol = columnHeaders.get(j).getColumnName();
@@ -184,7 +172,7 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
         final GenericResultsetData result = 
this.genericDataService.fillGenericResultSet(sql);
 
         final long elapsed = System.currentTimeMillis() - startTime;
-        LOG.info("FINISHING Report/Request Name: {} - {}     Elapsed Time: 
{}", new Object[] { name, type, elapsed });
+        LOG.info("FINISHING Report/Request Name: {} - {}     Elapsed Time: 
{}", name, type, elapsed);
         return result;
     }
 
@@ -213,11 +201,9 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
         sql = this.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 + "'";
         validateReportName(name);
 
@@ -234,7 +220,6 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
 
     @Override
     public String getReportType(final String reportName, final boolean 
isSelfServiceUserReport) {
-
         final String sql = "SELECT ifnull(report_type,'') as report_type FROM 
`stretchy_report` where report_name = '" + reportName
                 + "' and self_service_user_report = ?";
         validateReportName(reportName);
@@ -242,7 +227,7 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
 
         final String sqlWrapped = this.genericDataService.wrapSQL(sql);
 
-        final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped, new 
Object[] { isSelfServiceUserReport });
+        final SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sqlWrapped, 
isSelfServiceUserReport);
 
         if (rs.next()) {
             return rs.getString("report_type");
@@ -290,8 +275,8 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
             String currColType;
             String currVal;
             LOG.info("NO. of Rows: {}", data.size());
-            for (int i = 0; i < data.size(); i++) {
-                row = data.get(i).getRow();
+            for (ResultsetRowData element : data) {
+                row = element.getRow();
                 rSize = row.size();
                 for (int j = 0; j < rSize; j++) {
                     currColType = columnHeaders.get(j).getColumnType();
@@ -338,7 +323,7 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
 
         final String sql = rm.schema(id);
 
-        final Collection<ReportParameterJoinData> rpJoins = 
this.jdbcTemplate.query(sql, rm, new Object[] {});
+        final Collection<ReportParameterJoinData> rpJoins = 
this.jdbcTemplate.query(sql, rm);
 
         final Collection<ReportData> reportList = new ArrayList<>();
         if (rpJoins == null || rpJoins.size() == 0) {
@@ -410,22 +395,12 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
 
     @Override
     public Collection<ReportParameterData> getAllowedParameters() {
-
         final ReportParameterMapper rm = new ReportParameterMapper();
         final String sql = rm.schema();
-        final Collection<ReportParameterData> parameters = 
this.jdbcTemplate.query(sql, rm, new Object[] {});
+        final Collection<ReportParameterData> parameters = 
this.jdbcTemplate.query(sql, rm);
         return parameters;
     }
 
-    @Override
-    public Collection<String> getAllowedReportTypes() {
-        final List<String> reportTypes = new ArrayList<>();
-        reportTypes.add("Table");
-        reportTypes.add("Chart");
-        
reportTypes.addAll(this.reportingProcessServiceProvider.findAllReportingTypes());
-        return reportTypes;
-    }
-
     private static final class ReportParameterJoinMapper implements 
RowMapper<ReportParameterJoinData> {
 
         public String schema(final Long reportId) {
@@ -460,7 +435,6 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
 
         @Override
         public ReportParameterJoinData mapRow(final ResultSet rs, final int 
rowNum) throws SQLException {
-
             final Long reportId = rs.getLong("reportId");
             final String reportName = rs.getString("reportName");
             final String reportType = rs.getString("reportType");
@@ -491,9 +465,7 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
     private static final class ReportParameterMapper implements 
RowMapper<ReportParameterData> {
 
         public String schema() {
-
             return "select p.id as id, p.parameter_name as parameterName from 
stretchy_parameter p where ifnull(p.special,'') != 'Y' order by p.id";
-
         }
 
         @Override
@@ -516,12 +488,11 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
         final GenericResultsetData result = 
this.genericDataService.fillGenericResultSet(sql);
 
         final long elapsed = System.currentTimeMillis() - startTime;
-        LOG.info("FINISHING Report/Request Name: {} - {}     Elapsed Time: 
{}", new Object[] { name, type, elapsed });
+        LOG.info("FINISHING Report/Request Name: {} - {}     Elapsed Time: 
{}", name, type, elapsed);
         return result;
     }
 
-    @Override
-    public String sqlToRunForSmsEmailCampaign(final String name, final String 
type, final Map<String, String> queryParams) {
+    private String sqlToRunForSmsEmailCampaign(final String name, final String 
type, final Map<String, String> queryParams) {
         String sql = getSql(name, type);
 
         final Set<String> keys = queryParams.keySet();
@@ -599,7 +570,6 @@ public class ReadReportingServiceImpl implements 
ReadReportingService {
     }
 
     private void validateReportName(final String name) {
-
         if (!StringUtils.isBlank(name) && 
!name.matches(REPORT_NAME_REGEX_PATTERN)) {
             throw new SQLInjectionException();
         }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
index 8794b58..35069be 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReportWritePlatformServiceImpl.java
@@ -39,6 +39,7 @@ import 
org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository;
 import 
org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
 import 
org.apache.fineract.infrastructure.dataqueries.exception.ReportParameterNotFoundException;
 import 
org.apache.fineract.infrastructure.dataqueries.serialization.ReportCommandFromApiJsonDeserializer;
+import 
org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.useradministration.domain.Permission;
 import org.apache.fineract.useradministration.domain.PermissionRepository;
@@ -62,20 +63,20 @@ public class ReportWritePlatformServiceImpl implements 
ReportWritePlatformServic
     private final ReportParameterUsageRepository 
reportParameterUsageRepository;
     private final ReportParameterRepository reportParameterRepository;
     private final PermissionRepository permissionRepository;
-    private final ReadReportingService readReportingService;
+    private final ReportingProcessServiceProvider 
reportingProcessServiceProvider;
 
     @Autowired
     public ReportWritePlatformServiceImpl(final PlatformSecurityContext 
context,
             final ReportCommandFromApiJsonDeserializer 
fromApiJsonDeserializer, final ReportRepository reportRepository,
             final ReportParameterRepository reportParameterRepository, final 
ReportParameterUsageRepository reportParameterUsageRepository,
-            final PermissionRepository permissionRepository, final 
ReadReportingService readReportingService) {
+            final PermissionRepository permissionRepository, final 
ReportingProcessServiceProvider reportingProcessServiceProvider) {
         this.context = context;
         this.fromApiJsonDeserializer = fromApiJsonDeserializer;
         this.reportRepository = reportRepository;
         this.reportParameterRepository = reportParameterRepository;
         this.reportParameterUsageRepository = reportParameterUsageRepository;
         this.permissionRepository = permissionRepository;
-        this.readReportingService = readReportingService;
+        this.reportingProcessServiceProvider = reportingProcessServiceProvider;
     }
 
     @Transactional
@@ -87,7 +88,7 @@ public class ReportWritePlatformServiceImpl implements 
ReportWritePlatformServic
 
             this.fromApiJsonDeserializer.validate(command.json());
 
-            final Report report = Report.fromJson(command, 
this.readReportingService.getAllowedReportTypes());
+            final Report report = Report.fromJson(command, 
this.reportingProcessServiceProvider.findAllReportingTypes());
             final Set<ReportParameterUsage> reportParameterUsages = 
assembleSetOfReportParameterUsages(report, command);
             report.update(reportParameterUsages);
 
@@ -121,7 +122,7 @@ public class ReportWritePlatformServiceImpl implements 
ReportWritePlatformServic
 
             final Report report = 
this.reportRepository.findById(reportId).orElseThrow(() -> new 
ReportNotFoundException(reportId));
 
-            final Map<String, Object> changes = report.update(command, 
this.readReportingService.getAllowedReportTypes());
+            final Map<String, Object> changes = report.update(command, 
this.reportingProcessServiceProvider.findAllReportingTypes());
 
             if (changes.containsKey("reportParameters")) {
                 final Set<ReportParameterUsage> reportParameterUsages = 
assembleSetOfReportParameterUsages(report, command);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
index 8c72b1b..a101e1f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/annotation/ReportService.java
@@ -32,5 +32,5 @@ public @interface ReportService {
     /**
      * @return the type of the report
      */
-    String type();
+    String[] type();
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
index c52b1c3..84e812a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/provider/ReportingProcessServiceProvider.java
@@ -18,68 +18,46 @@
  */
 package org.apache.fineract.infrastructure.report.provider;
 
+import com.google.common.collect.ImmutableMap;
 import java.util.Collection;
-import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.fineract.infrastructure.report.annotation.ReportService;
 import 
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 
 @Component
 @Scope("singleton")
-public class ReportingProcessServiceProvider implements 
ApplicationContextAware {
+public class ReportingProcessServiceProvider {
 
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(ReportingProcessServiceProvider.class);
-
-    private ApplicationContext applicationContext;
+    public static final String SERVICE_MISSING = "There is no 
ReportingProcessService registered in the ReportingProcessServiceProvider for 
this report type: ";
 
-    Map<String, String> reportingProcessServices = null;
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ReportingProcessServiceProvider.class);
 
-    ReportingProcessServiceProvider() {
+    private final Map<String, ReportingProcessService> 
reportingProcessServices;
 
-    }
-
-    public ReportingProcessService findReportingProcessService(final String 
reportType) {
-        if (this.reportingProcessServices.containsKey(reportType)) {
-            return (ReportingProcessService) 
this.applicationContext.getBean(this.reportingProcessServices.get(reportType));
+    @Autowired
+    public ReportingProcessServiceProvider(List<ReportingProcessService> 
reportingProcessServices) {
+        var mapBuilder = ImmutableMap.<String, 
ReportingProcessService>builder();
+        for (ReportingProcessService s : reportingProcessServices) {
+            String[] reportTypes = 
s.getClass().getAnnotation(ReportService.class).type();
+            for (String type : reportTypes) {
+                mapBuilder.put(type, s);
+            }
+            LOGGER.info("Registered report service '{}' for type/s '{}'", s, 
reportTypes);
         }
-        return null;
+        this.reportingProcessServices = mapBuilder.build();
     }
 
-    @Override
-    public void setApplicationContext(ApplicationContext applicationContext) 
throws BeansException {
-        this.applicationContext = applicationContext;
-        this.initializeRegistry();
+    public ReportingProcessService findReportingProcessService(final String 
reportType) {
+        return reportingProcessServices.getOrDefault(reportType, null);
     }
 
     public Collection<String> findAllReportingTypes() {
         return this.reportingProcessServices.keySet();
-
-    }
-
-    private void initializeRegistry() {
-        if (this.reportingProcessServices == null) {
-            this.reportingProcessServices = new HashMap<>();
-
-            final String[] reportServiceBeans = 
this.applicationContext.getBeanNamesForAnnotation(ReportService.class);
-            if (ArrayUtils.isNotEmpty(reportServiceBeans)) {
-                for (final String reportName : reportServiceBeans) {
-                    LOGGER.info("Register report service '{}' ...", 
reportName);
-                    final ReportService service = 
this.applicationContext.findAnnotationOnBean(reportName, ReportService.class);
-                    try {
-                        this.reportingProcessServices.put(service.type(), 
reportName);
-                    } catch (final Throwable th) {
-                        LOGGER.error("Unable to register reporting service 
'{}'!", reportName, th);
-                    }
-                }
-            }
-        }
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
index 990bd0d..406157f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/report/service/ReportingProcessService.java
@@ -18,11 +18,28 @@
  */
 package org.apache.fineract.infrastructure.report.service;
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
+import org.apache.fineract.infrastructure.security.utils.SQLInjectionValidator;
 
 public interface ReportingProcessService {
 
     Response processRequest(String reportName, MultivaluedMap<String, String> 
queryParams);
 
+    default Map<String, String> getReportParams(final MultivaluedMap<String, 
String> queryParams) {
+        final Map<String, String> reportParams = new HashMap<>();
+        final Set<String> keys = queryParams.keySet();
+        for (final String k : keys) {
+            if (k.startsWith("R_")) {
+                String pKey = "${" + k.substring(2) + "}";
+                String pValue = queryParams.get(k).get(0);
+                SQLInjectionValidator.validateSQLInput(pValue);
+                reportParams.put(pKey, pValue);
+            }
+        }
+        return reportParams;
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
index 47d45ec..1de29e3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
@@ -471,7 +471,7 @@ public class ReportMailingJobWritePlatformServiceImpl 
implements ReportMailingJo
                     errorLog.append("Response object entity is not equal to 
ByteArrayOutputStream ---------- ");
                 }
             } else {
-                errorLog.append("ReportingProcessService object is null 
---------- ");
+                
errorLog.append(ReportingProcessServiceProvider.SERVICE_MISSING + reportType);
             }
         } catch (Exception e) {
             errorLog.append("The 
ReportMailingJobWritePlatformServiceImpl.generateReportOutputStream method 
threw an Exception: " + e
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
index 8eebfe9..03a1157 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
@@ -45,7 +45,7 @@ public class ReportsTest extends IntegrationTest {
     @Test
     void runExpectedPaymentsPentahoReportWithoutPlugin() {
         assertThat(fineract().reportsRun.runReportGetFile("Expected Payments 
By Date - Formatted", Map.of("R_endDate", "2013-04-30",
-                "R_loanOfficerId", "-1", "R_officeId", "1", "R_startDate", 
"2013-04-16", "output-type", "PDF"), false)).hasHttpStatus(404);
+                "R_loanOfficerId", "-1", "R_officeId", "1", "R_startDate", 
"2013-04-16", "output-type", "PDF"), false)).hasHttpStatus(503);
     }
 
     @Test

Reply via email to