package com.leansoft.luxun.producer.async; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; //import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.leansoft.luxun.common.exception.AsyncProducerInterruptedException; import com.leansoft.luxun.common.exception.QueueClosedException; import com.leansoft.luxun.common.exception.QueueFullException; import com.leansoft.luxun.mx.AsyncProducerQueueSizeStats; import com.leansoft.luxun.mx.AsyncProducerStats; import com.leansoft.luxun.producer.ProducerConfig; import com.leansoft.luxun.producer.SyncProducer; import com.leansoft.luxun.serializer.Encoder; import com.leansoft.luxun.utils.Utils; /** * * @author bulldog * */ public class AsyncProducer<T> implements Closeable { private final Logger logger = LoggerFactory.getLogger(AsyncProducer.class); private static final Random random = new Random(); private static final String ProducerQueueSizeMBeanName = "luxun.producer.Producer:type=AsyncProducerQueueSizeStats"; ///////////////////////////////////////////////////////////////////// final AsyncProducerConfig config; final SyncProducer producer; final Encoder<T> serializer; final EventHandler<T> eventHandler; final Properties eventHandlerProperties; final CallbackHandler<T> callbackHandler; final Properties callbackHandlerProperties; ///////////////////////////////////////////////////////////////////// final AtomicBoolean closed = new AtomicBoolean(false); final LinkedBlockingQueue<QueueItem<T>> queue; final int asyncProducerID = AsyncProducer.random.nextInt(); ///////////////////////////////////////////////////////////////////// final ProducerSendThread<T> sendThread; private final int enqueueTimeoutMs; private final Object shutdownData = new Object(); public AsyncProducer(AsyncProducerConfig config, // SyncProducer producer, // Encoder<T> serializer, // EventHandler<T> eventHandler,// Properties eventHandlerProperties, // CallbackHandler<T> callbackHandler, // Properties callbackHandlerProperties) { super(); this.config = config; this.producer = producer; this.serializer = serializer; this.eventHandler = eventHandler; this.eventHandlerProperties = eventHandlerProperties; this.callbackHandler = callbackHandler; this.callbackHandlerProperties = callbackHandlerProperties; this.enqueueTimeoutMs = config.getEnqueueTimeoutMs(); this.queue = new LinkedBlockingQueue<QueueItem<T>>(config.getQueueSize()); if (eventHandler != null) { eventHandler.init(eventHandlerProperties); } if (callbackHandler != null) { callbackHandler.init(callbackHandlerProperties); } this.sendThread = new ProducerSendThread<T>("ProducerSendThread-" + asyncProducerID, queue, serializer, producer, eventHandler != null ? eventHandler : new DefaultEventHandler<T>(new ProducerConfig(config.getProperties()), callbackHandler), callbackHandler, config.getQueueTime(), config.getBatchSize(), shutdownData); this.sendThread.setDaemon(false); AsyncProducerQueueSizeStats<T> stats = new AsyncProducerQueueSizeStats<T>(queue); stats.setMbeanName(ProducerQueueSizeMBeanName + "-" + asyncProducerID); Utils.registerMBean(stats); } @SuppressWarnings("unchecked") public AsyncProducer(AsyncProducerConfig config) { this(config// , new SyncProducer(config)// , (Encoder<T>)Utils.getObject(config.getSerializerClass())// , (EventHandler<T>)Utils.getObject(config.getEventHandler())// , config.getEventHandlerProperties()// , (CallbackHandler<T>)Utils.getObject(config.getCbkHandler())// , config.getCbkHandlerProperties()); } public void start() { sendThread.start(); } public void send(String topic, T event) { AsyncProducerStats.recordEvent(); if (closed.get()) { throw new QueueClosedException("Attempt to add event to a closed queue."); } QueueItem<T> data = new QueueItem<T>(event, topic); if (this.callbackHandler != null) { QueueItem<T> items = this.callbackHandler.beforeEnqueue(data); if (items != null) { data = items; } } boolean added = false; try { if (enqueueTimeoutMs == 0) { added = queue.offer(data); } else if (enqueueTimeoutMs < 0) { queue.put(data); added = true; } else { added = queue.offer(data, enqueueTimeoutMs, TimeUnit.MILLISECONDS); } } catch (InterruptedException e) { throw new AsyncProducerInterruptedException(e); } if (this.callbackHandler != null) { this.callbackHandler.afterEnqueue(data, queue.size(), added); } if (!added) { AsyncProducerStats.recordDroppedEvents(); throw new QueueFullException("Event queue is full of unsent messages, could not send event: " + event); } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void close() throws IOException { if (this.callbackHandler != null) { if (queue.size() > 0) { logger.info("Remained Queue Size: " + queue.size()); List<QueueItem<T>> remainedItems = new ArrayList<QueueItem<T>>(); while (queue.size() > 0) { remainedItems.add(queue.poll()); } callbackHandler.close(remainedItems); } else { callbackHandler.close(); } } closed.set(true); try { queue.put(new QueueItem(shutdownData, null)); } catch (InterruptedException e) { throw new AsyncProducerInterruptedException(e); } sendThread.shutdown(); sendThread.awaitShutdown(); producer.close(); logger.info("Closed AsyncProducer"); } }