Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Performance testing java-apns #95

Merged
merged 7 commits into from

2 participants

@karldmoore

I've added server socket implementations that allow me to push data through the java-apns library without using the apple servers. This will let me do benchmarking and also performance tuning of the library.

@karldmoore karldmoore closed this
@karldmoore karldmoore reopened this
@froh42 froh42 merged commit 05284a2 into from
@froh42

Probably an autocompletion+autoimport Bug ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 25, 2013
  1. added feedback server returns

    Karl Moore authored
  2. added exception to method signature

    Karl Moore authored
  3. missed catch block

    Karl Moore authored
  4. added build for server sockets

    Karl Moore authored
Commits on Mar 26, 2013
  1. handle multiple requests from a single socket

    Karl Moore authored
  2. output needs to be buffered

    Karl Moore authored
This page is out of date. Refresh to see the latest.
View
104 src/test/java/com/notnoop/apns/AbstractApnsServerSocket.java
@@ -0,0 +1,104 @@
+package com.notnoop.apns;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+
+/**
+ * Represents the Apple server. This allows testing outside of the Apple
+ * servers. Sub-classes should implement the specific handing of new socket
+ * connections.
+ */
+public abstract class AbstractApnsServerSocket {
+ private final SSLServerSocket serverSocket;
+ private final ExecutorService executorService;
+ private final ApnsServerExceptionDelegate exceptionDelegate;
+
+ public AbstractApnsServerSocket(SSLContext sslContext, int port,
+ ExecutorService executorService,
+ ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
+ SSLServerSocketFactory serverSocketFactory = sslContext
+ .getServerSocketFactory();
+ serverSocket = (SSLServerSocket) serverSocketFactory
+ .createServerSocket(port);
+ this.executorService = executorService;
+ this.exceptionDelegate = exceptionDelegate;
+ }
+
+ /**
+ * Start the server accept process. This method is non-blocking.
+ */
+ public final void start() {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ startAccept();
+ }
+ }).start();
+ }
+
+ private void startAccept() {
+
+ try {
+ while (true) {
+ Socket accept = serverSocket.accept();
+ executorService.execute(new SocketHandler(accept));
+ }
+ } catch (IOException ioe) {
+ executorService.shutdown();
+ }
+ }
+
+ /**
+ * Stops the server socket. This method is blocking.
+ */
+ public final void stop() {
+ try {
+ serverSocket.close();
+ } catch (IOException ioe) {
+ // don't care
+ }
+
+ executorService.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
+ executorService.shutdownNow(); // Cancel currently executing
+ // tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
+ System.err.println("Pool did not terminate");
+ }
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ executorService.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private class SocketHandler implements Runnable {
+ private final Socket socket;
+
+ SocketHandler(Socket socket) {
+ this.socket = socket;
+ }
+
+ @Override
+ public void run() {
+ try {
+ handleSocket(socket);
+ } catch (IOException ioe) {
+ exceptionDelegate.handleRequestFailed(ioe);
+ }
+ }
+ }
+
+ abstract void handleSocket(Socket socket) throws IOException;
+}
View
42 src/test/java/com/notnoop/apns/ApnsFeedbackServerSocket.java
@@ -0,0 +1,42 @@
+package com.notnoop.apns;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.Date;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutorService;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * Represents the Apple Feedback server. This allows testing outside of the
+ * Apple servers.
+ */
+public class ApnsFeedbackServerSocket extends AbstractApnsServerSocket {
+ private final ApnsServerService requestDelegate;
+
+ public ApnsFeedbackServerSocket(SSLContext sslContext, int port,
+ ExecutorService executorService, ApnsServerService requestDelegate,
+ ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
+ super(sslContext, port, executorService, exceptionDelegate);
+ this.requestDelegate = requestDelegate;
+ }
+
+ @Override
+ void handleSocket(Socket socket) throws IOException {
+ Map<byte[], Date> inactiveDevices = requestDelegate
+ .getInactiveDevices();
+ DataOutputStream dataStream = new DataOutputStream(
+ socket.getOutputStream());
+ for (Entry<byte[], Date> entry : inactiveDevices.entrySet()) {
+ int time = (int) (entry.getValue().getTime() / 1000L);
+ dataStream.writeInt(time);
+ byte[] bytes = entry.getKey();
+ dataStream.writeShort(bytes.length);
+ dataStream.write(bytes);
+ }
+ dataStream.close();
+ }
+}
View
97 src/test/java/com/notnoop/apns/ApnsGatewayServerSocket.java
@@ -0,0 +1,97 @@
+package com.notnoop.apns;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+
+import javax.net.ssl.SSLContext;
+
+import sun.java2d.pipe.BufferedPaints;
+
+/**
+ * Represents the Apple APNS server. This allows testing outside of the Apple
+ * servers.
+ */
+public class ApnsGatewayServerSocket extends AbstractApnsServerSocket {
+ private final ApnsServerService apnsServerService;
+
+ public ApnsGatewayServerSocket(SSLContext sslContext, int port,
+ ExecutorService executorService,
+ ApnsServerService apnsServerService,
+ ApnsServerExceptionDelegate exceptionDelegate) throws IOException {
+ super(sslContext, port, executorService, exceptionDelegate);
+ this.apnsServerService = apnsServerService;
+ }
+
+ @Override
+ void handleSocket(Socket socket) throws IOException {
+ InputStream inputStream = socket.getInputStream();
+ DataInputStream dataInputStream = new DataInputStream(inputStream);
+ while (true) {
+ int identifier = 0;
+ try {
+ int read = dataInputStream.read();
+ if (read == -1) {
+ break;
+ }
+
+ boolean enhancedFormat = read == 1;
+ int expiry = 0;
+ if (enhancedFormat) {
+ identifier = dataInputStream.readInt();
+ expiry = dataInputStream.readInt();
+ }
+
+ int deviceTokenLength = dataInputStream.readShort();
+ byte[] deviceTokenBytes = toArray(inputStream,
+ deviceTokenLength);
+
+ int payloadLength = dataInputStream.readShort();
+ byte[] payloadBytes = toArray(inputStream, payloadLength);
+
+ ApnsNotification message;
+ if (enhancedFormat) {
+ message = new EnhancedApnsNotification(identifier, expiry,
+ deviceTokenBytes, payloadBytes);
+ } else {
+ message = new SimpleApnsNotification(deviceTokenBytes,
+ payloadBytes);
+ }
+ apnsServerService.messageReceived(message);
+ } catch (IOException ioe) {
+ writeResponse(socket, identifier, 8, 1);
+ break;
+ } catch (Exception expt) {
+ writeResponse(socket, identifier, 8, 1);
+ break;
+ }
+ }
+ }
+
+ private void writeResponse(Socket socket, int identifier, int command,
+ int status) {
+ try {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ socket.getOutputStream());
+ DataOutputStream dataOutputStream = new DataOutputStream(bos);
+ dataOutputStream.writeByte(command);
+ dataOutputStream.writeByte(status);
+ dataOutputStream.writeInt(identifier);
+ dataOutputStream.flush();
+ } catch (IOException ioe) {
+ // if we can't write a response, nothing we can do
+ }
+ }
+
+ private byte[] toArray(InputStream inputStream, int size)
+ throws IOException {
+ byte[] bytes = new byte[size];
+ inputStream.read(bytes);
+ return bytes;
+ }
+}
View
15 src/test/java/com/notnoop/apns/ApnsServerExceptionDelegate.java
@@ -0,0 +1,15 @@
+package com.notnoop.apns;
+
+/**
+ * A delegate that gets notified of failures.
+ */
+public interface ApnsServerExceptionDelegate {
+
+ void handleRequestFailed(Throwable thr);
+
+ public static final ApnsServerExceptionDelegate EMPTY = new ApnsServerExceptionDelegate() {
+ @Override
+ public void handleRequestFailed(Throwable thr) {
+ }
+ };
+}
View
39 src/test/java/com/notnoop/apns/ApnsServerService.java
@@ -0,0 +1,39 @@
+package com.notnoop.apns;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * A delegate that gets notified of the delivery of messages.
+ */
+public interface ApnsServerService {
+
+
+ /**
+ * Called when message was successfully received
+ *
+ * @param message the notification that was received.
+ * @throws Exception
+ */
+ void messageReceived(ApnsNotification message) throws Exception;
+
+
+ /**
+ * Called to determine if any of the devices is judged to be inactive.
+ *
+ * @return a map of inactive devices.
+ */
+ Map<byte[], Date> getInactiveDevices();
+
+ public static final ApnsServerService EMPTY = new ApnsServerService() {
+ @Override
+ public void messageReceived(ApnsNotification message) throws Exception {
+ }
+
+ @Override
+ public Map<byte[], Date> getInactiveDevices() {
+ return Collections.emptyMap();
+ }
+ };
+}
View
358 src/test/java/com/notnoop/apns/ApnsServerSocketBuilder.java
@@ -0,0 +1,358 @@
+/*
+ * 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;
+
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_FEEDBACK_PORT;
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_GATEWAY_PORT;
+import static com.notnoop.apns.internal.Utilities.SANDBOX_FEEDBACK_PORT;
+import static com.notnoop.apns.internal.Utilities.SANDBOX_GATEWAY_PORT;
+import static com.notnoop.apns.internal.Utilities.newSSLContext;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+
+import com.notnoop.apns.internal.Utilities;
+import com.notnoop.exceptions.InvalidSSLConfig;
+import com.notnoop.exceptions.RuntimeIOException;
+
+/**
+ * The class is used to create instances of {@link ApnsService}.
+ *
+ * Note that this class is not synchronized. If multiple threads access a
+ * {@code ApnsServiceBuilder} instance concurrently, and at least on of the
+ * threads modifies one of the attributes structurally, it must be synchronized
+ * externally.
+ *
+ * Starting a new {@code ApnsService} is easy:
+ *
+ * <pre>
+ * ApnsService = APNS.newService()
+ * .withCert(&quot;/path/to/certificate.p12&quot;, &quot;MyCertPassword&quot;)
+ * .withSandboxDestination().build()
+ * </pre>
+ */
+public class ApnsServerSocketBuilder {
+ private static final String KEYSTORE_TYPE = "PKCS12";
+ private static final String KEY_ALGORITHM = "sunx509";
+
+ private SSLContext sslContext;
+ private int gatewayPort = -1;
+ private int feedbackPort = -1;
+ private ExecutorService executor = null;
+ private ApnsServerService serverService = ApnsServerService.EMPTY;
+ private ApnsServerExceptionDelegate exceptionDelegate = ApnsServerExceptionDelegate.EMPTY;
+
+ /**
+ * Constructs a new instance of {@code ApnsServiceBuilder}
+ */
+ public ApnsServerSocketBuilder() {
+ sslContext = null;
+ }
+
+ /**
+ * Specify the certificate used to connect to Apple APNS servers. This
+ * relies on the path (absolute or relative to working path) to the keystore
+ * (*.p12) containing the certificate, along with the given password.
+ *
+ * The keystore needs to be of PKCS12 and the keystore needs to be encrypted
+ * using the SunX509 algorithm. Both of these settings are the default.
+ *
+ * This library does not support password-less p12 certificates, due to a
+ * Oracle Java library <a
+ * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6415637"> Bug
+ * 6415637</a>. There are three workarounds: use a password-protected
+ * certificate, use a different boot Java SDK implementation, or constract
+ * the `SSLContext` yourself! Needless to say, the password-protected
+ * certificate is most recommended option.
+ *
+ * @param fileName
+ * the path to the certificate
+ * @param password
+ * the password of the keystore
+ * @return this
+ * @throws RuntimeIOException
+ * if it {@code fileName} cannot be found or read
+ * @throws InvalidSSLConfig
+ * if fileName is invalid Keystore or the password is invalid
+ */
+ public ApnsServerSocketBuilder withCert(String fileName, String password)
+ throws RuntimeIOException, InvalidSSLConfig {
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(fileName);
+ return withCert(stream, password);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeIOException(e);
+ } finally {
+ Utilities.close(stream);
+ }
+ }
+
+ /**
+ * Specify the certificate used to connect to Apple APNS servers. This
+ * relies on the stream of keystore (*.p12) containing the certificate,
+ * along with the given password.
+ *
+ * The keystore needs to be of PKCS12 and the keystore needs to be encrypted
+ * using the SunX509 algorithm. Both of these settings are the default.
+ *
+ * This library does not support password-less p12 certificates, due to a
+ * Oracle Java library <a
+ * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6415637"> Bug
+ * 6415637</a>. There are three workarounds: use a password-protected
+ * certificate, use a different boot Java SDK implementation, or constract
+ * the `SSLContext` yourself! Needless to say, the password-protected
+ * certificate is most recommended option.
+ *
+ * @param stream
+ * the keystore represented as input stream
+ * @param password
+ * the password of the keystore
+ * @return this
+ * @throws InvalidSSLConfig
+ * if stream is invalid Keystore or the password is invalid
+ */
+ public ApnsServerSocketBuilder withCert(InputStream stream, String password)
+ throws InvalidSSLConfig {
+ assertPasswordNotEmpty(password);
+ return withSSLContext(newSSLContext(stream, password, KEYSTORE_TYPE,
+ KEY_ALGORITHM));
+ }
+
+ /**
+ * Specify the certificate used to connect to Apple APNS servers. This
+ * relies on a keystore (*.p12) containing the certificate, along with the
+ * given password.
+ *
+ * This library does not support password-less p12 certificates, due to a
+ * Oracle Java library <a
+ * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6415637"> Bug
+ * 6415637</a>. There are three workarounds: use a password-protected
+ * certificate, use a different boot Java SDK implementation, or constract
+ * the `SSLContext` yourself! Needless to say, the password-protected
+ * certificate is most recommended option.
+ *
+ * @param stream
+ * the keystore
+ * @param password
+ * the password of the keystore
+ * @return this
+ * @throws InvalidSSLConfig
+ * if stream is invalid Keystore or the password is invalid
+ */
+ public ApnsServerSocketBuilder withCert(KeyStore keyStore, String password)
+ throws InvalidSSLConfig {
+ assertPasswordNotEmpty(password);
+ return withSSLContext(newSSLContext(keyStore, password, KEY_ALGORITHM));
+ }
+
+ private void assertPasswordNotEmpty(String password) {
+ if (password == null || password.length() == 0) {
+ throw new IllegalArgumentException(
+ "Passwords must be specified."
+ + "Oracle Java SDK does not support passwordless p12 certificates");
+ }
+ }
+
+ /**
+ * Specify the SSLContext that should be used to initiate the connection to
+ * Apple Server.
+ *
+ * Most clients would use {@link #withCert(InputStream, String)} or
+ * {@link #withCert(String, String)} instead. But some clients may need to
+ * represent the Keystore in a different format than supported.
+ *
+ * @param sslContext
+ * Context to be used to create secure connections
+ * @return this
+ */
+ public ApnsServerSocketBuilder withSSLContext(SSLContext sslContext) {
+ this.sslContext = sslContext;
+ return this;
+ }
+
+ /**
+ * Specify the gateway server for sending Apple iPhone notifications.
+ *
+ * @param port
+ * port of the notification gateway of Apple
+ * @return this
+ */
+ public ApnsServerSocketBuilder withGatewayDestination(int port) {
+ this.gatewayPort = port;
+ return this;
+ }
+
+ /**
+ * Specify the Feedback for getting failed devices from Apple iPhone Push
+ * servers.
+ *
+ * @param port
+ * port of the feedback server of Apple
+ * @return this
+ */
+ public ApnsServerSocketBuilder withFeedbackDestination(int port) {
+ this.feedbackPort = port;
+ return this;
+ }
+
+ /**
+ * Specify to use the Apple sandbox servers as iPhone gateway and feedback
+ * servers.
+ *
+ * This is desired when in testing and pushing notifications with a
+ * development provision.
+ *
+ * @return this
+ */
+ public ApnsServerSocketBuilder withSandboxDestination() {
+ return withGatewayDestination(SANDBOX_GATEWAY_PORT)
+ .withFeedbackDestination(SANDBOX_FEEDBACK_PORT);
+ }
+
+ /**
+ * Specify to use the Apple Production servers as iPhone gateway and
+ * feedback servers.
+ *
+ * This is desired when sending notifications to devices with a production
+ * provision (whether through App Store or Ad hoc distribution).
+ *
+ * @return this
+ */
+ public ApnsServerSocketBuilder withProductionDestination() {
+ return withGatewayDestination(PRODUCTION_GATEWAY_PORT)
+ .withFeedbackDestination(PRODUCTION_FEEDBACK_PORT);
+ }
+
+ /**
+ * Constructs a pool of connections to the notification servers.
+ *
+ * Apple servers recommend using a pooled connection up to 15 concurrent
+ * persistent connections to the gateways.
+ *
+ * Note: This option has no effect when using non-blocking connections.
+ */
+ public ApnsServerSocketBuilder asPool(int maxConnections) {
+ return asPool(new ThreadPoolExecutor(maxConnections, Integer.MAX_VALUE,
+ 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()));
+ }
+
+ /**
+ * Constructs a pool of connections to the notification servers.
+ *
+ * Apple servers recommend using a pooled connection up to 15 concurrent
+ * persistent connections to the gateways.
+ *
+ * Note: This option has no effect when using non-blocking connections.
+ */
+ public ApnsServerSocketBuilder asPool(ExecutorService executor) {
+ this.executor = executor;
+ return this;
+ }
+
+ /**
+ * Sets the delegate of the service, that gets notified of the status of
+ * message delivery.
+ *
+ * Note: This option has no effect when using non-blocking connections.
+ */
+ public ApnsServerSocketBuilder withService(ApnsServerService serverService) {
+ this.serverService = serverService == null ? ApnsServerService.EMPTY
+ : serverService;
+ return this;
+ }
+
+ /**
+ * Sets the delegate of the service, that gets notified of the status of
+ * message delivery.
+ *
+ * Note: This option has no effect when using non-blocking connections.
+ */
+ public ApnsServerSocketBuilder withDelegate(
+ ApnsServerExceptionDelegate exceptionDelegate) {
+ this.exceptionDelegate = exceptionDelegate == null ? ApnsServerExceptionDelegate.EMPTY
+ : exceptionDelegate;
+ return this;
+ }
+
+ /**
+ * Returns a fully initialized instance of {@link ApnsService}, according to
+ * the requested settings.
+ *
+ * @return a new instance of ApnsService
+ * @throws IOException
+ */
+ public ApnsSocketService build() throws IOException {
+ checkInitialization();
+
+ AbstractApnsServerSocket apnsPushServerSocket = new ApnsGatewayServerSocket(
+ sslContext, gatewayPort, executor, serverService,
+ exceptionDelegate);
+ AbstractApnsServerSocket apnsFeedbackServerSocket = new ApnsFeedbackServerSocket(
+ sslContext, feedbackPort, executor, serverService,
+ exceptionDelegate);
+
+ ApnsSocketService service = new ApnsSocketService(apnsPushServerSocket,
+ apnsFeedbackServerSocket);
+ service.start();
+ return service;
+ }
+
+ private void checkInitialization() {
+ if (sslContext == null) {
+ throw new IllegalStateException(
+ "SSL Certificates and attribute are not initialized\n"
+ + "Use .withCert() methods.");
+ }
+
+ if (executor == null) {
+ throw new IllegalStateException(
+ "SSL Certificates and attribute are not initialized\n"
+ + "Use .withCert() methods.");
+ }
+
+ if (gatewayPort == -1 || feedbackPort == -1) {
+ throw new IllegalStateException(
+ "The Destination APNS server is not stated\n"
+ + "Use .withDestination(), withSandboxDestination(), "
+ + "or withProductionDestination().");
+ }
+ }
+}
View
25 src/test/java/com/notnoop/apns/ApnsSocketService.java
@@ -0,0 +1,25 @@
+package com.notnoop.apns;
+
+import java.io.IOException;
+
+public class ApnsSocketService {
+ private final AbstractApnsServerSocket apnsPushServerSocket;
+ private final AbstractApnsServerSocket apnsFeedbackServerSocket;
+
+ public ApnsSocketService(AbstractApnsServerSocket apnsPushServerSocket,
+ AbstractApnsServerSocket apnsFeedbackServerSocket)
+ throws IOException {
+ this.apnsPushServerSocket = apnsPushServerSocket;
+ this.apnsFeedbackServerSocket = apnsFeedbackServerSocket;
+ }
+
+ public void start() {
+ apnsPushServerSocket.start();
+ apnsFeedbackServerSocket.start();
+ }
+
+ public void stop() {
+ apnsPushServerSocket.stop();
+ apnsFeedbackServerSocket.stop();
+ }
+}
Something went wrong with that request. Please try again.