use of com.notnoop.apns.DeliveryError in project java-apns by notnoop.
the class ApnsConnectionImpl method monitorSocket.
private void monitorSocket(final Socket socketToMonitor) {
logger.debug("Launching Monitoring Thread for socket {}", socketToMonitor);
Thread t = threadFactory.newThread(new Runnable() {
static final int EXPECTED_SIZE = 6;
@SuppressWarnings("InfiniteLoopStatement")
@Override
public void run() {
logger.debug("Started monitoring thread");
try {
InputStream in;
try {
in = socketToMonitor.getInputStream();
} catch (IOException ioe) {
logger.warn("The value of socket is null", ioe);
in = null;
}
byte[] bytes = new byte[EXPECTED_SIZE];
while (in != null && readPacket(in, bytes)) {
logger.debug("Error-response packet {}", Utilities.encodeHex(bytes));
// Quickly close socket, so we won't ever try to send push notifications
// using the defective socket.
Utilities.close(socketToMonitor);
int command = bytes[0] & 0xFF;
if (command != 8) {
throw new IOException("Unexpected command byte " + command);
}
int statusCode = bytes[1] & 0xFF;
DeliveryError e = DeliveryError.ofCode(statusCode);
int id = Utilities.parseBytes(bytes[2], bytes[3], bytes[4], bytes[5]);
logger.debug("Closed connection cause={}; id={}", e, id);
delegate.connectionClosed(e, id);
Queue<ApnsNotification> tempCache = new LinkedList<ApnsNotification>();
ApnsNotification notification = null;
boolean foundNotification = false;
while (!cachedNotifications.isEmpty()) {
notification = cachedNotifications.poll();
logger.debug("Candidate for removal, message id {}", notification.getIdentifier());
if (notification.getIdentifier() == id) {
logger.debug("Bad message found {}", notification.getIdentifier());
foundNotification = true;
break;
}
tempCache.add(notification);
}
if (foundNotification) {
logger.debug("delegate.messageSendFailed, message id {}", notification.getIdentifier());
delegate.messageSendFailed(notification, new ApnsDeliveryErrorException(e));
} else {
cachedNotifications.addAll(tempCache);
int resendSize = tempCache.size();
logger.warn("Received error for message that wasn't in the cache...");
if (autoAdjustCacheLength) {
cacheLength = cacheLength + (resendSize / 2);
delegate.cacheLengthExceeded(cacheLength);
}
logger.debug("delegate.messageSendFailed, unknown id");
delegate.messageSendFailed(null, new ApnsDeliveryErrorException(e));
}
int resendSize = 0;
while (!cachedNotifications.isEmpty()) {
resendSize++;
final ApnsNotification resendNotification = cachedNotifications.poll();
logger.debug("Queuing for resend {}", resendNotification.getIdentifier());
notificationsBuffer.add(resendNotification);
}
logger.debug("resending {} notifications", resendSize);
delegate.notificationsResent(resendSize);
}
logger.debug("Monitoring input stream closed by EOF");
} catch (IOException e) {
// An exception when reading the error code is non-critical, it will cause another retry
// sending the message. Other than providing a more stable network connection to the APNS
// server we can't do much about it - so let's not spam the application's error log.
logger.info("Exception while waiting for error code", e);
delegate.connectionClosed(DeliveryError.UNKNOWN, -1);
} finally {
Utilities.close(socketToMonitor);
drainBuffer();
}
}
/**
* Read a packet like in.readFully(bytes) does - but do not throw an exception and return false if nothing
* could be read at all.
* @param in the input stream
* @param bytes the array to be filled with data
* @return true if a packet as been read, false if the stream was at EOF right at the beginning.
* @throws IOException When a problem occurs, especially EOFException when there's an EOF in the middle of the packet.
*/
private boolean readPacket(final InputStream in, final byte[] bytes) throws IOException {
final int len = bytes.length;
int n = 0;
while (n < len) {
try {
int count = in.read(bytes, n, len - n);
if (count < 0) {
throw new EOFException("EOF after reading " + n + " bytes of new packet.");
}
n += count;
} catch (IOException ioe) {
if (n == 0)
return false;
throw new IOException("Error after reading " + n + " bytes of packet", ioe);
}
}
return true;
}
});
t.start();
}
use of com.notnoop.apns.DeliveryError in project java-apns by notnoop.
the class ApnsConnectionCacheTest method handleReTransmissionError5Good1Bad7Good.
/**
* Test1 to make sure that after rejected notification
* in-flight notifications are re-transmitted
*
* @throws InterruptedException
*/
@Test(timeout = 5000)
public void handleReTransmissionError5Good1Bad7Good() throws InterruptedException {
server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory());
//5 success 1 fail 7 success 7 resent
final CountDownLatch sync = new CountDownLatch(20);
final AtomicInteger numResent = new AtomicInteger();
final AtomicInteger numSent = new AtomicInteger();
final AtomicInteger numStartSend = new AtomicInteger();
int EXPECTED_RESEND_COUNT = 7;
int EXPECTED_SEND_COUNT = 12;
server.getWaitForError().acquire();
server.start();
ApnsService service = APNS.newService().withSSLContext(clientContext()).withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()).withDelegate(new StartSendingApnsDelegate() {
public void startSending(final ApnsNotification message, final boolean resent) {
if (!resent) {
numStartSend.incrementAndGet();
}
}
public void messageSent(ApnsNotification message, boolean resent) {
if (!resent) {
numSent.incrementAndGet();
}
sync.countDown();
}
public void messageSendFailed(ApnsNotification message, Throwable e) {
numSent.decrementAndGet();
}
public void connectionClosed(DeliveryError e, int messageIdentifier) {
}
public void cacheLengthExceeded(int newCacheLength) {
}
public void notificationsResent(int resendCount) {
numResent.set(resendCount);
}
}).build();
server.stopAt(eMsg1.length() * 5 + eMsg2.length() + eMsg3.length() * 14);
for (int i = 0; i < 5; ++i) {
service.push(eMsg1);
}
service.push(eMsg2);
for (int i = 0; i < 7; ++i) {
service.push(eMsg3);
}
server.sendError(8, eMsg2.getIdentifier());
server.getWaitForError().release();
server.getMessages().acquire();
sync.await();
Assert.assertEquals(EXPECTED_RESEND_COUNT, numResent.get());
Assert.assertEquals(EXPECTED_SEND_COUNT, numSent.get());
Assert.assertEquals(EXPECTED_SEND_COUNT + 1, numStartSend.get());
}
Aggregations