diff --git a/spring-kafka/src/main/java/org/springframework/kafka/core/DefaultKafkaProducerFactory.java b/spring-kafka/src/main/java/org/springframework/kafka/core/DefaultKafkaProducerFactory.java index 7bc0d614..7988205d 100644 --- a/spring-kafka/src/main/java/org/springframework/kafka/core/DefaultKafkaProducerFactory.java +++ b/spring-kafka/src/main/java/org/springframework/kafka/core/DefaultKafkaProducerFactory.java @@ -16,26 +16,128 @@ package org.springframework.kafka.core; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.kafka.clients.producer.Callback; import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.Metric; +import org.apache.kafka.common.MetricName; +import org.apache.kafka.common.PartitionInfo; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.Lifecycle; /** * @author Gary Russell * */ -public class DefaultKafkaProducerFactory implements ProducerFactory { +public class DefaultKafkaProducerFactory implements ProducerFactory, Lifecycle, DisposableBean { + + private static final Log logger = LogFactory.getLog(DefaultKafkaProducerFactory.class); private final Map configs; + private volatile CloseSafeProducer producer; + + private volatile boolean running; + public DefaultKafkaProducerFactory(Map configs) { this.configs = new HashMap<>(configs); } + @Override + public void destroy() throws Exception { + CloseSafeProducer producer = this.producer; + this.producer = null; + if (producer != null) { + producer.delegate.close(); + } + } + + + @Override + public void start() { + this.running = true; + } + + + @Override + public void stop() { + try { + destroy(); + } + catch (Exception e) { + logger.error("Exception while stopping producer", e); + } + } + + + @Override + public boolean isRunning() { + return this.running; + } + @Override public Producer createProducer() { - return new KafkaProducer<>(this.configs); + if (this.producer == null) { + synchronized (this) { + if (this.producer == null) { + this.producer = new CloseSafeProducer(new KafkaProducer(this.configs)); + } + } + } + return this.producer; + } + + private static class CloseSafeProducer implements Producer { + + private final Producer delegate; + + public CloseSafeProducer(Producer delegate) { + this.delegate = delegate; + } + + @Override + public Future send(ProducerRecord record) { + return this.delegate.send(record); + } + + @Override + public Future send(ProducerRecord record, Callback callback) { + return this.delegate.send(record, callback); + } + + @Override + public void flush() { + this.delegate.flush(); + } + + @Override + public List partitionsFor(String topic) { + return this.delegate.partitionsFor(topic); + } + + @Override + public Map metrics() { + return this.delegate.metrics(); + } + + @Override + public void close() { + } + + @Override + public void close(long timeout, TimeUnit unit) { + } + } }