diff --git a/src/main/java/com/notnoop/apns/ApnsService.java b/src/main/java/com/notnoop/apns/ApnsService.java index 2f34ed78..62c5b13d 100644 --- a/src/main/java/com/notnoop/apns/ApnsService.java +++ b/src/main/java/com/notnoop/apns/ApnsService.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.Date; import java.util.Map; +import java.util.Set; import com.notnoop.exceptions.NetworkIOException; @@ -160,6 +161,16 @@ public interface ApnsService { */ Map getInactiveDevices() throws NetworkIOException; + /** + * Returns the list of devices got a bad status when tring to send + * + * The result is map, mapping the device tokens as Hex Strings + * mapped to a set of DeliveryError + * @throws NetworkIOException if a network error occurred + * while retrieving invalid device connection + */ + Map> getDeliveryErrorDevices(); + /** * Test that the service is setup properly and the Apple servers * are reachable. diff --git a/src/main/java/com/notnoop/apns/internal/ApnsConnection.java b/src/main/java/com/notnoop/apns/internal/ApnsConnection.java index 757a81b3..5b4d0747 100644 --- a/src/main/java/com/notnoop/apns/internal/ApnsConnection.java +++ b/src/main/java/com/notnoop/apns/internal/ApnsConnection.java @@ -31,8 +31,11 @@ package com.notnoop.apns.internal; import java.io.Closeable; +import java.util.Map; +import java.util.Set; import com.notnoop.apns.ApnsNotification; +import com.notnoop.apns.DeliveryError; import com.notnoop.exceptions.NetworkIOException; public interface ApnsConnection extends Closeable { @@ -49,4 +52,6 @@ public interface ApnsConnection extends Closeable { void setCacheLength(int cacheLength); int getCacheLength(); + + Map> getDeliveryErrorDevices(); } diff --git a/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java b/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java index 6739c52d..70bde48e 100644 --- a/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java +++ b/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java @@ -36,8 +36,12 @@ import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; +import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; @@ -61,6 +65,7 @@ public class ApnsConnectionImpl implements ApnsConnection { private static final Logger logger = LoggerFactory.getLogger(ApnsConnectionImpl.class); + Map> deliveryErrorDevices = new HashMap>(); private final SocketFactory factory; private final String host; private final int port; @@ -132,7 +137,7 @@ public synchronized void close() { Utilities.close(socket); } - private void monitorSocket(final Socket socket) { + private void monitorSocket(final ApnsNotification apnsNotification, final Socket socket) { logger.debug("Launching Monitoring Thread for socket {}", socket); Thread t = threadFactory.newThread(new Runnable() { @@ -164,6 +169,15 @@ public void run() { int statusCode = bytes[1] & 0xFF; DeliveryError e = DeliveryError.ofCode(statusCode); + String token = Utilities.encodeHex(apnsNotification.getDeviceToken()); + Set deliveryErrors = deliveryErrorDevices.get(token); + if (deliveryErrors == null) { + deliveryErrors = new HashSet(); + + deliveryErrorDevices.put(token, deliveryErrors); + } + deliveryErrors.add(e); + int id = Utilities.parseBytes(bytes[2], bytes[3], bytes[4], bytes[5]); logger.debug("Closed connection cause={}; id={}", e, id); @@ -256,7 +270,7 @@ private boolean readPacket(final InputStream in, final byte[] bytes) throws IOEx t.start(); } - private synchronized Socket getOrCreateSocket(boolean resend) throws NetworkIOException { + private synchronized Socket getOrCreateSocket(ApnsNotification apnsNotification, boolean resend) throws NetworkIOException { if (reconnectPolicy.shouldReconnect()) { logger.debug("Reconnecting due to reconnectPolicy dictating it"); Utilities.close(socket); @@ -292,7 +306,7 @@ private synchronized Socket getOrCreateSocket(boolean resend) throws NetworkIOEx socket.setKeepAlive(true); if (errorDetection) { - monitorSocket(socket); + monitorSocket(apnsNotification, socket); } reconnectPolicy.reconnected(); @@ -325,7 +339,7 @@ private synchronized void sendMessage(ApnsNotification m, boolean fromBuffer) th while (true) { try { attempts++; - Socket socket = getOrCreateSocket(fromBuffer); + Socket socket = getOrCreateSocket(m, fromBuffer); socket.getOutputStream().write(m.marshall()); socket.getOutputStream().flush(); cacheNotification(m); @@ -408,4 +422,9 @@ public void setCacheLength(int cacheLength) { public int getCacheLength() { return cacheLength; } + + @Override + public Map> getDeliveryErrorDevices() { + return deliveryErrorDevices; + } } diff --git a/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java b/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java index e27d1702..f3d60a4a 100644 --- a/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java +++ b/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java @@ -30,8 +30,11 @@ */ package com.notnoop.apns.internal; +import java.util.Map; +import java.util.Set; import java.util.concurrent.*; import com.notnoop.apns.ApnsNotification; +import com.notnoop.apns.DeliveryError; import com.notnoop.exceptions.NetworkIOException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.slf4j.Logger; @@ -118,4 +121,9 @@ public synchronized void setCacheLength(int cacheLength) { public int getCacheLength() { return prototypes.peek().getCacheLength(); } + + @Override + public Map> getDeliveryErrorDevices() { + return prototype.getDeliveryErrorDevices(); + } } diff --git a/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java b/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java index 387f5a8f..7c9dd234 100644 --- a/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java +++ b/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java @@ -30,7 +30,11 @@ */ package com.notnoop.apns.internal; +import java.util.Map; +import java.util.Set; + import com.notnoop.apns.ApnsNotification; +import com.notnoop.apns.DeliveryError; import com.notnoop.exceptions.NetworkIOException; public class ApnsServiceImpl extends AbstractApnsService { @@ -56,4 +60,9 @@ public void stop() { public void testConnection() { connection.testConnection(); } + + @Override + public Map> getDeliveryErrorDevices() { + return connection.getDeliveryErrorDevices(); + } } diff --git a/src/main/java/com/notnoop/apns/internal/BatchApnsService.java b/src/main/java/com/notnoop/apns/internal/BatchApnsService.java index d7dcbefb..f77011b8 100644 --- a/src/main/java/com/notnoop/apns/internal/BatchApnsService.java +++ b/src/main/java/com/notnoop/apns/internal/BatchApnsService.java @@ -32,7 +32,9 @@ import static java.util.concurrent.Executors.defaultThreadFactory; +import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -41,6 +43,7 @@ import java.util.concurrent.TimeUnit; import com.notnoop.apns.ApnsNotification; +import com.notnoop.apns.DeliveryError; import com.notnoop.exceptions.NetworkIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,4 +143,9 @@ public void run() { } } } + + @Override + public Map> getDeliveryErrorDevices() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java b/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java index 21cc8202..a80c3dcf 100644 --- a/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java +++ b/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java @@ -32,6 +32,7 @@ import java.util.Date; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executors; @@ -43,6 +44,7 @@ import com.notnoop.apns.ApnsNotification; import com.notnoop.apns.ApnsService; +import com.notnoop.apns.DeliveryError; import com.notnoop.exceptions.NetworkIOException; public class QueuedApnsService extends AbstractApnsService { @@ -123,4 +125,9 @@ public void testConnection() throws NetworkIOException { service.testConnection(); } + @Override + public Map> getDeliveryErrorDevices() { + throw new UnsupportedOperationException(); + } + } diff --git a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java b/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java index 846a9a6f..a0c1043f 100644 --- a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java +++ b/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java @@ -33,6 +33,8 @@ import static org.junit.Assert.*; import java.io.IOException; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Semaphore; import org.junit.Test; @@ -40,6 +42,7 @@ import com.notnoop.apns.ApnsNotification; import com.notnoop.apns.ApnsService; +import com.notnoop.apns.DeliveryError; import com.notnoop.apns.EnhancedApnsNotification; import com.notnoop.exceptions.NetworkIOException; @@ -146,5 +149,10 @@ public void setCacheLength(int cacheLength) { public int getCacheLength() { return -1; } + + @Override + public Map> getDeliveryErrorDevices() { + throw new UnsupportedOperationException(); + } } }