2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ============LICENSE_END=========================================================
24 package org.onap.appc.requesthandler.impl;
26 import com.att.eelf.configuration.EELFLogger;
27 import com.att.eelf.configuration.EELFManager;
28 import org.apache.commons.lang.ObjectUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.onap.appc.configuration.Configuration;
31 import org.onap.appc.configuration.ConfigurationFactory;
32 import org.onap.appc.domainmodel.lcm.Flags;
33 import org.onap.appc.domainmodel.lcm.RequestContext;
34 import org.onap.appc.domainmodel.lcm.RequestStatus;
35 import org.onap.appc.domainmodel.lcm.ResponseContext;
36 import org.onap.appc.domainmodel.lcm.RuntimeContext;
37 import org.onap.appc.domainmodel.lcm.Status;
38 import org.onap.appc.domainmodel.lcm.TransactionRecord;
39 import org.onap.appc.domainmodel.lcm.VNFOperation;
40 import org.onap.appc.exceptions.APPCException;
41 import org.onap.appc.exceptions.InvalidInputException;
42 import org.onap.appc.executor.objects.LCMCommandStatus;
43 import org.onap.appc.executor.objects.Params;
44 import org.onap.appc.lockmanager.api.LockException;
45 import org.onap.appc.logging.LoggingConstants;
46 import org.onap.appc.logging.LoggingUtils;
47 import org.onap.appc.messageadapter.MessageAdapter;
48 import org.onap.appc.messageadapter.impl.MessageAdapterImpl;
49 import org.onap.appc.metricservice.MetricRegistry;
50 import org.onap.appc.metricservice.MetricService;
51 import org.onap.appc.metricservice.metric.DispatchingFuntionMetric;
52 import org.onap.appc.metricservice.metric.Metric;
53 import org.onap.appc.metricservice.metric.MetricType;
54 import org.onap.appc.metricservice.policy.PublishingPolicy;
55 import org.onap.appc.metricservice.publisher.LogPublisher;
56 import org.onap.appc.requesthandler.RequestHandler;
57 import org.onap.appc.requesthandler.exceptions.RequestValidationException;
58 import org.onap.appc.requesthandler.helper.RequestValidator;
59 import org.onap.appc.requesthandler.objects.RequestHandlerInput;
60 import org.onap.appc.requesthandler.objects.RequestHandlerOutput;
61 import org.onap.appc.transactionrecorder.TransactionRecorder;
62 import org.onap.appc.transactionrecorder.objects.TransactionConstants;
63 import org.osgi.framework.BundleContext;
64 import org.osgi.framework.FrameworkUtil;
65 import org.osgi.framework.ServiceReference;
68 import java.net.InetAddress;
69 import java.net.UnknownHostException;
70 import java.text.SimpleDateFormat;
71 import java.time.Instant;
72 import java.util.Date;
73 import java.util.HashMap;
75 import java.util.Properties;
76 import java.util.UUID;
77 import java.util.TimeZone;
78 import static com.att.eelf.configuration.Configuration.MDC_INSTANCE_UUID;
79 import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
80 import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN;
81 import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS;
82 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID;
83 import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
86 * This class provides application logic for the Request/Response Handler Component.
88 public abstract class AbstractRequestHandlerImpl implements RequestHandler {
90 private RequestValidator requestValidator;
92 protected TransactionRecorder transactionRecorder;
94 private MessageAdapter messageAdapter;
96 static MetricRegistry metricRegistry;
98 private boolean isMetricEnabled = false;
100 protected final Configuration configuration = ConfigurationFactory.getConfiguration();
102 private final EELFLogger logger = EELFManager.getInstance().getLogger(AbstractRequestHandlerImpl.class);
104 AbstractRequestHandlerImpl() {
105 messageAdapter = new MessageAdapterImpl();
106 messageAdapter.init();
107 Properties properties = configuration.getProperties();
108 if (properties != null && properties.getProperty("metric.enabled") != null) {
109 isMetricEnabled = Boolean.valueOf(properties.getProperty("metric.enabled"));
111 if (isMetricEnabled) {
116 public void setTransactionRecorder(TransactionRecorder transactionRecorder) {
117 this.transactionRecorder = transactionRecorder;
120 public void setRequestValidator(RequestValidator requestValidator) {
121 this.requestValidator = requestValidator;
124 public void setMessageAdapter(MessageAdapter messageAdapter) {
125 this.messageAdapter = messageAdapter;
129 * It receives requests from the north-bound REST API (Communication) Layer and
130 * performs following validations.
131 * 1. VNF exists in A&AI for the given targetID (VnfID)
132 * 2. For the current VNF Orchestration Status, the command can be executed
133 * 3. For the given VNF type and Operation, there exists work-flow definition in the APPC database
134 * If any of the validation fails, it returns appropriate response *
136 * @param input RequestHandlerInput object which contains request header and other request parameters like
137 * command , target Id , payload etc.
138 * @return response for request as enum with Return code and message.
141 public RequestHandlerOutput handleRequest(RequestHandlerInput input) {
142 if (logger.isTraceEnabled())
143 logger.trace("Entering to handleRequest with RequestHandlerInput = " + ObjectUtils.toString(input) + ")");
144 String errorMessage = null;
145 RequestStatus requestStatus;
146 TransactionRecord transactionRecord = createTransactionRecord(input);
147 RuntimeContext runtimeContext = createRuntimeContext(input, transactionRecord);
148 ResponseContext responseContext = createResponseContext(input);
149 runtimeContext.setResponseContext(responseContext);
150 RequestHandlerOutput output = new RequestHandlerOutput();
152 transactionRecorder.store(transactionRecord);
153 requestValidator.validateRequest(runtimeContext);
155 setInitialLogProperties(input.getRequestContext());
157 handleRequest(runtimeContext);
158 output.setResponseContext(runtimeContext.getResponseContext());
159 } catch (RequestValidationException e) {
160 errorMessage = e.getMessage();
161 logger.error(errorMessage, e);
162 if (!StringUtils.isEmpty(e.getLogMessage()))
163 storeErrorMessageToLog(runtimeContext, e.getTargetEntity(), e.getTargetService(), e.getLogMessage());
164 output = buildRequestHandlerOutput(e.getLcmCommandStatus(), e.getParams());
165 } catch (InvalidInputException e) {
166 logger.error("InvalidInputException : " + e.getMessage(), e);
167 errorMessage = e.getMessage() != null ? e.getMessage() : e.toString();
168 output = buildRequestHandlerOutput(LCMCommandStatus.INVALID_INPUT_PARAMETER, new Params().addParam
169 ("errorMsg", errorMessage));
170 } catch (LockException e) {
171 logger.error("LockException : " + e.getMessage(), e);
172 Params params = new Params().addParam("errorMsg", e.getMessage());
173 fillStatus(runtimeContext, LCMCommandStatus.LOCKED_VNF_ID, params);
174 output = buildRequestHandlerOutput(LCMCommandStatus.LOCKED_VNF_ID, params);
175 } catch (Exception e) {
176 logger.error("Exception : " + e.getMessage(), e);
177 storeErrorMessageToLog(runtimeContext, "", "", "Exception = " + e.getMessage());
178 errorMessage = e.getMessage() != null ? e.getMessage() : e.toString();
179 Params params = new Params().addParam("errorMsg", errorMessage);
180 output = buildRequestHandlerOutput(LCMCommandStatus.UNEXPECTED_ERROR, params);
182 final int statusCode = output.getResponseContext().getStatus().getCode();
183 if (statusCode == LCMCommandStatus.ACCEPTED.getResponseCode()) {
184 requestStatus = RequestStatus.ACCEPTED;
185 } else if (statusCode == LCMCommandStatus.SUCCESS.getResponseCode()) {
186 requestStatus = RequestStatus.SUCCESSFUL;
188 ((DispatchingFuntionMetric) metricRegistry.metric("DISPATCH_FUNCTION")).incrementAcceptedRequest();
190 requestStatus = (statusCode == LCMCommandStatus.EXPIRED_REQUEST.getResponseCode()) ? RequestStatus
191 .TIMEOUT : RequestStatus.REJECTED;
193 ((DispatchingFuntionMetric) metricRegistry.metric("DISPATCH_FUNCTION")).incrementRejectedRequest();
196 if (errorMessage != null && logger.isDebugEnabled())
197 logger.debug("error occurred in handleRequest " + errorMessage);
198 logger.debug("output.getResponseContext().getStatus().getCode(): " + statusCode);
199 runtimeContext.setResponseContext(output.getResponseContext());
201 runtimeContext.getTransactionRecord().setRequestState(requestStatus);
202 runtimeContext.getTransactionRecord().setResultCode(output.getResponseContext().getStatus().getCode());
203 updateTransactionStatus(runtimeContext.getTransactionRecord());
204 recordAndClearLogProperties(runtimeContext);
207 if (logger.isTraceEnabled()) {
208 logger.trace("Exiting from handleRequest with (RequestHandlerOutput = " +
209 ObjectUtils.toString(output.getResponseContext()) + ")");
214 private ResponseContext createResponseContext(RequestHandlerInput input) {
215 final ResponseContext responseContext = new ResponseContext();
216 responseContext.setStatus(new Status());
217 responseContext.setAdditionalContext(new HashMap<>(4));
218 responseContext.setCommonHeader(input.getRequestContext().getCommonHeader());
219 return responseContext;
222 private void updateTransactionStatus(TransactionRecord record) {
223 Map<TransactionConstants.TRANSACTION_ATTRIBUTES, String> updateColumns = new HashMap<>();
224 if (!StringUtils.isBlank(record.getTargetType())) {
225 updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.TARGET_TYPE, record.getTargetType());
227 updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.STATE, record.getRequestState());
228 updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.RESULT_CODE,
229 String.valueOf(record.getResultCode()));
230 if (RequestStatus.valueOf(record.getRequestState()).isTerminal()) {
231 Date endTime = new Date();
232 updateColumns.put(TransactionConstants.TRANSACTION_ATTRIBUTES.END_TIME,
233 dateToStringConverterMillis(endTime));
236 transactionRecorder.update(record.getTransactionId(), record.getRequestId(), updateColumns);
237 } catch (APPCException e) {
238 logger.error("Error accessing database", e);
242 private RuntimeContext createRuntimeContext(RequestHandlerInput input, TransactionRecord transactionRecord) {
243 RuntimeContext runtimeContext;
244 runtimeContext = new RuntimeContext();
245 runtimeContext.setRequestContext(input.getRequestContext());
246 runtimeContext.setTimeStart(transactionRecord.getStartTime());
247 runtimeContext.setRpcName(input.getRpcName());
248 runtimeContext.setTransactionRecord(transactionRecord);
249 return runtimeContext;
252 private TransactionRecord createTransactionRecord(RequestHandlerInput input) {
253 Instant startTime = Instant.now();
254 TransactionRecord record = new TransactionRecord();
255 record.setTransactionId(UUID.randomUUID().toString());
256 record.setRequestState(RequestStatus.RECEIVED);
257 record.setRequestId(input.getRequestContext().getCommonHeader().getRequestId());
258 record.setSubRequestId(input.getRequestContext().getCommonHeader().getSubRequestId());
259 record.setOriginatorId(input.getRequestContext().getCommonHeader().getOriginatorId());
260 record.setOriginTimestamp(input.getRequestContext().getCommonHeader().getTimeStamp().toInstant());
261 record.setStartTime(startTime);
262 record.setOperation(VNFOperation.valueOf(input.getRequestContext().getAction().name()));
263 record.setTargetId(input.getRequestContext().getActionIdentifiers().getVnfId());
264 record.setVnfcName(input.getRequestContext().getActionIdentifiers().getVnfcName());
265 record.setVserverId(input.getRequestContext().getActionIdentifiers().getVserverId());
266 record.setVfModuleId(input.getRequestContext().getActionIdentifiers().getVfModuleId());
267 record.setServiceInstanceId(input.getRequestContext().getActionIdentifiers().getServiceInstanceId());
269 if (input.getRequestContext().getCommonHeader().getFlags() != null &&
270 input.getRequestContext().getCommonHeader().getFlags().getMode() != null) {
271 mode = input.getRequestContext().getCommonHeader().getFlags().getMode();
273 mode = Flags.Mode.NORMAL;
275 record.setMode(mode);
279 private void recordAndClearLogProperties(RuntimeContext runtimeContext) {
280 storeAuditLogRecord(runtimeContext);
281 storeMetricLogRecord(runtimeContext);
282 clearRequestLogProperties();
285 void storeErrorMessageToLog(RuntimeContext runtimeContext, String targetEntity, String targetServiceName,
286 String additionalMessage) {
287 LoggingUtils.logErrorMessage(runtimeContext.getResponseContext().getStatus() != null ?
288 String.valueOf(runtimeContext.getResponseContext().getStatus().getCode()) : "",
289 runtimeContext.getResponseContext().getStatus() != null ?
290 String.valueOf(runtimeContext.getResponseContext().getStatus().getMessage()) : "",
294 this.getClass().getCanonicalName());
297 protected abstract void handleRequest(RuntimeContext runtimeContext);
299 void fillStatus(RuntimeContext runtimeContext, LCMCommandStatus lcmCommandStatus, Params params) {
300 runtimeContext.getResponseContext().getStatus().setCode(lcmCommandStatus.getResponseCode());
301 runtimeContext.getResponseContext().getStatus().setMessage(lcmCommandStatus.getFormattedMessage(params));
304 private void clearRequestLogProperties() {
306 MDC.remove(MDC_KEY_REQUEST_ID);
307 MDC.remove(MDC_SERVICE_INSTANCE_ID);
308 MDC.remove(MDC_SERVICE_NAME);
309 MDC.remove(LoggingConstants.MDCKeys.PARTNER_NAME);
310 MDC.remove(LoggingConstants.MDCKeys.TARGET_VIRTUAL_ENTITY);
311 } catch (Exception e) {
312 logger.error("Error clearing MDC log properties. " + e.getMessage(), e);
316 private void setInitialLogProperties(RequestContext requestContext) {
319 MDC.put(MDC_KEY_REQUEST_ID, requestContext.getCommonHeader().getRequestId());
320 if (requestContext.getActionIdentifiers().getServiceInstanceId() != null) {
321 MDC.put(MDC_SERVICE_INSTANCE_ID, requestContext.getActionIdentifiers().getServiceInstanceId());
323 MDC.put(LoggingConstants.MDCKeys.PARTNER_NAME, requestContext.getCommonHeader().getOriginatorId());
324 MDC.put(MDC_INSTANCE_UUID, ""); // value should be created in the future
325 //Don't change it to a.getHostName() again please. It's wrong!
326 MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getCanonicalHostName());
327 MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
328 MDC.put(LoggingConstants.MDCKeys.SERVER_NAME, InetAddress.getLocalHost().getHostName());
329 MDC.put(MDC_SERVICE_NAME, requestContext.getAction().name());
330 MDC.put(LoggingConstants.MDCKeys.TARGET_VIRTUAL_ENTITY, requestContext.getActionIdentifiers().getVnfId());
331 } catch (UnknownHostException e) {
332 logger.error("Error occured while setting initial log properties", e);
336 private static RequestHandlerOutput buildRequestHandlerOutput(LCMCommandStatus response, Params params) {
337 RequestHandlerOutput output = new RequestHandlerOutput();
338 ResponseContext responseContext = new ResponseContext();
339 org.onap.appc.domainmodel.lcm.Status status = new org.onap.appc.domainmodel.lcm.Status();
340 status.setCode(response.getResponseCode());
341 status.setMessage(response.getFormattedMessage(params));
342 responseContext.setStatus(status);
343 output.setResponseContext(responseContext);
348 * This method perform following operations required after execution of workflow.
349 * It posts asynchronous response to message bus (DMaaP).
351 * Removes request from request registry.
352 * Generate audit logs.
353 * Adds transaction record to database id if transaction logging is enabled.
356 public void onRequestExecutionEnd(RuntimeContext runtimeContext) {
357 if (logger.isTraceEnabled()) {
358 logger.trace("Entering to onRequestExecutionEnd with runtimeContext = " +
359 ObjectUtils.toString(runtimeContext));
361 postMessageToDMaaP(runtimeContext.getRequestContext().getAction(), runtimeContext.getRpcName(),
362 runtimeContext.getResponseContext());
363 final int statusCode = runtimeContext.getResponseContext().getStatus().getCode();
364 RequestStatus requestStatus =
365 (statusCode == LCMCommandStatus.SUCCESS.getResponseCode()) ?
366 RequestStatus.SUCCESSFUL : RequestStatus.FAILED;
367 runtimeContext.getTransactionRecord().setRequestState(requestStatus);
368 runtimeContext.getTransactionRecord().setResultCode(runtimeContext.getResponseContext().getStatus().getCode());
369 updateTransactionStatus(runtimeContext.getTransactionRecord());
370 storeAuditLogRecord(runtimeContext);
373 private void storeAuditLogRecord(RuntimeContext runtimeContext) {
374 LoggingUtils.logAuditMessage(runtimeContext.getTimeStart(),
376 String.valueOf(runtimeContext.getResponseContext().getStatus().getCode()),
377 runtimeContext.getResponseContext().getStatus().getMessage(),
378 this.getClass().getCanonicalName());
381 private void storeMetricLogRecord(RuntimeContext runtimeContext) {
382 LoggingUtils.logMetricsMessage(runtimeContext.getTimeStart(),
384 LoggingConstants.TargetNames.APPC,
385 runtimeContext.getRequestContext().getAction().name(),
386 runtimeContext.getResponseContext().getStatus().getCode() == LCMCommandStatus.ACCEPTED.getResponseCode()
387 ? LoggingConstants.StatusCodes.COMPLETE : LoggingConstants.StatusCodes.ERROR,
388 String.valueOf(runtimeContext.getResponseContext().getStatus().getCode()),
389 runtimeContext.getResponseContext().getStatus().getMessage(),
390 this.getClass().getCanonicalName());
393 private void postMessageToDMaaP(VNFOperation operation, String rpcName, ResponseContext responseContext) {
394 if (logger.isTraceEnabled()) {
395 logger.trace("Entering to postMessageToDMaaP with AsyncResponse = " +
396 ObjectUtils.toString(responseContext));
398 logger.debug("In postMessageToDMaap before invoking post()");
399 boolean callbackResponse = messageAdapter.post(operation, rpcName, responseContext);
400 logger.debug("In postMessageToDMaap after invoking post()");
401 if (!callbackResponse) {
402 logger.error("DMaaP posting status: false", "dmaapMessage: " + responseContext);
404 if (logger.isTraceEnabled())
405 logger.trace("Exiting from postMessageToDMaaP with (callbackResponse = " +
406 ObjectUtils.toString(callbackResponse) + ")");
409 private void initMetric() {
410 if (logger.isDebugEnabled())
411 logger.debug("Metric getting initialized");
412 MetricService metricService = getMetricservice();
413 // Check for the metric service created before trying to create registry using
414 // the metricService object
415 if (metricService == null) {
416 // Cannot find service reference for org.onap.appc.metricservice.MetricService
417 throw new NullPointerException("org.onap.appc.metricservice.MetricService is null. " +
418 "Failed to init Metric");
420 metricRegistry = metricService.createRegistry("APPC");
421 DispatchingFuntionMetric dispatchingFuntionMetric = metricRegistry.metricBuilderFactory().
422 dispatchingFunctionCounterBuilder().
423 withName("DISPATCH_FUNCTION").withType(MetricType.COUNTER).
424 withAcceptRequestValue(0)
425 .withRejectRequestValue(0)
427 if (metricRegistry.register(dispatchingFuntionMetric)) {
428 Metric[] metrics = new Metric[]{dispatchingFuntionMetric};
429 LogPublisher logPublisher = new LogPublisher(metricRegistry, metrics);
430 LogPublisher[] logPublishers = new LogPublisher[1];
431 logPublishers[0] = logPublisher;
432 PublishingPolicy manuallyScheduledPublishingPolicy = metricRegistry.policyBuilderFactory()
433 .scheduledPolicyBuilder()
434 .withPublishers(logPublishers)
435 .withMetrics(metrics)
438 if (logger.isDebugEnabled())
439 logger.debug("Policy getting initialized");
440 manuallyScheduledPublishingPolicy.init();
441 if (logger.isDebugEnabled())
442 logger.debug("Metric initialized");
446 private MetricService getMetricservice() {
447 BundleContext bctx = FrameworkUtil.getBundle(MetricService.class).getBundleContext();
448 ServiceReference sref = bctx.getServiceReference(MetricService.class.getName());
450 logger.info("Metric Service from bundlecontext");
451 return (MetricService) bctx.getService(sref);
453 logger.info("Metric Service error from bundlecontext");
454 logger.warn("Cannot find service reference for org.onap.appc.metricservice.MetricService");
460 * This method returns the count of in progress requests
461 * * @return in progress requests count
464 public int getInprogressRequestCount() throws APPCException {
465 if (logger.isTraceEnabled()) {
466 logger.trace("Entering to getInprogressRequestCount");
468 return transactionRecorder.getInProgressRequestsCount();
471 public static String dateToStringConverterMillis(Date date) {
472 SimpleDateFormat customDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
473 customDate.setTimeZone(TimeZone.getTimeZone("UTC"));
475 return customDate.format(date);