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