Package com.relayrides.pushy.apns

Source Code of com.relayrides.pushy.apns.ApnsConnectionTest$TestListener

/* Copyright (c) 2014 RelayRides
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package com.relayrides.pushy.apns;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

import javax.net.ssl.SSLHandshakeException;

import org.junit.Test;

import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;

public class ApnsConnectionTest extends BasePushyTest {

  private static final String TEST_CONNECTION_NAME = "Test connection";

  private class TestListener implements ApnsConnectionListener<SimpleApnsPushNotification> {

    private final Object mutex;

    private boolean connectionSucceeded = false;
    private boolean connectionFailed = false;
    private boolean connectionClosed = false;

    private Throwable connectionFailureCause;

    private final ArrayList<SimpleApnsPushNotification> writeFailures = new ArrayList<SimpleApnsPushNotification>();

    private SimpleApnsPushNotification rejectedNotification;
    private RejectedNotificationReason rejectionReason;

    private final ArrayList<SimpleApnsPushNotification> unprocessedNotifications = new ArrayList<SimpleApnsPushNotification>();

    public TestListener(final Object mutex) {
      this.mutex = mutex;
    }

    @Override
    public void handleConnectionSuccess(final ApnsConnection<SimpleApnsPushNotification> connection) {
      synchronized (this.mutex) {
        this.connectionSucceeded = true;
        this.mutex.notifyAll();
      }
    }

    @Override
    public void handleConnectionFailure(final ApnsConnection<SimpleApnsPushNotification> connection, final Throwable cause) {
      synchronized (mutex) {
        this.connectionFailed = true;
        this.connectionFailureCause = cause;

        this.mutex.notifyAll();
      }
    }

    @Override
    public void handleConnectionClosure(final ApnsConnection<SimpleApnsPushNotification> connection) {
      try {
        connection.waitForPendingWritesToFinish();
      } catch (InterruptedException ignored) {
      }

      synchronized (mutex) {
        this.connectionClosed = true;
        this.mutex.notifyAll();
      }
    }

    @Override
    public void handleWriteFailure(final ApnsConnection<SimpleApnsPushNotification> connection,
        final SimpleApnsPushNotification notification, final Throwable cause) {

      this.writeFailures.add(notification);
    }

    @Override
    public void handleRejectedNotification(final ApnsConnection<SimpleApnsPushNotification> connection,
        final SimpleApnsPushNotification rejectedNotification, final RejectedNotificationReason reason) {

      this.rejectedNotification = rejectedNotification;
      this.rejectionReason = reason;
    }

    @Override
    public void handleUnprocessedNotifications(final ApnsConnection<SimpleApnsPushNotification> connection,
        final Collection<SimpleApnsPushNotification> unprocessedNotifications) {

      this.unprocessedNotifications.addAll(unprocessedNotifications);
    }

    @Override
    public void handleConnectionWritabilityChange(final ApnsConnection<SimpleApnsPushNotification> connection, final boolean writable) {
    }
  }

  @Test
  public void testApnsConnectionNullListener() throws Exception {
    new ApnsConnection<ApnsPushNotification>(TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(),
        this.getEventLoopGroup(), new ApnsConnectionConfiguration(), null, TEST_CONNECTION_NAME);
  }

  @Test(expected = NullPointerException.class)
  public void testApnsConnectionNullEnvironment() throws Exception {
    new ApnsConnection<ApnsPushNotification>(null, SSLTestUtil.createSSLContextForTestClient(),
        this.getEventLoopGroup(), new ApnsConnectionConfiguration(), null, TEST_CONNECTION_NAME);
  }

  @Test(expected = NullPointerException.class)
  public void testApnsConnectionNullSslContext() throws Exception {
    new ApnsConnection<ApnsPushNotification>(TEST_ENVIRONMENT, null, this.getEventLoopGroup(),
        new ApnsConnectionConfiguration(), null, TEST_CONNECTION_NAME);
  }

  @Test(expected = NullPointerException.class)
  public void testApnsConnectionNullEventLoopGroup() throws Exception {
    new ApnsConnection<ApnsPushNotification>(TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(),
        null, new ApnsConnectionConfiguration(), null, TEST_CONNECTION_NAME);
  }

  @Test(expected = NullPointerException.class)
  public void testApnsConnectionNullConfiguration() throws Exception {
    new ApnsConnection<ApnsPushNotification>(TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(),
        this.getEventLoopGroup(), null, null, TEST_CONNECTION_NAME);
  }

  @Test(expected = NullPointerException.class)
  public void testApnsConnectionNullName() throws Exception {
    new ApnsConnection<ApnsPushNotification>(TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(),
        this.getEventLoopGroup(), new ApnsConnectionConfiguration(), null, null);
  }

  @Test
  public void testConnect() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {
    // For this test, we just want to make sure that connection succeeds and nothing explodes.
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);
  }

  @Test(expected = IllegalStateException.class)
  public void testDoubleConnect() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), new TestListener(new Object()), TEST_CONNECTION_NAME);

    apnsConnection.connect();
    apnsConnection.connect();
  }

  @Test
  public void testConnectEmptyKeystore() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {

    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient("/empty-keystore.jks"),
            this.getEventLoopGroup(), new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionFailed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionFailed);
    assertTrue(listener.connectionFailureCause instanceof SSLHandshakeException);
  }

  @Test
  public void testConnectUntrustedKeystore() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {

    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient("/pushy-test-client-untrusted.jks"),
            this.getEventLoopGroup(), new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionFailed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionFailed);
    assertTrue(listener.connectionFailureCause instanceof SSLHandshakeException);
  }

  @Test
  public void testConnectionRefusal() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsEnvironment connectionRefusedEnvironment = new ApnsEnvironment("localhost", 7876, "localhost", 7877);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            connectionRefusedEnvironment, SSLTestUtil.createSSLContextForTestClient("/pushy-test-client.jks"),
            this.getEventLoopGroup(), new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionFailed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionFailed);
  }

  @Test
  public void testSendNotification() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    final CountDownLatch latch = this.getApnsServer().getAcceptedNotificationCountDownLatch(1);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    apnsConnection.sendNotification(this.createTestNotification());
    this.waitForLatch(latch);
  }

  @Test
  public void testSendNotificationWithNullPriority() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    final CountDownLatch latch = this.getApnsServer().getAcceptedNotificationCountDownLatch(1);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    final byte[] token = new byte[32];
    new Random().nextBytes(token);

    final SimpleApnsPushNotification nullPriorityNotification = new SimpleApnsPushNotification(
        token, "This is a bogus payload, but that's okay.", null, null);

    apnsConnection.sendNotification(nullPriorityNotification);
    this.waitForLatch(latch);
  }

  @Test
  public void testSendNotificationWithError() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    final SimpleApnsPushNotification bogusNotification =
        new SimpleApnsPushNotification(new byte[] {}, "This is a bogus notification and should be rejected.");

    synchronized (mutex) {
      apnsConnection.sendNotification(bogusNotification);

      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
    assertEquals(bogusNotification, listener.rejectedNotification);
    assertEquals(RejectedNotificationReason.MISSING_TOKEN, listener.rejectionReason);
  }

  @Test
  public void testShutdownGracefully() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    synchronized (mutex) {
      apnsConnection.shutdownGracefully();

      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
    assertNull(listener.rejectedNotification);
    assertNull(listener.rejectionReason);
    assertTrue(listener.unprocessedNotifications.isEmpty());
  }

  @Test
  public void testDoubleShutdownGracefully() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    synchronized (mutex) {
      apnsConnection.shutdownGracefully();
      apnsConnection.shutdownGracefully();

      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
    assertNull(listener.rejectedNotification);
    assertNull(listener.rejectionReason);
    assertTrue(listener.unprocessedNotifications.isEmpty());
  }

  @Test
  public void testShutdownGracefullyBeforeConnect() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    apnsConnection.shutdownGracefully();
  }

  @Test
  public void testShutdownImmediately() throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, InterruptedException {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    synchronized (mutex) {
      apnsConnection.shutdownImmediately();

      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
  }

  @Test
  public void testShutdownImmediatelyBeforeConnect() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);
    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

    apnsConnection.shutdownImmediately();
  }

  @Test
  public void testWaitForPendingOperationsToFinish() throws Exception {
    // For the purposes of this test, we're happy just as long as we don't time out waiting for writes to finish.

    {
      final Object mutex = new Object();

      final TestListener listener = new TestListener(mutex);
      final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
          new ApnsConnection<SimpleApnsPushNotification>(
              TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
              new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

      apnsConnection.waitForPendingWritesToFinish();
      apnsConnection.shutdownImmediately();
    }

    {
      final Object mutex = new Object();

      final TestListener listener = new TestListener(mutex);
      final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
          new ApnsConnection<SimpleApnsPushNotification>(
              TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
              new ApnsConnectionConfiguration(), listener, TEST_CONNECTION_NAME);

      synchronized (mutex) {
        apnsConnection.connect();

        while (!listener.connectionSucceeded) {
          mutex.wait();
        }
      }

      assertTrue(listener.connectionSucceeded);

      for (int i = 0; i < 1000; i++) {
        apnsConnection.sendNotification(this.createTestNotification());
      }

      apnsConnection.waitForPendingWritesToFinish();
      apnsConnection.shutdownGracefully();
    }
  }

  @Test
  public void testWriteTimeout() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);

    final ApnsConnectionConfiguration writeTimeoutConfiguration = new ApnsConnectionConfiguration();
    writeTimeoutConfiguration.setCloseAfterInactivityTime(1);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            writeTimeoutConfiguration, listener, TEST_CONNECTION_NAME);

    synchronized (mutex) {
      apnsConnection.connect();

      // Do nothing, but wait for the connection to time out due to inactivity
      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
  }

  @Test
  public void testGracefulShutdownTimeout() throws Exception {
    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);

    final ApnsConnectionConfiguration gracefulShutdownTimeoutConfiguration = new ApnsConnectionConfiguration();
    gracefulShutdownTimeoutConfiguration.setGracefulShutdownTimeout(1);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            gracefulShutdownTimeoutConfiguration, listener, TEST_CONNECTION_NAME);

    // We'll pretend that we have a "dead" connection; it will be up to the graceful shutdown timeout to close the
    // connection.
    this.getApnsServer().setShouldSendErrorResponses(false);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    synchronized (mutex) {
      apnsConnection.shutdownGracefully();

      while (!listener.connectionClosed) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionClosed);
  }

  @Test
  public void testShutdownAtSendAttemptLimit() throws Exception {

    final int notificationCount = 1000;
    final int sendAttemptLimit = 100;

    final Object mutex = new Object();

    final TestListener listener = new TestListener(mutex);

    final ApnsConnectionConfiguration sendAttemptLimitConfiguration = new ApnsConnectionConfiguration();
    sendAttemptLimitConfiguration.setSendAttemptLimit(100);

    final ApnsConnection<SimpleApnsPushNotification> apnsConnection =
        new ApnsConnection<SimpleApnsPushNotification>(
            TEST_ENVIRONMENT, SSLTestUtil.createSSLContextForTestClient(), this.getEventLoopGroup(),
            sendAttemptLimitConfiguration, listener, TEST_CONNECTION_NAME);

    final CountDownLatch totalLatch = this.getApnsServer().getAcceptedNotificationCountDownLatch(notificationCount);
    final CountDownLatch limitLatch = this.getApnsServer().getAcceptedNotificationCountDownLatch(sendAttemptLimit);

    synchronized (mutex) {
      apnsConnection.connect();

      while (!listener.connectionSucceeded) {
        mutex.wait();
      }
    }

    assertTrue(listener.connectionSucceeded);

    for (int i = 0; i < notificationCount; i++) {
      apnsConnection.sendNotification(this.createTestNotification());
    }

    this.waitForLatch(limitLatch);
    assertEquals(notificationCount - sendAttemptLimit, totalLatch.getCount());
  }
}
TOP

Related Classes of com.relayrides.pushy.apns.ApnsConnectionTest$TestListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.