|
@@ -0,0 +1,760 @@
|
|
|
+/*
|
|
|
+ * 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 com.notnoop.apns.internal.ApnsConnection;
|
|
|
+import com.notnoop.apns.internal.ApnsConnectionImpl;
|
|
|
+import com.notnoop.apns.internal.ApnsFeedbackConnection;
|
|
|
+import com.notnoop.apns.internal.ApnsPooledConnection;
|
|
|
+import com.notnoop.apns.internal.ApnsServiceImpl;
|
|
|
+import com.notnoop.apns.internal.BatchApnsService;
|
|
|
+import com.notnoop.apns.internal.QueuedApnsService;
|
|
|
+import com.notnoop.apns.internal.SSLContextBuilder;
|
|
|
+import com.notnoop.apns.internal.Utilities;
|
|
|
+import com.notnoop.exceptions.InvalidSSLConfig;
|
|
|
+import com.notnoop.exceptions.RuntimeIOException;
|
|
|
+
|
|
|
+import javax.net.ssl.SSLContext;
|
|
|
+import javax.net.ssl.SSLSocketFactory;
|
|
|
+import java.io.FileInputStream;
|
|
|
+import java.io.FileNotFoundException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.net.InetSocketAddress;
|
|
|
+import java.net.Proxy;
|
|
|
+import java.net.Socket;
|
|
|
+import java.security.KeyStore;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.ScheduledExecutorService;
|
|
|
+import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
|
+import java.util.concurrent.ThreadFactory;
|
|
|
+
|
|
|
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_FEEDBACK_HOST;
|
|
|
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_FEEDBACK_PORT;
|
|
|
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_GATEWAY_HOST;
|
|
|
+import static com.notnoop.apns.internal.Utilities.PRODUCTION_GATEWAY_PORT;
|
|
|
+import static com.notnoop.apns.internal.Utilities.SANDBOX_FEEDBACK_HOST;
|
|
|
+import static com.notnoop.apns.internal.Utilities.SANDBOX_FEEDBACK_PORT;
|
|
|
+import static com.notnoop.apns.internal.Utilities.SANDBOX_GATEWAY_HOST;
|
|
|
+import static com.notnoop.apns.internal.Utilities.SANDBOX_GATEWAY_PORT;
|
|
|
+import static java.util.concurrent.Executors.defaultThreadFactory;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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("/path/to/certificate.p12", "MyCertPassword")
|
|
|
+ * .withSandboxDestination()
|
|
|
+ * .build()
|
|
|
+ * </pre>
|
|
|
+ */
|
|
|
+public class ApnsServiceBuilder {
|
|
|
+ private static final String KEYSTORE_TYPE = "PKCS12";
|
|
|
+ private static final String KEY_ALGORITHM = ((java.security.Security.getProperty("ssl.KeyManagerFactory.algorithm") == null)? "sunx509" : java.security.Security.getProperty("ssl.KeyManagerFactory.algorithm"));
|
|
|
+
|
|
|
+ private SSLContext sslContext;
|
|
|
+
|
|
|
+ private int readTimeout;
|
|
|
+ private int connectTimeout;
|
|
|
+
|
|
|
+ private String gatewayHost;
|
|
|
+ private int gatewayPort = -1;
|
|
|
+
|
|
|
+ private String feedbackHost;
|
|
|
+ private int feedbackPort;
|
|
|
+ private int pooledMax = 1;
|
|
|
+ private int cacheLength = ApnsConnection.DEFAULT_CACHE_LENGTH;
|
|
|
+ private boolean autoAdjustCacheLength = true;
|
|
|
+ private ExecutorService executor;
|
|
|
+
|
|
|
+ private ReconnectPolicy reconnectPolicy = ReconnectPolicy.Provided.EVERY_HALF_HOUR.newObject();
|
|
|
+ private boolean isQueued;
|
|
|
+ private ThreadFactory queueThreadFactory;
|
|
|
+
|
|
|
+ private boolean isBatched;
|
|
|
+ private int batchWaitTimeInSec;
|
|
|
+ private int batchMaxWaitTimeInSec;
|
|
|
+ private ScheduledExecutorService batchThreadPoolExecutor;
|
|
|
+
|
|
|
+ private ApnsDelegate delegate = ApnsDelegate.EMPTY;
|
|
|
+ private Proxy proxy;
|
|
|
+ private String proxyUsername;
|
|
|
+ private String proxyPassword;
|
|
|
+ private boolean errorDetection = true;
|
|
|
+ private ThreadFactory errorDetectionThreadFactory;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constructs a new instance of {@code ApnsServiceBuilder}
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder() { 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 construct
|
|
|
+ * 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 ApnsServiceBuilder 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 ApnsServiceBuilder withCert(InputStream stream, String password)
|
|
|
+ throws InvalidSSLConfig {
|
|
|
+ assertPasswordNotEmpty(password);
|
|
|
+ return withSSLContext(new SSLContextBuilder()
|
|
|
+ .withAlgorithm(KEY_ALGORITHM)
|
|
|
+ .withCertificateKeyStore(stream, password, KEYSTORE_TYPE)
|
|
|
+ .withDefaultTrustKeyStore()
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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 construct
|
|
|
+ * the `SSLContext` yourself! Needless to say, the password-protected
|
|
|
+ * certificate is most recommended option.
|
|
|
+ *
|
|
|
+ * @param keyStore the keystore
|
|
|
+ * @param password the password of the keystore
|
|
|
+ * @return this
|
|
|
+ * @throws InvalidSSLConfig if stream is invalid Keystore
|
|
|
+ * or the password is invalid
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withCert(KeyStore keyStore, String password)
|
|
|
+ throws InvalidSSLConfig {
|
|
|
+ assertPasswordNotEmpty(password);
|
|
|
+ return withSSLContext(new SSLContextBuilder()
|
|
|
+ .withAlgorithm(KEY_ALGORITHM)
|
|
|
+ .withCertificateKeyStore(keyStore, password)
|
|
|
+ .withDefaultTrustKeyStore()
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the certificate store used to connect to Apple APNS
|
|
|
+ * servers. This relies on the stream of keystore (*.p12 | *.jks)
|
|
|
+ * containing the keys and certificates, along with the given
|
|
|
+ * password and alias.
|
|
|
+ *
|
|
|
+ * The keystore can be either PKCS12 or JKS and the keystore
|
|
|
+ * needs to be encrypted using the SunX509 algorithm.
|
|
|
+ *
|
|
|
+ * 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
|
|
|
+ * @param alias the alias identifing the key to be used
|
|
|
+ * @return this
|
|
|
+ * @throws InvalidSSLConfig if stream is an invalid Keystore,
|
|
|
+ * the password is invalid or the alias is not found
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withCert(InputStream stream, String password, String alias)
|
|
|
+ throws InvalidSSLConfig {
|
|
|
+ assertPasswordNotEmpty(password);
|
|
|
+ return withSSLContext(new SSLContextBuilder()
|
|
|
+ .withAlgorithm(KEY_ALGORITHM)
|
|
|
+ .withCertificateKeyStore(stream, password, KEYSTORE_TYPE, alias)
|
|
|
+ .withDefaultTrustKeyStore()
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the certificate store used to connect to Apple APNS
|
|
|
+ * servers. This relies on the stream of keystore (*.p12 | *.jks)
|
|
|
+ * containing the keys and certificates, along with the given
|
|
|
+ * password and alias.
|
|
|
+ *
|
|
|
+ * The keystore can be either PKCS12 or JKS and the keystore
|
|
|
+ * needs to be encrypted using the SunX509 algorithm.
|
|
|
+ *
|
|
|
+ * 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 keyStore the keystore
|
|
|
+ * @param password the password of the keystore
|
|
|
+ * @param alias the alias identifing the key to be used
|
|
|
+ * @return this
|
|
|
+ * @throws InvalidSSLConfig if stream is an invalid Keystore,
|
|
|
+ * the password is invalid or the alias is not found
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withCert(KeyStore keyStore, String password, String alias)
|
|
|
+ throws InvalidSSLConfig {
|
|
|
+ assertPasswordNotEmpty(password);
|
|
|
+ return withSSLContext(new SSLContextBuilder()
|
|
|
+ .withAlgorithm(KEY_ALGORITHM)
|
|
|
+ .withCertificateKeyStore(keyStore, password, alias)
|
|
|
+ .withDefaultTrustKeyStore()
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 ApnsServiceBuilder withSSLContext(SSLContext sslContext) {
|
|
|
+ this.sslContext = sslContext;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the timeout value to be set in new setSoTimeout in created
|
|
|
+ * sockets, for both feedback and push connections, in milliseconds.
|
|
|
+ * @param readTimeout timeout value to be set in new setSoTimeout
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withReadTimeout(int readTimeout) {
|
|
|
+ this.readTimeout = readTimeout;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the timeout value to use for connectionTimeout in created
|
|
|
+ * sockets, for both feedback and push connections, in milliseconds.
|
|
|
+ * @param connectTimeout timeout value to use for connectionTimeout
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withConnectTimeout(int connectTimeout) {
|
|
|
+ this.connectTimeout = connectTimeout;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the gateway server for sending Apple iPhone
|
|
|
+ * notifications.
|
|
|
+ *
|
|
|
+ * Most clients should use {@link #withSandboxDestination()}
|
|
|
+ * or {@link #withProductionDestination()}. Clients may use
|
|
|
+ * this method to connect to mocking tests and such.
|
|
|
+ *
|
|
|
+ * @param host hostname the notification gateway of Apple
|
|
|
+ * @param port port of the notification gateway of Apple
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withGatewayDestination(String host, int port) {
|
|
|
+ this.gatewayHost = host;
|
|
|
+ this.gatewayPort = port;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the Feedback for getting failed devices from
|
|
|
+ * Apple iPhone Push servers.
|
|
|
+ *
|
|
|
+ * Most clients should use {@link #withSandboxDestination()}
|
|
|
+ * or {@link #withProductionDestination()}. Clients may use
|
|
|
+ * this method to connect to mocking tests and such.
|
|
|
+ *
|
|
|
+ * @param host hostname of the feedback server of Apple
|
|
|
+ * @param port port of the feedback server of Apple
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withFeedbackDestination(String host, int port) {
|
|
|
+ this.feedbackHost = host;
|
|
|
+ this.feedbackPort = port;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify to use Apple servers as iPhone gateway and feedback servers.
|
|
|
+ *
|
|
|
+ * If the passed {@code isProduction} is true, then it connects to the
|
|
|
+ * production servers, otherwise, it connects to the sandbox servers
|
|
|
+ *
|
|
|
+ * @param isProduction determines which Apple servers should be used:
|
|
|
+ * production or sandbox
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withAppleDestination(boolean isProduction) {
|
|
|
+ if (isProduction) {
|
|
|
+ return withProductionDestination();
|
|
|
+ } else {
|
|
|
+ return withSandboxDestination();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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 ApnsServiceBuilder withSandboxDestination() {
|
|
|
+ return withGatewayDestination(SANDBOX_GATEWAY_HOST, SANDBOX_GATEWAY_PORT)
|
|
|
+ .withFeedbackDestination(SANDBOX_FEEDBACK_HOST, 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 ApnsServiceBuilder withProductionDestination() {
|
|
|
+ return withGatewayDestination(PRODUCTION_GATEWAY_HOST, PRODUCTION_GATEWAY_PORT)
|
|
|
+ .withFeedbackDestination(PRODUCTION_FEEDBACK_HOST, PRODUCTION_FEEDBACK_PORT);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the reconnection policy for the socket connection.
|
|
|
+ *
|
|
|
+ * Note: This option has no effect when using non-blocking
|
|
|
+ * connections.
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withReconnectPolicy(ReconnectPolicy rp) {
|
|
|
+ this.reconnectPolicy = rp;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify if the notification cache should auto adjust.
|
|
|
+ * Default is true
|
|
|
+ *
|
|
|
+ * @param autoAdjustCacheLength the notification cache should auto adjust.
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withAutoAdjustCacheLength(boolean autoAdjustCacheLength) {
|
|
|
+ this.autoAdjustCacheLength = autoAdjustCacheLength;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the reconnection policy for the socket connection.
|
|
|
+ *
|
|
|
+ * Note: This option has no effect when using non-blocking
|
|
|
+ * connections.
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withReconnectPolicy(ReconnectPolicy.Provided rp) {
|
|
|
+ this.reconnectPolicy = rp.newObject();
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the address of the SOCKS proxy the connection should
|
|
|
+ * use.
|
|
|
+ *
|
|
|
+ * <p>Read the <a href="http://java.sun.com/javase/6/docs/technotes/guides/net/proxies.html">
|
|
|
+ * Java Networking and Proxies</a> guide to understand the
|
|
|
+ * proxies complexity.
|
|
|
+ *
|
|
|
+ * <p>Be aware that this method only handles SOCKS proxies, not
|
|
|
+ * HTTPS proxies. Use {@link #withProxy(Proxy)} instead.
|
|
|
+ *
|
|
|
+ * @param host the hostname of the SOCKS proxy
|
|
|
+ * @param port the port of the SOCKS proxy server
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withSocksProxy(String host, int port) {
|
|
|
+ Proxy proxy = new Proxy(Proxy.Type.SOCKS,
|
|
|
+ new InetSocketAddress(host, port));
|
|
|
+ return withProxy(proxy);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the proxy and the authentication parameters to be used
|
|
|
+ * to establish the connections to Apple Servers.
|
|
|
+ *
|
|
|
+ * <p>Read the <a href="http://java.sun.com/javase/6/docs/technotes/guides/net/proxies.html">
|
|
|
+ * Java Networking and Proxies</a> guide to understand the
|
|
|
+ * proxies complexity.
|
|
|
+ *
|
|
|
+ * @param proxy the proxy object to be used to create connections
|
|
|
+ * @param proxyUsername a String object representing the username of the proxy server
|
|
|
+ * @param proxyPassword a String object representing the password of the proxy server
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withAuthProxy(Proxy proxy, String proxyUsername, String proxyPassword) {
|
|
|
+ this.proxy = proxy;
|
|
|
+ this.proxyUsername = proxyUsername;
|
|
|
+ this.proxyPassword = proxyPassword;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the proxy to be used to establish the connections
|
|
|
+ * to Apple Servers
|
|
|
+ *
|
|
|
+ * <p>Read the <a href="http://java.sun.com/javase/6/docs/technotes/guides/net/proxies.html">
|
|
|
+ * Java Networking and Proxies</a> guide to understand the
|
|
|
+ * proxies complexity.
|
|
|
+ *
|
|
|
+ * @param proxy the proxy object to be used to create connections
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withProxy(Proxy proxy) {
|
|
|
+ this.proxy = proxy;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the number of notifications to cache for error purposes.
|
|
|
+ * Default is 100
|
|
|
+ *
|
|
|
+ * @param cacheLength Number of notifications to cache for error purposes
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withCacheLength(int cacheLength) {
|
|
|
+ this.cacheLength = cacheLength;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specify the socket to be used as underlying socket to connect
|
|
|
+ * to the APN service.
|
|
|
+ *
|
|
|
+ * This assumes that the socket connects to a SOCKS proxy.
|
|
|
+ *
|
|
|
+ * @deprecated use {@link ApnsServiceBuilder#withProxy(Proxy)} instead
|
|
|
+ * @param proxySocket the underlying socket for connections
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ @Deprecated
|
|
|
+ public ApnsServiceBuilder withProxySocket(Socket proxySocket) {
|
|
|
+ return this.withProxy(new Proxy(Proxy.Type.SOCKS,
|
|
|
+ proxySocket.getRemoteSocketAddress()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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 ApnsServiceBuilder asPool(int maxConnections) {
|
|
|
+ return asPool(Executors.newFixedThreadPool(maxConnections), maxConnections);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * Note: The maxConnections here is used as a hint to how many connections
|
|
|
+ * get created.
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asPool(ExecutorService executor, int maxConnections) {
|
|
|
+ this.pooledMax = maxConnections;
|
|
|
+ this.executor = executor;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constructs a new thread with a processing queue to process
|
|
|
+ * notification requests.
|
|
|
+ *
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asQueued() {
|
|
|
+ return asQueued(Executors.defaultThreadFactory());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Constructs a new thread with a processing queue to process
|
|
|
+ * notification requests.
|
|
|
+ *
|
|
|
+ * @param threadFactory
|
|
|
+ * thread factory to use for queue processing
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asQueued(ThreadFactory threadFactory) {
|
|
|
+ this.isQueued = true;
|
|
|
+ this.queueThreadFactory = threadFactory;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Construct service which will process notification requests in batch.
|
|
|
+ * After each request batch will wait <code>waitTimeInSec (set as 5sec)</code> for more request to come
|
|
|
+ * before executing but not more than <code>maxWaitTimeInSec (set as 10sec)</code>
|
|
|
+ *
|
|
|
+ * Note: It is not recommended to use pooled connection
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asBatched() {
|
|
|
+ return asBatched(5, 10);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Construct service which will process notification requests in batch.
|
|
|
+ * After each request batch will wait <code>waitTimeInSec</code> for more request to come
|
|
|
+ * before executing but not more than <code>maxWaitTimeInSec</code>
|
|
|
+ *
|
|
|
+ * Note: It is not recommended to use pooled connection
|
|
|
+ *
|
|
|
+ * @param waitTimeInSec
|
|
|
+ * time to wait for more notification request before executing
|
|
|
+ * batch
|
|
|
+ * @param maxWaitTimeInSec
|
|
|
+ * maximum wait time for batch before executing
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asBatched(int waitTimeInSec, int maxWaitTimeInSec) {
|
|
|
+ return asBatched(waitTimeInSec, maxWaitTimeInSec, (ThreadFactory)null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Construct service which will process notification requests in batch.
|
|
|
+ * After each request batch will wait <code>waitTimeInSec</code> for more request to come
|
|
|
+ * before executing but not more than <code>maxWaitTimeInSec</code>
|
|
|
+ *
|
|
|
+ * Each batch creates new connection and close it after finished.
|
|
|
+ * In case reconnect policy is specified it will be applied by batch processing.
|
|
|
+ * E.g.: {@link ReconnectPolicy.Provided#EVERY_HALF_HOUR} will reconnect the connection in case batch is running for more than half an hour
|
|
|
+ *
|
|
|
+ * Note: It is not recommended to use pooled connection
|
|
|
+ *
|
|
|
+ * @param waitTimeInSec
|
|
|
+ * time to wait for more notification request before executing
|
|
|
+ * batch
|
|
|
+ * @param maxWaitTimeInSec
|
|
|
+ * maximum wait time for batch before executing
|
|
|
+ * @param threadFactory
|
|
|
+ * thread factory to use for batch processing
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asBatched(int waitTimeInSec, int maxWaitTimeInSec, ThreadFactory threadFactory) {
|
|
|
+ return asBatched(waitTimeInSec, maxWaitTimeInSec, new ScheduledThreadPoolExecutor(1, threadFactory != null ? threadFactory : defaultThreadFactory()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Construct service which will process notification requests in batch.
|
|
|
+ * After each request batch will wait <code>waitTimeInSec</code> for more request to come
|
|
|
+ * before executing but not more than <code>maxWaitTimeInSec</code>
|
|
|
+ *
|
|
|
+ * Each batch creates new connection and close it after finished.
|
|
|
+ * In case reconnect policy is specified it will be applied by batch processing.
|
|
|
+ * E.g.: {@link ReconnectPolicy.Provided#EVERY_HALF_HOUR} will reconnect the connection in case batch is running for more than half an hour
|
|
|
+ *
|
|
|
+ * Note: It is not recommended to use pooled connection
|
|
|
+ *
|
|
|
+ * @param waitTimeInSec
|
|
|
+ * time to wait for more notification request before executing
|
|
|
+ * batch
|
|
|
+ * @param maxWaitTimeInSec
|
|
|
+ * maximum wait time for batch before executing
|
|
|
+ * @param batchThreadPoolExecutor
|
|
|
+ * executor for batched processing (may be null)
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder asBatched(int waitTimeInSec, int maxWaitTimeInSec, ScheduledExecutorService batchThreadPoolExecutor) {
|
|
|
+ this.isBatched = true;
|
|
|
+ this.batchWaitTimeInSec = waitTimeInSec;
|
|
|
+ this.batchMaxWaitTimeInSec = maxWaitTimeInSec;
|
|
|
+ this.batchThreadPoolExecutor = batchThreadPoolExecutor;
|
|
|
+ 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 ApnsServiceBuilder withDelegate(ApnsDelegate delegate) {
|
|
|
+ this.delegate = delegate == null ? ApnsDelegate.EMPTY : delegate;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Disables the enhanced error detection, enabled by the
|
|
|
+ * enhanced push notification interface. Error detection is
|
|
|
+ * enabled by default.
|
|
|
+ *
|
|
|
+ * This setting is desired when the application shouldn't spawn
|
|
|
+ * new threads.
|
|
|
+ *
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withNoErrorDetection() {
|
|
|
+ this.errorDetection = false;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Provide a custom source for threads used for monitoring connections.
|
|
|
+ *
|
|
|
+ * This setting is desired when the application must obtain threads from a
|
|
|
+ * controlled environment Google App Engine.
|
|
|
+ * @param threadFactory
|
|
|
+ * thread factory to use for error detection
|
|
|
+ * @return this
|
|
|
+ */
|
|
|
+ public ApnsServiceBuilder withErrorDetectionThreadFactory(ThreadFactory threadFactory) {
|
|
|
+ this.errorDetectionThreadFactory = threadFactory;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a fully initialized instance of {@link ApnsService},
|
|
|
+ * according to the requested settings.
|
|
|
+ *
|
|
|
+ * @return a new instance of ApnsService
|
|
|
+ */
|
|
|
+ public ApnsService build() {
|
|
|
+ checkInitialization();
|
|
|
+ ApnsService service;
|
|
|
+
|
|
|
+ SSLSocketFactory sslFactory = sslContext.getSocketFactory();
|
|
|
+ ApnsFeedbackConnection feedback = new ApnsFeedbackConnection(sslFactory, feedbackHost, feedbackPort, proxy, readTimeout, connectTimeout, proxyUsername, proxyPassword);
|
|
|
+
|
|
|
+ ApnsConnection conn = new ApnsConnectionImpl(sslFactory, gatewayHost,
|
|
|
+ gatewayPort, proxy, proxyUsername, proxyPassword, reconnectPolicy,
|
|
|
+ delegate, errorDetection, errorDetectionThreadFactory, cacheLength,
|
|
|
+ autoAdjustCacheLength, readTimeout, connectTimeout);
|
|
|
+ if (pooledMax != 1) {
|
|
|
+ conn = new ApnsPooledConnection(conn, pooledMax, executor);
|
|
|
+ }
|
|
|
+
|
|
|
+ service = new ApnsServiceImpl(conn, feedback);
|
|
|
+
|
|
|
+ if (isQueued) {
|
|
|
+ service = new QueuedApnsService(service, queueThreadFactory);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isBatched) {
|
|
|
+ service = new BatchApnsService(conn, feedback, batchWaitTimeInSec, batchMaxWaitTimeInSec, batchThreadPoolExecutor);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 (gatewayHost == null || gatewayPort == -1)
|
|
|
+ throw new IllegalStateException(
|
|
|
+ "The Destination APNS server is not stated\n"
|
|
|
+ + "Use .withDestination(), withSandboxDestination(), "
|
|
|
+ + "or withProductionDestination().");
|
|
|
+ }
|
|
|
+}
|