// In case of notifications coming from post notifications, start and end time need to be // populated. private void updateContextWithTime(WorkflowExecutionContext context) { try { InstancesResult result = WorkflowEngineFactory.getWorkflowEngine() .getJobDetails(context.getClusterName(), context.getWorkflowId()); Date startTime = result.getInstances()[0].startTime; Date endTime = result.getInstances()[0].endTime; Date now = new Date(); if (startTime == null) { startTime = now; } if (endTime == null) { endTime = now; } context.setValue(WorkflowExecutionArgs.WF_START_TIME, Long.toString(startTime.getTime())); context.setValue(WorkflowExecutionArgs.WF_END_TIME, Long.toString(endTime.getTime())); } catch (FalconException e) { LOG.error( "Unable to retrieve job details for " + context.getWorkflowId() + " on cluster " + context.getClusterName(), e); } }
// The method retrieves the conf from the cache if it is in cache. // Else, queries WF Engine to retrieve the conf of the workflow private void updateContextFromWFConf(WorkflowExecutionContext context) { try { Properties wfProps = contextMap.get(context.getWorkflowId()); if (wfProps == null) { Entity entity = CONFIG_STORE.get(EntityType.valueOf(context.getEntityType()), context.getEntityName()); // Entity can be null in case of delete. Engine will generate notifications for instance // kills. // But, the entity would no longer be in the config store. if (entity == null) { return; } for (String cluster : EntityUtil.getClustersDefinedInColos(entity)) { try { InstancesResult.Instance[] instances = WorkflowEngineFactory.getWorkflowEngine() .getJobDetails(cluster, context.getWorkflowId()) .getInstances(); if (instances != null && instances.length > 0) { wfProps = getWFProps(instances[0].getWfParams()); // Required by RetryService. But, is not part of conf. wfProps.setProperty( WorkflowExecutionArgs.RUN_ID.getName(), Integer.toString(instances[0].getRunId())); } } catch (FalconException e) { // Do Nothing. The workflow may not have been deployed on this cluster. continue; } contextMap.put(context.getWorkflowId(), wfProps); } } // No extra props to enhance the context with. if (wfProps == null || wfProps.isEmpty()) { return; } for (WorkflowExecutionArgs arg : WorkflowExecutionArgs.values()) { if (wfProps.containsKey(arg.getName())) { context.setValue(arg, wfProps.getProperty(arg.getName())); } } } catch (FalconException e) { LOG.error("Unable to retrieve entity {} of type {} from config store.", e); } }
// This method handles both success and failure notifications. private void notifyWorkflowEnd(WorkflowExecutionContext context) { // Need to distinguish notification from post processing for backward compatibility if (context.getContextType() == WorkflowExecutionContext.Type.POST_PROCESSING) { boolean engineNotifEnabled = false; try { engineNotifEnabled = WorkflowEngineFactory.getWorkflowEngine() .isNotificationEnabled(context.getClusterName(), context.getWorkflowId()); } catch (FalconException e) { LOG.debug( "Received error while checking if notification is enabled. " + "Hence, assuming notification is not enabled."); } // Ignore the message from post processing as there will be one more from Oozie. if (engineNotifEnabled) { LOG.info("Ignoring message from post processing as engine notification is enabled."); return; } else { updateContextWithTime(context); } } else { updateContextFromWFConf(context); } LOG.debug("Sending workflow end notification to listeners with context : {} ", context); for (WorkflowExecutionListener listener : listeners) { try { if (context.hasWorkflowSucceeded()) { listener.onSuccess(context); instrumentAlert(context); } else { listener.onFailure(context); if (context.hasWorkflowBeenKilled() || context.hasWorkflowFailed()) { instrumentAlert(context); } } } catch (Throwable t) { // do not rethrow as other listeners do not get a chance LOG.error("Error in listener {}", listener.getClass().getName(), t); } } contextMap.remove(context.getWorkflowId()); }