730c1451d5429081f52ea8d5fd09e1b7ed5ab0b3
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / openecomp / mso / bpmn / core / plugins / LoggingAndURNMappingPlugin.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
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.core.plugins;
22
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27
28 import org.camunda.bpm.engine.delegate.DelegateExecution;
29 import org.camunda.bpm.engine.delegate.ExecutionListener;
30 import org.camunda.bpm.engine.impl.bpmn.parser.AbstractBpmnParseListener;
31 import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener;
32 import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin;
33 import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
34 import org.camunda.bpm.engine.impl.context.Context;
35 import org.camunda.bpm.engine.impl.interceptor.Command;
36 import org.camunda.bpm.engine.impl.interceptor.CommandContext;
37 import org.camunda.bpm.engine.impl.persistence.entity.ProcessDefinitionEntity;
38 import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl;
39 import org.camunda.bpm.engine.impl.pvm.process.ScopeImpl;
40 import org.camunda.bpm.engine.impl.pvm.process.TransitionImpl;
41 import org.camunda.bpm.engine.impl.util.xml.Element;
42 import org.camunda.bpm.engine.impl.variable.VariableDeclaration;
43
44 import org.openecomp.mso.bpmn.core.BPMNLogger;
45 import org.openecomp.mso.bpmn.core.PropertyConfiguration;
46 import org.openecomp.mso.bpmn.core.mybatis.CustomMyBatisSessionFactory;
47 import org.openecomp.mso.bpmn.core.mybatis.URNMapping;
48 import org.openecomp.mso.logger.MessageEnum;
49 import org.openecomp.mso.logger.MsoLogger;
50
51 /**
52  * Plugin for MSO logging and URN mapping.
53  */
54 public class LoggingAndURNMappingPlugin extends AbstractProcessEnginePlugin {
55         private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
56         private static final String FSPROPKEY = "URNMapping.FileSystemLoading.Enabled";
57
58         @Override
59         public void preInit(
60                         ProcessEngineConfigurationImpl processEngineConfiguration) {
61                 List<BpmnParseListener> preParseListeners = processEngineConfiguration
62                                 .getCustomPreBPMNParseListeners();
63                 if (preParseListeners == null) {
64                         preParseListeners = new ArrayList<>();
65                         processEngineConfiguration.setCustomPreBPMNParseListeners(preParseListeners);
66                 }
67                 preParseListeners.add(new LoggingParseListener());
68         }
69         
70         /**
71          * Called when a process flow is parsed so we can inject listeners.
72          */
73         public static class LoggingParseListener extends AbstractBpmnParseListener {
74                 private void injectLogExecutionListener(ActivityImpl activity) {
75                         activity.addListener(
76                                         ExecutionListener.EVENTNAME_END,
77                                         new LoggingExecutionListener("END"));
78
79                         activity.addListener(
80                                         ExecutionListener.EVENTNAME_START,
81                                         new LoggingExecutionListener("START"));
82
83                         activity.addListener(
84                                         ExecutionListener.EVENTNAME_TAKE,
85                                         new LoggingExecutionListener("TAKE"));
86                 }
87
88                 @Override
89                 public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
90                 }
91
92                 @Override
93                 public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity) {
94                         // Inject these listeners only on the main start event for the flow, not on any embedded subflow start events
95                         if (scope instanceof ProcessDefinitionEntity) {
96                                 startEventActivity.addListener(ExecutionListener.EVENTNAME_START, new URNMappingInitializerListener("START"));
97                                 startEventActivity.addListener(ExecutionListener.EVENTNAME_START, new LoggingInitializerListener("START"));
98                         }
99
100                         injectLogExecutionListener(startEventActivity);
101                 }
102
103                 @Override
104                 public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity) {
105                         injectLogExecutionListener(activity);
106                 }
107
108                 @Override
109                 public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
110                         injectLogExecutionListener(activity);
111                 }
112
113                 @Override
114                 public void parseInclusiveGateway(Element inclusiveGwElement, ScopeImpl scope, ActivityImpl activity) {
115                         injectLogExecutionListener(activity);
116                 }
117
118                 @Override
119                 public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity) {
120                         injectLogExecutionListener(activity);
121                 }
122
123                 @Override
124                 public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity) {
125                         injectLogExecutionListener(activity);
126                 }
127
128                 @Override
129                 public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity) {
130                         injectLogExecutionListener(activity);
131                 }
132
133                 @Override
134                 public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity) {
135                         injectLogExecutionListener(activity);
136                 }
137
138                 @Override
139                 public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity) {
140                         injectLogExecutionListener(activity);
141                 }
142
143                 @Override
144                 public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) {
145                         injectLogExecutionListener(activity);
146                 }
147
148                 @Override
149                 public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity) {
150                         injectLogExecutionListener(activity);
151                 }
152
153                 @Override
154                 public void parseBoundaryTimerEventDefinition(Element timerEventDefinition, boolean interrupting, ActivityImpl timerActivity) {
155                         injectLogExecutionListener(timerActivity);
156                 }
157
158                 @Override
159                 public void parseBoundaryErrorEventDefinition(Element errorEventDefinition, boolean interrupting, ActivityImpl activity, ActivityImpl nestedErrorEventActivity) {
160                         injectLogExecutionListener(activity);
161                 }
162
163                 @Override
164                 public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity) {
165                         injectLogExecutionListener(activity);
166                 }
167
168                 @Override
169                 public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity) {
170                         injectLogExecutionListener(activity);
171                 }
172
173                 @Override
174                 public void parseProperty(Element propertyElement, VariableDeclaration variableDeclaration, ActivityImpl activity) {
175                         injectLogExecutionListener(activity);
176                 }
177
178                 @Override
179                 public void parseSequenceFlow(Element sequenceFlowElement, ScopeImpl scopeElement, TransitionImpl transition) {
180                         //injectLogExecutionListener(activity);
181                 }
182
183                 @Override
184                 public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity) {
185                         injectLogExecutionListener(activity);
186                 }
187
188                 @Override
189                 public void parseMultiInstanceLoopCharacteristics(Element activityElement, Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity) {
190                         injectLogExecutionListener(activity);
191                 }
192
193                 @Override
194                 public void parseIntermediateTimerEventDefinition(Element timerEventDefinition, ActivityImpl timerActivity) {
195                         injectLogExecutionListener(timerActivity);
196                 }
197
198                 @Override
199                 public void parseRootElement(Element rootElement, List<ProcessDefinitionEntity> processDefinitions) {
200                         //injectLogExecutionListener(activity);
201                 }
202
203                 @Override
204                 public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope, ActivityImpl activity) {
205                         injectLogExecutionListener(activity);
206                 }
207
208                 @Override
209                 public void parseIntermediateSignalCatchEventDefinition(Element signalEventDefinition, ActivityImpl signalActivity) {
210                         injectLogExecutionListener(signalActivity);
211                 }
212
213                 @Override
214                 public void parseBoundarySignalEventDefinition(Element signalEventDefinition, boolean interrupting, ActivityImpl signalActivity) {
215                         injectLogExecutionListener(signalActivity);
216                 }
217
218                 @Override
219                 public void parseEventBasedGateway(Element eventBasedGwElement, ScopeImpl scope, ActivityImpl activity) {
220                         injectLogExecutionListener(activity);
221                 }
222
223                 @Override
224                 public void parseTransaction(Element transactionElement, ScopeImpl scope, ActivityImpl activity) {
225                         injectLogExecutionListener(activity);
226                 }
227
228                 @Override
229                 public void parseCompensateEventDefinition(Element compensateEventDefinition, ActivityImpl compensationActivity) {
230                         injectLogExecutionListener(compensationActivity);
231                 }
232
233                 @Override
234                 public void parseIntermediateThrowEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
235                         injectLogExecutionListener(activity);
236                 }
237
238                 @Override
239                 public void parseIntermediateCatchEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
240                         injectLogExecutionListener(activity);
241                 }
242
243                 @Override
244                 public void parseBoundaryEvent(Element boundaryEventElement, ScopeImpl scopeElement, ActivityImpl nestedActivity) {
245                         injectLogExecutionListener(nestedActivity);
246                 }
247
248                 @Override
249                 public void parseIntermediateMessageCatchEventDefinition(Element messageEventDefinition, ActivityImpl nestedActivity) {
250                         injectLogExecutionListener(nestedActivity);
251                 }
252
253                 @Override
254                 public void parseBoundaryMessageEventDefinition(Element element, boolean interrupting, ActivityImpl messageActivity) {
255                         injectLogExecutionListener(messageActivity);
256                 }
257         }
258
259         /**
260          * Initializes URN mapping variables on process entry.
261          */
262         public static class URNMappingInitializerListener implements ExecutionListener {
263                 private String event;
264
265                 public URNMappingInitializerListener(String eventData) {
266                         this.event = eventData;
267                 }
268
269                 public String getEvent() {
270                         return event;
271                 }
272
273                 @Override
274                 public void notify(DelegateExecution execution) throws Exception {
275                         ProcessEngineConfigurationImpl processEngineConfiguration =
276                                 Context.getProcessEngineConfiguration();
277                         loadURNProperties(execution, processEngineConfiguration);
278                 }
279
280                 private void loadURNProperties(DelegateExecution execution,
281                                 ProcessEngineConfigurationImpl processEngineConfiguration) {
282                         Map<String,String> bpmnProps = PropertyConfiguration.getInstance().getProperties("mso.bpmn.properties");
283                         if (bpmnProps == null) {
284                                 LOGGER.debug("Unable to load mso.bpmn.properties; loading URN Mapping from DB");
285                                 
286                                 LOGGER.error (MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, 
287                                         "Unable to load mso.bpmn.properties; loading URN Mapping from DB");
288                                 
289                                 loadFromDB(execution, processEngineConfiguration);
290                         } else {
291                                 String fsEnabled = bpmnProps.get(FSPROPKEY);
292                                 if (fsEnabled != null) {
293                                         if (Boolean.parseBoolean(fsEnabled)) {
294                                                 LOGGER.debug("File system loading is enabled; loading URN properties from File system");
295                                                 LOGGER.info(MessageEnum.BPMN_GENERAL_INFO,  "BPMN",  "File system loading is enabled; loading URN properties from File System");
296                                                 loadFromFileSystem(execution);
297                                         } else {
298                                                 LOGGER.debug("File system loading is disabled; loading URN properties from DB");
299                                                 LOGGER.info (MessageEnum.BPMN_GENERAL_INFO, "BPMN", "File system loading is disabled; loading URN properties from DB");
300                                                 
301                                                 loadFromDB(execution, processEngineConfiguration);
302                                         }
303                                 } else {
304                                         
305                                         LOGGER.error (MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, 
306                                                 "Unable to retrieve URNMapping.FileSystemLoading.Enabled from mso.bpmn.properties; loading URN Mapping from DB");
307                                         
308                                         loadFromDB(execution, processEngineConfiguration);
309                                 }
310                         }
311                 }
312
313                 private void loadFromFileSystem(DelegateExecution execution) {
314                         PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
315                         Map<String,String> props = propertyConfiguration.getProperties("mso.bpmn.urn.properties");
316                         for (String key : props.keySet()) {
317                                 String varName = URNMapping.createIdentifierFromURN(key);
318                                 String varValue = props.get(key);
319                                 execution.setVariable(varName, varValue);
320                         }
321                 }
322
323                 private void loadFromDB(DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) {
324                         Command<List<URNMapping>> command = commandContext -> (List<URNMapping>) commandContext.getDbSqlSession()
325                                 .selectList("mso.urnMapping.selectAll", null);
326
327                         CustomMyBatisSessionFactory sessionFactory = new CustomMyBatisSessionFactory();
328                         sessionFactory.initFromProcessEngineConfiguration(processEngineConfiguration,
329                                 "customMyBatisConfiguration.xml");
330
331                         List<URNMapping> mappings = sessionFactory.getCommandExecutorTxRequired().execute(command);
332
333                         if (mappings != null && !mappings.isEmpty()) {
334                                 for (URNMapping mapping : mappings) {
335                                         String varName = URNMapping.createIdentifierFromURN(mapping.getName());
336                                         String varValue = mapping.getValue();
337
338                                         LOGGER.debug("URN Mapping = '" + mapping.getName()
339                                                 + "', setting variable '" + varName + "' to '" + varValue + "'");
340
341                                         execution.setVariable(varName, varValue);
342                                 }
343                         }
344                 }
345         }
346
347         /**
348          * Sets the isDebugLogEnabled variable on process entry.
349          */
350         public static class LoggingInitializerListener implements ExecutionListener {
351                 private String event;
352
353                 public LoggingInitializerListener(String eventData) {
354                         this.event = eventData;
355                 }
356
357                 public String getEvent() {
358                         return event;
359                 }
360
361                 @Override
362                 public void notify(DelegateExecution execution) throws Exception {
363                         String processKey = execution.getProcessEngineServices().getRepositoryService()
364                                 .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
365
366                         // If a "true" value is already injected, e.g. from a top-level flow, it SHOULD NOT be
367                         // overridden by the value in the URN mapping. This allows a top-level flow and all
368                         // invoked subflows to be debugged by turning on the debug flag for just the top-level
369                         // flow, assuming the isDebugEnabled flag variable is passed from the top-level flow to
370                         // its subflows.
371
372                         // If a "false" value is already injected, e.g. from a top-level flow, it SHOULD be
373                         // overridden by the value in the URN mapping.  This allows a subflow to be debugged
374                         // without turning on the the debug flag for the top-level flow.
375
376                         String injectedValue = (String) execution.getVariable("isDebugLogEnabled");
377                         String urnValue = "true".equals(execution.getVariable("URN_log_debug_" + processKey)) ? "true" : "false";
378
379                         if ("true".equals(injectedValue)) {
380                                 LOGGER.debug("Setting isDebugLogEnabled to \"" + injectedValue + "\" for process: " + processKey + " (injected value)");
381                                 execution.setVariable("isDebugLogEnabled", injectedValue);
382                         } else {
383                                 LOGGER.debug("Setting isDebugLogEnabled to \"" + urnValue + "\" for process: " + processKey + " (from URN mapping)");
384                                 execution.setVariable("isDebugLogEnabled", urnValue);
385                         }
386                 }
387         }
388         
389         /**
390          * Logs details about the current activity.
391          */
392         public static class LoggingExecutionListener implements ExecutionListener {
393                 private static MsoLogger LOGGER = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
394                 private static ConcurrentHashMap<String, Long> startTimes = new ConcurrentHashMap<>();
395
396                 private String event;
397
398                 public LoggingExecutionListener(String event) {
399                         this.event = event;
400                 }
401
402                 public String getEvent() {
403                         return event;
404                 }
405
406                 public void notify(DelegateExecution execution) throws Exception {
407                         BPMNLogger.debug(
408                                 (String) execution.getVariable("isDebugLogEnabled"),
409                                 "Logging for activity---------------:" + event + ":"
410                                                 + execution.getCurrentActivityName()
411                                                 + ", processDefinitionId="
412                                                 + execution.getProcessDefinitionId() + ", activtyId="
413                                                 + execution.getCurrentActivityId() + ", activtyName='"
414                                                 + execution.getCurrentActivityName() + "'"
415                                                 + ", processInstanceId="
416                                                 + execution.getProcessInstanceId() + ", businessKey="
417                                                 + execution.getProcessBusinessKey() + ", executionId="
418                                                 + execution.getId());
419
420                         if (!isBlank(execution.getCurrentActivityName())) {
421                                 try {
422                                         String id = execution.getId();
423                                         if ("START".equals(event) && id != null ) {
424                                                 startTimes.put(id, (Long)System.currentTimeMillis());
425                                         } else if ("END".equals(event) && id != null) {
426                                                 String prefix = (String) execution.getVariable("prefix");
427
428                                                 if (prefix != null ) {
429                                                         MsoLogger.setServiceName("MSO." + prefix.substring(0,prefix.length()-1));
430                                                 }
431
432                                                 String requestId = (String) execution.getVariable("mso-request-id");
433                                                 String svcid = (String) execution.getVariable("mso-service-instance-id");
434                                                 MsoLogger.setLogContext(requestId, svcid);
435                                                 long startTime = startTimes.remove(id);
436
437                                                 if (startTime != 0) {
438                                                         
439                                                         LOGGER.recordMetricEvent (startTime, MsoLogger.StatusCode.COMPLETE, MsoLogger.ResponseCode.Suc, 
440                                                                         event + ": " + execution.getCurrentActivityName(), "BPMN", execution.getCurrentActivityName(), null);
441                                                         
442                                                 }
443                                         }
444                                 } catch(Exception e) {
445                                         LOGGER.debug("Exception at notify: " + e);
446                                 }
447                         }
448                 }
449
450                 private boolean isBlank(Object object) {
451                         return object == null || "".equals(object.toString().trim());
452                 }
453         }
454 }