2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 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=========================================================
21 package org.openecomp.aai.logging;
23 import com.att.eelf.configuration.EELFLogger;
24 import com.att.eelf.configuration.EELFManager;
25 import org.apache.commons.lang.StringUtils;
26 import org.openecomp.aai.exceptions.AAIException;
27 import org.openecomp.aai.logging.LoggingContext.StatusCode;
28 import org.openecomp.aai.util.AAIConstants;
29 import org.openecomp.aai.util.MapperUtil;
32 import javax.ws.rs.core.MediaType;
33 import javax.xml.bind.JAXBContext;
34 import javax.xml.bind.Marshaller;
35 import java.io.FileInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.StringWriter;
40 import java.util.Map.Entry;
44 * This classes loads the application error properties file
45 * and provides a method that returns an ErrorObject
49 public class ErrorLogHelper {
51 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(ErrorLogHelper.class);
52 private static final HashMap<String, ErrorObject> ERROR_OBJECTS = new HashMap<String, ErrorObject> ();
57 } catch (IOException e) {
58 throw new RuntimeException("Failed to load error.properties file", e);
59 } catch (ErrorObjectFormatException e) {
60 throw new RuntimeException("Failed to parse error.properties file", e);
66 * @throws ErrorObjectFormatException
67 * @throws Exception the exception
69 public static void loadProperties() throws IOException, ErrorObjectFormatException {
70 final String filePath = AAIConstants.AAI_HOME_ETC_APP_PROPERTIES + "error.properties";
71 final InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
72 final Properties properties = new Properties();
77 try (final FileInputStream fis = new FileInputStream(filePath)) {
82 for (Entry<Object, Object> entry : properties.entrySet()) {
83 final String key = (String) entry.getKey();
84 final String value = (String) entry.getValue();
85 final String[] errorProperties = value.split(":");
87 if (errorProperties.length != 7) throw new ErrorObjectFormatException();
89 final ErrorObject errorObject = new ErrorObject();
91 errorObject.setDisposition(errorProperties[0].trim());
92 errorObject.setCategory(errorProperties[1].trim());
93 errorObject.setSeverity(errorProperties[2].trim());
94 errorObject.setErrorCode(errorProperties[3].trim());
95 errorObject.setHTTPResponseCode(errorProperties[4].trim());
96 errorObject.setRESTErrorCode(errorProperties[5].trim());
97 errorObject.setErrorText(errorProperties[6].trim());
99 ERROR_OBJECTS.put(key, errorObject);
104 * Logs a known A&AI exception (i.e. one that can be found in error.properties)
106 * @param key The key for the error in the error.properties file
107 * @throws IOException
108 * @throws ErrorObjectNotFoundException
109 * @throws ErrorObjectFormatException
111 public static ErrorObject getErrorObject(String code) throws ErrorObjectNotFoundException {
113 if (code == null) throw new IllegalArgumentException("Key cannot be null");
115 final ErrorObject errorObject = ERROR_OBJECTS.get(code);
117 if (errorObject == null) {
118 LOGGER.warn("Unknown AAIException with code=" + code + ". Using default AAIException");
119 return ERROR_OBJECTS.get(AAIException.DEFAULT_EXCEPTION_CODE);
126 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
127 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
128 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
129 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
130 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
131 * @param variables optional list of variables to flesh out text in error string
132 * @return appropriately formatted JSON response per the REST API spec.
133 * @throws ErrorObjectFormatException
134 * @throws ErrorObjectNotFoundException
135 * @throws IOException
138 public static String getRESTAPIErrorResponse(AAIException are, ArrayList<String> variables) {
139 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
140 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
142 return getRESTAPIErrorResponse(acceptHeaders, are, variables);
146 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
147 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
148 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
149 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
151 * @param acceptHeadersOrig the accept headers orig
152 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
153 * @param variables optional list of variables to flesh out text in error string
154 * @return appropriately formatted JSON response per the REST API spec.
155 * @throws ErrorObjectFormatException
156 * @throws ErrorObjectNotFoundException
157 * @throws IOException
159 public static String getRESTAPIErrorResponse(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
162 StringBuilder text = new StringBuilder();
163 String response = null;
165 List<MediaType> acceptHeaders = new ArrayList<MediaType>();
166 // we might have an exception but no accept header, so we'll set default to JSON
167 boolean foundValidAcceptHeader = false;
168 for (MediaType mt : acceptHeadersOrig) {
169 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt) ||
170 MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
171 acceptHeaders.add(mt);
172 foundValidAcceptHeader = true;
175 if (foundValidAcceptHeader == false) {
176 // override the exception, client needs to set an appropriate Accept header
177 are = new AAIException("AAI_4014");
178 acceptHeaders.add(MediaType.APPLICATION_JSON_TYPE);
181 final ErrorObject eo = are.getErrorObject();
183 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
185 ErrorObject restErrorObject;
188 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
189 } catch (ErrorObjectNotFoundException e) {
190 LOGGER.warn("Failed to find related error object AAI_" + restErrorCode + " for error object " + eo.getErrorCode() + "; using AAI_" + restErrorCode);
191 restErrorObject = eo;
194 text.append(restErrorObject.getErrorText());
196 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
197 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
198 // error, are ordered based on the error string.
199 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
200 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
202 if (variables == null)
204 variables = new ArrayList<String>();
207 if (variables.size() < localDataIndex) {
208 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
209 while (variables.size() < localDataIndex) {
210 variables.add("null");
214 // This will put the error code and error text into the right positions
215 if (are.getMessage() == null || are.getMessage().length() == 0) {
216 variables.add(localDataIndex++, eo.getErrorText());
219 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
221 variables.add(localDataIndex, eo.getErrorCodeString());
223 for (MediaType mediaType : acceptHeaders) {
224 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mediaType)) {
225 JAXBContext context = null;
227 if(eo.getCategory().equals("1")) {
229 context = JAXBContext.newInstance(org.openecomp.aai.domain.restPolicyException.Fault.class);
230 Marshaller m = context.createMarshaller();
231 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
232 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
234 org.openecomp.aai.domain.restPolicyException.ObjectFactory factory = new org.openecomp.aai.domain.restPolicyException.ObjectFactory();
235 org.openecomp.aai.domain.restPolicyException.Fault fault = factory.createFault();
236 org.openecomp.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
237 org.openecomp.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
238 org.openecomp.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
240 policyException.setMessageId("POL" + eo.getRESTErrorCode());
241 policyException.setText(text.toString());
242 for (int i=0;i<variables.size();i++)
244 polvariables.getVariable().add(variables.get(i));
246 policyException.setVariables(polvariables);
247 requestError.setPolicyException(policyException);
248 fault.setRequestError(requestError);
250 StringWriter sw = new StringWriter();
251 m.marshal(fault, sw);
253 response = sw.toString();
257 context = JAXBContext.newInstance(org.openecomp.aai.domain.restServiceException.Fault.class);
258 Marshaller m = context.createMarshaller();
259 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
260 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
262 org.openecomp.aai.domain.restServiceException.ObjectFactory factory = new org.openecomp.aai.domain.restServiceException.ObjectFactory();
263 org.openecomp.aai.domain.restServiceException.Fault fault = factory.createFault();
264 org.openecomp.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
265 org.openecomp.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
266 org.openecomp.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
267 serviceException.setMessageId("SVC" + eo.getRESTErrorCode());
268 serviceException.setText(text.toString());
269 for (int i=0;i<variables.size();i++)
271 svcvariables.getVariable().add(variables.get(i));
273 serviceException.setVariables(svcvariables);
274 requestError.setServiceException(serviceException);
275 fault.setRequestError(requestError);
277 StringWriter sw = new StringWriter();
278 m.marshal(fault, sw);
280 response = sw.toString();
283 } catch (Exception ex) {
284 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error", ex);
289 if(eo.getCategory().equals("1")) {
290 org.openecomp.aai.domain.restPolicyException.RESTResponse restresp = new org.openecomp.aai.domain.restPolicyException.RESTResponse();
291 org.openecomp.aai.domain.restPolicyException.RequestError reqerr = new org.openecomp.aai.domain.restPolicyException.RequestError();
292 org.openecomp.aai.domain.restPolicyException.PolicyException polexc = new org.openecomp.aai.domain.restPolicyException.PolicyException();
293 polexc.setMessageId("POL" + eo.getRESTErrorCode());
294 polexc.setText(text.toString());
295 polexc.setVariables(variables);
296 reqerr.setPolicyException(polexc);
297 restresp.setRequestError(reqerr);
298 response = (MapperUtil.writeAsJSONString((Object) restresp));
301 org.openecomp.aai.domain.restServiceException.RESTResponse restresp = new org.openecomp.aai.domain.restServiceException.RESTResponse();
302 org.openecomp.aai.domain.restServiceException.RequestError reqerr = new org.openecomp.aai.domain.restServiceException.RequestError();
303 org.openecomp.aai.domain.restServiceException.ServiceException svcexc = new org.openecomp.aai.domain.restServiceException.ServiceException();
304 svcexc.setMessageId("SVC" + eo.getRESTErrorCode());
305 svcexc.setText(text.toString());
306 svcexc.setVariables(variables);
307 reqerr.setServiceException(svcexc);
308 restresp.setRequestError(reqerr);
309 response = (MapperUtil.writeAsJSONString((Object) restresp));
311 } catch (AAIException ex) {
312 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error", ex);
322 * Gets the RESTAPI error response with logging.
324 * @param acceptHeadersOrig the accept headers orig
326 * @param variables the variables
327 * @param logline the logline
328 * @return the RESTAPI error response with logging
329 * @throws ErrorObjectFormatException
330 * @throws ErrorObjectNotFoundException
331 * @throws IOException
333 public static String getRESTAPIErrorResponseWithLogging(List<MediaType> acceptHeadersOrig, AAIException are, ArrayList<String> variables) {
334 String response = ErrorLogHelper.getRESTAPIErrorResponse(acceptHeadersOrig, are, variables);
336 LOGGER.error(are.getMessage(), are);
342 * Gets the RESTAPI info response.
344 * @param acceptHeaders the accept headers
345 * @param areList the are list
346 * @return the RESTAPI info response
347 * @throws ErrorObjectFormatException
348 * @throws ErrorObjectNotFoundException
349 * @throws IOException
351 public static Object getRESTAPIInfoResponse(List<MediaType> acceptHeaders, HashMap<AAIException,ArrayList<String>> areList) {
353 Object respObj = null;
355 org.openecomp.aai.domain.restResponseInfo.ObjectFactory factory = new org.openecomp.aai.domain.restResponseInfo.ObjectFactory();
356 org.openecomp.aai.domain.restResponseInfo.Info info = factory.createInfo();
357 org.openecomp.aai.domain.restResponseInfo.Info.ResponseMessages responseMessages = factory.createInfoResponseMessages();
358 Iterator<Entry<AAIException, ArrayList<String>>> it = areList.entrySet().iterator();
360 while (it.hasNext()) {
361 Entry<AAIException,ArrayList<String>> pair = (Entry<AAIException, ArrayList<String>>)it.next();
362 AAIException are = pair.getKey();
363 ArrayList<String> variables = pair.getValue();
365 StringBuilder text = new StringBuilder();
367 ErrorObject eo = are.getErrorObject();
369 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
370 ErrorObject restErrorObject;
372 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+String.format("%04d", restErrorCode));
373 } catch (ErrorObjectNotFoundException e) {
374 restErrorObject = eo;
376 text.append(restErrorObject.getErrorText());
378 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
379 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
380 // error, are ordered based on the error string.
381 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
382 text.append(" (msg=%").append(localDataIndex+1).append(") (rc=%").append(localDataIndex+2).append(")");
384 if (variables == null)
386 variables = new ArrayList<String>();
389 if (variables.size() < localDataIndex) {
390 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
391 while (variables.size() < localDataIndex) {
392 variables.add("null");
396 // This will put the error code and error text into the right positions
397 if (are.getMessage() == null) {
398 variables.add(localDataIndex++, eo.getErrorText());
401 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
403 variables.add(localDataIndex, eo.getErrorCodeString());
406 org.openecomp.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage responseMessage = factory.createInfoResponseMessagesResponseMessage();
407 org.openecomp.aai.domain.restResponseInfo.Info.ResponseMessages.ResponseMessage.Variables infovariables = factory.createInfoResponseMessagesResponseMessageVariables();
409 responseMessage.setMessageId("INF" + eo.getRESTErrorCode());
410 responseMessage.setText(text.toString());
411 for (int i=0;i<variables.size();i++)
413 infovariables.getVariable().add(variables.get(i));
416 responseMessage.setVariables(infovariables);
417 responseMessages.getResponseMessage().add(responseMessage);
419 } catch (Exception ex) {
420 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error", ex);
424 info.setResponseMessages(responseMessages);
425 respObj = (Object) info;
432 * Determines whether category is policy or not. If policy (1), this is a POL error, else it's a SVC error.
433 * The AAIRESTException may contain a different ErrorObject than that created with the REST error key.
434 * This allows lower level exception detail to be returned to the client to help troubleshoot the problem.
435 * If no error object is embedded in the AAIException, one will be created using the error object from the AAIException.
436 * @param are must have a restError value whose numeric value must match what should be returned in the REST API
437 * @param variables optional list of variables to flesh out text in error string
438 * @return appropriately formatted JSON response per the REST API spec.
439 * @throws ErrorObjectFormatException
440 * @throws ErrorObjectNotFoundException
441 * @throws IOException
443 public static String getRESTAPIPolicyErrorResponseXML(AAIException are, ArrayList<String> variables) {
445 StringBuilder text = new StringBuilder();
446 String response = null;
447 JAXBContext context = null;
449 ErrorObject eo = are.getErrorObject();
451 int restErrorCode = Integer.parseInt(eo.getRESTErrorCode());
452 ErrorObject restErrorObject;
454 restErrorObject = ErrorLogHelper.getErrorObject("AAI_"+restErrorCode);
455 } catch (ErrorObjectNotFoundException e) {
456 restErrorObject = eo;
459 text.append(restErrorObject.getErrorText());
461 // We want to always append the (msg=%n) (ec=%n+1) to the text, but have to find value of n
462 // This assumes that the variables in the ArrayList, which might be more than are needed to flesh out the
463 // error, are ordered based on the error string.
464 int localDataIndex = StringUtils.countMatches(restErrorObject.getErrorText(), "%");
465 text.append(" (msg=%").append(localDataIndex+1).append(") (ec=%").append(localDataIndex+2).append(")");
467 if (variables == null)
469 variables = new ArrayList<String>();
472 if (variables.size() < localDataIndex) {
473 ErrorLogHelper.logError("AAI_4011", "data missing for rest error");
474 while (variables.size() < localDataIndex) {
475 variables.add("null");
479 // This will put the error code and error text into the right positions
480 if (are.getMessage() == null) {
481 variables.add(localDataIndex++, eo.getErrorText());
484 variables.add(localDataIndex++, eo.getErrorText() + ":" + are.getMessage());
486 variables.add(localDataIndex, eo.getErrorCodeString());
489 if(eo.getCategory().equals("1")) {
491 context = JAXBContext.newInstance(org.openecomp.aai.domain.restPolicyException.Fault.class);
492 Marshaller m = context.createMarshaller();
493 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
494 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
496 org.openecomp.aai.domain.restPolicyException.ObjectFactory factory = new org.openecomp.aai.domain.restPolicyException.ObjectFactory();
497 org.openecomp.aai.domain.restPolicyException.Fault fault = factory.createFault();
498 org.openecomp.aai.domain.restPolicyException.Fault.RequestError requestError = factory.createFaultRequestError();
499 org.openecomp.aai.domain.restPolicyException.Fault.RequestError.PolicyException policyException = factory.createFaultRequestErrorPolicyException();
500 org.openecomp.aai.domain.restPolicyException.Fault.RequestError.PolicyException.Variables polvariables = factory.createFaultRequestErrorPolicyExceptionVariables();
502 policyException.setMessageId("POL" + eo.getRESTErrorCode());
503 policyException.setText(text.toString());
504 for (int i=0;i<variables.size();i++)
506 polvariables.getVariable().add(variables.get(i));
508 policyException.setVariables(polvariables);
509 requestError.setPolicyException(policyException);
510 fault.setRequestError(requestError);
512 StringWriter sw = new StringWriter();
513 m.marshal(fault, sw);
515 response = sw.toString();
519 context = JAXBContext.newInstance(org.openecomp.aai.domain.restServiceException.Fault.class);
520 Marshaller m = context.createMarshaller();
521 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
522 m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
524 org.openecomp.aai.domain.restServiceException.ObjectFactory factory = new org.openecomp.aai.domain.restServiceException.ObjectFactory();
525 org.openecomp.aai.domain.restServiceException.Fault fault = factory.createFault();
526 org.openecomp.aai.domain.restServiceException.Fault.RequestError requestError = factory.createFaultRequestError();
527 org.openecomp.aai.domain.restServiceException.Fault.RequestError.ServiceException serviceException = factory.createFaultRequestErrorServiceException();
528 org.openecomp.aai.domain.restServiceException.Fault.RequestError.ServiceException.Variables svcvariables = factory.createFaultRequestErrorServiceExceptionVariables();
529 serviceException.setMessageId("POL" + eo.getRESTErrorCode());
530 serviceException.setText(text.toString());
531 for (int i=0;i<variables.size();i++)
533 svcvariables.getVariable().add(variables.get(i));
535 serviceException.setVariables(svcvariables);
536 requestError.setServiceException(serviceException);
537 fault.setRequestError(requestError);
539 StringWriter sw = new StringWriter();
540 m.marshal(fault, sw);
542 response = sw.toString();
545 } catch (Exception ex) {
546 LOGGER.error("We were unable to create a rest exception to return on an API because of a parsing error", ex);
551 public static void logException(AAIException e) {
552 final ErrorObject errorObject = e.getErrorObject();
554 MDC.put("severity", errorObject.getSeverity()); //TODO Use LoggingContext.severity(int severity)
556 final String errorMessage = new StringBuilder()
557 .append(errorObject.getErrorText())
559 .append(errorObject.getRESTErrorCode())
561 .append(errorObject.getHTTPResponseCode())
563 .append(e.getMessage())
566 LoggingContext.responseCode(errorObject.getErrorCodeString());
567 LoggingContext.responseDescription(errorMessage);
568 LoggingContext.statusCode(StatusCode.ERROR);
570 if (errorObject.getSeverity().equalsIgnoreCase("WARN"))
571 LOGGER.warn(errorMessage, e);
572 else if (errorObject.getSeverity().equalsIgnoreCase("ERROR"))
573 LOGGER.error(errorMessage, e);
574 else if (errorObject.getSeverity().equalsIgnoreCase("FATAL"))
575 LOGGER.error(errorMessage, e);
576 else if (errorObject.getSeverity().equals("INFO"))
577 LOGGER.info(errorMessage + ", " + e.getMessage());
580 public static void logError(String code) {
584 public static void logError(String code, String message) {
585 logException(new AAIException(code, message));