1
1
/*
2
- * Copyright 2017-2020 the original author or authors.
2
+ * Copyright 2017-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
18
18
19
19
import java .time .Duration ;
20
20
21
+ import org .jspecify .annotations .Nullable ;
22
+
21
23
import org .springframework .kafka .core .KafkaResourceHolder ;
22
24
import org .springframework .kafka .core .ProducerFactory ;
23
25
import org .springframework .kafka .core .ProducerFactoryUtils ;
45
47
* <p>
46
48
* Application code is required to retrieve the transactional Kafka resources via
47
49
* {@link ProducerFactoryUtils#getTransactionalResourceHolder(ProducerFactory, String, java.time.Duration)}.
48
- * Spring's {@link org.springframework.kafka.core.KafkaTemplate KafkaTemplate} will auto
49
- * detect a thread-bound Producer and automatically participate in it.
50
+ * Spring's {@link org.springframework.kafka.core.KafkaTemplate KafkaTemplate} will auto-detect
51
+ * a thread-bound Producer and automatically participate in it.
50
52
*
51
53
* <p>
52
54
* <b>The use of {@link org.springframework.kafka.core.DefaultKafkaProducerFactory
63
65
* @param <V> the value type.
64
66
*
65
67
* @author Gary Russell
68
+ * @author Soby Chacko
66
69
*/
67
70
@ SuppressWarnings ("serial" )
68
71
public class KafkaTransactionManager <K , V > extends AbstractPlatformTransactionManager
@@ -72,7 +75,7 @@ public class KafkaTransactionManager<K, V> extends AbstractPlatformTransactionMa
72
75
73
76
private final ProducerFactory <K , V > producerFactory ;
74
77
75
- private String transactionIdPrefix ;
78
+ private @ Nullable String transactionIdPrefix ;
76
79
77
80
private Duration closeTimeout = ProducerFactoryUtils .DEFAULT_CLOSE_TIMEOUT ;
78
81
@@ -121,7 +124,7 @@ public void setCloseTimeout(Duration closeTimeout) {
121
124
@ SuppressWarnings (UNCHECKED )
122
125
@ Override
123
126
protected Object doGetTransaction () {
124
- KafkaTransactionObject <K , V > txObject = new KafkaTransactionObject <K , V >();
127
+ KafkaTransactionObject <K , V > txObject = new KafkaTransactionObject <>();
125
128
txObject .setResourceHolder ((KafkaResourceHolder <K , V >) TransactionSynchronizationManager
126
129
.getResource (getProducerFactory ()));
127
130
return txObject ;
@@ -149,10 +152,10 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
149
152
logger .debug ("Created Kafka transaction on producer [" + resourceHolder .getProducer () + "]" );
150
153
}
151
154
txObject .setResourceHolder (resourceHolder );
152
- txObject . getResourceHolder () .setSynchronizedWithTransaction (true );
155
+ resourceHolder .setSynchronizedWithTransaction (true );
153
156
int timeout = determineTimeout (definition );
154
157
if (timeout != TransactionDefinition .TIMEOUT_DEFAULT ) {
155
- txObject . getResourceHolder () .setTimeoutInSeconds (timeout );
158
+ resourceHolder .setTimeoutInSeconds (timeout );
156
159
}
157
160
}
158
161
catch (Exception ex ) {
@@ -172,9 +175,13 @@ protected Object doSuspend(Object transaction) {
172
175
}
173
176
174
177
@ Override
175
- protected void doResume ( Object transaction , Object suspendedResources ) {
176
- @ SuppressWarnings ( UNCHECKED )
178
+ @ SuppressWarnings ( UNCHECKED )
179
+ protected void doResume ( @ Nullable Object transaction , Object suspendedResources ) {
177
180
KafkaResourceHolder <K , V > producerHolder = (KafkaResourceHolder <K , V >) suspendedResources ;
181
+ if (transaction != null ) {
182
+ KafkaTransactionObject <K , V > txObject = (KafkaTransactionObject <K , V >) transaction ;
183
+ txObject .setResourceHolder (producerHolder );
184
+ }
178
185
TransactionSynchronizationManager .bindResource (getProducerFactory (), producerHolder );
179
186
}
180
187
@@ -183,31 +190,41 @@ protected void doCommit(DefaultTransactionStatus status) {
183
190
@ SuppressWarnings (UNCHECKED )
184
191
KafkaTransactionObject <K , V > txObject = (KafkaTransactionObject <K , V >) status .getTransaction ();
185
192
KafkaResourceHolder <K , V > resourceHolder = txObject .getResourceHolder ();
186
- resourceHolder .commit ();
193
+ if (resourceHolder != null ) {
194
+ resourceHolder .commit ();
195
+ }
187
196
}
188
197
189
198
@ Override
190
199
protected void doRollback (DefaultTransactionStatus status ) {
191
200
@ SuppressWarnings (UNCHECKED )
192
201
KafkaTransactionObject <K , V > txObject = (KafkaTransactionObject <K , V >) status .getTransaction ();
193
202
KafkaResourceHolder <K , V > resourceHolder = txObject .getResourceHolder ();
194
- resourceHolder .rollback ();
203
+ if (resourceHolder != null ) {
204
+ resourceHolder .rollback ();
205
+ }
195
206
}
196
207
197
208
@ Override
198
209
protected void doSetRollbackOnly (DefaultTransactionStatus status ) {
199
210
@ SuppressWarnings (UNCHECKED )
200
211
KafkaTransactionObject <K , V > txObject = (KafkaTransactionObject <K , V >) status .getTransaction ();
201
- txObject .getResourceHolder ().setRollbackOnly ();
212
+ KafkaResourceHolder <K , V > kafkaResourceHolder = txObject .getResourceHolder ();
213
+ if (kafkaResourceHolder != null ) {
214
+ kafkaResourceHolder .setRollbackOnly ();
215
+ }
202
216
}
203
217
204
218
@ Override
205
219
protected void doCleanupAfterCompletion (Object transaction ) {
206
220
@ SuppressWarnings (UNCHECKED )
207
221
KafkaTransactionObject <K , V > txObject = (KafkaTransactionObject <K , V >) transaction ;
208
222
TransactionSynchronizationManager .unbindResource (getProducerFactory ());
209
- txObject .getResourceHolder ().close ();
210
- txObject .getResourceHolder ().clear ();
223
+ KafkaResourceHolder <K , V > kafkaResourceHolder = txObject .getResourceHolder ();
224
+ if (kafkaResourceHolder != null ) {
225
+ kafkaResourceHolder .close ();
226
+ kafkaResourceHolder .clear ();
227
+ }
211
228
}
212
229
213
230
/**
@@ -217,22 +234,22 @@ protected void doCleanupAfterCompletion(Object transaction) {
217
234
*/
218
235
private static class KafkaTransactionObject <K , V > implements SmartTransactionObject {
219
236
220
- private KafkaResourceHolder <K , V > resourceHolder ;
237
+ private @ Nullable KafkaResourceHolder <K , V > resourceHolder ;
221
238
222
239
KafkaTransactionObject () {
223
240
}
224
241
225
- public void setResourceHolder (KafkaResourceHolder <K , V > resourceHolder ) {
242
+ public void setResourceHolder (@ Nullable KafkaResourceHolder <K , V > resourceHolder ) {
226
243
this .resourceHolder = resourceHolder ;
227
244
}
228
245
229
- public KafkaResourceHolder <K , V > getResourceHolder () {
246
+ public @ Nullable KafkaResourceHolder <K , V > getResourceHolder () {
230
247
return this .resourceHolder ;
231
248
}
232
249
233
250
@ Override
234
251
public boolean isRollbackOnly () {
235
- return this .resourceHolder .isRollbackOnly ();
252
+ return this .resourceHolder != null && this . resourceHolder .isRollbackOnly ();
236
253
}
237
254
238
255
@ Override
0 commit comments