2  * ============LICENSE_START=======================================================
\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 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
\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 
  21 package org.openecomp.mso.bpmn.common.workflow.service;
\r 
  23 import java.util.HashMap;
\r 
  24 import java.util.List;
\r 
  25 import java.util.Map;
\r 
  26 import java.util.UUID;
\r 
  27 import java.util.concurrent.atomic.AtomicLong;
\r 
  29 import javax.ws.rs.Consumes;
\r 
  30 import javax.ws.rs.POST;
\r 
  31 import javax.ws.rs.Path;
\r 
  32 import javax.ws.rs.PathParam;
\r 
  33 import javax.ws.rs.Produces;
\r 
  34 import javax.ws.rs.core.Context;
\r 
  35 import javax.ws.rs.core.Response;
\r 
  36 import javax.ws.rs.core.UriInfo;
\r 
  38 import org.camunda.bpm.engine.HistoryService;
\r 
  39 import org.camunda.bpm.engine.ProcessEngineException;
\r 
  40 import org.camunda.bpm.engine.ProcessEngineServices;
\r 
  41 import org.camunda.bpm.engine.ProcessEngines;
\r 
  42 import org.camunda.bpm.engine.RuntimeService;
\r 
  43 import org.camunda.bpm.engine.history.HistoricVariableInstance;
\r 
  44 import org.camunda.bpm.engine.runtime.ProcessInstance;
\r 
  45 import org.camunda.bpm.engine.variable.VariableMap;
\r 
  46 import org.camunda.bpm.engine.variable.Variables;
\r 
  47 import org.camunda.bpm.engine.variable.Variables.SerializationDataFormats;
\r 
  48 import org.camunda.bpm.engine.variable.impl.VariableMapImpl;
\r 
  49 import org.openecomp.mso.bpmn.core.WorkflowException;
\r 
  50 import org.openecomp.mso.logger.MessageEnum;
\r 
  51 import org.openecomp.mso.logger.MsoLogger;
\r 
  52 import org.slf4j.MDC;
\r 
  55 public class WorkflowResource {
\r 
  57         private ProcessEngineServices pes4junit = null;
\r 
  59         private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
\r 
  60         private static final String LOGMARKER = "[WRKFLOW-RESOURCE]";
\r 
  62         private static final int DEFAULT_WAIT_TIME = 30000;
\r 
  65         private UriInfo uriInfo = null;
\r 
  68          * Starts the process instance and responds to client synchronously
\r 
  69          * If the request does not contain mso-service-request-timeout then it waits for the value specified in DEFAULT_WAIT_TIME
\r 
  70          * Note: value specified in mso-service-request-timeout is in seconds
\r 
  71          * During polling time, if there is an exception encountered in the process execution then polling is stopped and the error response is 
\r 
  72          * returned to the client
\r 
  74          * @param variableMap
\r 
  78         @Path("/services/{processKey}")
\r 
  79         @Produces("application/json")
\r 
  80         @Consumes("application/json")
\r 
  81         public Response startProcessInstanceByKey(@PathParam("processKey") String processKey,
\r 
  82                         VariableMapImpl variableMap) {
\r 
  84                 Map<String, Object> inputVariables = getInputVariables(variableMap);    
\r 
  85                 setLogContext(processKey, inputVariables);
\r 
  87                 WorkflowResponse workflowResponse = new WorkflowResponse();
\r 
  88                 long startTime = System.currentTimeMillis();
\r 
  89                 ProcessInstance processInstance = null;
\r 
  92                         //Kickoff the process
\r 
  93                         ProcessThread thread = new ProcessThread(inputVariables,processKey,msoLogger);
\r 
  96                         Map<String, Object> responseMap = null;
\r 
  98                         //wait for process to be completed
\r 
  99                         long waitTime = getWaitTime(inputVariables);
\r 
 100                         long now = System.currentTimeMillis();
\r 
 102                         long endTime = start + waitTime;
\r 
 103                         long pollingInterval = 500;
\r 
 105                         // TEMPORARY LOGIC FOR UNIT TEST REFACTORING
\r 
 106                         // If this is a unit test (method is invoked directly), wait a max
\r 
 107                         // of 5 seconds after process ended for a result.  In production,
\r 
 108                         // wait up to 60 seconds.
\r 
 109                         long timeToWaitAfterProcessEnded = uriInfo == null ? 5000 : 60000;
\r 
 110                         AtomicLong timeProcessEnded = new AtomicLong(0);
\r 
 111                         boolean endedWithNoResponse = false;
\r 
 113                         while (now <= endTime) {
\r 
 114                                 Thread.sleep(pollingInterval);
\r 
 116                                 now = System.currentTimeMillis();
\r 
 118                                 // Increase the polling interval over time
\r 
 120                                 long elapsed = now - start;
\r 
 122                                 if (elapsed > 60000) {
\r 
 123                                         pollingInterval = 5000;
\r 
 124                                 } else if (elapsed > 10000) {
\r 
 125                                         pollingInterval = 1000;
\r 
 127                                 Exception exception = thread.getException();
\r 
 128                                 if (exception != null) {
\r 
 129                                         throw new Exception(exception);
\r 
 132                                 processInstance = thread.getProcessInstance();
\r 
 134                                 if (processInstance == null) {
\r 
 135                                         msoLogger.debug(LOGMARKER + processKey + " process has not been created yet");
\r 
 139                                 String processInstanceId = processInstance.getId();
\r 
 140                                 workflowResponse.setProcessInstanceID(processInstanceId);                               
\r 
 142                                 responseMap = getResponseMap(processInstance, processKey, timeProcessEnded);
\r 
 144                                 if (responseMap == null) {
\r 
 145                                         msoLogger.debug(LOGMARKER + processKey + " has not produced a response yet");
\r 
 147                                         if (timeProcessEnded.longValue() != 0) {
\r 
 148                                                 long elapsedSinceEnded = System.currentTimeMillis() - timeProcessEnded.longValue();
\r 
 150                                                 if (elapsedSinceEnded > timeToWaitAfterProcessEnded) {
\r 
 151                                                         endedWithNoResponse = true;
\r 
 156                                         processResponseMap(workflowResponse, responseMap);
\r 
 157                                         recordEvents(processKey, workflowResponse, startTime);
\r 
 158                                         return Response.status(workflowResponse.getMessageCode()).entity(workflowResponse).build();
\r 
 162                         //if we dont get response after waiting then send timeout response
\r 
 165                         String processInstanceId;
\r 
 167                         if (processInstance == null) {
\r 
 168                                 processInstanceId = "N/A";
\r 
 169                                 state = "NOT STARTED";
\r 
 171                                 processInstanceId = processInstance.getProcessInstanceId();
\r 
 172                                 state = isProcessEnded(processInstanceId) ? "ENDED" : "NOT ENDED";
\r 
 175                         workflowResponse.setMessage("Fail");
\r 
 176                         if (endedWithNoResponse) {
\r 
 177                                 workflowResponse.setResponse("Process ended without producing a response");
\r 
 179                                 workflowResponse.setResponse("Request timed out, process state: " + state);
\r 
 181                         workflowResponse.setProcessInstanceID(processInstanceId);
\r 
 182                         recordEvents(processKey, workflowResponse, startTime);
\r 
 183                         workflowResponse.setMessageCode(500);
\r 
 184                         return Response.status(500).entity(workflowResponse).build();
\r 
 185                 } catch (Exception ex) {
\r 
 186                         msoLogger.debug(LOGMARKER + "Exception in startProcessInstance by key");
\r 
 187                         ex.printStackTrace();
\r 
 188                         workflowResponse.setMessage("Fail" );
\r 
 189                         workflowResponse.setResponse("Error occurred while executing the process: " + ex.getMessage());
\r 
 190                         if (processInstance != null) workflowResponse.setProcessInstanceID(processInstance.getId());
\r 
 192                         msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION_ARG,  "BPMN", MDC.get(processKey), 
\r 
 193                                         MsoLogger.ErrorCode.UnknownError, LOGMARKER + workflowResponse.getMessage()
\r 
 194                                         + " for processKey: " + processKey + " with response: " + workflowResponse.getResponse());
\r 
 196                         workflowResponse.setMessageCode(500);
\r 
 197                         recordEvents(processKey, workflowResponse, startTime);
\r 
 198                         return Response.status(500).entity(workflowResponse).build();
\r 
 203          * Returns the wait time, this is used by the resource on how long it should wait to send a response
\r 
 204          * If none specified DEFAULT_WAIT_TIME is used
\r 
 205          * @param inputVariables
\r 
 208         private int getWaitTime(Map<String, Object> inputVariables)
\r 
 210                 String timeout = inputVariables.get("mso-service-request-timeout") == null
\r 
 211                         ? null : inputVariables.get("mso-service-request-timeout").toString();          
\r 
 213                 if (timeout != null) {
\r 
 215                                 return Integer.parseInt(timeout)*1000;
\r 
 216                         } catch (NumberFormatException nex) {
\r 
 217                                 msoLogger.debug("Invalid input for mso-service-request-timeout");
\r 
 220                 return DEFAULT_WAIT_TIME;
\r 
 223         private void recordEvents(String processKey, WorkflowResponse response, long startTime) {
\r 
 225                 msoLogger.recordMetricEvent ( startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
\r 
 226                                 LOGMARKER + response.getMessage() + " for processKey: "
\r 
 227                                 + processKey + " with response: " + response.getResponse(), "BPMN", MDC.get(processKey), null);
\r 
 229                 msoLogger.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
\r 
 230                                 LOGMARKER + response.getMessage() + " for processKey: "
\r 
 231                                 + processKey + " with response: " + response.getResponse());
\r 
 234         private void setLogContext(String processKey, Map<String, Object> inputVariables) {
\r 
 235                 MsoLogger.setServiceName("MSO." + processKey);
\r 
 236                 if (inputVariables != null) {
\r 
 237                         MsoLogger.setLogContext(getValueFromInputVariables(inputVariables, "mso-request-id"),
\r 
 238                                 getValueFromInputVariables(inputVariables, "mso-service-instance-id"));
\r 
 242         private String getValueFromInputVariables(Map<String,Object> inputVariables, String key) {
\r 
 243                 Object value = inputVariables.get(key);
\r 
 244                 if (value == null) {
\r 
 247                         return value.toString();
\r 
 252          * Checks to see if the specified process is ended.
\r 
 253          * @param processInstanceId the process instance ID
\r 
 254          * @return true if the process is ended
\r 
 256         private boolean isProcessEnded(String processInstanceId) {
\r 
 257                 ProcessEngineServices pes = getProcessEngineServices();
\r 
 259                         return pes.getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult() == null ? true : false ;
\r 
 260                 } catch (Exception e) {
\r 
 265         private void processResponseMap(WorkflowResponse workflowResponse, Map<String, Object> responseMap) {
\r 
 266                 Object object = responseMap.get("Response");
\r 
 267                 String response = object == null ? null : String.valueOf(object);
\r 
 268                 if(response == null){
\r 
 269                         object = responseMap.get("WorkflowResponse");
\r 
 270                         response = object == null ? null : String.valueOf(object);
\r 
 273                 workflowResponse.setResponse(response); 
\r 
 275                 object = responseMap.get("ResponseCode");
\r 
 276                 String responseCode = object == null ? null : String.valueOf(object);
\r 
 279                         workflowResponse.setMessageCode(Integer.parseInt(responseCode));
\r 
 280                 } catch(NumberFormatException nex) {
\r 
 281                         msoLogger.debug(LOGMARKER + "Failed to parse ResponseCode: " + responseCode);
\r 
 282                         workflowResponse.setMessageCode(-1);
\r 
 285                 Object status = responseMap.get("Status");
\r 
 287                 if ("Success".equalsIgnoreCase(String.valueOf(status))) {
\r 
 288                         workflowResponse.setMessage("Success");
\r 
 289                 } else if ("Fail".equalsIgnoreCase(String.valueOf(status))) {
\r 
 290                         workflowResponse.setMessage("Fail");
\r 
 292                         msoLogger.debug(LOGMARKER + "Unrecognized Status: " + responseCode);
\r 
 293                         workflowResponse.setMessage("Fail");
\r 
 299          * Triggers the workflow in a separate thread
\r 
 301         private class ProcessThread extends Thread {
\r 
 302                 private final Map<String,Object> inputVariables;
\r 
 303                 private final String processKey;
\r 
 304                 private final MsoLogger msoLogger;
\r 
 305                 private final String businessKey;
\r 
 306                 private ProcessInstance processInstance = null;
\r 
 307                 private Exception exception = null;
\r 
 309                 public ProcessThread(Map<String, Object> inputVariables, String processKey, MsoLogger msoLogger) {
\r 
 310                         this.inputVariables = inputVariables;
\r 
 311                         this.processKey = processKey;
\r 
 312                         this.msoLogger = msoLogger;
\r 
 313                         this.businessKey = UUID.randomUUID().toString();
\r 
 317                  * If an exception occurs when starting the process instance, it may
\r 
 318                  * be obtained by calling this method.  Note that exceptions are only
\r 
 319                  * recorded while the process is executing in its original thread.
\r 
 320                  * Once a process is suspended, exception recording stops.
\r 
 321                  * @return the exception, or null if none has occurred
\r 
 323                 public Exception getException() {
\r 
 328                 public ProcessInstance getProcessInstance() {
\r 
 329                         return this.processInstance;
\r 
 333                  * Sets the process instance exception.
\r 
 334                  * @param exception the exception
\r 
 336                 private void setException(Exception exception) {
\r 
 337                         this.exception = exception;
\r 
 340                 public void run() {
\r 
 341                         setLogContext(processKey, inputVariables);
\r 
 343                         long startTime = System.currentTimeMillis();
\r 
 346                                 msoLogger.debug(LOGMARKER + "***Received MSO startProcessInstanceByKey with processKey:"
\r 
 347                                         + processKey + " and variables: " + inputVariables);
\r 
 349                                 msoLogger.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, LOGMARKER
\r 
 350                                                 + "Call to MSO workflow/services in Camunda. Received MSO startProcessInstanceByKey with"
\r 
 351                                                 + " processKey:" + processKey
\r 
 352                                                 + " businessKey:" + businessKey
\r 
 353                                                 + " variables: " + inputVariables);
\r 
 355                                 RuntimeService runtimeService = getProcessEngineServices().getRuntimeService();
\r 
 357                                 // Note that this method doesn't return until the process suspends
\r 
 358                                 // itself or finishes.  We provide a business key so we can identify
\r 
 359                                 // the process instance immediately.
\r 
 360                                 processInstance = runtimeService.startProcessInstanceByKey(
\r 
 361                                         processKey, inputVariables);
\r 
 363                         } catch (Exception e) {
\r 
 364                                 msoLogger.debug(LOGMARKER + "ProcessThread caught an exception executing "
\r 
 365                                         + processKey + ": " + e);
\r 
 372         private Map<String, Object> getInputVariables(VariableMapImpl variableMap) {
\r 
 373                 VariableMap inputVariables = Variables.createVariables();
\r 
 374                 @SuppressWarnings("unchecked")
\r 
 375                 Map<String, Object> vMap = (Map<String, Object>) variableMap.get("variables");
\r 
 376                 for (String key : vMap.keySet()) { //variabe name vn
\r 
 377                         @SuppressWarnings("unchecked")
\r 
 378                         Map<String, Object> valueMap = (Map<String,Object>)vMap.get(key); //value, type
\r 
 379                         inputVariables.putValueTyped(key, Variables
\r 
 380                         .objectValue(valueMap.get("value"))
\r 
 381                         .serializationDataFormat(SerializationDataFormats.JAVA) // tells the engine to use java serialization for persisting the value
\r 
 384                 return inputVariables;
\r 
 388          * Attempts to get a response map from the specified process instance.
\r 
 389          * @return the response map, or null if it is unavailable
\r 
 391         private Map<String, Object> getResponseMap(ProcessInstance processInstance,
\r 
 392                         String processKey, AtomicLong timeProcessEnded) {
\r 
 394                 String responseMapVariable = processKey + "ResponseMap";
\r 
 395                 String processInstanceId = processInstance.getId();
\r 
 397                 // Query the runtime service to see if a response map is ready.
\r 
 399 /*              RuntimeService runtimeService = getProcessEngineServices().getRuntimeService();
\r 
 400                 List<Execution> executions = runtimeService.createExecutionQuery()
\r 
 401                         .processInstanceId(processInstanceId).list();
\r 
 403                 for (Execution execution : executions) {
\r 
 404                         @SuppressWarnings("unchecked")
\r 
 405                         Map<String, Object> responseMap = (Map<String, Object>)
\r 
 406                                 getVariableFromExecution(runtimeService, execution.getId(),
\r 
 407                                         responseMapVariable);
\r 
 409                         if (responseMap != null) {
\r 
 410                                 msoLogger.debug(LOGMARKER + "Obtained " + responseMapVariable
\r 
 411                                         + " from process " + processInstanceId + " execution "
\r 
 412                                         + execution.getId());
\r 
 413                                 return responseMap;
\r 
 417                 //Querying history seem to return consistent results compared to querying the runtime service
\r 
 419                 boolean alreadyEnded = timeProcessEnded.longValue() != 0;
\r 
 421                 if (alreadyEnded || isProcessEnded(processInstance.getId())) {
\r 
 422                         if (!alreadyEnded) {
\r 
 423                                 timeProcessEnded.set(System.currentTimeMillis());
\r 
 426                         // Query the history service to see if a response map exists.
\r 
 428                         HistoryService historyService = getProcessEngineServices().getHistoryService();
\r 
 429                         @SuppressWarnings("unchecked")
\r 
 430                         Map<String, Object> responseMap = (Map<String, Object>)
\r 
 431                                 getVariableFromHistory(historyService, processInstance.getId(),
\r 
 432                                         responseMapVariable);
\r 
 434                         if (responseMap != null) {
\r 
 435                                 msoLogger.debug(LOGMARKER + "Obtained " + responseMapVariable
\r 
 436                                         + " from process " + processInstanceId + " history");
\r 
 437                                 return responseMap;
\r 
 440                         // Query the history service for old-style response variables.
\r 
 442                         String prefix = (String) getVariableFromHistory(historyService, processInstanceId, "prefix");
\r 
 444                         if (prefix != null) {
\r 
 446                                 // Check for 'WorkflowResponse' variable
\r 
 447                                 Object workflowResponseObject = getVariableFromHistory(historyService, processInstanceId, "WorkflowResponse");
\r 
 448                                 String workflowResponse = workflowResponseObject == null ? null : String.valueOf(workflowResponseObject);
\r 
 449                                 msoLogger.debug(LOGMARKER + "WorkflowResponse: " + workflowResponse);
\r 
 451                                 if (workflowResponse != null) {
\r 
 452                                         Object responseCodeObject = getVariableFromHistory(historyService, processInstanceId, prefix + "ResponseCode");
\r 
 453                                         String responseCode = responseCodeObject == null ? null : String.valueOf(responseCodeObject);
\r 
 454                                         msoLogger.debug(LOGMARKER + prefix + "ResponseCode: " + responseCode);
\r 
 455                                         responseMap = new HashMap<String, Object>();
\r 
 456                                         responseMap.put("WorkflowResponse", workflowResponse);
\r 
 457                                         responseMap.put("ResponseCode", responseCode);
\r 
 458                                         responseMap.put("Status", "Success");
\r 
 459                                         return responseMap;
\r 
 463                                 // Check for 'WorkflowException' variable
\r 
 464                                 WorkflowException workflowException = null;
\r 
 465                                 String workflowExceptionText = null;
\r 
 467                                 Object workflowExceptionObject = getVariableFromHistory(historyService, processInstanceId, "WorkflowException");
\r 
 468                                 if(workflowExceptionObject != null) {
\r 
 469                                         if(workflowExceptionObject instanceof WorkflowException) {
\r 
 470                                                 workflowException = (WorkflowException) workflowExceptionObject;
\r 
 471                                                 workflowExceptionText = workflowException.toString();
\r 
 472                                                 responseMap = new HashMap<String, Object>();
\r 
 473                                                 responseMap.put("WorkflowException", workflowExceptionText);
\r 
 474                                                 responseMap.put("ResponseCode", workflowException.getErrorCode());
\r 
 475                                                 responseMap.put("Status", "Fail");
\r 
 476                                                 return responseMap;
\r 
 478                                         else if (workflowExceptionObject instanceof String) {
\r 
 479                                                 Object object = getVariableFromHistory(historyService, processInstanceId, prefix + "ResponseCode");
\r 
 480                                                 String responseCode = object == null ? null : String.valueOf(object);
\r 
 481                                                 workflowExceptionText = (String) workflowExceptionObject;
\r 
 482                                                 responseMap = new HashMap<String, Object>();
\r 
 483                                                 responseMap.put("WorkflowException", workflowExceptionText);
\r 
 484                                                 responseMap.put("ResponseCode", responseCode);
\r 
 485                                                 responseMap.put("Status", "Fail");
\r 
 486                                                 return responseMap;
\r 
 490                                 msoLogger.debug(LOGMARKER + "WorkflowException: " + workflowExceptionText);
\r 
 492                                 // BEGIN LEGACY SUPPORT.  TODO: REMOVE THIS CODE
\r 
 493                                 Object object = getVariableFromHistory(historyService, processInstanceId, processKey + "Response");
\r 
 494                                 String response = object == null ? null : String.valueOf(object);
\r 
 495                                 msoLogger.debug(LOGMARKER + processKey + "Response: " + response);
\r 
 497                                 if (response != null) {
\r 
 498                                         object = getVariableFromHistory(historyService, processInstanceId, prefix + "ResponseCode");
\r 
 499                                         String responseCode = object == null ? null : String.valueOf(object);
\r 
 500                                         msoLogger.debug(LOGMARKER + prefix + "ResponseCode: " + responseCode);
\r 
 501                                         responseMap = new HashMap<String, Object>();
\r 
 502                                         responseMap.put("Response", response);
\r 
 503                                         responseMap.put("ResponseCode", responseCode);
\r 
 504                                         responseMap.put("Status", "Success");
\r 
 505                                         return responseMap;
\r 
 508                                 object = getVariableFromHistory(historyService, processInstanceId, prefix + "ErrorResponse");
\r 
 509                                 String errorResponse = object == null ? null : String.valueOf(object);
\r 
 510                                 msoLogger.debug(LOGMARKER + prefix + "ErrorResponse: " + errorResponse);
\r 
 512                                 if (errorResponse != null) {
\r 
 513                                         object = getVariableFromHistory(historyService, processInstanceId, prefix + "ResponseCode");
\r 
 514                                         String responseCode = object == null ? null : String.valueOf(object);
\r 
 515                                         msoLogger.debug(LOGMARKER + prefix + "ResponseCode: " + responseCode);
\r 
 516                                         responseMap = new HashMap<String, Object>();
\r 
 517                                         responseMap.put("Response", errorResponse);
\r 
 518                                         responseMap.put("ResponseCode", responseCode);
\r 
 519                                         responseMap.put("Status", "Fail");
\r 
 520                                         return responseMap;
\r 
 522                                 // END LEGACY SUPPORT.  TODO: REMOVE THIS CODE
\r 
 529          * Gets a variable value from the specified execution.
\r 
 530          * @return the variable value, or null if the variable could not be
\r 
 533         private Object getVariableFromExecution(RuntimeService runtimeService,
\r 
 534                         String executionId, String variableName) {
\r 
 536                         return runtimeService.getVariable(executionId, variableName);
\r 
 537                 } catch (ProcessEngineException e) {
\r 
 538                         // Most likely cause is that the execution no longer exists.
\r 
 539                         msoLogger.debug("Error retrieving execution " + executionId
\r 
 540                                 + " variable " + variableName + ": " + e);
\r 
 545          * Gets a variable value from specified historical process instance.
\r 
 546          * @return the variable value, or null if the variable could not be
\r 
 549         private Object getVariableFromHistory(HistoryService historyService,
\r 
 550                         String processInstanceId, String variableName) {
\r 
 552                         HistoricVariableInstance v = historyService.createHistoricVariableInstanceQuery()
\r 
 553                                 .processInstanceId(processInstanceId).variableName(variableName).singleResult();
\r 
 554                         return v == null ? null : v.getValue();
\r 
 555                 } catch (Exception e) {
\r 
 556                         msoLogger.debug("Error retrieving process " + processInstanceId
\r 
 557                                 + " variable " + variableName + " from history: " + e);
\r 
 563         @Path("/services/{processKey}/{processInstanceId}")
\r 
 564         @Produces("application/json")
\r 
 565         @Consumes("application/json")
\r 
 566         public WorkflowResponse getProcessVariables(@PathParam("processKey") String processKey, @PathParam("processInstanceId") String processInstanceId) {
\r 
 567                 //TODO filter only set of variables
\r 
 568                 WorkflowResponse response = new WorkflowResponse();
\r 
 570                 long startTime = System.currentTimeMillis();
\r 
 572                         ProcessEngineServices engine = getProcessEngineServices();
\r 
 573                         List<HistoricVariableInstance> variables = engine.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
\r 
 574                         Map<String,String> variablesMap = new HashMap<String,String>();
\r 
 575                         for (HistoricVariableInstance variableInstance: variables) {
\r 
 576                                 variablesMap.put(variableInstance.getName(), variableInstance.getValue().toString());
\r 
 579                         msoLogger.debug(LOGMARKER + "***Received MSO getProcessVariables with processKey:" + processKey + " and variables: " + variablesMap.toString());
\r 
 581                         msoLogger.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, LOGMARKER 
\r 
 582                                         + "Call to MSO workflow/services in Camunda. Received MSO getProcessVariables with processKey:" 
\r 
 583                                         + processKey + " and variables: " 
\r 
 584                                         + variablesMap.toString());
\r 
 587                         response.setVariables(variablesMap);
\r 
 588                         response.setMessage("Success");
\r 
 589                         response.setResponse("Successfully retrieved the variables"); 
\r 
 590                         response.setProcessInstanceID(processInstanceId);
\r 
 592                         msoLogger.debug(LOGMARKER + response.getMessage() + " for processKey: " + processKey + " with response: " + response.getResponse());
\r 
 593                 } catch (Exception ex) {
\r 
 594                         response.setMessage("Fail");
\r 
 595                         response.setResponse("Failed to retrieve the variables," + ex.getMessage()); 
\r 
 596                         response.setProcessInstanceID(processInstanceId);
\r 
 598                         msoLogger.error (MessageEnum.BPMN_GENERAL_EXCEPTION_ARG, "BPMN", MDC.get(processKey), MsoLogger.ErrorCode.UnknownError, LOGMARKER 
\r 
 599                                         + response.getMessage() 
\r 
 600                                         + " for processKey: " 
\r 
 602                                         + " with response: " 
\r 
 603                                         + response.getResponse());
\r 
 607                 msoLogger.recordMetricEvent ( startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
\r 
 608                                 LOGMARKER + response.getMessage() + " for processKey: "
\r 
 609                                 + processKey + " with response: " + response.getResponse(), "BPMN", MDC.get(processKey), null);
\r 
 611                 msoLogger.recordAuditEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
\r 
 612                                 LOGMARKER + response.getMessage() + " for processKey: "
\r 
 613                                 + processKey + " with response: " + response.getResponse());
\r 
 618         private ProcessEngineServices getProcessEngineServices() {
\r 
 619                 if (pes4junit == null) {
\r 
 620                         return ProcessEngines.getDefaultProcessEngine();
\r 
 626         public void setProcessEngineServices4junit(ProcessEngineServices pes) {
\r