package com.notnoop.apns.internal; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import com.notnoop.apns.ApnsNotification; import com.notnoop.exceptions.NetworkIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BatchApnsService extends AbstractApnsService { private static final Logger logger = LoggerFactory.getLogger(BatchApnsService.class); /** * How many seconds to wait for more messages before batch is send. * Each message reset the wait time * * @see #maxBatchWaitTimeInSec */ private int batchWaitTimeInSec = 5; /** * How many seconds can be batch delayed before execution. * This time is not exact amount after which the batch will run its roughly the time */ private int maxBatchWaitTimeInSec = 10; private long firstMessageArrivedTime; private ApnsConnection prototype; private Queue<ApnsNotification> batch = new ConcurrentLinkedQueue<ApnsNotification>(); private ScheduledExecutorService scheduleService; private ScheduledFuture<?> taskFuture; private Runnable batchRunner = new SendMessagesBatch(); public BatchApnsService(ApnsConnection prototype, ApnsFeedbackConnection feedback, int batchWaitTimeInSec, int maxBachWaitTimeInSec, ThreadFactory tf) { super(feedback); this.prototype = prototype; this.batchWaitTimeInSec = batchWaitTimeInSec; this.maxBatchWaitTimeInSec = maxBachWaitTimeInSec; this.scheduleService = new ScheduledThreadPoolExecutor(1, tf == null ? Executors.defaultThreadFactory() : tf); } public void start() { // no code } public void stop() { Utilities.close(prototype); if (taskFuture != null) { taskFuture.cancel(true); } scheduleService.shutdownNow(); } public void testConnection() throws NetworkIOException { prototype.testConnection(); } @Override public void push(ApnsNotification message) throws NetworkIOException { if (batch.isEmpty()) { firstMessageArrivedTime = System.nanoTime(); } long sinceFirstMessageSec = (System.nanoTime() - firstMessageArrivedTime) / 1000 / 1000 / 1000; if (taskFuture != null && sinceFirstMessageSec < maxBatchWaitTimeInSec) { taskFuture.cancel(false); } batch.add(message); if (taskFuture == null || taskFuture.isDone()) { taskFuture = scheduleService.schedule(batchRunner, batchWaitTimeInSec, TimeUnit.SECONDS); } } class SendMessagesBatch implements Runnable { public void run() { ApnsConnection newConnection = prototype.copy(); try { ApnsNotification msg; while ((msg = batch.poll()) != null) { try { newConnection.sendMessage(msg); } catch (NetworkIOException e) { logger.warn("Network exception sending message msg "+ msg.getIdentifier(), e); } } } finally { Utilities.close(newConnection); } } } }