summaryrefslogtreecommitdiffstats
path: root/aai-resources/src/main/java
diff options
context:
space:
mode:
authorRaghav Kataria <raghav.kataria@bell.ca>2022-04-01 10:22:58 +0000
committerRommel Pawar <rommel.pawar@bell.ca>2022-09-21 13:03:25 -0700
commit5b383326540f84c17806831ed299c4c9234db8e1 (patch)
treeeec0ba370cd3c63b094dfbd3b14404c1e792370f /aai-resources/src/main/java
parent11c5d21682b7c27cd72949a8cdf73ecabc24d018 (diff)
Add aai-resources healthcheck based on Cassandra DB Healthcheck
Issue-ID: AAI-3528 Signed-off-by: Rommel Pawar <rommel.pawar@bell.ca> Change-Id: I21022bd81c5cd9eed2dd841f96812855adebe528
Diffstat (limited to 'aai-resources/src/main/java')
-rw-r--r--aai-resources/src/main/java/org/onap/aai/ResourcesApp.java13
-rw-r--r--aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java200
-rw-r--r--aai-resources/src/main/java/org/onap/aai/tasks/AaiGraphChecker.java208
3 files changed, 331 insertions, 90 deletions
diff --git a/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java b/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java
index 6377af8..419fee5 100644
--- a/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java
+++ b/aai-resources/src/main/java/org/onap/aai/ResourcesApp.java
@@ -1,4 +1,4 @@
-/**
+/*
* ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
@@ -17,8 +17,11 @@
* limitations under the License.
* ============LICENSE_END=========================================================
*/
+
package org.onap.aai;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.onap.aai.aailog.logs.AaiDebugLog;
import org.onap.aai.config.SpringContextAware;
@@ -32,7 +35,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
@@ -40,9 +42,6 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-
@SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
@@ -87,8 +86,8 @@ public class ResourcesApp {
@Autowired
private SpringContextAware loaderFactory;
-
-
+
+
@PostConstruct
private void init() throws AAIException {
System.setProperty("org.onap.aai.serverStarted", "false");
diff --git a/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java
index b72d113..8b5eef6 100644
--- a/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java
+++ b/aai-resources/src/main/java/org/onap/aai/rest/util/EchoResponse.java
@@ -1,4 +1,4 @@
-/**
+/*
* ============LICENSE_START=======================================================
* org.onap.aai
* ================================================================================
@@ -8,7 +8,7 @@
* 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
+ * 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,
@@ -17,12 +17,11 @@
* limitations under the License.
* ============LICENSE_END=========================================================
*/
-package org.onap.aai.rest.util;
-import org.onap.aai.exceptions.AAIException;
-import org.onap.aai.logging.ErrorLogHelper;
-import org.onap.aai.restcore.RESTAPI;
+package org.onap.aai.rest.util;
+import java.util.ArrayList;
+import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -33,88 +32,123 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.HashMap;
+import org.apache.commons.lang3.BooleanUtils;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.onap.aai.restcore.RESTAPI;
+import org.onap.aai.tasks.AaiGraphChecker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
/**
* The Class EchoResponse.
*/
+@Component
@Path("/util")
public class EchoResponse extends RESTAPI {
-
- protected static String authPolicyFunctionName = "util";
-
- public static final String echoPath = "/util/echo";
-
- /**
- * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients.
- * If there is a query string, a transaction gets logged into hbase, proving the application is connected to the data store.
- * If there is no query string, no transacction logging is done to hbase.
- *
- * @param headers the headers
- * @param req the req
- * @param myAction if exists will cause transaction to be logged to hbase
- * @return the response
- */
- @GET
- @Produces( { MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
- @Path("/echo")
- public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req,
- @QueryParam("action") String myAction) {
- Response response = null;
-
- AAIException ex = null;
- String fromAppId = null;
- String transId = null;
-
- try {
- fromAppId = getFromAppId(headers );
- transId = getTransId(headers);
- } catch (AAIException e) {
- ArrayList<String> templateVars = new ArrayList<String>();
- templateVars.add("PUT uebProvider");
- templateVars.add("addTopic");
- return Response
- .status(e.getErrorObject().getHTTPResponseCode())
- .entity(ErrorLogHelper.getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars))
- .build();
- }
-
- try {
-
- HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<AAIException, ArrayList<String>>();
-
- ArrayList<String> templateVars = new ArrayList<String>();
- templateVars.add(fromAppId);
- templateVars.add(transId);
-
- exceptionList.put(new AAIException("AAI_0002", "OK"), templateVars);
-
- response = Response.status(Status.OK)
- .entity(ErrorLogHelper.getRESTAPIInfoResponse(
- headers.getAcceptableMediaTypes(), exceptionList))
- .build();
-
- } catch (Exception e) {
- ex = new AAIException("AAI_4000", e);
- ArrayList<String> templateVars = new ArrayList<String>();
- templateVars.add(Action.GET.name());
- templateVars.add(fromAppId +" "+transId);
-
- response = Response
- .status(Status.INTERNAL_SERVER_ERROR)
- .entity(ErrorLogHelper.getRESTAPIErrorResponse(
- headers.getAcceptableMediaTypes(), ex,
- templateVars)).build();
-
- } finally {
- if (ex != null) {
- ErrorLogHelper.logException(ex);
- }
-
- }
-
- return response;
- }
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EchoResponse.class);
+
+ private static final String CHECK_DB_STATUS_ACTION = "checkDB";
+
+ private static final String CHECK_DB_STATUS_NOW_ACTION = "checkDBNow";
+
+ private final AaiGraphChecker aaiGraphChecker;
+
+ public EchoResponse(AaiGraphChecker aaiGraphChecker) {
+ this.aaiGraphChecker = aaiGraphChecker;
+ }
+
+ /**
+ * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients.
+ * If there is a query string, a transaction gets logged into hbase, proving the application is connected to the
+ * data store.
+ * If there is no query string, no transacction logging is done to hbase.
+ *
+ * @param headers the headers
+ * @param req the req
+ * @param myAction if exists will cause transaction to be logged to hbase
+ * @return the response
+ */
+ @GET
+ @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+ @Path("/echo")
+ public Response echoResult(
+ @Context HttpHeaders headers, @Context HttpServletRequest req, @QueryParam("action") String myAction) {
+
+ String fromAppId;
+ String transId;
+ try {
+ fromAppId = getFromAppId(headers);
+ transId = getTransId(headers);
+ } catch (AAIException aaiException) {
+ ArrayList<String> templateVars = new ArrayList<>();
+ templateVars.add("PUT uebProvider");
+ templateVars.add("addTopic");
+ LOGGER.error("Error while getting ids", aaiException);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ }
+
+ ArrayList<String> templateVars = new ArrayList<>();
+ templateVars.add(fromAppId);
+ templateVars.add(transId);
+
+ try {
+ if (CHECK_DB_STATUS_ACTION.equalsIgnoreCase(myAction) ||
+ CHECK_DB_STATUS_NOW_ACTION.equalsIgnoreCase(myAction)) {
+ validateDBStatus(myAction);
+ }
+ return generateSuccessResponse(headers, templateVars);
+
+ } catch (AAIException aaiException) {
+ LOGGER.error("Error while processing echo request ", aaiException);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ } catch (Exception exception) {
+ AAIException aaiException = new AAIException("AAI_4000", exception);
+ LOGGER.error("Error while generating echo response", exception);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ }
+ }
+
+ /**
+ * Validates if Janus Graph can process request using AAIGraphChecker.
+ *
+ * @param action expected input values 'checkDB' 'checkDBNow'
+ * @throws AAIException exception thrown if DB is not available
+ */
+ private void validateDBStatus(String action) throws AAIException {
+
+ Boolean dbAvailable = null;
+ if (CHECK_DB_STATUS_ACTION.equalsIgnoreCase(action)) {
+ dbAvailable = aaiGraphChecker.isAaiGraphDbAvailable(AaiGraphChecker.CheckerType.CACHED);
+ } else if (CHECK_DB_STATUS_NOW_ACTION.equalsIgnoreCase(action)) {
+ dbAvailable = aaiGraphChecker.isAaiGraphDbAvailable(AaiGraphChecker.CheckerType.ACTUAL);
+ } else {
+ LOGGER.error("Invalid check db action specified to generate echo response: '{}'", action);
+ }
+
+ if (BooleanUtils.isFalse(dbAvailable)) {
+ throw new AAIException("AAI_5105", "Error establishing a database connection");
+ }
+
+ }
+
+ private Response generateSuccessResponse(HttpHeaders headers, ArrayList<String> templateVariables) {
+ HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<>();
+ exceptionList.put(new AAIException("AAI_0002", "OK"), templateVariables);
+ return Response.status(Status.OK)
+ .entity(
+ ErrorLogHelper.getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList)).build();
+ }
+
+ private Response generateFailureResponse(HttpHeaders headers, ArrayList<String> templateVariables,
+ AAIException aaiException) {
+ return Response.status(aaiException.getErrorObject().getHTTPResponseCode())
+ .entity(
+ ErrorLogHelper.getRESTAPIErrorResponseWithLogging(
+ headers.getAcceptableMediaTypes(), aaiException, templateVariables))
+ .build();
+ }
}
diff --git a/aai-resources/src/main/java/org/onap/aai/tasks/AaiGraphChecker.java b/aai-resources/src/main/java/org/onap/aai/tasks/AaiGraphChecker.java
new file mode 100644
index 0000000..d1ea478
--- /dev/null
+++ b/aai-resources/src/main/java/org/onap/aai/tasks/AaiGraphChecker.java
@@ -0,0 +1,208 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aai.tasks;
+
+import com.google.common.collect.Iterators;
+import java.util.Iterator;
+import java.util.Timer;
+import java.util.TimerTask;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.Validate;
+import org.janusgraph.core.JanusGraphException;
+import org.janusgraph.core.JanusGraphTransaction;
+import org.janusgraph.core.JanusGraphVertex;
+import org.onap.aai.dbmap.AAIGraph;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.onap.aai.util.AAIConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+/**
+ * Singleton class responsible to check that AAI service is able to connect to its back-end database.
+ * The check can run as a scheduled task or on demand.
+ */
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+public class AaiGraphChecker extends TimerTask {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AaiGraphChecker.class);
+
+ // Default indicator to enable or disable scheduled task
+ private static final String DEFAULT_SCHEDULE_ENABLED_VALUE = "false";
+ // Default delay, in seconds, before the scheduled task is started, if enabled
+ private static final String DEFAULT_SCHEDULE_DELAY_VALUE = "5";
+ // Default period, in seconds, between two consecutive executions of the scheduled task, if enabled
+ private static final String DEFAULT_SCHEDULE_PERIOD_VALUE = "60";
+
+ // Database availability cached indicator
+ private volatile Boolean isAaiGraphDbAvailableCache = null;
+
+ private Timer timer = null;
+
+ /**
+ * Enumeration of check type that can be made.
+ */
+ public enum CheckerType {
+ ACTUAL,
+ CACHED
+ }
+
+ private AaiGraphChecker() {
+ }
+
+ @PostConstruct
+ private void setupTimer() {
+
+ boolean scheduleEnabled =
+ Boolean.parseBoolean(
+ getConfigurationValueOrDefault(
+ "aai.graph.checker.task.enabled", DEFAULT_SCHEDULE_ENABLED_VALUE));
+ long scheduleDelay =
+ Long.parseLong(
+ getConfigurationValueOrDefault(
+ "aai.graph.checker.task.delay", DEFAULT_SCHEDULE_DELAY_VALUE));
+ long schedulePeriod =
+ Long.parseLong(
+ getConfigurationValueOrDefault(
+ "aai.graph.checker.task.period", DEFAULT_SCHEDULE_PERIOD_VALUE));
+ LOGGER.debug(
+ "Setting up AaiGraphChecker with scheduleEnabled={}, scheduleDelay={}, schedulePeriod={} ",
+ scheduleEnabled, scheduleDelay, schedulePeriod);
+
+ if (scheduleEnabled) {
+ timer = new Timer();
+ timer.schedule(this, scheduleDelay * 1000, schedulePeriod * 1000);
+ }
+ }
+
+ @PreDestroy
+ private void tearDownTimer() {
+ LOGGER.debug("Tear down AaiGraphChecker");
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ }
+ }
+
+ @Override
+ public void run() {
+ isAaiGraphDbAvailable(CheckerType.ACTUAL);
+ }
+
+ /**
+ * Clear database availability cached indicator.
+ */
+ public void clearDbAvailabilityCachedIndicator() {
+ isAaiGraphDbAvailableCache = null;
+ }
+
+ /**
+ * Indicate if AAI Graph database is available either from actual db connection or from cached property state.
+ * @param checkerType the type of check to be made (actual or cached). Null is not supported.
+ * @return
+ * <li>true, if database is available</li>
+ * <li>false, if database is NOT available</li>
+ * <li>null, if database availability can not be determined</li>
+ */
+ public Boolean isAaiGraphDbAvailable(CheckerType checkerType) {
+ Validate.notNull(checkerType);
+ if (CheckerType.ACTUAL.equals(checkerType)) {
+ isAaiGraphDbAvailableCache = isAaiGraphDbAvailableActual();
+ }
+ logDbState(checkerType);
+ return isAaiGraphDbAvailableCache;
+ }
+
+ private Boolean isAaiGraphDbAvailableActual() {
+ Boolean dbAvailable;
+ JanusGraphTransaction transaction = null;
+ try {
+ transaction = AAIGraph.getInstance().getGraph().newTransaction();
+ final Iterator<JanusGraphVertex> vertexIterator = transaction.query().limit(1).vertices().iterator();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Number of vertices retrieved while checking db: {}", Iterators.size(vertexIterator));
+ }
+ vertexIterator.hasNext();
+ LOGGER.debug("Actual database availability is true");
+ dbAvailable = Boolean.TRUE;
+ } catch (JanusGraphException e) {
+ String message = "Actual database availability is false (after JanusGraph exception)";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = Boolean.FALSE;
+ } catch (Error e) {
+ // Following error occurs when aai resources is starting:
+ // - UnsatisfiedLinkError (for org.onap.aai.dbmap.AAIGraph$Helper instantiation)
+ // Following errors are raised when aai resources is starting and cassandra is not running:
+ // - ExceptionInInitializerError
+ // - NoClassDefFoundError (definition for org.onap.aai.dbmap.AAIGraph$Helper is not found)
+ String message = "Actual database availability is false (after error)";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = Boolean.FALSE;
+ } catch (Exception e) {
+ String message = "Actual database availability can not be determined";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = null;
+ } finally {
+ if (transaction != null && !transaction.isClosed()) {
+ //check if transaction is open then close instead of flag
+ try {
+ transaction.rollback();
+ } catch (Exception e) {
+ String message = "Exception occurred while closing transaction";
+ LOGGER.error(message, e);
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ }
+ }
+ }
+ return dbAvailable;
+ }
+
+ private void logDbState(CheckerType type) {
+ if (BooleanUtils.isTrue(isAaiGraphDbAvailableCache)) {
+ LOGGER.debug("Database is available from {} check.", type);
+ } else if (BooleanUtils.isFalse(isAaiGraphDbAvailableCache)) {
+ LOGGER.error("Database is NOT available from {} check.", type);
+ } else {
+ LOGGER.error("Database availability is UNKNOWN from {} check.", type);
+ }
+ }
+
+ private String getConfigurationValueOrDefault(String property, String defaultValue) {
+ String result;
+ try {
+ result = AAIConfig.get(property);
+ } catch (AAIException e) {
+ LOGGER.error(
+ "Unable to get defined configuration value for '{}' property, then default '{}' value is used",
+ property, defaultValue);
+ result = defaultValue;
+ }
+ return result;
+ }
+
+}