/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jbpm.bpmn2.handler;
import java.util.HashMap;
import java.util.Map;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.WorkItem;
import org.kie.api.runtime.process.WorkItemHandler;
import org.kie.api.runtime.process.WorkItemManager;
/**
* This class will wrap a {@link WorkItemHandler} instance so that an event (signal, error or other) can be sent to the process
* instance if and when the wrapped {@link WorkItemHandler} instance throws an exception (during a
* {@link WorkItemHandler#executeWorkItem(WorkItem, WorkItemManager)} or
* {@link WorkItemHandler#abortWorkItem(WorkItem, WorkItemManager)} method.
*
* </p>In order to prevent an endless loop, the signal will only be sent once. If the signal should be sent the next time the same
* wrapped {@link WorkItemHandler} instance throws an exception, the {@link SignallingTaskHandlerDecorator} instance must either be
* reset via the {@link SignallingTaskHandlerDecorator#clear()} or {@link SignallingTaskHandlerDecorator#clearProcessInstance(Long)}
* methods.
* <p>Otherwise, the number of exceptions handled can be changed via the {@link WorkItemHandler#setExceptionCountLimit} method.
*
* </p>This class is <b>not</b> thread-safe.
*/
public class SignallingTaskHandlerDecorator extends AbstractExceptionHandlingTaskHandler {
final private String eventType;
private String workItemExceptionParameterName = "jbpm.workitem.exception";
final private Map<Long, Integer> processInstanceExceptionMap = new HashMap<Long, Integer>();
private int exceptionCountLimit = 1;
/**
* Constructs an instance that uses the given <code>eventType</code> parameter to signal the process instance using the given
* {@link KieSession} <code>ksession</code> parameter when an instance of the class specified by the
* <code>originalTaskHandlerClass</code> throws an exception upon {@link WorkItemHandler#executeWorkItem(WorkItem, WorkItemManager)}
* @param originalTaskHandlerClass
* @param eventType
*/
public SignallingTaskHandlerDecorator(Class<? extends WorkItemHandler> originalTaskHandlerClass, String eventType) {
super(originalTaskHandlerClass);
this.eventType = eventType;
}
public SignallingTaskHandlerDecorator(WorkItemHandler originalTaskHandler, String eventType) {
super(originalTaskHandler);
this.eventType = eventType;
}
public SignallingTaskHandlerDecorator(Class<? extends WorkItemHandler> originalTaskHandlerClass, String eventType, int exceptionCountLimit) {
super(originalTaskHandlerClass);
this.eventType = eventType;
this.exceptionCountLimit = exceptionCountLimit;
}
public SignallingTaskHandlerDecorator(WorkItemHandler originalTaskHandler, String eventType, int exceptionCountLimit) {
super(originalTaskHandler);
this.eventType = eventType;
this.exceptionCountLimit = exceptionCountLimit;
}
public void setWorkItemExceptionParameterName(String parameterName) {
this.workItemExceptionParameterName = parameterName;
}
public String getWorkItemExceptionParameterName() {
return this.workItemExceptionParameterName;
}
@Override
public void handleExecuteException(Throwable cause, WorkItem workItem, WorkItemManager manager) {
if( getAndIncreaseExceptionCount(workItem.getProcessInstanceId()) < exceptionCountLimit ) {
workItem.getParameters().put(this.workItemExceptionParameterName, cause);
((org.drools.core.process.instance.WorkItemManager) manager).signalEvent(this.eventType, (org.drools.core.process.instance.WorkItem) workItem, workItem.getProcessInstanceId());
} else {
if( cause instanceof RuntimeException ) {
throw (RuntimeException) cause;
} else {
throw new WorkItemHandlerRuntimeException(cause,
"Signalling process instance " + workItem.getProcessInstanceId() + " with '" + this.eventType + "' resulted this exception.");
}
}
}
@Override
public void handleAbortException(Throwable cause, WorkItem workItem, WorkItemManager manager) {
if( getAndIncreaseExceptionCount(workItem.getProcessInstanceId()) < exceptionCountLimit ) {
workItem.getParameters().put(this.workItemExceptionParameterName, cause);
((org.drools.core.process.instance.WorkItemManager) manager).signalEvent(this.eventType, (org.drools.core.process.instance.WorkItem) workItem, workItem.getProcessInstanceId());
}
}
private int getAndIncreaseExceptionCount(Long processInstanceId) {
Integer count = processInstanceExceptionMap.get(processInstanceId);
if( count == null ) {
count = 0;
}
processInstanceExceptionMap.put( processInstanceId, ++count);
return (count-1);
}
public void setExceptionCountLimit(int limit) {
this.exceptionCountLimit = limit;
}
public void clearProcessInstance(Long processInstanceId ) {
processInstanceExceptionMap.remove(processInstanceId);
}
public void clear() {
processInstanceExceptionMap.clear();
}
}