Handle exceptions around DB transcations, data integrity 59/127459/2
authorRashmi Pujar <rashmi.pujar1@bell.ca>
Thu, 3 Mar 2022 03:12:02 +0000 (22:12 -0500)
committerRashmi Pujar <rashmi.pujar1@bell.ca>
Thu, 3 Mar 2022 03:17:08 +0000 (03:17 +0000)
Issue-ID: POLICY-3976
Signed-off-by: Rashmi Pujar <rashmi.pujar1@bell.ca>
Change-Id: I9cdfff788d901824904bc74fdb7e2d482d28d562

main/src/main/java/org/onap/policy/api/main/config/PolicyApiConfig.java
main/src/main/java/org/onap/policy/api/main/exception/ServiceExceptionHandler.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/api/main/rest/ApiRestController.java
main/src/main/java/org/onap/policy/api/main/rest/CommonRestController.java
main/src/main/java/org/onap/policy/api/main/rest/provider/healthcheck/HealthCheckProvider.java [moved from main/src/main/java/org/onap/policy/api/main/rest/provider/HealthCheckProvider.java with 95% similarity]
main/src/main/java/org/onap/policy/api/main/rest/provider/statistics/ApiStatisticsManager.java [moved from main/src/main/java/org/onap/policy/api/main/rest/ApiStatisticsManager.java with 99% similarity]
main/src/main/java/org/onap/policy/api/main/rest/provider/statistics/StatisticsProvider.java [moved from main/src/main/java/org/onap/policy/api/main/rest/provider/StatisticsProvider.java with 94% similarity]
main/src/main/java/org/onap/policy/api/main/rest/provider/statistics/StatisticsReport.java [moved from main/src/main/java/org/onap/policy/api/main/rest/StatisticsReport.java with 97% similarity]
main/src/test/java/org/onap/policy/api/main/rest/TestApiRestServer.java
main/src/test/java/org/onap/policy/api/main/rest/TestApiStatisticsManager.java
main/src/test/java/org/onap/policy/api/main/rest/TestStatisticsReport.java

index 48202a8..0510d65 100644 (file)
@@ -21,7 +21,7 @@
 
 package org.onap.policy.api.main.config;
 
-import org.onap.policy.api.main.rest.StatisticsReport;
+import org.onap.policy.api.main.rest.provider.statistics.StatisticsReport;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
diff --git a/main/src/main/java/org/onap/policy/api/main/exception/ServiceExceptionHandler.java b/main/src/main/java/org/onap/policy/api/main/exception/ServiceExceptionHandler.java
new file mode 100644 (file)
index 0000000..f75013c
--- /dev/null
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Bell Canada. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.api.main.exception;
+
+import javax.ws.rs.core.Response;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Aspect;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.errors.concepts.ErrorResponse;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.TransactionException;
+
+@Aspect
+@Component
+public class ServiceExceptionHandler {
+
+    /**
+     * Handle any exceptions that are not already handled.
+     * For e.g., runtime exceptions that could happen during SQL query execution related to data integrity etc.
+     *
+     * @param joinPoint the point of execution
+     * @param exception the exception
+     */
+    @AfterThrowing(pointcut = "execution(* org.onap.policy.api.main.service.*.*(..))", throwing = "exception")
+    public ResponseEntity<Object> handleServiceException(JoinPoint joinPoint, RuntimeException exception) {
+        if (exception instanceof PolicyApiRuntimeException || exception instanceof PfModelRuntimeException) {
+            throw exception;
+        } else {
+            final var errorResponse = new ErrorResponse();
+            errorResponse.setResponseCode(Response.Status.INTERNAL_SERVER_ERROR);
+            errorResponse.setErrorMessage(exception.getMessage());
+            throw new PolicyApiRuntimeException(exception.getMessage(), exception.getCause(), errorResponse, null);
+        }
+    }
+
+    /**
+     * Handle DB Transaction related exceptions.
+     * All service classes in org.onap.policy.api.main.service are transactional and autowiring these service classes
+     * can cause TransactionException.
+     * For e.g., JDBC connection failure occurs and failed to open transaction at service level
+     *
+     * @param joinPoint the point of execution
+     * @param exception the exception
+     */
+    @AfterThrowing(pointcut = "execution(* org.onap.policy.api.main..*.*(..))"
+        + " && !execution(* org.onap.policy.api.main.rest.provider.statistics.*.*(..))", throwing = "exception")
+    public ResponseEntity<Object> handleTransactionException(JoinPoint joinPoint, TransactionException exception) {
+        final var errorResponse = new ErrorResponse();
+        errorResponse.setResponseCode(Response.Status.INTERNAL_SERVER_ERROR);
+        errorResponse.setErrorMessage(exception.getMessage());
+        throw new PolicyApiRuntimeException(exception.getMessage(), exception.getCause(), errorResponse, null);
+    }
+}
\ No newline at end of file
index 84b26c8..d6f6d6a 100644 (file)
@@ -44,8 +44,10 @@ import java.util.UUID;
 import javax.ws.rs.core.Response.Status;\r
 import lombok.RequiredArgsConstructor;\r
 import org.onap.policy.api.main.exception.PolicyApiRuntimeException;\r
-import org.onap.policy.api.main.rest.provider.HealthCheckProvider;\r
-import org.onap.policy.api.main.rest.provider.StatisticsProvider;\r
+import org.onap.policy.api.main.rest.provider.healthcheck.HealthCheckProvider;\r
+import org.onap.policy.api.main.rest.provider.statistics.ApiStatisticsManager;\r
+import org.onap.policy.api.main.rest.provider.statistics.StatisticsProvider;\r
+import org.onap.policy.api.main.rest.provider.statistics.StatisticsReport;\r
 import org.onap.policy.api.main.service.ToscaServiceTemplateService;\r
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;\r
 import org.onap.policy.common.endpoints.report.HealthCheckReport;\r
index 5e4c3ee..ce479bc 100644 (file)
@@ -31,9 +31,9 @@ import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.common.utils.coder.StandardCoder;\r
 import org.slf4j.Logger;\r
 import org.slf4j.LoggerFactory;\r
-import org.springframework.http.HttpHeaders;\r
 import org.springframework.http.ResponseEntity;\r
 import org.springframework.web.bind.annotation.ExceptionHandler;\r
+import org.springframework.web.context.request.WebRequest;\r
 \r
 /**\r
  * Super class from which REST controllers are derived.\r
@@ -63,7 +63,7 @@ public class CommonRestController {
     protected static final String VERSION_LATEST_NAME = "X-LatestVersion";\r
     protected static final String VERSION_LATEST_DESCRIPTION = "Used only to communicate an API's latest version";\r
 \r
-    protected static final String REQUEST_ID_NAME = "X-ONAP-RequestID";\r
+    public static final String REQUEST_ID_NAME = "X-ONAP-RequestID";\r
     protected static final String REQUEST_ID_HDR_DESCRIPTION = "Used to track REST transactions for logging purpose";\r
     protected static final String REQUEST_ID_PARAM_DESCRIPTION = "RequestID for http transaction";\r
 \r
@@ -79,32 +79,34 @@ public class CommonRestController {
     protected final Coder coder = new StandardCoder();\r
 \r
     protected <T> ResponseEntity<T> makeOkResponse(UUID requestId, T respEntity) {\r
-        HttpHeaders headers = new HttpHeaders();\r
-        addVersionControlHeaders(headers);\r
-        addLoggingHeaders(headers, requestId);\r
-        return ResponseEntity.ok().headers(headers).body(respEntity);\r
+        return CommonRestController.addLoggingHeaders(addVersionControlHeaders(ResponseEntity.ok()), requestId)\r
+            .body(respEntity);\r
     }\r
 \r
-    protected <T> ResponseEntity<T> makeErrorResponse(UUID requestId, T respEntity, int status) {\r
-        HttpHeaders headers = new HttpHeaders();\r
-        addVersionControlHeaders(headers);\r
-        addLoggingHeaders(headers, requestId);\r
-        return ResponseEntity.status(status).headers(headers).body(respEntity);\r
-    }\r
-\r
-    private void addVersionControlHeaders(HttpHeaders headers) {\r
-        headers.add("X-MinorVersion", "0");\r
-        headers.add("X-PatchVersion", "0");\r
-        headers.add("X-LatestVersion", "1.0.0");\r
+    /**\r
+     * Adds version headers to the response.\r
+     *\r
+     * @param respBuilder response builder\r
+     * @return the response builder, with version headers\r
+     */\r
+    public static ResponseEntity.BodyBuilder addVersionControlHeaders(ResponseEntity.BodyBuilder respBuilder) {\r
+        return respBuilder.header(VERSION_MINOR_NAME, "0").header(VERSION_PATCH_NAME, "0").header(VERSION_LATEST_NAME,\r
+            API_VERSION);\r
     }\r
 \r
-    private void addLoggingHeaders(HttpHeaders headers, UUID requestId) {\r
+    /**\r
+     * Adds logging headers to the response.\r
+     *\r
+     * @param respBuilder response builder\r
+     * @return the response builder, with version logging\r
+     */\r
+    public static ResponseEntity.BodyBuilder addLoggingHeaders(ResponseEntity.BodyBuilder respBuilder, UUID requestId) {\r
         if (requestId == null) {\r
             // Generate a random uuid if client does not embed requestId in rest request\r
-            headers.add("X-ONAP-RequestID", UUID.randomUUID().toString());\r
-        } else {\r
-            headers.add("X-ONAP-RequestID", requestId.toString());\r
+            return respBuilder.header(REQUEST_ID_NAME, UUID.randomUUID().toString());\r
         }\r
+\r
+        return respBuilder.header(REQUEST_ID_NAME, requestId.toString());\r
     }\r
 \r
     /**\r
@@ -128,9 +130,12 @@ public class CommonRestController {
     }\r
 \r
     @ExceptionHandler(value = {PolicyApiRuntimeException.class})\r
-    protected ResponseEntity<Object> handleException(PolicyApiRuntimeException ex) {\r
+    protected ResponseEntity<Object> handleException(PolicyApiRuntimeException ex, WebRequest req) {\r
         LOGGER.warn(ex.getMessage(), ex.getCause());\r
-        return makeErrorResponse(ex.getRequestId(), ex.getErrorResponse(),\r
-            ex.getErrorResponse().getResponseCode().getStatusCode());\r
+        final var requestId = req.getHeader(CommonRestController.REQUEST_ID_NAME);\r
+        final var status = ex.getErrorResponse().getResponseCode().getStatusCode();\r
+        return CommonRestController.addLoggingHeaders(\r
+            CommonRestController.addVersionControlHeaders(ResponseEntity.status(status)),\r
+            requestId != null ? UUID.fromString(requestId) : ex.getRequestId()).body(ex.getErrorResponse());\r
     }\r
 }
\ No newline at end of file
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.api.main.rest.provider;
+package org.onap.policy.api.main.rest.provider.healthcheck;
 
 import lombok.RequiredArgsConstructor;
-import org.onap.policy.api.main.rest.PolicyFetchMode;
 import org.onap.policy.api.main.service.ToscaServiceTemplateService;
 import org.onap.policy.common.endpoints.report.HealthCheckReport;
 import org.onap.policy.common.utils.network.NetworkUtil;
-import org.onap.policy.models.base.PfModelException;
 import org.onap.policy.models.base.PfModelRuntimeException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,7 +21,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.api.main.rest;
+package org.onap.policy.api.main.rest.provider.statistics;
 
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.api.main.rest.provider;
+package org.onap.policy.api.main.rest.provider.statistics;
 
 import lombok.RequiredArgsConstructor;
-import org.onap.policy.api.main.rest.StatisticsReport;
 import org.springframework.stereotype.Service;
 
 /**
@@ -21,7 +21,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.policy.api.main.rest;
+package org.onap.policy.api.main.rest.provider.statistics;
 
 import lombok.Getter;
 import lombok.Setter;
@@ -52,4 +52,4 @@ public class StatisticsReport {
     protected long policyTypeGetFailureCount;
     protected long policyTypePostSuccessCount;
     protected long policyTypePostFailureCount;
-}
+}
\ No newline at end of file
index a22a0a0..bf35d27 100644 (file)
@@ -40,6 +40,8 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.onap.policy.api.main.PolicyApiApplication;
+import org.onap.policy.api.main.rest.provider.statistics.ApiStatisticsManager;
+import org.onap.policy.api.main.rest.provider.statistics.StatisticsReport;
 import org.onap.policy.api.main.rest.utils.CommonTestRestController;
 import org.onap.policy.common.endpoints.report.HealthCheckReport;
 import org.onap.policy.common.utils.coder.StandardCoder;
index 48484d6..f343e13 100644 (file)
@@ -26,10 +26,10 @@ package org.onap.policy.api.main.rest;
 
 import static org.junit.Assert.assertEquals;
 
-import lombok.RequiredArgsConstructor;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.onap.policy.api.main.PolicyApiApplication;
+import org.onap.policy.api.main.rest.provider.statistics.ApiStatisticsManager;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.ActiveProfiles;
index 01d5a9c..664c959 100644 (file)
@@ -29,6 +29,7 @@ import com.openpojo.validation.rule.impl.SetterMustExistRule;
 import com.openpojo.validation.test.impl.GetterTester;
 import com.openpojo.validation.test.impl.SetterTester;
 import org.junit.Test;
+import org.onap.policy.api.main.rest.provider.statistics.StatisticsReport;
 import org.onap.policy.common.utils.test.ToStringTester;
 
 /**
@@ -45,4 +46,4 @@ public class TestStatisticsReport {
         validator.validate(StatisticsReport.class.getPackage().getName(),
                 new FilterClassName(StatisticsReport.class.getName()));
     }
-}
+}
\ No newline at end of file