Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ protected void logException(JobEntity job) {
if(exception != null) {
job.setExceptionMessage(exception.getMessage());
job.setExceptionStacktrace(getExceptionStacktrace());
job.setException(this.exception);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class IncidentContext {
protected String jobDefinitionId;
protected String historyConfiguration;
protected String failedActivityId;
private transient Throwable throwable;

public IncidentContext() {}

Expand Down Expand Up @@ -107,4 +108,12 @@ public void setFailedActivityId(String failedActivityId) {
this.failedActivityId = failedActivityId;
}

public Throwable getThrowable() {
return throwable;
}

public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ public abstract class JobEntity extends AcquirableJobEntity

protected String batchId;

// transient fields
private transient Throwable exception;

public void execute(CommandContext commandContext) {
if (executionId != null) {
ExecutionEntity execution = getExecution();
Expand Down Expand Up @@ -379,6 +382,7 @@ protected void createFailedJobIncident() {
incidentContext.setActivityId(getActivityId());
incidentContext.setHistoryConfiguration(getLastFailureLogId());
incidentContext.setFailedActivityId(getFailedActivityId());
incidentContext.setThrowable(this.getException());

IncidentHandling.createIncident(incidentHandlerType, incidentContext, exceptionMessage);

Expand Down Expand Up @@ -486,6 +490,14 @@ public String getExceptionMessage() {
return exceptionMessage;
}

public Throwable getException() {
return exception;
}

public void setException(Throwable exception) {
this.exception = exception;
}

@Override
public String getJobDefinitionId() {
return jobDefinitionId;
Expand Down Expand Up @@ -555,6 +567,7 @@ protected void clearFailedJobException() {

this.exceptionByteArrayId = null;
this.exceptionMessage = null;
this.exception = null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,33 @@ public void shouldCreateOneIncident() {
assertThat(JOB_HANDLER.getDeleteEvents()).isEmpty();
}

@Deployment(resources = {
"org/finos/fluxnova/bpm/engine/test/api/mgmt/IncidentTest.testShouldCreateOneIncident.bpmn" })
@Test
public void checkThrowableOnIncidentCreationProcess() {
//given
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("failingProcess");

//when
testRule.executeAvailableJobs();

List<Incident> incidents = runtimeService.createIncidentQuery().processInstanceId(processInstance.getId()).list();

//then
assertThat(incidents).hasSize(1);

assertThat(JOB_HANDLER.getCreateEvents()).hasSize(1);
assertThat((JOB_HANDLER.getCreateEvents()).getFirst()).isNotNull();
//The exception is exposed through the Incident Context while the Incident is handled.
assertThat((JOB_HANDLER.getCreateEvents()).getFirst().getThrowable()).isNotNull();
//We assert that the actual message raised by the delegate is found as part of the Exception.
assertThat((JOB_HANDLER.getCreateEvents()).getFirst().getThrowable().getMessage()).contains(
AlwaysFailingDelegate.MESSAGE);

assertThat(JOB_HANDLER.getResolveEvents()).isEmpty();
assertThat(JOB_HANDLER.getDeleteEvents()).isEmpty();
}

@Deployment(resources = { "org/finos/fluxnova/bpm/engine/test/api/mgmt/IncidentTest.testShouldCreateOneIncident.bpmn" })
@Test
public void shouldResolveIncidentAfterJobRetriesRefresh() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

Expand All @@ -29,10 +30,12 @@
import org.finos.fluxnova.bpm.engine.impl.db.DbEntity;
import org.finos.fluxnova.bpm.engine.impl.interceptor.Command;
import org.finos.fluxnova.bpm.engine.impl.interceptor.CommandContext;
import org.finos.fluxnova.bpm.engine.impl.persistence.entity.JobEntity;
import org.finos.fluxnova.bpm.engine.impl.persistence.entity.MessageEntity;
import org.finos.fluxnova.bpm.engine.runtime.Job;
import org.finos.fluxnova.bpm.engine.runtime.JobQuery;
import org.finos.fluxnova.bpm.engine.test.Deployment;
import org.finos.fluxnova.bpm.engine.test.api.mgmt.AlwaysFailingDelegate;
import org.finos.fluxnova.bpm.engine.test.util.PluggableProcessEngineTest;
import org.finos.fluxnova.bpm.model.bpmn.Bpmn;
import org.junit.After;
Expand Down Expand Up @@ -202,6 +205,41 @@ public void testFailingTransactionListener() {
assertTrue("unexpected stacktrace, was <" + stacktrace + ">", stacktrace.contains("java.lang.RuntimeException: exception in transaction listener"));
}

@Test
public void testTransientExceptionIsNotPersistedPostJobFailure() {
// given
testRule.deploy(Bpmn.createExecutableProcess("testProcess")
.fluxnovaHistoryTimeToLive(180)
.startEvent()
.serviceTask("theServiceTask")
.fluxnovaAsyncBefore()
.fluxnovaClass(AlwaysFailingDelegate.class)
.fluxnovaFailedJobRetryTimeCycle("R0/PT30S")
.endEvent()
.done());

runtimeService.startProcessInstanceByKey("testProcess");
Job job = managementService.createJobQuery().singleResult();
assertNotNull("Job should not be null", job);
assertNull(job.getExceptionMessage());
assertNull(((JobEntity) job).getException());

// when
try {
managementService.executeJob(job.getId());
} catch (Exception e) {
// expected exception
}

// then
job = managementService.createJobQuery().singleResult();
assertNotNull("Job should not be null", job);
//The message is obtained from the persistence layer.
assertEquals(AlwaysFailingDelegate.MESSAGE, job.getExceptionMessage());
//The exception at this point is null but already propagated to the Incident Handler.
assertNull(((JobEntity) job).getException());
}

protected void createJob(final String handlerType) {
processEngineConfiguration.getCommandExecutorTxRequired().execute(new Command<String>() {

Expand Down