/*
* Copyright 2009, Mahmood Ali.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Mahmood Ali. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.notnoop.apns.integration;
import com.notnoop.apns.APNS;
import com.notnoop.apns.ApnsDelegate;
import com.notnoop.apns.ApnsNotification;
import com.notnoop.apns.ApnsService;
import com.notnoop.apns.DeliveryError;
import com.notnoop.apns.EnhancedApnsNotification;
import com.notnoop.apns.integration.ApnsDelegateRecorder.MessageSentFailedRecord;
import com.notnoop.apns.utils.FixedCertificates;
import com.notnoop.apns.utils.Simulator.ApnsResponse;
import com.notnoop.apns.utils.Simulator.ApnsSimulatorWithVerification;
import com.notnoop.exceptions.ApnsDeliveryErrorException;
import com.notnoop.exceptions.NetworkIOException;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
import static com.notnoop.apns.utils.FixedCertificates.LOCALHOST;
import static com.notnoop.apns.utils.FixedCertificates.clientContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ApnsConnectionResendTest {
private static EnhancedApnsNotification NOTIFICATION_0 = buildNotification(0);
private static EnhancedApnsNotification NOTIFICATION_1 = buildNotification(1);
private static EnhancedApnsNotification NOTIFICATION_2 = buildNotification(2);
private static ApnsSimulatorWithVerification apnsSim;
private ApnsDelegateRecorder delegateRecorder;
private ApnsService testee;
@Before
public void setUp() {
if (apnsSim == null) {
apnsSim = new ApnsSimulatorWithVerification(FixedCertificates.serverContext().getServerSocketFactory());
apnsSim.start();
}
apnsSim.reset();
delegateRecorder = new ApnsDelegateRecorder();
testee = build(delegateRecorder);
}
@AfterClass
public static void tearDownClass() {
if (apnsSim != null) {
apnsSim.stop();
apnsSim = null;
}
}
/*
* Test when we submit 3 messages to APNS 0, 1, 2. 0 is an error but we don't see the error response back until
* 1,2 have already been submitted. Then at this point the network connection to APNS cannot be made, so that
* when retrying the submissions we have to notify the client that delivery failed for 1 and 2.
*/
@Test
public void testGivenFailedSubmissionDueToErrorThenApnsDownWithNotificationsInBufferEnsureClientNotified()
throws Exception {
final DeliveryError deliveryError = DeliveryError.INVALID_PAYLOAD_SIZE;
apnsSim.when(NOTIFICATION_0).thenDoNothing();
apnsSim.when(NOTIFICATION_1).thenDoNothing();
apnsSim.when(NOTIFICATION_2).thenRespond(ApnsResponse.returnErrorAndShutdown(deliveryError, NOTIFICATION_0));
testee.push(NOTIFICATION_0);
testee.push(NOTIFICATION_1);
testee.push(NOTIFICATION_2);
// Give some time for connection failure to take place
Thread.sleep(5000);
// Verify received expected notifications
apnsSim.verify();
// verify delegate calls
assertEquals(3, delegateRecorder.getSent().size());
final List<MessageSentFailedRecord> failed = delegateRecorder.getFailed();
assertEquals(3, failed.size());
// first is failed delivery due to payload size
failed.get(0).assertRecord(NOTIFICATION_0, new ApnsDeliveryErrorException(deliveryError));
// second and third are due to not being able to connect to APNS
assertNetworkIoExForRedelivery(NOTIFICATION_1, failed.get(1));
assertNetworkIoExForRedelivery(NOTIFICATION_2, failed.get(2));
}
private void assertNetworkIoExForRedelivery(ApnsNotification notification, MessageSentFailedRecord failed) {
failed.assertRecord(notification, new NetworkIOException());
final NetworkIOException found = failed.getException();
assertTrue(found.isResend());
}
private ApnsService build(ApnsDelegate delegate) {
return APNS.newService()
.withConnectTimeout(1000)
.withSSLContext(clientContext())
.withGatewayDestination(LOCALHOST, apnsSim.getEffectiveGatewayPort())
.withFeedbackDestination(LOCALHOST, apnsSim.getEffectiveFeedbackPort())
.withDelegate(delegate).build();
}
private static EnhancedApnsNotification buildNotification(int id) {
final String deviceToken = ApnsSimulatorWithVerification.deviceTokenForId(id);
return new EnhancedApnsNotification(id, 1, deviceToken, "{\"aps\":{}}");
}
}