2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.logging;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.StringWriter;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
33 import java.util.Map.Entry;
34 import java.util.Properties;
36 import javax.ws.rs.core.MediaType;
37 import javax.xml.bind.JAXBContext;
38 import javax.xml.bind.Marshaller;
40 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;
49 import com.att.eelf.configuration.EELFLogger;
50 import com.att.eelf.configuration.EELFManager;
54 * This classes loads the application error properties file
55 * and provides a method that returns an ErrorObject
59 public class ErrorLogHelper {
61 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ErrorLogHelper.class);
62 private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject> ();
67 } catch (IOException e) {
68 throw new RuntimeException("Failed to load error.properties file", e);
69 } catch (ErrorObjectFormatException e) {
70 throw new RuntimeException("Failed to parse error.properties file", e);
76 * @throws ErrorObjectFormatException
77 * @throws Exception the exception
79 public static void loadProperties() throws IOException, ErrorObjectFormatException {
80 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
81 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
82 final Properties properties = new Properties();
87 try (final FileInputStream fis = new FileInputStream(filePath)) {
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) 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 {
123 if (code == null) throw new IllegalArgumentException("Key cannot be null");
125 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
127 if (errorObject == null) {
128 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
129 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
136 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
137 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
138 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
139 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
140 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
141 * @param variables optional list of variables to flesh out text in error string
142 * @return appropriately formatted JSON response per the REST API spec.
143 * @throws ErrorObjectFormatException
144 * @throws ErrorObjectNotFoundException
145 * @throws IOException
148 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
149 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
150 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
152 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
156 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
157 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
158 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
159 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
161 * @param acceptHeadersOrig the accept headers orig
162 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
163 * @param variables optional list of variables to flesh out text in error string
164 * @return appropriately formatted JSON response per the REST API spec.
165 * @throws ErrorObjectFormatException
166 * @throws ErrorObjectNotFoundException
167 * @throws IOException
169 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
172 StringBuilder text = new StringBuilder();
173 String response = null;
175 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
176 // we might have an exception but no accept header, so we'll set default to JSON
177 boolean foundValidAcceptHeader = false;
178 for (MediaType mt : acceptHeadersOrig) {
179 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) ||
180 MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
181 acceptHeaders.add(mt);
182 foundValidAcceptHeader = true;
185 if (foundValidAcceptHeader == false) {
186 // override the exception, client needs to set an appropriate Accept header
187 are = new AAIException("AAI_4014");
188 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
191 final ErrorObject eo = are.getErrorObject();
193 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
195 ErrorObject restErrorObject;
198 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
199 } catch (ErrorObjectNotFoundException e) {
200 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object " + eo.getErrorCode() + "; using AAI_" + restErrorCode);
201 restErrorObject = eo;
204 text.append(restErrorObject.getErrorText());
206 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
207 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
208 // error, are ordered based on the error string.
209 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
210 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
212 if (variables == null)
214 variables = new ArrayList<String>();
217 if (variables.size() < localDataIndex) {
218 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
219 while (variables.size() < localDataIndex) {
220 variables.add("null");
224 // This will put the error code and error text into the right positions
225 if (are.getMessage() == null || are.getMessage().length() == 0) {
226 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 = new org.onap.aai.domain.restPolicyException.ObjectFactory();
245 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
246 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
247 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
248 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
250 policyException.setMessageId("POL" + eo.getRESTErrorCode());
251 policyException.setText(text.toString());
252 for (int i=0;i<variables.size();i++)
254 polvariables.getVariable().add(variables.get(i));
256 policyException.setVariables(polvariables);
257 requestError.setPolicyException(policyException);
258 fault.setRequestError(requestError);
260 StringWriter sw = new StringWriter();
261 m.marshal(fault, sw);
263 response = sw.toString();
267 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
268 Marshaller m = context.createMarshaller();
269 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
270 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
272 org.onap.aai.domain.restServiceException.ObjectFactory factory = new org.onap.aai.domain.restServiceException.ObjectFactory();
273 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
274 org.onap.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
275 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
276 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
277 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
278 serviceException.setText(text.toString());
279 for (int i=0;i<variables.size();i++)
281 svcvariables.getVariable().add(variables.get(i));
283 serviceException.setVariables(svcvariables);
284 requestError.setServiceException(serviceException);
285 fault.setRequestError(requestError);
287 StringWriter sw = new StringWriter();
288 m.marshal(fault, sw);
290 response = sw.toString();
293 } catch (Exception ex) {
294 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
299 if(eo.getCategory().equals("1")) {
300 org.onap.aai.domain.restPolicyException.RESTResponse restresp = new org.onap.aai.domain.restPolicyException.RESTResponse();
301 org.onap.aai.domain.restPolicyException.RequestError reqerr = new org.onap.aai.domain.restPolicyException.RequestError();
302 org.onap.aai.domain.restPolicyException.PolicyException polexc = new org.onap.aai.domain.restPolicyException.PolicyException();
303 polexc.setMessageId("POL" + eo.getRESTErrorCode());
304 polexc.setText(text.toString());
305 polexc.setVariables(variables);
306 reqerr.setPolicyException(polexc);
307 restresp.setRequestError(reqerr);
308 response = (MapperUtil.writeAsJSONString((Object) restresp));
311 org.onap.aai.domain.restServiceException.RESTResponse restresp = new org.onap.aai.domain.restServiceException.RESTResponse();
312 org.onap.aai.domain.restServiceException.RequestError reqerr = new org.onap.aai.domain.restServiceException.RequestError();
313 org.onap.aai.domain.restServiceException.ServiceException svcexc = new org.onap.aai.domain.restServiceException.ServiceException();
314 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
315 svcexc.setText(text.toString());
316 svcexc.setVariables(variables);
317 reqerr.setServiceException(svcexc);
318 restresp.setRequestError(reqerr);
319 response = (MapperUtil.writeAsJSONString((Object) restresp));
321 } catch (AAIException ex) {
322 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
332 * Gets the RESTAPI error response with logging.
334 * @param acceptHeadersOrig the accept headers orig
336 * @param variables the variables
337 * @param logline the logline
338 * @return the RESTAPI error response with logging
339 * @throws ErrorObjectFormatException
340 * @throws ErrorObjectNotFoundException
341 * @throws IOException
343 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
344 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
346 LOGGER.error(are.getMessage() + " " + LogFormatTools.getStackTop(are));
352 * Gets the RESTAPI info response.
354 * @param acceptHeaders the accept headers
355 * @param areList the are list
356 * @return the RESTAPI info response
357 * @throws ErrorObjectFormatException
358 * @throws ErrorObjectNotFoundException
359 * @throws IOException
361 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders, HashMap<AAIException,ArrayList<String>> areList) {
363 Object respObj = null;
365 org.onap.aai.domain.restResponseInfo.ObjectFactory factory = new org.onap.aai.domain.restResponseInfo.ObjectFactory();
366 org.onap.aai.domain.restResponseInfo.Info info = factory.createInfo();
367 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages = factory.createInfoResponseMessages();
368 Iterator<Map.Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
370 while (it.hasNext()) {
371 Map.Entry<AAIException,ArrayList<String>> pair = (Map.Entry<AAIException, ArrayList<String>>)it.next();
372 AAIException are = pair.getKey();
373 ArrayList<String> variables = pair.getValue();
375 StringBuilder text = new StringBuilder();
377 ErrorObject eo = are.getErrorObject();
379 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
380 ErrorObject restErrorObject;
382 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+String.format("%04d", restErrorCode));
383 } catch (ErrorObjectNotFoundException e) {
384 restErrorObject = eo;
386 text.append(restErrorObject.getErrorText());
388 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
389 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
390 // error, are ordered based on the error string.
391 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
392 text.append(" (msg=%").append(localDataIndex+1).append(") (rc=%").append(localDataIndex+2).append(")");
394 if (variables == null)
396 variables = new ArrayList<String>();
399 if (variables.size() < localDataIndex) {
400 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
401 while (variables.size() < localDataIndex) {
402 variables.add("null");
406 // This will put the error code and error text into the right positions
407 if (are.getMessage() == null) {
408 variables.add(localDataIndex++, eo.getErrorText());
411 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
413 variables.add(localDataIndex, eo.getErrorCodeString());
416 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage = factory.createInfoResponseMessagesResponseMessage();
417 org.onap.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables = factory.createInfoResponseMessagesResponseMessageVariables();
419 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
420 responseMessage.setText(text.toString());
421 for (int i=0;i<variables.size();i++)
423 infovariables.getVariable().add(variables.get(i));
426 responseMessage.setVariables(infovariables);
427 responseMessages.getResponseMessage().add(responseMessage);
429 } catch (Exception ex) {
430 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error " + ex.getMessage());
434 info.setResponseMessages(responseMessages);
435 respObj = (Object) info;
442 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
443 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
444 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
445 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
446 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
447 * @param variables optional list of variables to flesh out text in error string
448 * @return appropriately formatted JSON response per the REST API spec.
449 * @throws ErrorObjectFormatException
450 * @throws ErrorObjectNotFoundException
451 * @throws IOException
453 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
455 StringBuilder text = new StringBuilder();
456 String response = null;
457 JAXBContext context = null;
459 ErrorObject eo = are.getErrorObject();
461 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
462 ErrorObject restErrorObject;
464 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
465 } catch (ErrorObjectNotFoundException e) {
466 restErrorObject = eo;
469 text.append(restErrorObject.getErrorText());
471 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
472 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
473 // error, are ordered based on the error string.
474 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
475 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
477 if (variables == null)
479 variables = new ArrayList<String>();
482 if (variables.size() < localDataIndex) {
483 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
484 while (variables.size() < localDataIndex) {
485 variables.add("null");
489 // This will put the error code and error text into the right positions
490 if (are.getMessage() == null) {
491 variables.add(localDataIndex++, eo.getErrorText());
494 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
496 variables.add(localDataIndex, eo.getErrorCodeString());
499 if(eo.getCategory().equals("1")) {
501 context = JAXBContext.newInstance(org.onap.aai.domain.restPolicyException.Fault.class);
502 Marshaller m = context.createMarshaller();
503 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
504 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
506 org.onap.aai.domain.restPolicyException.ObjectFactory factory = new org.onap.aai.domain.restPolicyException.ObjectFactory();
507 org.onap.aai.domain.restPolicyException.Fault fault = factory.createFault();
508 org.onap.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
509 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
510 org.onap.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
512 policyException.setMessageId("POL" + eo.getRESTErrorCode());
513 policyException.setText(text.toString());
514 for (int i=0;i<variables.size();i++)
516 polvariables.getVariable().add(variables.get(i));
518 policyException.setVariables(polvariables);
519 requestError.setPolicyException(policyException);
520 fault.setRequestError(requestError);
522 StringWriter sw = new StringWriter();
523 m.marshal(fault, sw);
525 response = sw.toString();
529 context = JAXBContext.newInstance(org.onap.aai.domain.restServiceException.Fault.class);
530 Marshaller m = context.createMarshaller();
531 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
532 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
534 org.onap.aai.domain.restServiceException.ObjectFactory factory = new org.onap.aai.domain.restServiceException.ObjectFactory();
535 org.onap.aai.domain.restServiceException.Fault fault = factory.createFault();
536 org.onap.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
537 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
538 org.onap.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
539 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
540 serviceException.setText(text.toString());
541 for (int i=0;i<variables.size();i++)
543 svcvariables.getVariable().add(variables.get(i));
545 serviceException.setVariables(svcvariables);
546 requestError.setServiceException(serviceException);
547 fault.setRequestError(requestError);
549 StringWriter sw = new StringWriter();
550 m.marshal(fault, sw);
552 response = sw.toString();
555 } catch (Exception ex) {
556 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error "+ ex.getMessage());
561 public static void logException(AAIException e) {
562 final ErrorObject errorObject = e.getErrorObject();
564 // MDC.put("severity", errorObject.getSeverity()); //TODO Use LoggingContext.severity(int severity)
565 String severityCode = errorObject.getSeverityCode(errorObject.getSeverity());
567 if (!AAIConfig.isEmpty(severityCode)) {
568 int sevCode = Integer.parseInt(severityCode);
569 if (sevCode > 0 && sevCode <= 3 )
571 LoggingContext.severity(sevCode);
574 String stackTrace = "";
576 stackTrace = LogFormatTools.getStackTop(e);
578 catch (Exception a) {
581 final String errorMessage = new StringBuilder()
582 .append(errorObject.getErrorText())
584 .append(errorObject.getRESTErrorCode())
586 .append(errorObject.getHTTPResponseCode())
588 .append(e.getMessage())
591 LoggingContext.responseCode(Integer.toString(errorObject.getHTTPResponseCode().getStatusCode()));
592 LoggingContext.responseDescription(errorMessage);
593 LoggingContext.statusCode(StatusCode.ERROR);
595 final String details = new StringBuilder().append(errorObject.getErrorCodeString())
600 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
601 LOGGER.warn(details);
602 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
603 LOGGER.error(details);
604 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
605 LOGGER.error(details);
606 else if (errorObject.getSeverity().equals("INFO"))
607 LOGGER.info(details);
610 public static void logError(String code) {
614 public static void logError(String code, String message) {
615 logException(new AAIException(code, message));