package com.notnoop.apns.internal; import java.util.concurrent.*; import com.notnoop.apns.ApnsNotification; import com.notnoop.exceptions.NetworkIOException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ApnsPooledConnection implements ApnsConnection { private static final Logger logger = LoggerFactory.getLogger(ApnsPooledConnection.class); private final ApnsConnection prototype; private final int max; private final ExecutorService executors; private final ConcurrentLinkedQueue<ApnsConnection> prototypes; public ApnsPooledConnection(ApnsConnection prototype, int max) { this(prototype, max, Executors.newFixedThreadPool(max)); } public ApnsPooledConnection(ApnsConnection prototype, int max, ExecutorService executors) { this.prototype = prototype; this.max = max; this.executors = executors; this.prototypes = new ConcurrentLinkedQueue<ApnsConnection>(); } private final ThreadLocal<ApnsConnection> uniquePrototype = new ThreadLocal<ApnsConnection>() { protected ApnsConnection initialValue() { ApnsConnection newCopy = prototype.copy(); prototypes.add(newCopy); return newCopy; } }; public void sendMessage(final ApnsNotification m) throws NetworkIOException { Future<Void> future = executors.submit(new Callable<Void>() { public Void call() throws Exception { uniquePrototype.get().sendMessage(m); return null; } }); try { future.get(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } catch (ExecutionException ee) { if (ee.getCause() instanceof NetworkIOException) { throw (NetworkIOException) ee.getCause(); } } } public ApnsConnection copy() { // TODO: Should copy executor properly.... What should copy do // really?! return new ApnsPooledConnection(prototype, max); } public void close() { executors.shutdown(); try { executors.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { logger.warn("pool termination interrupted", e); } for (ApnsConnection conn : prototypes) { Utilities.close(conn); } Utilities.close(prototype); } public void testConnection() { prototype.testConnection(); } public synchronized void setCacheLength(int cacheLength) { for (ApnsConnection conn : prototypes) { conn.setCacheLength(cacheLength); } } @SuppressFBWarnings(value = "UG_SYNC_SET_UNSYNC_GET", justification = "prototypes is a MT-safe container") public int getCacheLength() { return prototypes.peek().getCacheLength(); } }