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=========================================================
20 package org.onap.aai.logging;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.StringWriter;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 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.lang.StringUtils;
41 import org.onap.aai.exceptions.AAIException;
42 import org.onap.aai.logging.LoggingContext.StatusCode;
43 import org.onap.aai.util.AAIConfig;
44 import org.onap.aai.util.AAIConstants;
45 import org.onap.aai.util.MapperUtil;
47 import com.att.eelf.configuration.EELFLogger;
48 import com.att.eelf.configuration.EELFManager;
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);
74 * @throws ErrorObjectFormatException
75 * @throws Exception the exception
77 public static void loadProperties() throws IOException, ErrorObjectFormatException {
78 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
79 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
80 final Properties properties = new Properties();
85 try (final FileInputStream fis = new FileInputStream(filePath)) {
90 for (Entry<Object, Object> entry : properties.entrySet()) {
91 final String key = (String) entry.getKey();
92 final String value = (String) entry.getValue();
93 final String[] errorProperties = value.split(":");
95 if (errorProperties.length != 7) throw new ErrorObjectFormatException();
97 final ErrorObject errorObject = new ErrorObject();
99 errorObject.setDisposition(errorProperties[0].trim());
100 errorObject.setCategory(errorProperties[1].trim());
101 errorObject.setSeverity(errorProperties[2].trim());
102 errorObject.setErrorCode(errorProperties[3].trim());
103 errorObject.setHTTPResponseCode(errorProperties[4].trim());
104 errorObject.setRESTErrorCode(errorProperties[5].trim());
105 errorObject.setErrorText(errorProperties[6].trim());
107 ERROR_OBJECTS.put(key, errorObject);
112 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
114 * @param key The key for the error in the error.properties file
115 * @throws IOException
116 * @throws ErrorObjectNotFoundException
117 * @throws ErrorObjectFormatException
119 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
121 if (code == null) throw new IllegalArgumentException("Key cannot be null");
123 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
125 if (errorObject == null) {
126 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
127 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
134 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
135 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
136 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
137 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
138 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
139 * @param variables optional list of variables to flesh out text in error string
140 * @return appropriately formatted JSON response per the REST API spec.
141 * @throws ErrorObjectFormatException
142 * @throws ErrorObjectNotFoundException
143 * @throws IOException
146 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
147 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
148 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
150 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
154 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
155 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
156 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
157 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
159 * @param acceptHeadersOrig the accept headers orig
160 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
161 * @param variables optional list of variables to flesh out text in error string
162 * @return appropriately formatted JSON response per the REST API spec.
163 * @throws ErrorObjectFormatException
164 * @throws ErrorObjectNotFoundException
165 * @throws IOException
167 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
170 StringBuilder text = new StringBuilder();
171 String response = null;
173 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
174 // we might have an exception but no accept header, so we'll set default to JSON
175 boolean foundValidAcceptHeader = false;
176 for (MediaType mt : acceptHeadersOrig) {
177 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) ||
178 MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
179 acceptHeaders.add(mt);
180 foundValidAcceptHeader = true;
183 if (foundValidAcceptHeader == false) {
184 // override the exception, client needs to set an appropriate Accept header
185 are = new AAIException("AAI_4014");
186 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
189 final ErrorObject eo = are.getErrorObject();
191 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
193 ErrorObject restErrorObject;
196 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
197 } catch (ErrorObjectNotFoundException e) {
198 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object " + eo.getErrorCode() + "; using AAI_" + restErrorCode);
199 restErrorObject = eo;
202 text.append(restErrorObject.getErrorText());
204 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
205 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
206 // error, are ordered based on the error string.
207 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
208 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
210 if (variables == null)
212 variables = new ArrayList<String>();
215 if (variables.size() < localDataIndex) {
216 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
217 while (variables.size() < localDataIndex) {
218 variables.add("null");
222 // This will put the error code and error text into the right positions
223 if (are.getMessage() == null || are.getMessage().length() == 0) {
224 variables.add(localDataIndex++, eo.getErrorText());
227 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
229 variables.add(localDataIndex, eo.getErrorCodeString());
231 for (MediaType mediaType : acceptHeaders) {
232 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) {
233 JAXBContext context = null;
235 if(eo.getCategory().equals("1")) {
237 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
238 Marshaller m = context.createMarshaller();
239 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
240 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
242 org.onap.aai.domain.restPolicyException.ObjectFactory factory = new org.onap.aai.domain.restPolicyException.ObjectFactory();
243 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
244 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
245 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
246 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
248 policyException.setMessageId("POL" + eo.getRESTErrorCode());
249 policyException.setText(text.toString());
250 for (int i=0;i<variables.size();i++)
252 polvariables.getVariable().add(variables.get(i));
254 policyException.setVariables(polvariables);
255 requestError.setPolicyException(policyException);
256 fault.setRequestError(requestError);
258 StringWriter sw = new StringWriter();
259 m.marshal(fault, sw);
261 response = sw.toString();
265 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
266 Marshaller m = context.createMarshaller();
267 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
268 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
270 org.onap.aai.domain.restServiceException.ObjectFactory factory = new org.onap.aai.domain.restServiceException.ObjectFactory();
271 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
272 org.onap.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
273 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
274 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
275 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
276 serviceException.setText(text.toString());
277 for (int i=0;i<variables.size();i++)
279 svcvariables.getVariable().add(variables.get(i));
281 serviceException.setVariables(svcvariables);
282 requestError.setServiceException(serviceException);
283 fault.setRequestError(requestError);
285 StringWriter sw = new StringWriter();
286 m.marshal(fault, sw);
288 response = sw.toString();
291 } catch (Exception ex) {
292 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
297 if(eo.getCategory().equals("1")) {
298 org.onap.aai.domain.restPolicyException.RESTResponse restresp = new org.onap.aai.domain.restPolicyException.RESTResponse();
299 org.onap.aai.domain.restPolicyException.RequestError reqerr = new org.onap.aai.domain.restPolicyException.RequestError();
300 org.onap.aai.domain.restPolicyException.PolicyException polexc = new org.onap.aai.domain.restPolicyException.PolicyException();
301 polexc.setMessageId("POL" + eo.getRESTErrorCode());
302 polexc.setText(text.toString());
303 polexc.setVariables(variables);
304 reqerr.setPolicyException(polexc);
305 restresp.setRequestError(reqerr);
306 response = (MapperUtil.writeAsJSONString((Object) restresp));
309 org.onap.aai.domain.restServiceException.RESTResponse restresp = new org.onap.aai.domain.restServiceException.RESTResponse();
310 org.onap.aai.domain.restServiceException.RequestError reqerr = new org.onap.aai.domain.restServiceException.RequestError();
311 org.onap.aai.domain.restServiceException.ServiceException svcexc = new org.onap.aai.domain.restServiceException.ServiceException();
312 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
313 svcexc.setText(text.toString());
314 svcexc.setVariables(variables);
315 reqerr.setServiceException(svcexc);
316 restresp.setRequestError(reqerr);
317 response = (MapperUtil.writeAsJSONString((Object) restresp));
319 } catch (AAIException ex) {
320 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
330 * Gets the RESTAPI error response with logging.
332 * @param acceptHeadersOrig the accept headers orig
334 * @param variables the variables
335 * @param logline the logline
336 * @return the RESTAPI error response with logging
337 * @throws ErrorObjectFormatException
338 * @throws ErrorObjectNotFoundException
339 * @throws IOException
341 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
342 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
344 LOGGER.error(are.getMessage() + " " + LogFormatTools.getStackTop(are));
350 * Gets the RESTAPI info response.
352 * @param acceptHeaders the accept headers
353 * @param areList the are list
354 * @return the RESTAPI info response
355 * @throws ErrorObjectFormatException
356 * @throws ErrorObjectNotFoundException
357 * @throws IOException
359 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders, HashMap<AAIException,ArrayList<String>> areList) {
361 Object respObj = null;
363 org.onap.aai.domain.restResponseInfo.ObjectFactory factory = new org.onap.aai.domain.restResponseInfo.ObjectFactory();
364 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
365 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages = factory.createInfoResponseMessages();
366 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
368 while (it.hasNext()) {
369 Entry<AAIException,ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>)it.next();
370 AAIException are = pair.getKey();
371 ArrayList<String> variables = pair.getValue();
373 StringBuilder text = new StringBuilder();
375 ErrorObject eo = are.getErrorObject();
377 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
378 ErrorObject restErrorObject;
380 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+String.format("%04d", restErrorCode));
381 } catch (ErrorObjectNotFoundException e) {
382 restErrorObject = eo;
384 text.append(restErrorObject.getErrorText());
386 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
387 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
388 // error, are ordered based on the error string.
389 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
390 text.append(" (msg=%").append(localDataIndex+1).append(") (rc=%").append(localDataIndex+2).append(")");
392 if (variables == null)
394 variables = new ArrayList<String>();
397 if (variables.size() < localDataIndex) {
398 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
399 while (variables.size() < localDataIndex) {
400 variables.add("null");
404 // This will put the error code and error text into the right positions
405 if (are.getMessage() == null) {
406 variables.add(localDataIndex++, eo.getErrorText());
409 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
411 variables.add(localDataIndex, eo.getErrorCodeString());
414 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage = factory.createInfoResponseMessagesResponseMessage();
415 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables = factory.createInfoResponseMessagesResponseMessageVariables();
417 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
418 responseMessage.setText(text.toString());
419 for (int i=0;i<variables.size();i++)
421 infovariables.getVariable().add(variables.get(i));
424 responseMessage.setVariables(infovariables);
425 responseMessages.getResponseMessage().add(responseMessage);
427 } catch (Exception ex) {
428 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
432 info.setResponseMessages(responseMessages);
433 respObj = (Object) info;
440 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
441 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
442 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
443 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
444 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
445 * @param variables optional list of variables to flesh out text in error string
446 * @return appropriately formatted JSON response per the REST API spec.
447 * @throws ErrorObjectFormatException
448 * @throws ErrorObjectNotFoundException
449 * @throws IOException
451 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
453 StringBuilder text = new StringBuilder();
454 String response = null;
455 JAXBContext context = null;
457 ErrorObject eo = are.getErrorObject();
459 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
460 ErrorObject restErrorObject;
462 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
463 } catch (ErrorObjectNotFoundException e) {
464 restErrorObject = eo;
467 text.append(restErrorObject.getErrorText());
469 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
470 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
471 // error, are ordered based on the error string.
472 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
473 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
475 if (variables == null)
477 variables = new ArrayList<String>();
480 if (variables.size() < localDataIndex) {
481 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
482 while (variables.size() < localDataIndex) {
483 variables.add("null");
487 // This will put the error code and error text into the right positions
488 if (are.getMessage() == null) {
489 variables.add(localDataIndex++, eo.getErrorText());
492 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
494 variables.add(localDataIndex, eo.getErrorCodeString());
497 if(eo.getCategory().equals("1")) {
499 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
500 Marshaller m = context.createMarshaller();
501 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
502 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
504 org.onap.aai.domain.restPolicyException.ObjectFactory factory = new org.onap.aai.domain.restPolicyException.ObjectFactory();
505 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
506 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
507 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
508 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
510 policyException.setMessageId("POL" + eo.getRESTErrorCode());
511 policyException.setText(text.toString());
512 for (int i=0;i<variables.size();i++)
514 polvariables.getVariable().add(variables.get(i));
516 policyException.setVariables(polvariables);
517 requestError.setPolicyException(policyException);
518 fault.setRequestError(requestError);
520 StringWriter sw = new StringWriter();
521 m.marshal(fault, sw);
523 response = sw.toString();
527 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
528 Marshaller m = context.createMarshaller();
529 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
530 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
532 org.onap.aai.domain.restServiceException.ObjectFactory factory = new org.onap.aai.domain.restServiceException.ObjectFactory();
533 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
534 org.onap.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
535 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
536 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
537 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
538 serviceException.setText(text.toString());
539 for (int i=0;i<variables.size();i++)
541 svcvariables.getVariable().add(variables.get(i));
543 serviceException.setVariables(svcvariables);
544 requestError.setServiceException(serviceException);
545 fault.setRequestError(requestError);
547 StringWriter sw = new StringWriter();
548 m.marshal(fault, sw);
550 response = sw.toString();
553 } catch (Exception ex) {
554 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "+ ex.getMessage());
559 public static void logException(AAIException e) {
560 final ErrorObject errorObject = e.getErrorObject();
562 // MDC.put("severity", errorObject.getSeverity()); //TODO Use LoggingContext.severity(int severity)
563 String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
565 if (!AAIConfig.isEmpty(severityCode)) {
566 int sevCode = Integer.parseInt(severityCode);
567 if (sevCode > 0 && sevCode <= 3 )
569 LoggingContext.severity(sevCode);
572 String stackTrace = "";
574 stackTrace = LogFormatTools.getStackTop(e);
576 catch (Exception a) {
579 final String errorMessage = new StringBuilder()
580 .append(errorObject.getErrorText())
582 .append(errorObject.getRESTErrorCode())
584 .append(errorObject.getHTTPResponseCode())
586 .append(e.getMessage())
588 .replaceAll("\\n", "^");
590 LoggingContext.responseCode(Integer.toString(errorObject.getHTTPResponseCode().getStatusCode()));
591 LoggingContext.responseDescription(errorMessage);
592 LoggingContext.statusCode(StatusCode.ERROR);
594 final String details = new StringBuilder().append(errorObject.getErrorCodeString())
599 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
600 LOGGER.warn(details);
601 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
602 LOGGER.error(details);
603 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
604 LOGGER.error(details);
605 else if (errorObject.getSeverity().equals("INFO"))
606 LOGGER.info(details);
609 public static void logError(String code) {
613 public static void logError(String code, String message) {
614 logException(new AAIException(code, message));