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

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

There is a newer version: 3.2.0
Show newest version
/*
 * Copyright 2017 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
 *
 *      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.springframework.kafka.transaction;

import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaResourceHolder;
import org.springframework.kafka.core.KafkaTemplate;
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.ResourceTransactionManager;
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)}. * Spring's {@link KafkaTemplate} will auto detect a thread-bound Producer and * automatically participate in it. * *

* The use of {@link 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 ResourceTransactionManager { private final ProducerFactory producerFactory; /** * Create a new KafkaTransactionManager, given a ConnectionFactory. * 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; } /** * Get the producer factory. * @return the producerFactory */ public ProducerFactory getProducerFactory() { return this.producerFactory; } @Override public Object getResourceFactory() { return getProducerFactory(); } @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()); 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() { super(); } 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