All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.kafka.transaction.KafkaTransactionManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://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.springframework.kafka.transaction;

import java.time.Duration;

import org.springframework.kafka.core.KafkaResourceHolder;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.core.ProducerFactoryUtils;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.SmartTransactionObject;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

/**
 * {@link org.springframework.transaction.PlatformTransactionManager} implementation for a
 * single Kafka {@link ProducerFactory}. Binds a Kafka producer from the specified
 * ProducerFactory to the thread, potentially allowing for one thread-bound producer per
 * ProducerFactory.
 *
 * 

* This local strategy is an alternative to executing Kafka operations within, and * synchronized with, external transactions. This strategy is not able to provide * XA transactions, for example in order to share transactions between messaging and * database access. * *

* Application code is required to retrieve the transactional Kafka resources via * {@link ProducerFactoryUtils#getTransactionalResourceHolder(ProducerFactory, String, java.time.Duration)}. * Spring's {@link org.springframework.kafka.core.KafkaTemplate KafkaTemplate} will auto * detect a thread-bound Producer and automatically participate in it. * *

* The use of {@link org.springframework.kafka.core.DefaultKafkaProducerFactory * DefaultKafkaProducerFactory} as a target for this transaction manager is strongly * recommended. Because it caches producers for reuse. * *

* Transaction synchronization is turned off by default, as this manager might be used * alongside a datastore-based Spring transaction manager such as the JDBC * org.springframework.jdbc.datasource.DataSourceTransactionManager, which has stronger * needs for synchronization. * * @param the key type. * @param the value type. * * @author Gary Russell */ @SuppressWarnings("serial") public class KafkaTransactionManager extends AbstractPlatformTransactionManager implements KafkaAwareTransactionManager { private static final String UNCHECKED = "unchecked"; private final ProducerFactory producerFactory; private String transactionIdPrefix; private Duration closeTimeout = ProducerFactoryUtils.DEFAULT_CLOSE_TIMEOUT; /** * Create a new KafkaTransactionManager, given a ProducerFactory. * Transaction synchronization is turned off by default, as this manager might be used alongside a datastore-based * Spring transaction manager like DataSourceTransactionManager, which has stronger needs for synchronization. Only * one manager is allowed to drive synchronization at any point of time. * @param producerFactory the ProducerFactory to use */ public KafkaTransactionManager(ProducerFactory producerFactory) { Assert.notNull(producerFactory, "The 'ProducerFactory' cannot be null"); Assert.isTrue(producerFactory.transactionCapable(), "The 'ProducerFactory' must support transactions"); setTransactionSynchronization(SYNCHRONIZATION_NEVER); this.producerFactory = producerFactory; } /** * Set a transaction id prefix to override the prefix in the producer factory. * @param transactionIdPrefix the prefix. * @since 2.3 */ public void setTransactionIdPrefix(String transactionIdPrefix) { this.transactionIdPrefix = transactionIdPrefix; } /** * Get the producer factory. * @return the producerFactory */ @Override public ProducerFactory getProducerFactory() { return this.producerFactory; } /** * Set the maximum time to wait when closing a producer; default 5 seconds. * @param closeTimeout the close timeout. * @since 2.1.14 */ public void setCloseTimeout(Duration closeTimeout) { Assert.notNull(closeTimeout, "'closeTimeout' cannot be null"); this.closeTimeout = closeTimeout; } @SuppressWarnings(UNCHECKED) @Override protected Object doGetTransaction() { KafkaTransactionObject txObject = new KafkaTransactionObject(); txObject.setResourceHolder((KafkaResourceHolder) TransactionSynchronizationManager .getResource(getProducerFactory())); return txObject; } @Override protected boolean isExistingTransaction(Object transaction) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) transaction; return (txObject.getResourceHolder() != null); } @Override protected void doBegin(Object transaction, TransactionDefinition definition) { if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) { throw new InvalidIsolationLevelException("Apache Kafka does not support an isolation level concept"); } @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) transaction; KafkaResourceHolder resourceHolder = null; try { resourceHolder = ProducerFactoryUtils.getTransactionalResourceHolder(getProducerFactory(), this.transactionIdPrefix, this.closeTimeout); if (logger.isDebugEnabled()) { logger.debug("Created Kafka transaction on producer [" + resourceHolder.getProducer() + "]"); } txObject.setResourceHolder(resourceHolder); txObject.getResourceHolder().setSynchronizedWithTransaction(true); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getResourceHolder().setTimeoutInSeconds(timeout); } } catch (Exception ex) { if (resourceHolder != null) { ProducerFactoryUtils.releaseResources(resourceHolder); } throw new CannotCreateTransactionException("Could not create Kafka transaction", ex); } } @Override protected Object doSuspend(Object transaction) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) transaction; txObject.setResourceHolder(null); return TransactionSynchronizationManager.unbindResource(getProducerFactory()); } @Override protected void doResume(Object transaction, Object suspendedResources) { @SuppressWarnings(UNCHECKED) KafkaResourceHolder producerHolder = (KafkaResourceHolder) suspendedResources; TransactionSynchronizationManager.bindResource(getProducerFactory(), producerHolder); } @Override protected void doCommit(DefaultTransactionStatus status) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) status.getTransaction(); KafkaResourceHolder resourceHolder = txObject.getResourceHolder(); resourceHolder.commit(); } @Override protected void doRollback(DefaultTransactionStatus status) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) status.getTransaction(); KafkaResourceHolder resourceHolder = txObject.getResourceHolder(); resourceHolder.rollback(); } @Override protected void doSetRollbackOnly(DefaultTransactionStatus status) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) status.getTransaction(); txObject.getResourceHolder().setRollbackOnly(); } @Override protected void doCleanupAfterCompletion(Object transaction) { @SuppressWarnings(UNCHECKED) KafkaTransactionObject txObject = (KafkaTransactionObject) transaction; TransactionSynchronizationManager.unbindResource(getProducerFactory()); txObject.getResourceHolder().close(); txObject.getResourceHolder().clear(); } /** * Kafka transaction object, representing a KafkaResourceHolder. Used as transaction object by * KafkaTransactionManager. * @see KafkaResourceHolder */ private static class KafkaTransactionObject implements SmartTransactionObject { private KafkaResourceHolder resourceHolder; KafkaTransactionObject() { } public void setResourceHolder(KafkaResourceHolder resourceHolder) { this.resourceHolder = resourceHolder; } public KafkaResourceHolder getResourceHolder() { return this.resourceHolder; } @Override public boolean isRollbackOnly() { return this.resourceHolder.isRollbackOnly(); } @Override public void flush() { // no-op } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy