Initial OpenECOMP MSO commit
[so.git] / bpmn / MSOGammaBPMN / src / main / java / org / openecomp / mso / bpmn / gamma / workflow / service / WorkflowContextHolder.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.openecomp.mso.bpmn.gamma.workflow.service;
22
23 import java.util.concurrent.DelayQueue;
24 import java.util.concurrent.TimeUnit;
25
26 import javax.ws.rs.core.Response;
27
28 import org.jboss.resteasy.spi.AsynchronousResponse;
29 import org.slf4j.MDC;
30
31 import org.openecomp.mso.logger.MessageEnum;
32 import org.openecomp.mso.logger.MsoLogger;
33
34 /**
35  * Workflow Context Holder instance which can be accessed elsewhere either in groovy scripts or Java
36  * @version 1.0
37  *
38  */
39 public class WorkflowContextHolder {
40
41         private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
42         private static final String logMarker = "[WORKFLOW-CONTEXT-HOLDER]";
43         private static WorkflowContextHolder instance = null;
44
45         /**
46          * Delay Queue which holds workflow context holder objects
47          */
48         private final DelayQueue<WorkflowContext> responseQueue = new DelayQueue<WorkflowContext>();
49         private final TimeoutThread timeoutThread = new TimeoutThread();
50
51         private WorkflowContextHolder() {
52                 timeoutThread.start();
53         }
54
55         /**
56          * Singleton holder which eliminates hot lock
57          * Since the JVM synchronizes static method there is no synchronization needed for this method
58          * @return
59          */
60         public static synchronized WorkflowContextHolder getInstance() {
61                 if (instance == null) {
62                         instance = new WorkflowContextHolder();
63                 }
64                 return instance;
65         }
66         
67         public void put(WorkflowContext context) {
68                 msoLogger.debug(logMarker + " Adding context to the queue: "
69                         + context.getRequestId());
70                 responseQueue.put(context);
71         }
72         
73         public void remove(WorkflowContext context) {
74                 msoLogger.debug(logMarker + " Removing context from the queue: "
75                         + context.getRequestId());
76                 responseQueue.remove(context);
77         }
78         
79         public WorkflowContext getWorkflowContext(String requestId) {
80                 // Note: DelayQueue interator is threadsafe
81                 for (WorkflowContext context : responseQueue) {
82                         if (requestId.equals(context.getRequestId())) {
83                                 msoLogger.debug("Found context for request id: " + requestId);
84                                 return context;
85                         }
86                 }
87
88                 msoLogger.debug("Unable to find context for request id: " + requestId);
89                 return null;
90         }
91         
92         /**
93          * Builds the callback response object to respond to client
94          * @param processKey
95          * @param processInstanceId
96          * @param requestId
97          * @param callbackResponse
98          * @return
99          */
100         public Response processCallback(String processKey, String processInstanceId,
101                         String requestId, WorkflowCallbackResponse callbackResponse) {
102                 WorkflowResponse workflowResponse = new WorkflowResponse();
103                 WorkflowContext workflowContext = getWorkflowContext(requestId);
104
105                 if (workflowContext == null) {
106                         msoLogger.debug("Unable to correlate workflow context for request id: " + requestId
107                                 + ":processInstance Id:" + processInstanceId
108                                 + ":process key:" + processKey);
109                         workflowResponse.setMessage("Fail");
110                         workflowResponse.setMessageCode(400);
111                         workflowResponse.setResponse("Unable to correlate workflow context, bad request. Request Id: " + requestId);
112                         return Response.serverError().entity(workflowResponse).build();
113                 }
114
115                 responseQueue.remove(workflowContext);
116
117                 msoLogger.debug("Using callback response for request id: " + requestId);
118                 workflowResponse.setResponse(callbackResponse.getResponse());
119                 workflowResponse.setProcessInstanceID(processInstanceId);
120                 workflowResponse.setMessageCode(callbackResponse.getStatusCode());
121                 workflowResponse.setMessage(callbackResponse.getMessage());
122                 sendWorkflowResponseToClient(processKey, workflowContext, workflowResponse);
123                 return Response.ok().entity(workflowResponse).build();
124         }
125         
126         /**
127          * Send the response to client asynchronously when invoked by the BPMN process
128          * @param processKey
129          * @param workflowContext
130          * @param workflowResponse
131          */
132         private void sendWorkflowResponseToClient(String processKey, WorkflowContext workflowContext,
133                         WorkflowResponse workflowResponse) {
134                 msoLogger.debug(logMarker + "Sending the response for request id: " + workflowContext.getRequestId());
135                 recordEvents(processKey, workflowResponse, workflowContext.getStartTime());
136                 Response response = Response.status(workflowResponse.getMessageCode()).entity(workflowResponse).build();
137                 AsynchronousResponse asyncResp = workflowContext.getAsynchronousResponse();
138                 asyncResp.setResponse(response);
139         }
140
141         /**
142          * Timeout thread which monitors the delay queue for expired context and send timeout response
143          * to client
144          *
145          * */
146         private class TimeoutThread extends Thread {
147                 public void run() {
148                         while (!isInterrupted()) {
149                                 try {
150                                         WorkflowContext requestObject = responseQueue.take();                                   
151                                         msoLogger.debug("Time remaining for request id: " + requestObject.getRequestId() + ":" + requestObject.getDelay(TimeUnit.MILLISECONDS));
152                                         msoLogger.debug("Preparing timeout response for " + requestObject.getProcessKey() + ":" + ":" + requestObject.getRequestId());
153                                         WorkflowResponse response = new WorkflowResponse();
154                                         response.setMessage("Fail");
155                                         response.setResponse("Request timedout, request id:" + requestObject.getRequestId());
156                                         //response.setProcessInstanceID(requestObject.getProcessInstance().getProcessInstanceId());
157                                         recordEvents(requestObject.getProcessKey(), response, requestObject.getStartTime());
158                                         response.setMessageCode(500);
159                                         Response result = Response.status(500).entity(response).build();
160                                         requestObject.getAsynchronousResponse().setResponse(result);
161                                         msoLogger.debug("Sending timeout response for request id:" + requestObject.getRequestId() + ":response:" + response);
162                                 } catch (InterruptedException e) {
163                                         break;
164                                 } catch (Exception e) {
165                                         msoLogger.debug("WorkflowContextHolder timeout thread caught exception: " + e);
166                                 msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), 
167                                                 MsoLogger.ErrorCode.UnknownError, "Error in WorkflowContextHolder timeout thread");
168                                 
169                                 }
170                         }
171
172                         msoLogger.debug("WorkflowContextHolder timeout thread interrupted, quitting");
173                 }
174         }
175         
176         private static void recordEvents(String processKey, WorkflowResponse response,
177                         long startTime) {
178
179                 msoLogger.recordMetricEvent ( startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
180                                 logMarker + response.getMessage() + " for processKey: "
181                                 + processKey + " with response: " + response.getResponse(), "BPMN", MDC.get(processKey), null);
182                 
183                 msoLogger.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, logMarker 
184                                 + response.getMessage() + " for processKey: " 
185                                 + processKey + " with response: " + response.getResponse());
186                 
187         }
188 }