2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.aai.logging;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.StringWriter;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map.Entry;
32 import java.util.Properties;
34 import javax.ws.rs.core.MediaType;
35 import javax.xml.bind.JAXBContext;
36 import javax.xml.bind.Marshaller;
38 import org.apache.commons.lang3.StringUtils;
39 import org.onap.aai.exceptions.AAIException;
40 import org.onap.aai.util.AAIConstants;
41 import org.onap.aai.util.MapperUtil;
42 import org.onap.logging.filter.base.Constants;
43 import org.onap.logging.filter.base.MDCSetup;
44 import org.onap.logging.ref.slf4j.ONAPLogConstants;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
51 * This classes loads the application error properties file
52 * and provides a method that returns an ErrorObject
56 public class ErrorLogHelper {
57 private static final Logger LOGGER = LoggerFactory.getLogger(ErrorLogHelper.class);
58 private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject>();
63 } catch (IOException e) {
64 throw new RuntimeException("Failed to load error.properties file", e);
65 } catch (ErrorObjectFormatException e) {
66 throw new RuntimeException("Failed to parse error.properties file", e);
73 * @throws IOException the exception
74 * @throws ErrorObjectFormatException
76 public static void loadProperties() throws IOException, ErrorObjectFormatException {
77 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
78 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("error.properties");
79 final Properties properties = new Properties();
81 try (final FileInputStream fis = new FileInputStream(filePath)) {
82 LOGGER.info("Found the error.properties in the following location: {}",
83 AAIConstants.AAI_HOME_ETC_APP_PROPERTIES);
85 } catch (Exception ex) {
86 LOGGER.info("Unable to find the error.properties from filesystem so using file in jar");
90 LOGGER.error("Expected to find the error.properties in the jar but unable to find it");
94 for (Entry<Object, Object> entry : properties.entrySet()) {
95 final String key = (String) entry.getKey();
96 final String value = (String) entry.getValue();
97 final String[] errorProperties = value.split(":");
99 if (errorProperties.length < 7)
100 throw new ErrorObjectFormatException();
102 final ErrorObject errorObject = new ErrorObject();
104 errorObject.setDisposition(errorProperties[0].trim());
105 errorObject.setCategory(errorProperties[1].trim());
106 errorObject.setSeverity(errorProperties[2].trim());
107 errorObject.setErrorCode(errorProperties[3].trim());
108 errorObject.setHTTPResponseCode(errorProperties[4].trim());
109 errorObject.setRESTErrorCode(errorProperties[5].trim());
110 errorObject.setErrorText(errorProperties[6].trim());
111 if (errorProperties.length > 7) {
112 errorObject.setAaiElsErrorCode(errorProperties[7].trim());
115 ERROR_OBJECTS.put(key, errorObject);
120 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
122 * @param code for the error in the error.properties file
123 * @throws IOException
124 * @throws ErrorObjectNotFoundException
126 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
129 throw new IllegalArgumentException("Key cannot be null");
131 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
133 if (errorObject == null) {
134 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
135 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
142 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
143 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
144 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
145 * If no error object is embedded in the AAIException, one will be created using the error object from the
148 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
149 * @param variables optional list of variables to flesh out text in error string
150 * @return appropriately formatted JSON response per the REST API spec.
151 * @throws IOException
154 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
155 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
156 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
158 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
162 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
163 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
164 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
165 * If no error object is embedded in the AAIException, one will be created using the error object from the
168 * @param acceptHeadersOrig the accept headers orig
169 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
170 * @param variables optional list of variables to flesh out text in error string
171 * @return appropriately formatted JSON response per the REST API spec.
173 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are,
174 ArrayList<String> variables) {
176 StringBuilder text = new StringBuilder();
177 String response = null;
179 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
180 // we might have an exception but no accept header, so we'll set default to JSON
181 boolean foundValidAcceptHeader = false;
182 for (MediaType mt : acceptHeadersOrig) {
183 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) || MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
184 acceptHeaders.add(mt);
185 foundValidAcceptHeader = true;
188 if (foundValidAcceptHeader == false) {
189 // override the exception, client needs to set an appropriate Accept header
190 are = new AAIException("AAI_4014");
191 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
194 final ErrorObject eo = are.getErrorObject();
196 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
198 ErrorObject restErrorObject;
201 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
202 } catch (ErrorObjectNotFoundException e) {
203 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object "
204 + eo.getErrorCode() + "; using AAI_" + restErrorCode);
205 restErrorObject = eo;
208 text.append(restErrorObject.getErrorText());
210 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
211 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
212 // error, are ordered based on the error string.
213 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
214 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
216 if (variables == null) {
217 variables = new ArrayList<String>();
220 if (variables.size() < localDataIndex) {
221 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
222 while (variables.size() < localDataIndex) {
223 variables.add("null");
227 // This will put the error code and error text into the right positions
228 if (are.getMessage() == null || are.getMessage().length() == 0) {
229 variables.add(localDataIndex++, eo.getErrorText());
231 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
233 variables.add(localDataIndex, eo.getErrorCodeString());
235 for (MediaType mediaType : acceptHeaders) {
236 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) {
237 JAXBContext context = null;
239 if (eo.getCategory().equals("1")) {
241 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
242 Marshaller m = context.createMarshaller();
243 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
244 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
246 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
247 new org.onap.aai.domain.restPolicyException.ObjectFactory();
248 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
249 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
250 factory.createFaultRequestError();
251 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
252 factory.createFaultRequestErrorPolicyException();
253 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
254 factory.createFaultRequestErrorPolicyExceptionVariables();
256 policyException.setMessageId("POL" + eo.getRESTErrorCode());
257 policyException.setText(text.toString());
258 for (int i = 0; i < variables.size(); i++) {
259 polvariables.getVariable().add(variables.get(i));
261 policyException.setVariables(polvariables);
262 requestError.setPolicyException(policyException);
263 fault.setRequestError(requestError);
265 StringWriter sw = new StringWriter();
266 m.marshal(fault, sw);
268 response = sw.toString();
272 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
273 Marshaller m = context.createMarshaller();
274 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
275 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
277 org.onap.aai.domain.restServiceException.ObjectFactory factory =
278 new org.onap.aai.domain.restServiceException.ObjectFactory();
279 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
280 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
281 factory.createFaultRequestError();
282 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
283 factory.createFaultRequestErrorServiceException();
284 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
285 factory.createFaultRequestErrorServiceExceptionVariables();
286 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
287 serviceException.setText(text.toString());
288 for (int i = 0; i < variables.size(); i++) {
289 svcvariables.getVariable().add(variables.get(i));
291 serviceException.setVariables(svcvariables);
292 requestError.setServiceException(serviceException);
293 fault.setRequestError(requestError);
295 StringWriter sw = new StringWriter();
296 m.marshal(fault, sw);
298 response = sw.toString();
301 } catch (Exception ex) {
303 "We were unable to create a rest exception to return on an API because of a parsing error "
308 if (eo.getCategory().equals("1")) {
309 org.onap.aai.domain.restPolicyException.RESTResponse restresp =
310 new org.onap.aai.domain.restPolicyException.RESTResponse();
311 org.onap.aai.domain.restPolicyException.RequestError reqerr =
312 new org.onap.aai.domain.restPolicyException.RequestError();
313 org.onap.aai.domain.restPolicyException.PolicyException polexc =
314 new org.onap.aai.domain.restPolicyException.PolicyException();
315 polexc.setMessageId("POL" + eo.getRESTErrorCode());
316 polexc.setText(text.toString());
317 polexc.setVariables(variables);
318 reqerr.setPolicyException(polexc);
319 restresp.setRequestError(reqerr);
320 response = (MapperUtil.writeAsJSONString((Object) restresp));
323 org.onap.aai.domain.restServiceException.RESTResponse restresp =
324 new org.onap.aai.domain.restServiceException.RESTResponse();
325 org.onap.aai.domain.restServiceException.RequestError reqerr =
326 new org.onap.aai.domain.restServiceException.RequestError();
327 org.onap.aai.domain.restServiceException.ServiceException svcexc =
328 new org.onap.aai.domain.restServiceException.ServiceException();
329 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
330 svcexc.setText(text.toString());
331 svcexc.setVariables(variables);
332 reqerr.setServiceException(svcexc);
333 restresp.setRequestError(reqerr);
334 response = (MapperUtil.writeAsJSONString((Object) restresp));
336 } catch (Exception ex) {
338 "We were unable to create a rest exception to return on an API because of a parsing error "
348 * Gets the RESTAPI error response with logging.
350 * @param acceptHeadersOrig the accept headers orig
352 * @param variables the variables
354 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are,
355 ArrayList<String> variables) {
356 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
362 * Gets the RESTAPI info response.
364 * @param acceptHeaders the accept headers
365 * @param areList the are list
366 * @return the RESTAPI info response
368 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders,
369 HashMap<AAIException, ArrayList<String>> areList) {
371 Object respObj = null;
373 org.onap.aai.domain.restResponseInfo.ObjectFactory factory =
374 new org.onap.aai.domain.restResponseInfo.ObjectFactory();
375 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
376 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages =
377 factory.createInfoResponseMessages();
378 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
380 while (it.hasNext()) {
381 Entry<AAIException, ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>) it.next();
382 AAIException are = pair.getKey();
383 ArrayList<String> variables = pair.getValue();
385 StringBuilder text = new StringBuilder();
387 ErrorObject eo = are.getErrorObject();
389 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
390 ErrorObject restErrorObject;
392 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + String.format("%04d", restErrorCode));
393 } catch (ErrorObjectNotFoundException e) {
394 restErrorObject = eo;
396 text.append(restErrorObject.getErrorText());
398 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
399 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
400 // error, are ordered based on the error string.
401 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
402 text.append(" (msg=%").append(localDataIndex + 1).append(") (rc=%").append(localDataIndex + 2).append(")");
404 if (variables == null) {
405 variables = new ArrayList<String>();
408 if (variables.size() < localDataIndex) {
409 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
410 while (variables.size() < localDataIndex) {
411 variables.add("null");
415 // This will put the error code and error text into the right positions
416 if (are.getMessage() == null) {
417 variables.add(localDataIndex++, eo.getErrorText());
419 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
421 variables.add(localDataIndex, eo.getErrorCodeString());
424 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage =
425 factory.createInfoResponseMessagesResponseMessage();
426 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables =
427 factory.createInfoResponseMessagesResponseMessageVariables();
429 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
430 responseMessage.setText(text.toString());
431 for (int i = 0; i < variables.size(); i++) {
432 infovariables.getVariable().add(variables.get(i));
435 responseMessage.setVariables(infovariables);
436 responseMessages.getResponseMessage().add(responseMessage);
438 } catch (Exception ex) {
439 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
444 info.setResponseMessages(responseMessages);
445 respObj = (Object) info;
451 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
452 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
453 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
454 * If no error object is embedded in the AAIException, one will be created using the error object from the
457 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
458 * @param variables optional list of variables to flesh out text in error string
459 * @return appropriately formatted JSON response per the REST API spec.
461 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
463 StringBuilder text = new StringBuilder();
464 String response = null;
465 JAXBContext context = null;
467 ErrorObject eo = are.getErrorObject();
469 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
470 ErrorObject restErrorObject;
472 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
473 } catch (ErrorObjectNotFoundException e) {
474 restErrorObject = eo;
477 text.append(restErrorObject.getErrorText());
479 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
480 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
481 // error, are ordered based on the error string.
482 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
483 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
485 if (variables == null) {
486 variables = new ArrayList<String>();
489 if (variables.size() < localDataIndex) {
490 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
491 while (variables.size() < localDataIndex) {
492 variables.add("null");
496 // This will put the error code and error text into the right positions
497 if (are.getMessage() == null) {
498 variables.add(localDataIndex++, eo.getErrorText());
500 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
502 variables.add(localDataIndex, eo.getErrorCodeString());
505 if (eo.getCategory().equals("1")) {
507 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
508 Marshaller m = context.createMarshaller();
509 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
510 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
512 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
513 new org.onap.aai.domain.restPolicyException.ObjectFactory();
514 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
515 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
516 factory.createFaultRequestError();
517 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
518 factory.createFaultRequestErrorPolicyException();
519 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
520 factory.createFaultRequestErrorPolicyExceptionVariables();
522 policyException.setMessageId("POL" + eo.getRESTErrorCode());
523 policyException.setText(text.toString());
524 for (int i = 0; i < variables.size(); i++) {
525 polvariables.getVariable().add(variables.get(i));
527 policyException.setVariables(polvariables);
528 requestError.setPolicyException(policyException);
529 fault.setRequestError(requestError);
531 StringWriter sw = new StringWriter();
532 m.marshal(fault, sw);
534 response = sw.toString();
538 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
539 Marshaller m = context.createMarshaller();
540 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
541 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
543 org.onap.aai.domain.restServiceException.ObjectFactory factory =
544 new org.onap.aai.domain.restServiceException.ObjectFactory();
545 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
546 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
547 factory.createFaultRequestError();
548 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
549 factory.createFaultRequestErrorServiceException();
550 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
551 factory.createFaultRequestErrorServiceExceptionVariables();
552 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
553 serviceException.setText(text.toString());
554 for (int i = 0; i < variables.size(); i++) {
555 svcvariables.getVariable().add(variables.get(i));
557 serviceException.setVariables(svcvariables);
558 requestError.setServiceException(serviceException);
559 fault.setRequestError(requestError);
561 StringWriter sw = new StringWriter();
562 m.marshal(fault, sw);
564 response = sw.toString();
567 } catch (Exception ex) {
568 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
574 public static void logException(AAIException e) {
575 final ErrorObject errorObject = e.getErrorObject();
577 * String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
579 * Severify should be left empty per Logging Specification 2019.11
580 * if (!StringUtils.isEmpty(severityCode)) {
581 * int sevCode = Integer.parseInt(severityCode);
582 * if (sevCode > 0 && sevCode <= 3) {
583 * LoggingContext.severity(sevCode);
587 String stackTrace = "";
589 stackTrace = LogFormatTools.getStackTop(e);
590 } catch (Exception a) {
593 final String errorMessage = new StringBuilder().append(errorObject.getErrorText()).append(":")
594 .append(errorObject.getRESTErrorCode()).append(":").append(errorObject.getHTTPResponseCode())
595 .append(":").append(e.getMessage()).toString().replaceAll("\\n", "^");
597 MDCSetup mdcSetup = new MDCSetup();
598 mdcSetup.setResponseStatusCode(errorObject.getHTTPResponseCode().getStatusCode());
599 mdcSetup.setErrorCode(Integer.parseInt(errorObject.getAaiElsErrorCode()));
600 String serviceName = MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME);
601 if (serviceName == null || serviceName.isEmpty()) {
602 MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, Constants.DefaultValues.UNKNOWN);
604 MDC.put(ONAPLogConstants.MDCs.ERROR_DESC, errorMessage);
605 final String details =
606 new StringBuilder().append(errorObject.getErrorCodeString()).append(" ").append(stackTrace).toString();
608 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
609 LOGGER.warn(details);
610 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
611 LOGGER.error(details);
612 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
613 LOGGER.error(details);
614 else if (errorObject.getSeverity().equals("INFO"))
615 LOGGER.info(details);
618 public static void logError(String code) {
622 public static void logError(String code, String message) {
623 logException(new AAIException(code, message));