Custom Incident Handler

Hi all,

has anyone ever managed to code a custom incident Handler? This is hardly documented. It seems that the Process Engine Plugin is injected as it is executed when I start up the engine in Debug mode. However, when I raise an incident, nothing happends. The incident is shown in cockpit but the code in the incident handler is not executed.

Process Engine Plugin “IncidentHandlerProcessEnginePlugin”:

public class IncidentHandlerProcessEnginePlugin  extends AbstractProcessEnginePlugin  {
@Override
  public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
    List<IncidentHandler> customIncidentHandlers = new ArrayList<IncidentHandler>();
    customIncidentHandlers.add(new myincidentHandler());
    processEngineConfiguration.setCustomIncidentHandlers(customIncidentHandlers );

}	

}

Incident Handler:

public class myincidentHandler implements IncidentHandler  {
	
	public final static String INCIDENT_HANDLER_TYPE = "failedJob";

	  public String getIncidentHandlerType() {
	    return INCIDENT_HANDLER_TYPE;
	}
	
	public void handleIncident(IncidentContext arg0, String arg1) {
		
			// Send Mail... -> COde is not Executed on Incident
			
	}
	
	public void resolveIncident(String processDefinitionId, String activityId, String executionId, String jobId)    {
			resolveIncident(processDefinitionId, activityId, executionId, jobId);
	}


	public void deleteIncident(IncidentContext arg0) {
		removeIncident(arg0, false);
	}


	public void resolveIncident(IncidentContext arg0) {
		removeIncident(arg0, true);
	}
	
	protected void removeIncident(IncidentContext context, boolean incidentResolved) {
	    List<Incident> incidents = Context
	        .getCommandContext()
	        .getIncidentManager()
	        .findIncidentByConfiguration(context.getConfiguration());

	    for (Incident currentIncident : incidents) {
	      IncidentEntity incident = (IncidentEntity) currentIncident;
	      if (incidentResolved) {
	        incident.resolve();
	      } else {
	        incident.delete();
	      }
	    }
	}



}

In My Test Case the Incident is thrown like this:

IncidentContext context = new IncidentContext();
		 context.setActivityId(delegateTask.getExecution().getCurrentActivityId());
		 context.setExecutionId(delegateTask.getExecution().getProcessInstanceId());
		 context.setProcessDefinitionId(delegateTask.getExecution().getProcessDefinitionId());

		 
		 IncidentEntity newIncident
		  = IncidentEntity.createAndInsertIncident(
		      "failedJob",
		      context,
		      "failedJob"
		    );
		 
		 newIncident.createRecursiveIncidents();

Has anyone an Idea or working example?

Thanks a lot!

2 Likes

Hi @Tristan1,

you have an issue in your test case. When you create the incident (i.e. the IncidentEntity) directly then the IncidentHandler is not called. It is invoked, for example when a job has no retries left - see the JobEntity.

Please re-write your test case so that it creates a job (e.g., using async. activity) which fails until it has no more retries left.

Best regards,
Philipp

Hi @Philipp_Ossler,

thanks for your that valuable information! I would have never found out. I will give it a try later. However, as there might be use cases out there where incidents are called programmatically and custom incident handlers are implement, there might be support for that. Probably that should be fixed in the future? Shall I open a JIRA Ticket?

Cheers,
Tristan

@Philipp_Ossler

I now tried that but when I try to let the task fail via the Rest-API from the external worker, I get the following exception, that only happends when “retries” are set to 0 and an incident shall be created. When retries are set to > 0 then this is okay. So there is a problem with the incident creation. It must be connected with the custom Incident Handler. Because when the custom incident handler is removed, then an incident is created without any problems.

org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
	org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:365)
	org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:233)
	org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:209)
	org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:557)
	org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
	org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:169)
	org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:212)
	org.jboss.resteasy.plugins.server.servlet.FilterDispatcher.doFilter(FilterDispatcher.java:59)
	org.camunda.bpm.engine.rest.filter.CacheControlFilter.doFilter(CacheControlFilter.java:41)
	org.camunda.bpm.engine.rest.filter.EmptyBodyFilter.doFilter(EmptyBodyFilter.java:95)
root cause

java.lang.NullPointerException
	org.camunda.bpm.engine.impl.persistence.entity.ExternalTaskEntity.createIncident(ExternalTaskEntity.java:361)
	org.camunda.bpm.engine.impl.persistence.entity.ExternalTaskEntity.setRetriesAndManageIncidents(ExternalTaskEntity.java:347)
	org.camunda.bpm.engine.impl.persistence.entity.ExternalTaskEntity.failed(ExternalTaskEntity.java:328)
	org.camunda.bpm.engine.impl.cmd.HandleExternalTaskFailureCmd.execute(HandleExternalTaskFailureCmd.java:56)
	org.camunda.bpm.engine.impl.cmd.HandleExternalTaskCmd.execute(HandleExternalTaskCmd.java:58)
	org.camunda.bpm.engine.impl.cmd.HandleExternalTaskCmd.execute(HandleExternalTaskCmd.java:30)
	org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:24)
	org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:104)
	org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:66)
	org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:30)
	org.camunda.bpm.engine.impl.ExternalTaskServiceImpl.handleFailure(ExternalTaskServiceImpl.java:55)
	org.camunda.bpm.engine.rest.sub.externaltask.impl.ExternalTaskResourceImpl.handleFailure(ExternalTaskResourceImpl.java:120)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:498)
	org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
	org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:257)
	org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:222)
	org.jboss.resteasy.core.ResourceLocator.invokeOnTargetObject(ResourceLocator.java:159)
	org.jboss.resteasy.core.ResourceLocator.invoke(ResourceLocator.java:107)
	org.jboss.resteasy.core.ResourceLocator.invokeOnTargetObject(ResourceLocator.java:154)
	org.jboss.resteasy.core.ResourceLocator.invoke(ResourceLocator.java:92)
	org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:542)
	org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:524)
	org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:169)
	org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:212)
	org.jboss.resteasy.plugins.server.servlet.FilterDispatcher.doFilter(FilterDispatcher.java:59)
	org.camunda.bpm.engine.rest.filter.CacheControlFilter.doFilter(CacheControlFilter.java:41)

Okay guys,

I found out what the mistake is. There are two different types of IncidentHandlers. One that handles the failed Jobs and one that handles the failed external Tasks. Therefore, you need two different custom incident handlers to handle all custom incidents. The custom incident Handler must return under method

public String getIncidentHandlerType() {
	    return Incident.EXTERNAL_TASK_HANDLER_TYPE;
}

or

	public String getIncidentHandlerType() {
	    return Incident.FAILED_JOB_HANDLER_TYPE;
	}

according to what handling is desired.

Both classes for Incident Handling must then be referenced in the ProcessEnginePlugin:

public class IncidentHandlerProcessEnginePlugin  extends AbstractProcessEnginePlugin  {

	private static final Logger log = Logger.getLogger(IncidentHandlerProcessEnginePlugin.class.getName());
	
	@Override
	  public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) {
	    List<IncidentHandler> customIncidentHandlers = new ArrayList<IncidentHandler>();
	    customIncidentHandlers.add(new myFailedJobIncidentHandler());
	    customIncidentHandlers.add(new myFailedExternalTaskIncidentHandler());
	    processEngineConfiguration.setCustomIncidentHandlers(customIncidentHandlers );
	
	}
}

The complete class for the incident Handler is then somehting like:

    public class myFailedExternalTaskIncidentHandler implements IncidentHandler  {`
    	public String getIncidentHandlerType() {
	    return Incident.EXTERNAL_TASK_HANDLER_TYPE;
	}

	public void handleIncident(IncidentContext arg0, String arg1) {
		
		// Do Stuff here... e.g. Send an Email to Admin
			
	}
	
	public void resolveIncident(String processDefinitionId, String activityId, String executionId, String jobId) {
		resolveIncident(processDefinitionId, activityId, executionId, jobId);
	}


	public void deleteIncident(IncidentContext arg0) {
		removeIncident(arg0, false);
	}


	public void resolveIncident(IncidentContext arg0) {
		removeIncident(arg0, true);
	}
	
	protected void removeIncident(IncidentContext context, boolean incidentResolved) {
	    List<Incident> incidents = Context
	        .getCommandContext()
	        .getIncidentManager()
	        .findIncidentByConfiguration(context.getConfiguration());

	    for (Incident currentIncident : incidents) {
	      IncidentEntity incident = (IncidentEntity) currentIncident;
	      if (incidentResolved) {
	        incident.resolve();
	      } else {
	        incident.delete();
	      }
	    }
	}
}

I hope that helps anybody who wants to do some custom Incident handling, like sending an Email to Admin in case of an incident.

3 Likes

Hi @Tristan1,

thank’s for sharing this information :thumbsup:

Regarding the programmatically incident creation, the recommend way is to use the incident handler which is provided by the process engine configuration. E.g., Context.getProcessEngineConfiguration().getIncidentHandler(type).handleIncident(context, message);

Best regards,
Philipp

@Tristan1 @Philipp_Ossler

Hi

I found your topic very interesting. I am new with Camunda, and have arrived at the point where I have to handle incidents. I think maybe you could be of help for me. :slight_smile:
I have a task that uses a JavaDelegate class to call a REST service. When I shut down the REST service, the task is marked as a incident.

I want the incident, or more correct, the task to be rerun, with the same payload, for example each hour, until the REST service is up again and the task succeds.

Do you have any information on how to do this?

Br

Frank