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);
72 * @throws IOException the exception
73 * @throws ErrorObjectFormatException
75 public static void loadProperties() throws IOException, ErrorObjectFormatException {
76 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
77 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("error.properties");
78 final Properties properties = new Properties();
80 try (final FileInputStream fis = new FileInputStream(filePath)) {
81 LOGGER.info("Found the error.properties in the following location: {}", AAIConstants.AAI_HOME_ETC_APP_PROPERTIES);
83 } catch(Exception ex){
84 LOGGER.info("Unable to find the error.properties from filesystem so using file in jar");
88 LOGGER.error("Expected to find the error.properties in the jar but unable to find it");
92 for (Entry<Object, Object> entry : properties.entrySet()) {
93 final String key = (String) entry.getKey();
94 final String value = (String) entry.getValue();
95 final String[] errorProperties = value.split(":");
97 if (errorProperties.length < 7)
98 throw new ErrorObjectFormatException();
100 final ErrorObject errorObject = new ErrorObject();
102 errorObject.setDisposition(errorProperties[0].trim());
103 errorObject.setCategory(errorProperties[1].trim());
104 errorObject.setSeverity(errorProperties[2].trim());
105 errorObject.setErrorCode(errorProperties[3].trim());
106 errorObject.setHTTPResponseCode(errorProperties[4].trim());
107 errorObject.setRESTErrorCode(errorProperties[5].trim());
108 errorObject.setErrorText(errorProperties[6].trim());
109 if (errorProperties.length > 7) {
110 errorObject.setAaiElsErrorCode(errorProperties[7].trim());
113 ERROR_OBJECTS.put(key, errorObject);
118 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
120 * @param code for the error in the error.properties file
121 * @throws IOException
122 * @throws ErrorObjectNotFoundException
124 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
127 throw new IllegalArgumentException("Key cannot be null");
129 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
131 if (errorObject == null) {
132 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
133 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
140 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
141 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
142 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
143 * If no error object is embedded in the AAIException, one will be created using the error object from the
146 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
147 * @param variables optional list of variables to flesh out text in error string
148 * @return appropriately formatted JSON response per the REST API spec.
149 * @throws IOException
152 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
153 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
154 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
156 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
160 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
161 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
162 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
163 * If no error object is embedded in the AAIException, one will be created using the error object from the
166 * @param acceptHeadersOrig the accept headers orig
167 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
168 * @param variables optional list of variables to flesh out text in error string
169 * @return appropriately formatted JSON response per the REST API spec.
171 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are,
172 ArrayList<String> variables) {
174 StringBuilder text = new StringBuilder();
175 String response = null;
177 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
178 // we might have an exception but no accept header, so we'll set default to JSON
179 boolean foundValidAcceptHeader = false;
180 for (MediaType mt : acceptHeadersOrig) {
181 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) || MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
182 acceptHeaders.add(mt);
183 foundValidAcceptHeader = true;
186 if (foundValidAcceptHeader == false) {
187 // override the exception, client needs to set an appropriate Accept header
188 are = new AAIException("AAI_4014");
189 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
192 final ErrorObject eo = are.getErrorObject();
194 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
196 ErrorObject restErrorObject;
199 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
200 } catch (ErrorObjectNotFoundException e) {
201 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object "
202 + eo.getErrorCode() + "; using AAI_" + restErrorCode);
203 restErrorObject = eo;
206 text.append(restErrorObject.getErrorText());
208 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
209 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
210 // error, are ordered based on the error string.
211 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
212 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
214 if (variables == null) {
215 variables = new ArrayList<String>();
218 if (variables.size() < localDataIndex) {
219 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
220 while (variables.size() < localDataIndex) {
221 variables.add("null");
225 // This will put the error code and error text into the right positions
226 if (are.getMessage() == null || are.getMessage().length() == 0) {
227 variables.add(localDataIndex++, eo.getErrorText());
229 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
231 variables.add(localDataIndex, eo.getErrorCodeString());
233 for (MediaType mediaType : acceptHeaders) {
234 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) {
235 JAXBContext context = null;
237 if (eo.getCategory().equals("1")) {
239 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
240 Marshaller m = context.createMarshaller();
241 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
242 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
244 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
245 new org.onap.aai.domain.restPolicyException.ObjectFactory();
246 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
247 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
248 factory.createFaultRequestError();
249 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
250 factory.createFaultRequestErrorPolicyException();
251 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
252 factory.createFaultRequestErrorPolicyExceptionVariables();
254 policyException.setMessageId("POL" + eo.getRESTErrorCode());
255 policyException.setText(text.toString());
256 for (int i = 0; i < variables.size(); i++) {
257 polvariables.getVariable().add(variables.get(i));
259 policyException.setVariables(polvariables);
260 requestError.setPolicyException(policyException);
261 fault.setRequestError(requestError);
263 StringWriter sw = new StringWriter();
264 m.marshal(fault, sw);
266 response = sw.toString();
270 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
271 Marshaller m = context.createMarshaller();
272 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
273 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
275 org.onap.aai.domain.restServiceException.ObjectFactory factory =
276 new org.onap.aai.domain.restServiceException.ObjectFactory();
277 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
278 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
279 factory.createFaultRequestError();
280 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
281 factory.createFaultRequestErrorServiceException();
282 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
283 factory.createFaultRequestErrorServiceExceptionVariables();
284 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
285 serviceException.setText(text.toString());
286 for (int i = 0; i < variables.size(); i++) {
287 svcvariables.getVariable().add(variables.get(i));
289 serviceException.setVariables(svcvariables);
290 requestError.setServiceException(serviceException);
291 fault.setRequestError(requestError);
293 StringWriter sw = new StringWriter();
294 m.marshal(fault, sw);
296 response = sw.toString();
299 } catch (Exception ex) {
301 "We were unable to create a rest exception to return on an API because of a parsing error "
306 if (eo.getCategory().equals("1")) {
307 org.onap.aai.domain.restPolicyException.RESTResponse restresp =
308 new org.onap.aai.domain.restPolicyException.RESTResponse();
309 org.onap.aai.domain.restPolicyException.RequestError reqerr =
310 new org.onap.aai.domain.restPolicyException.RequestError();
311 org.onap.aai.domain.restPolicyException.PolicyException polexc =
312 new org.onap.aai.domain.restPolicyException.PolicyException();
313 polexc.setMessageId("POL" + eo.getRESTErrorCode());
314 polexc.setText(text.toString());
315 polexc.setVariables(variables);
316 reqerr.setPolicyException(polexc);
317 restresp.setRequestError(reqerr);
318 response = (MapperUtil.writeAsJSONString((Object) restresp));
321 org.onap.aai.domain.restServiceException.RESTResponse restresp =
322 new org.onap.aai.domain.restServiceException.RESTResponse();
323 org.onap.aai.domain.restServiceException.RequestError reqerr =
324 new org.onap.aai.domain.restServiceException.RequestError();
325 org.onap.aai.domain.restServiceException.ServiceException svcexc =
326 new org.onap.aai.domain.restServiceException.ServiceException();
327 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
328 svcexc.setText(text.toString());
329 svcexc.setVariables(variables);
330 reqerr.setServiceException(svcexc);
331 restresp.setRequestError(reqerr);
332 response = (MapperUtil.writeAsJSONString((Object) restresp));
334 } catch (Exception ex) {
336 "We were unable to create a rest exception to return on an API because of a parsing error "
346 * Gets the RESTAPI error response with logging.
348 * @param acceptHeadersOrig the accept headers orig
350 * @param variables the variables
352 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are,
353 ArrayList<String> variables) {
354 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
360 * Gets the RESTAPI info response.
362 * @param acceptHeaders the accept headers
363 * @param areList the are list
364 * @return the RESTAPI info response
366 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders,
367 HashMap<AAIException, ArrayList<String>> areList) {
369 Object respObj = null;
371 org.onap.aai.domain.restResponseInfo.ObjectFactory factory =
372 new org.onap.aai.domain.restResponseInfo.ObjectFactory();
373 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
374 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages =
375 factory.createInfoResponseMessages();
376 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
378 while (it.hasNext()) {
379 Entry<AAIException, ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>) it.next();
380 AAIException are = pair.getKey();
381 ArrayList<String> variables = pair.getValue();
383 StringBuilder text = new StringBuilder();
385 ErrorObject eo = are.getErrorObject();
387 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
388 ErrorObject restErrorObject;
390 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + String.format("%04d", restErrorCode));
391 } catch (ErrorObjectNotFoundException e) {
392 restErrorObject = eo;
394 text.append(restErrorObject.getErrorText());
396 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
397 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
398 // error, are ordered based on the error string.
399 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
400 text.append(" (msg=%").append(localDataIndex + 1).append(") (rc=%").append(localDataIndex + 2).append(")");
402 if (variables == null) {
403 variables = new ArrayList<String>();
406 if (variables.size() < localDataIndex) {
407 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
408 while (variables.size() < localDataIndex) {
409 variables.add("null");
413 // This will put the error code and error text into the right positions
414 if (are.getMessage() == null) {
415 variables.add(localDataIndex++, eo.getErrorText());
417 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
419 variables.add(localDataIndex, eo.getErrorCodeString());
422 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage =
423 factory.createInfoResponseMessagesResponseMessage();
424 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables =
425 factory.createInfoResponseMessagesResponseMessageVariables();
427 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
428 responseMessage.setText(text.toString());
429 for (int i = 0; i < variables.size(); i++) {
430 infovariables.getVariable().add(variables.get(i));
433 responseMessage.setVariables(infovariables);
434 responseMessages.getResponseMessage().add(responseMessage);
436 } catch (Exception ex) {
437 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
442 info.setResponseMessages(responseMessages);
443 respObj = (Object) info;
449 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
450 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
451 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
452 * If no error object is embedded in the AAIException, one will be created using the error object from the
455 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
456 * @param variables optional list of variables to flesh out text in error string
457 * @return appropriately formatted JSON response per the REST API spec.
459 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
461 StringBuilder text = new StringBuilder();
462 String response = null;
463 JAXBContext context = null;
465 ErrorObject eo = are.getErrorObject();
467 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
468 ErrorObject restErrorObject;
470 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
471 } catch (ErrorObjectNotFoundException e) {
472 restErrorObject = eo;
475 text.append(restErrorObject.getErrorText());
477 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
478 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
479 // error, are ordered based on the error string.
480 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
481 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
483 if (variables == null) {
484 variables = new ArrayList<String>();
487 if (variables.size() < localDataIndex) {
488 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
489 while (variables.size() < localDataIndex) {
490 variables.add("null");
494 // This will put the error code and error text into the right positions
495 if (are.getMessage() == null) {
496 variables.add(localDataIndex++, eo.getErrorText());
498 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
500 variables.add(localDataIndex, eo.getErrorCodeString());
503 if (eo.getCategory().equals("1")) {
505 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
506 Marshaller m = context.createMarshaller();
507 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
508 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
510 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
511 new org.onap.aai.domain.restPolicyException.ObjectFactory();
512 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
513 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
514 factory.createFaultRequestError();
515 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
516 factory.createFaultRequestErrorPolicyException();
517 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
518 factory.createFaultRequestErrorPolicyExceptionVariables();
520 policyException.setMessageId("POL" + eo.getRESTErrorCode());
521 policyException.setText(text.toString());
522 for (int i = 0; i < variables.size(); i++) {
523 polvariables.getVariable().add(variables.get(i));
525 policyException.setVariables(polvariables);
526 requestError.setPolicyException(policyException);
527 fault.setRequestError(requestError);
529 StringWriter sw = new StringWriter();
530 m.marshal(fault, sw);
532 response = sw.toString();
536 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
537 Marshaller m = context.createMarshaller();
538 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
539 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
541 org.onap.aai.domain.restServiceException.ObjectFactory factory =
542 new org.onap.aai.domain.restServiceException.ObjectFactory();
543 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
544 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
545 factory.createFaultRequestError();
546 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
547 factory.createFaultRequestErrorServiceException();
548 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
549 factory.createFaultRequestErrorServiceExceptionVariables();
550 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
551 serviceException.setText(text.toString());
552 for (int i = 0; i < variables.size(); i++) {
553 svcvariables.getVariable().add(variables.get(i));
555 serviceException.setVariables(svcvariables);
556 requestError.setServiceException(serviceException);
557 fault.setRequestError(requestError);
559 StringWriter sw = new StringWriter();
560 m.marshal(fault, sw);
562 response = sw.toString();
565 } catch (Exception ex) {
566 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
572 public static void logException(AAIException e) {
573 final ErrorObject errorObject = e.getErrorObject();
575 String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
577 Severify should be left empty per Logging Specification 2019.11
578 if (!StringUtils.isEmpty(severityCode)) {
579 int sevCode = Integer.parseInt(severityCode);
580 if (sevCode > 0 && sevCode <= 3) {
581 LoggingContext.severity(sevCode);
585 String stackTrace = "";
587 stackTrace = LogFormatTools.getStackTop(e);
588 } catch (Exception a) {
591 final String errorMessage = new StringBuilder().append(errorObject.getErrorText()).append(":")
592 .append(errorObject.getRESTErrorCode()).append(":").append(errorObject.getHTTPResponseCode())
593 .append(":").append(e.getMessage()).toString().replaceAll("\\n", "^");
595 MDCSetup mdcSetup = new MDCSetup();
596 mdcSetup.setResponseStatusCode(errorObject.getHTTPResponseCode().getStatusCode());
597 mdcSetup.setErrorCode(Integer.parseInt(errorObject.getAaiElsErrorCode()));
598 String serviceName = MDC.get(ONAPLogConstants.MDCs.SERVICE_NAME);
599 if (serviceName == null || serviceName.isEmpty()) {
600 MDC.put(ONAPLogConstants.MDCs.SERVICE_NAME, Constants.DefaultValues.UNKNOWN);
602 MDC.put(ONAPLogConstants.MDCs.ERROR_DESC, errorMessage);
603 final String details =
604 new StringBuilder().append(errorObject.getErrorCodeString()).append(" ").append(stackTrace).toString();
606 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
607 LOGGER.warn(details);
608 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
609 LOGGER.error(details);
610 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
611 LOGGER.error(details);
612 else if (errorObject.getSeverity().equals("INFO"))
613 LOGGER.info(details);
616 public static void logError(String code) {
620 public static void logError(String code, String message) {
621 logException(new AAIException(code, message));