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 com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.StringWriter;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
35 import java.util.Map.Entry;
36 import java.util.Properties;
38 import javax.ws.rs.core.MediaType;
39 import javax.xml.bind.JAXBContext;
40 import javax.xml.bind.Marshaller;
42 import org.apache.commons.lang.StringUtils;
43 import org.onap.aai.exceptions.AAIException;
44 import org.onap.aai.logging.LoggingContext.StatusCode;
45 import org.onap.aai.util.AAIConfig;
46 import org.onap.aai.util.AAIConstants;
47 import org.onap.aai.util.MapperUtil;
52 * This classes loads the application error properties file
53 * and provides a method that returns an ErrorObject
57 public class ErrorLogHelper {
59 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ErrorLogHelper.class);
60 private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject>();
65 } catch (IOException e) {
66 throw new RuntimeException("Failed to load error.properties file", e);
67 } catch (ErrorObjectFormatException e) {
68 throw new RuntimeException("Failed to parse error.properties file", e);
75 * @throws ErrorObjectFormatException
76 * @throws Exception the exception
78 public static void loadProperties() throws IOException, ErrorObjectFormatException {
79 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
80 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
81 final Properties properties = new Properties();
86 try (final FileInputStream fis = new FileInputStream(filePath)) {
91 for (Entry<Object, Object> entry : properties.entrySet()) {
92 final String key = (String) entry.getKey();
93 final String value = (String) entry.getValue();
94 final String[] errorProperties = value.split(":");
96 if (errorProperties.length != 7)
97 throw new ErrorObjectFormatException();
99 final ErrorObject errorObject = new ErrorObject();
101 errorObject.setDisposition(errorProperties[0].trim());
102 errorObject.setCategory(errorProperties[1].trim());
103 errorObject.setSeverity(errorProperties[2].trim());
104 errorObject.setErrorCode(errorProperties[3].trim());
105 errorObject.setHTTPResponseCode(errorProperties[4].trim());
106 errorObject.setRESTErrorCode(errorProperties[5].trim());
107 errorObject.setErrorText(errorProperties[6].trim());
109 ERROR_OBJECTS.put(key, errorObject);
114 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
116 * @param key The key for the error in the error.properties file
117 * @throws IOException
118 * @throws ErrorObjectNotFoundException
119 * @throws ErrorObjectFormatException
121 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
124 throw new IllegalArgumentException("Key cannot be null");
126 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
128 if (errorObject == null) {
129 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
130 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
137 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
138 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
139 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
140 * If no error object is embedded in the AAIException, one will be created using the error object from the
143 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
144 * @param variables optional list of variables to flesh out text in error string
145 * @return appropriately formatted JSON response per the REST API spec.
146 * @throws ErrorObjectFormatException
147 * @throws ErrorObjectNotFoundException
148 * @throws IOException
151 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
152 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
153 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
155 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
159 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
160 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
161 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
162 * If no error object is embedded in the AAIException, one will be created using the error object from the
165 * @param acceptHeadersOrig the accept headers orig
166 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
167 * @param variables optional list of variables to flesh out text in error string
168 * @return appropriately formatted JSON response per the REST API spec.
169 * @throws ErrorObjectFormatException
170 * @throws ErrorObjectNotFoundException
171 * @throws IOException
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 (AAIException 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
353 * @param logline the logline
354 * @return the RESTAPI error response with logging
355 * @throws ErrorObjectFormatException
356 * @throws ErrorObjectNotFoundException
357 * @throws IOException
359 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are,
360 ArrayList<String> variables) {
361 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
363 LOGGER.error(are.getMessage() + " " + LogFormatTools.getStackTop(are));
369 * Gets the RESTAPI info response.
371 * @param acceptHeaders the accept headers
372 * @param areList the are list
373 * @return the RESTAPI info response
374 * @throws ErrorObjectFormatException
375 * @throws ErrorObjectNotFoundException
376 * @throws IOException
378 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders,
379 HashMap<AAIException, ArrayList<String>> areList) {
381 Object respObj = null;
383 org.onap.aai.domain.restResponseInfo.ObjectFactory factory =
384 new org.onap.aai.domain.restResponseInfo.ObjectFactory();
385 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
386 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages =
387 factory.createInfoResponseMessages();
388 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
390 while (it.hasNext()) {
391 Entry<AAIException, ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>) it.next();
392 AAIException are = pair.getKey();
393 ArrayList<String> variables = pair.getValue();
395 StringBuilder text = new StringBuilder();
397 ErrorObject eo = are.getErrorObject();
399 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
400 ErrorObject restErrorObject;
402 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + String.format("%04d", restErrorCode));
403 } catch (ErrorObjectNotFoundException e) {
404 restErrorObject = eo;
406 text.append(restErrorObject.getErrorText());
408 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
409 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
410 // error, are ordered based on the error string.
411 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
412 text.append(" (msg=%").append(localDataIndex + 1).append(") (rc=%").append(localDataIndex + 2).append(")");
414 if (variables == null) {
415 variables = new ArrayList<String>();
418 if (variables.size() < localDataIndex) {
419 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
420 while (variables.size() < localDataIndex) {
421 variables.add("null");
425 // This will put the error code and error text into the right positions
426 if (are.getMessage() == null) {
427 variables.add(localDataIndex++, eo.getErrorText());
429 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
431 variables.add(localDataIndex, eo.getErrorCodeString());
434 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage =
435 factory.createInfoResponseMessagesResponseMessage();
436 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables =
437 factory.createInfoResponseMessagesResponseMessageVariables();
439 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
440 responseMessage.setText(text.toString());
441 for (int i = 0; i < variables.size(); i++) {
442 infovariables.getVariable().add(variables.get(i));
445 responseMessage.setVariables(infovariables);
446 responseMessages.getResponseMessage().add(responseMessage);
448 } catch (Exception ex) {
449 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
454 info.setResponseMessages(responseMessages);
455 respObj = (Object) info;
461 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
462 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
463 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
464 * If no error object is embedded in the AAIException, one will be created using the error object from the
467 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
468 * @param variables optional list of variables to flesh out text in error string
469 * @return appropriately formatted JSON response per the REST API spec.
470 * @throws ErrorObjectFormatException
471 * @throws ErrorObjectNotFoundException
472 * @throws IOException
474 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
476 StringBuilder text = new StringBuilder();
477 String response = null;
478 JAXBContext context = null;
480 ErrorObject eo = are.getErrorObject();
482 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
483 ErrorObject restErrorObject;
485 restErrorObject = ErrorLogHelper.getErrorObject("AAI_" + restErrorCode);
486 } catch (ErrorObjectNotFoundException e) {
487 restErrorObject = eo;
490 text.append(restErrorObject.getErrorText());
492 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
493 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
494 // error, are ordered based on the error string.
495 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
496 text.append(" (msg=%").append(localDataIndex + 1).append(") (ec=%").append(localDataIndex + 2).append(")");
498 if (variables == null) {
499 variables = new ArrayList<String>();
502 if (variables.size() < localDataIndex) {
503 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
504 while (variables.size() < localDataIndex) {
505 variables.add("null");
509 // This will put the error code and error text into the right positions
510 if (are.getMessage() == null) {
511 variables.add(localDataIndex++, eo.getErrorText());
513 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
515 variables.add(localDataIndex, eo.getErrorCodeString());
518 if (eo.getCategory().equals("1")) {
520 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
521 Marshaller m = context.createMarshaller();
522 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
523 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
525 org.onap.aai.domain.restPolicyException.ObjectFactory factory =
526 new org.onap.aai.domain.restPolicyException.ObjectFactory();
527 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
528 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError =
529 factory.createFaultRequestError();
530 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException =
531 factory.createFaultRequestErrorPolicyException();
532 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables =
533 factory.createFaultRequestErrorPolicyExceptionVariables();
535 policyException.setMessageId("POL" + eo.getRESTErrorCode());
536 policyException.setText(text.toString());
537 for (int i = 0; i < variables.size(); i++) {
538 polvariables.getVariable().add(variables.get(i));
540 policyException.setVariables(polvariables);
541 requestError.setPolicyException(policyException);
542 fault.setRequestError(requestError);
544 StringWriter sw = new StringWriter();
545 m.marshal(fault, sw);
547 response = sw.toString();
551 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
552 Marshaller m = context.createMarshaller();
553 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
554 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
556 org.onap.aai.domain.restServiceException.ObjectFactory factory =
557 new org.onap.aai.domain.restServiceException.ObjectFactory();
558 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
559 org.onap.aai.domain.restServiceException.Fault.RequestError requestError =
560 factory.createFaultRequestError();
561 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException =
562 factory.createFaultRequestErrorServiceException();
563 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables =
564 factory.createFaultRequestErrorServiceExceptionVariables();
565 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
566 serviceException.setText(text.toString());
567 for (int i = 0; i < variables.size(); i++) {
568 svcvariables.getVariable().add(variables.get(i));
570 serviceException.setVariables(svcvariables);
571 requestError.setServiceException(serviceException);
572 fault.setRequestError(requestError);
574 StringWriter sw = new StringWriter();
575 m.marshal(fault, sw);
577 response = sw.toString();
580 } catch (Exception ex) {
581 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "
587 public static void logException(AAIException e) {
588 final ErrorObject errorObject = e.getErrorObject();
590 // MDC.put("severity", errorObject.getSeverity()); //TODO Use LoggingContext.severity(int severity)
591 String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
593 if (!AAIConfig.isEmpty(severityCode)) {
594 int sevCode = Integer.parseInt(severityCode);
595 if (sevCode > 0 && sevCode <= 3) {
596 LoggingContext.severity(sevCode);
599 String stackTrace = "";
601 stackTrace = LogFormatTools.getStackTop(e);
602 } catch (Exception a) {
605 final String errorMessage = new StringBuilder().append(errorObject.getErrorText()).append(":")
606 .append(errorObject.getRESTErrorCode()).append(":").append(errorObject.getHTTPResponseCode())
607 .append(":").append(e.getMessage()).toString().replaceAll("\\n", "^");
609 LoggingContext.responseCode(Integer.toString(errorObject.getHTTPResponseCode().getStatusCode()));
610 LoggingContext.responseDescription(errorMessage);
611 LoggingContext.statusCode(StatusCode.ERROR);
613 final String details =
614 new StringBuilder().append(errorObject.getErrorCodeString()).append(" ").append(stackTrace).toString();
616 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
617 LOGGER.warn(details);
618 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
619 LOGGER.error(details);
620 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
621 LOGGER.error(details);
622 else if (errorObject.getSeverity().equals("INFO"))
623 LOGGER.info(details);
626 public static void logError(String code) {
630 public static void logError(String code, String message) {
631 logException(new AAIException(code, message));