Skip to content

Commit 0d94dd4

Browse files
committed
Adjust NonTransactionalJobHandler to be able to easily execute something in a transaction after the non transactional part is done
1 parent c842272 commit 0d94dd4

4 files changed

Lines changed: 59 additions & 12 deletions

File tree

modules/flowable-engine/src/test/java/org/flowable/engine/test/jobexecutor/NonTransactionalJobHandlerTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public void testJobExecutedWithoutTransaction() {
7979

8080
assertThat(managementService.createJobQuery().count()).isEqualTo(0);
8181
assertThat(nonTransactionalTestJobHandler.getJobConfiguration()).isEqualTo("myTest");
82+
assertThat(nonTransactionalTestJobHandler.nonTransactionalOutput).isEqualTo("myTest");
83+
assertThat(nonTransactionalTestJobHandler.nonTransactionalCounter).hasValue(1);
8284
}
8385

8486
@Test
@@ -105,30 +107,34 @@ public void testJobExecutedWithoutTransactionThrowsException() {
105107
assertThat(nonTransactionalTestJobHandlerWithException.getWithoutTransactionCounter().get()).isEqualTo(1);
106108
assertThat(nonTransactionalTestJobHandlerWithException.getWithTransactionCounter().get()).isEqualTo(0);
107109
assertThat(nonTransactionalTestJobHandlerWithException.getWithoutCommandContext().get()).isEqualTo(1);
110+
assertThat(nonTransactionalTestJobHandlerWithException.nonTransactionalOutput).isNull();
111+
assertThat(nonTransactionalTestJobHandlerWithException.nonTransactionalCounter).hasValue(0);
108112

109113
assertThat(managementService.createJobQuery().count()).isEqualTo(0);
110114

111115
job = managementService.createTimerJobQuery().singleResult();
112116
assertThat(job.getRetries()).isEqualTo(initialRetries - 1);
113117
}
114118

115-
public static class NonTransactionalTestJobHandler implements NonTransactionalJobHandler {
119+
public static class NonTransactionalTestJobHandler implements NonTransactionalJobHandler<String> {
116120

117121
protected AtomicInteger counter = new AtomicInteger();
118122
protected AtomicInteger withCommandContext = new AtomicInteger(0);
119123
protected AtomicInteger withoutCommandContext = new AtomicInteger(0);
120124
protected AtomicInteger withTransactionCounter = new AtomicInteger(0);
121125
protected AtomicInteger withoutTransactionCounter = new AtomicInteger(0);
126+
protected AtomicInteger nonTransactionalCounter = new AtomicInteger(0);
122127

123128
protected String jobConfiguration;
129+
protected String nonTransactionalOutput;
124130

125131
@Override
126132
public String getType() {
127133
return "non-transactional";
128134
}
129135

130136
@Override
131-
public void executeNonTransactionally(JobEntity job, String configuration) {
137+
public String executeNonTransactionally(JobEntity job, String configuration) {
132138

133139
// Not checking the passed command context, but checking the low-level one on Context
134140
counter.incrementAndGet();
@@ -148,7 +154,14 @@ public void executeNonTransactionally(JobEntity job, String configuration) {
148154
}
149155

150156
this.jobConfiguration = job.getJobHandlerConfiguration();
157+
return jobConfiguration;
158+
159+
}
151160

161+
@Override
162+
public void afterExecute(JobEntity job, String configuration, String output, CommandContext commandContext) {
163+
nonTransactionalCounter.incrementAndGet();
164+
this.nonTransactionalOutput = output;
152165
}
153166

154167
public AtomicInteger getCounter() {
@@ -195,7 +208,7 @@ public String getType() {
195208
}
196209

197210
@Override
198-
public void executeNonTransactionally(JobEntity job, String configuration) {
211+
public String executeNonTransactionally(JobEntity job, String configuration) {
199212
super.executeNonTransactionally(job, configuration);
200213

201214
throw new RuntimeException();

modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/asyncexecutor/ExecuteAsyncRunnable.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ protected void handleTransactionalJob(boolean unlock) {
178178
new ExecuteAsyncRunnableJobCmd(job.getId(), jobEntityManager, jobServiceConfiguration, unlock));
179179
}
180180

181-
protected boolean handleNontransactionalJob(NonTransactionalJobHandler jobHandler, boolean unlock) {
181+
protected boolean handleNontransactionalJob(NonTransactionalJobHandler<Object> jobHandler, boolean unlock) {
182182

183183
JobProcessorUtil.callJobProcessors(jobServiceConfiguration, JobProcessorContext.Phase.BEFORE_EXECUTE, (JobEntity) job);
184184

@@ -189,7 +189,7 @@ protected boolean handleNontransactionalJob(NonTransactionalJobHandler jobHandle
189189
// If an exception is thrown during job handler exception, it goes up and will be caught in the general exception handling.
190190
// The delete at the end won't happen in that case.
191191

192-
jobHandler.executeNonTransactionally((JobEntity) job, job.getJobHandlerConfiguration());
192+
Object nonTransactionalOutput = jobHandler.executeNonTransactionally((JobEntity) job, job.getJobHandlerConfiguration());
193193

194194
FlowableEventDispatcher eventDispatcher = jobServiceConfiguration.getEventDispatcher();
195195
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
@@ -199,6 +199,7 @@ protected boolean handleNontransactionalJob(NonTransactionalJobHandler jobHandle
199199

200200
// The delete still needs to happen in a new transaction
201201
jobServiceConfiguration.getCommandExecutor().execute(commandContext -> {
202+
jobHandler.afterExecute((JobEntity) job, job.getJobHandlerConfiguration(), nonTransactionalOutput, commandContext);
202203
jobServiceConfiguration.getJobEntityManager().delete((JobEntity) job);
203204
return null;
204205
});

modules/flowable-job-service/src/main/java/org/flowable/job/service/impl/nontx/NonTransactionalJobHandler.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,34 @@
2525
*
2626
* @author Joram Barrez
2727
*/
28-
public interface NonTransactionalJobHandler extends JobHandler {
28+
public interface NonTransactionalJobHandler<Output> extends JobHandler {
2929

3030
default void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
3131
throw new UnsupportedOperationException();
3232
}
3333

34-
void executeNonTransactionally(JobEntity job, String configuration);
34+
/**
35+
* Execute the job handler logic outside a transaction.
36+
* If the handler needs to execute certain login transactionally,
37+
* it can return some output and that use that in the {@link #afterExecute(JobEntity, String, Object, CommandContext)} (which gets executed in a transaction).
38+
*
39+
* @param job the job that is being executed
40+
* @param configuration the job handler configuration
41+
* @return the result of the non-transactional execution
42+
*/
43+
Output executeNonTransactionally(JobEntity job, String configuration);
44+
45+
/**
46+
* Method invoked with the result from {@link #executeNonTransactionally(JobEntity, String)}.
47+
* This should be used when the result of the non-transactional execution should be executed in a transaction.
48+
*
49+
* @param job The job that is being executed
50+
* @param configuration the job configuration
51+
* @param output the output from the non-transactional execution
52+
* @param commandContext the command context of the current transaction
53+
*/
54+
default void afterExecute(JobEntity job, String configuration, Output output, CommandContext commandContext) {
55+
// Nothing to do by default
56+
}
3557

3658
}

modules/flowable-spring/src/test/java/org/flowable/spring/test/jobexecutor/SpringNonTransactionalJobHandlerTest.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
import org.flowable.engine.impl.test.JobTestHelper;
2323
import org.flowable.engine.impl.util.CommandContextUtil;
2424
import org.flowable.job.api.Job;
25-
import org.flowable.job.service.JobHandler;
2625
import org.flowable.job.service.JobService;
2726
import org.flowable.job.service.impl.nontx.NonTransactionalJobHandler;
2827
import org.flowable.job.service.impl.persistence.entity.JobEntity;
2928
import org.flowable.spring.impl.test.SpringFlowableTestCase;
30-
import org.flowable.variable.api.delegate.VariableScope;
3129
import org.junit.jupiter.api.AfterEach;
3230
import org.junit.jupiter.api.BeforeEach;
3331
import org.junit.jupiter.api.Test;
@@ -90,6 +88,8 @@ public void testJobExecutedWithoutTransaction() {
9088
assertThat(nonTransactionalTestJobHandler.getWithoutTransactionCounter().get()).isEqualTo(1);
9189
assertThat(nonTransactionalTestJobHandler.getWithTransactionCounter().get()).isEqualTo(0);
9290
assertThat(nonTransactionalTestJobHandler.getWithoutCommandContext().get()).isEqualTo(1);
91+
assertThat(nonTransactionalTestJobHandler.nonTransactionalOutput).isEqualTo("myTest");
92+
assertThat(nonTransactionalTestJobHandler.nonTransactionalCounter).hasValue(1);
9393

9494
assertThat(managementService.createJobQuery().count()).isEqualTo(0);
9595
}
@@ -118,30 +118,34 @@ public void testJobExecutedWithoutTransactionThrowsException() {
118118
assertThat(nonTransactionalTestJobHandlerWithException.getWithoutTransactionCounter().get()).isEqualTo(1);
119119
assertThat(nonTransactionalTestJobHandlerWithException.getWithTransactionCounter().get()).isEqualTo(0);
120120
assertThat(nonTransactionalTestJobHandlerWithException.getWithoutCommandContext().get()).isEqualTo(1);
121+
assertThat(nonTransactionalTestJobHandlerWithException.nonTransactionalOutput).isNull();
122+
assertThat(nonTransactionalTestJobHandlerWithException.nonTransactionalCounter).hasValue(0);
121123

122124
assertThat(managementService.createJobQuery().count()).isEqualTo(0);
123125

124126
job = managementService.createTimerJobQuery().singleResult();
125127
assertThat(job.getRetries()).isEqualTo(initialRetries - 1);
126128
}
127129

128-
public static class NonTransactionalTestJobHandler implements NonTransactionalJobHandler {
130+
public static class NonTransactionalTestJobHandler implements NonTransactionalJobHandler<String> {
129131

130132
protected AtomicInteger counter = new AtomicInteger();
131133
protected AtomicInteger withCommandContext = new AtomicInteger(0);
132134
protected AtomicInteger withoutCommandContext = new AtomicInteger(0);
133135
protected AtomicInteger withTransactionCounter = new AtomicInteger(0);
134136
protected AtomicInteger withoutTransactionCounter = new AtomicInteger(0);
137+
protected AtomicInteger nonTransactionalCounter = new AtomicInteger(0);
135138

136139
protected String jobConfiguration;
140+
protected String nonTransactionalOutput;
137141

138142
@Override
139143
public String getType() {
140144
return "non-transactional";
141145
}
142146

143147
@Override
144-
public void executeNonTransactionally(JobEntity job, String configuration) {
148+
public String executeNonTransactionally(JobEntity job, String configuration) {
145149

146150
// Not checking the passed command context, but checking the low-level one on Context
147151
counter.incrementAndGet();
@@ -162,9 +166,16 @@ public void executeNonTransactionally(JobEntity job, String configuration) {
162166
}
163167

164168
this.jobConfiguration = job.getJobHandlerConfiguration();
169+
return this.jobConfiguration;
165170

166171
}
167172

173+
@Override
174+
public void afterExecute(JobEntity job, String configuration, String output, CommandContext commandContext) {
175+
nonTransactionalCounter.incrementAndGet();
176+
this.nonTransactionalOutput = output;
177+
}
178+
168179
public AtomicInteger getCounter() {
169180
return counter;
170181
}
@@ -207,7 +218,7 @@ public String getType() {
207218
}
208219

209220
@Override
210-
public void executeNonTransactionally(JobEntity job, String configuration) {
221+
public String executeNonTransactionally(JobEntity job, String configuration) {
211222
super.executeNonTransactionally(job, configuration);
212223

213224
throw new RuntimeException();

0 commit comments

Comments
 (0)