package com.leansoft.luxun.producer;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.leansoft.luxun.broker.Broker;
import com.leansoft.luxun.common.annotations.ClientSide;
import com.leansoft.luxun.common.exception.InvalidConfigException;
import com.leansoft.luxun.common.exception.UnavailableProducerException;
import com.leansoft.luxun.message.Message;
import com.leansoft.luxun.message.MessageList;
import com.leansoft.luxun.message.generated.CompressionCodec;
import com.leansoft.luxun.producer.async.AsyncProducer;
import com.leansoft.luxun.producer.async.AsyncProducerConfig;
import com.leansoft.luxun.producer.async.CallbackHandler;
import com.leansoft.luxun.producer.async.DefaultEventHandler;
import com.leansoft.luxun.producer.async.EventHandler;
import com.leansoft.luxun.serializer.Encoder;
import com.leansoft.luxun.utils.Utils;
/**
*
* @author bulldog
*
* @param <V>
*/
@ClientSide
public class ProducerPool<V> implements Closeable {
private final ProducerConfig config;
private final Encoder<V> serializer;
private final ConcurrentMap<Integer, SyncProducer> syncProducers;
private final ConcurrentMap<Integer, AsyncProducer<V>> asyncProducers;
private final EventHandler<V> eventHandler;
private final CallbackHandler<V> callbackHandler;
private boolean sync = true;
private final Set<String> compressedTopics;
private final Logger logger = LoggerFactory.getLogger(ProducerPool.class);
public ProducerPool(ProducerConfig config,//
Encoder<V> serializer, //
ConcurrentMap<Integer, SyncProducer> syncProducers,//
ConcurrentMap<Integer, AsyncProducer<V>> asyncProducers, //
EventHandler<V> inputEventHandler, //
CallbackHandler<V> callbackHandler) {
super();
this.config = config;
this.serializer = serializer;
this.syncProducers = syncProducers;
this.asyncProducers = asyncProducers;
this.eventHandler = inputEventHandler != null ? inputEventHandler : new DefaultEventHandler<V>(config, callbackHandler);
this.callbackHandler = callbackHandler;
if (serializer == null) {
throw new InvalidConfigException("serializer passed in is null!");
}
this.sync = !"async".equalsIgnoreCase(config.getProducerType());
this.compressedTopics = new HashSet<String>(config.getCompressedTopics());
}
public ProducerPool(ProducerConfig config, Encoder<V> serializer,//
EventHandler<V> eventHandler,//
CallbackHandler<V> callbackHandler) {
this(config,//
serializer,//
new ConcurrentHashMap<Integer, SyncProducer>(),//
new ConcurrentHashMap<Integer, AsyncProducer<V>>(),//
eventHandler,//
callbackHandler);
}
@SuppressWarnings("unchecked")
public ProducerPool(ProducerConfig config, Encoder<V> serializer) {
this(config,//
serializer,//
new ConcurrentHashMap<Integer, SyncProducer>(),//
new ConcurrentHashMap<Integer, AsyncProducer<V>>(),//
(EventHandler<V>) Utils.getObject(config.getEventHandler()),//
(CallbackHandler<V>) Utils.getObject(config.getCbkHandler()));
}
/**
* add a new producer, either synchronous or asynchronous, connecting
* to the specified broker
*
* @param broker broker to producer
*/
public void addProducerForBroker(Broker broker) {
Properties props = new Properties();
props.put("host", broker.host);
props.put("port", "" + broker.port);
props.putAll(config.getProperties());
if (sync) {
SyncProducer producer = new SyncProducer(new SyncProducerConfig(props));
logger.info("Creating sync producer for broker id = " + broker.id + " at " + broker.host + ":" + broker.port);
syncProducers.put(broker.id, producer);
} else {
AsyncProducer<V> producer = new AsyncProducer<V>(new AsyncProducerConfig(props),//
new SyncProducer(new SyncProducerConfig(props)),//
serializer,//
eventHandler,//
config.getEventHandlerProperties(),//
this.callbackHandler, //
config.getCbkHandlerProperties());
producer.start();
logger.info("Creating async producer for broker id = " + broker.id + " at " + broker.host + ":" + broker.port);
asyncProducers.put(broker.id, producer);
}
}
/**
* selects either a synchronous or an asynchronous producer, for the
* specified broker id and calls the send API on the selected producer
* to publish the data to the specified broker
*
* @param ppd the producer pool request object
*/
public void send(ProducerPoolData<V> ppd) {
if (logger.isDebugEnabled()) {
logger.debug("send message: " + ppd);
}
if (sync) {
MessageList messageList = convert(ppd);
SyncProducer producer = syncProducers.get(ppd.broker.id);
if (producer == null) {
throw new UnavailableProducerException("Producer pool has not been initialized correctly. " + "Sync Producer for broker "
+ ppd.broker + " does not exist in the pool");
}
producer.send(ppd.topic, messageList);
} else {
AsyncProducer<V> asyncProducer = asyncProducers.get(ppd.broker.id);
if (asyncProducer == null) {
throw new UnavailableProducerException("Producer pool has not been initialized correctly. " + "Async Producer for broker "
+ ppd.broker + " does not exist in the pool");
}
for(V v : ppd.data) {
asyncProducer.send(ppd.topic, v);
}
}
}
public void send(List<ProducerPoolData<V>> poolData) {
for(ProducerPoolData<V> ppd : poolData) {
send(ppd);
}
}
// Convert to Luxun message list format
private MessageList convert(ProducerPoolData<V> ppd) {
CompressionCodec codec = config.getCompressionCodec();
if (codec != CompressionCodec.NO_COMPRESSION && !compressedTopics.isEmpty() && !compressedTopics.contains(ppd.topic)) {
codec = CompressionCodec.NO_COMPRESSION;
}
MessageList messageList = new MessageList(codec);
for(V v : ppd.data) {
Message message = serializer.toMessage(v);
messageList.add(message);
}
return messageList;
}
/**
* Closes all the producers in the pool
*/
@Override
public void close() throws IOException {
logger.info("Closing all producers");
if (sync) {
for (SyncProducer p : syncProducers.values()) {
p.close();
}
} else {
for (AsyncProducer<V> p : asyncProducers.values()) {
p.close();
}
}
}
/**
* This constructs and returns the value object for the producer pool
*
* @param topic the topic to which the data should be published
* @param broker the broker
* @param data the data to be published
*/
public ProducerPoolData<V> buildProducerPoolData(String topic, Broker broker, List<V> data) {
return new ProducerPoolData<V>(topic, broker, data);
}
}