diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0dca3bd4..00000000 --- a/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -.#* -*.sw? -bin -target -release.properties -*~ -.project -.classpath -.settings -tags -.idea -*.iml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 90828b32..00000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: Java -jdk: - - oraclejdk8 - - openjdk7 - - oraclejdk7 - - openjdk6 - -notifications: - email: - - java-apns@frohwalt.de diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index df598f7a..00000000 --- a/CHANGELOG +++ /dev/null @@ -1,49 +0,0 @@ -Changelog of java-apns - -Version 0.2.4.build-SNAPSHOT - (in progress) - - Performance testing java-apns (#95) - -Version 0.2.3 - Mar 30, 2013 - - return back to com.notnoop.apns group id (#91) - - - Add no operation delegate adapter and ability to build service with keystore (#93) - - Proxy support for Feedback connection (#92) - - Make APNS class final - - Batch APNS (#14) - - Update dependencies (#85 #84) - -Version 0.2.2 - Jan 20, 2013 - - ApnsConnection resend unsent notifications after error - - Do not stop QueuedApnsService on unsuccessful attempt to submit a message - - Continuous integration builds on travis ci - - Support Proxy.Type.HTTP. - - Do not log error messages for retries when sending a message, rather - log one when we can't send a message at all. - - When there is an error (error packet or exception in parsing error packet) - close the socket instead of waiting for APNS to close it. - - java-apns now is an osgi bundle - - Have ApnsService.push return sent messages - - Add changelog - -Version 0.1.6 - Jan 13, 2011 - - Publish maven artifacts to central maven repo - - Fix a bug cause error detection not to be disabled - -Version 0.1.5 - Aug 13, 2010 - - Add support for enhanced push notification - - Enable error detection for dropped connections - - Rely on slf4j as dependency instead of logback - - Support launch images in notifications - -Version 0.1.4 - Apr 14, 2010 - - Support SOCKS proxies - - Add better options for shrinking long alert messages (e.g. insert "...") - - Fix a bug related to reconnection policy in pooled connections - -Version 0.1.3 - Feb 5, 2010 - - Add an ApnsService.testConnection for testing setups and connections - - Add ApnsService delegate interface to monitor progress - - Add better more descriptive Exceptions - -Version 0.1.2 and older - Distance past... diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0fc411f2..00000000 --- a/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ - 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. diff --git a/LICENSE.contributor b/LICENSE.contributor deleted file mode 100644 index 9713c165..00000000 --- a/LICENSE.contributor +++ /dev/null @@ -1,27 +0,0 @@ - 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. diff --git a/Makefile.release b/Makefile.release deleted file mode 100644 index d7bb0e47..00000000 --- a/Makefile.release +++ /dev/null @@ -1,59 +0,0 @@ -.PHONY: all site site-prepare site-clone site-gen site-push - -all: prepare site repo - -CODE_GIT="git@github.com:notnoop/java-apns.git" -BRANCH=gh-pages - -WORK_DIR=target/deploy -SITE_DIR=${WORK_DIR}/site - -REPO_GIT="git@github.com:notnoop/m2-repo.git" -REPO_DIR=${WORK_DIR}/repo - -prepare: - rm -rf ${WORK_DIR} - mkdir -p ${WORK_DIR} - -################### Site ####################### - -site: site-prepare site-clone site-gen site-push - -site-prepare: - rm -rf ${SITE_DIR} - mkdir -p ${WORK_DIR} - -site-clone: - git clone -b ${BRANCH} ${CODE_GIT} ${SITE_DIR} - -site-gen: - rm -rf ${SITE_DIR}/* - mvn site:site - cp -r target/site/ ${SITE_DIR} - -site-push: - cd ${SITE_DIR}; git add .; git commit -a -m "Updating site..." - cd ${SITE_DIR}; git push -f - - -################### Repo ####################### - -repo: repo-gen repo-prepare repo-clone repo-copy repo-push - -repo-gen: - mvn release:prepare - mvn release:perform - -repo-prepare: - rm -rf ${REPO_DIR} - mkdir -p ${WORK_DIR} - -repo-clone: - git clone -b ${BRANCH} ${REPO_GIT} ${REPO_DIR} - -repo-copy: - cp -r target/checkout/${REPO_DIR}/ ${REPO_DIR} - -repo-push: - cd ${REPO_DIR}; git add .; git commit -a -m "Updating release repo" - cd ${REPO_DIR}; git push -f diff --git a/README.markdown b/README.markdown index 480c00ab..75504142 100644 --- a/README.markdown +++ b/README.markdown @@ -1,170 +1,13 @@ -Warning -------- -Apple will no longer support the legacy binary protocol after March 31, 2021. Java-Apns will stop working. See [details](https://developer.apple.com/news/?id=uzyxiriy) in their announcement. +Ye olde inactive fork +--------------------- -It is recommended to use [Pushy](https://github.com/jchambers/pushy) instead, which supports Apple's HTTP/2-based APNs protocol. +Once upon a time, when the king was away, the castle of notnoop/java-apns +fell into disrepair. All the knights moved out and built the temporary +settlement of java-apns/java-apns. -Build status: +But one beautiful day the king sent the keys to his old castle and +the knights returned home. Now they care about the castle again +and they lived happily ever after. - * Main fork [![Build Status](https://travis-ci.org/notnoop/java-apns.png)](https://travis-ci.org/notnoop/java-apns) - * Development [![Build Status](https://travis-ci.org/java-apns/java-apns.png)](https://travis-ci.org/java-apns/java-apns) - -Development - Version 1.0.0 ---------------------------- -There currently is a perelease for 1.0.0 which fixes a number of problems over 0.2.3. -There's still a CI test that sporadically fails on Travis-CI only, but not on other test -machines I have access to. Supposedly it is a still undetected race condition. - -However 1.0.0 Beta fixes a *lot* of problems over 0.2.x, so even as it is called beta -I'd recommend to use the beta instead of the 0.2.3 even for production. - -froh42 will return to develop for java-apns in October, so I expect the 1.0.0 final -to be released start of November. - - -Introduction ------------- - -java-apns is a Java client for Apple Push Notification service (APNs). -The library aims to provide a highly scalable interface to the Apple -server, while still being simple and modular. - -The interface aims to require very minimal code to achieve the most common -cases, but have it be reconfigurable so you can even use your own networking -connections or JSON library if necessary. - -Links: [Installation](http://wiki.github.com/notnoop/java-apns/installation) -- [Javadocs](http://notnoop.github.com/java-apns/apidocs/index.html) -- [Changelog](CHANGELOG) - -Features: --------------- - * Easy to use, high performance APNS Service API - * Supports Apple Feedback service - * Support Enhanced Apple Push Notification - * Support MDM and Newsstand Notifications - * Easy to use with Apple certificates - * Easy to extend and reuse - * Easy to integrate with dependency injection frameworks - * Easy to setup custom notification payloads - * Supports connection pooling - * Supports re-transmission of Notifications after error - -Getting started ---------------- - -Add the following dependencies to your `pom.xml` file: - - - - com.notnoop.apns - apns - 1.0.0.Beta6 - - -Sample Code ------------ - -To send a notification, you can do it in three steps: - -1. Setup the connection - - ApnsService service = - APNS.newService() - .withCert("/path/to/certificate.p12", "MyCertPassword") - .withSandboxDestination() - .build(); - -2. Create and send the message - - String payload = APNS.newPayload().alertBody("Can't be simpler than this!").build(); - String token = "fedfbcfb...."; - service.push(token, payload); - -3. To query the feedback service for inactive devices: - - Map inactiveDevices = service.getInactiveDevices(); - for (String deviceToken : inactiveDevices.keySet()) { - Date inactiveAsOf = inactiveDevices.get(deviceToken); - ... - } - -That's it! - -Custom Payloads ----------------- - -You can send a message payload, but providing custom fields and -localizable alert: - - String payload = APNS.newPayload() - .badge(3) - .customField("secret", "what do you think?") - .localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank") - .actionKey("Play").build(); - - service.push(token, payload); - - -Enhanced Notification Format ----------------- - -You can use the enhanced notification format to get feedback from Apple about notifications that were unable to be processed. - - String payload = APNS.newPayload() - .badge(3) - .customField("secret", "what do you think?"); - .localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank") - .actionKey("Play").build(); - - int now = (int)(new Date().getTime()/1000); - - EnhancedApnsNotification notification = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID() /* Next ID */, - now + 60 * 60 /* Expire in one hour */, - token /* Device Token */, - payload); - - service.push(notification); - - -License ----------------- - -Licensed under the [New 3-Clause BSD License](http://www.opensource.org/licenses/BSD-3-Clause). - - 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. - - -Contact ---------------- -Support mailing list: http://groups.google.com/group/java-apns-discuss +[java-apns](http://github.com/notnoop/java-apns) diff --git a/docs/cachedNotification_queue.md b/docs/cachedNotification_queue.md deleted file mode 100644 index 2ebe767d..00000000 --- a/docs/cachedNotification_queue.md +++ /dev/null @@ -1,17 +0,0 @@ -Reference: [APNS Problems](http://redth.codes/the-problem-with-apples-push-notification-ser/) - -java-apns maintains a per-connection sent queue (cachedNotifications) like described in the above article - - * java-apns queue is bounded (cacheNotification() will poll notifications out of the queue when the queue gets too big.) - - * It does not regularly check whether sent messages have been sent a few seconds ago to remove them from the sent queue. - -So if we send a lot of notifications without failure the old notifications will fall off the queue. This is typically ok (since they are "older" and probably have been sent successfully). The queue only serves to cache all the messages sent in between the client sending a bad notification and the APNS server replying. - -Improvements to consider: - - * Refactoring the queue into a full type, freeing the ApnsConnectionImpl from a lot of queue handling code. (no functional changes) - - * record the last send time in the notification (or a wrapper) and poll() messages out of the queue that are older than a certain threshold. (Would guarantee very short queues with low notification volume) - - * When queue gets full anyways (additionally to date handling) voluntarily inject a bad message to enforce an answer (+reconnect) from APNS (bad idea, SSL reconnect is expensive) diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 84646588..00000000 --- a/pom.xml +++ /dev/null @@ -1,361 +0,0 @@ - - 4.0.0 - com.notnoop.apns - apns - 1.0.0.Beta7-SNAPSHOT - jar - Java Apple Push Notification Service Library - - - - org.sonatype.oss - oss-parent - 9 - - - - scm:git:git://github.com/notnoop/java-apns.git - scm:git:git@github.com:notnoop/java-apns.git - http://github.com/notnoop/java-apns - HEAD - - - - UTF-8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - 1.6 - 1.6 - - - - maven-assembly-plugin - - - jar-with-dependencies - - - - - package - - single - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.3 - - - org.apache.maven.plugins - maven-release-plugin - 2.5 - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - test-jar - - - - - - maven-javadoc-plugin - 2.9.1 - - - - generate-javadoc - prepare-package - - jar - javadoc - - - - - - - maven-source-plugin - 2.2.1 - - - bind-sources - - jar - - - - - - maven-eclipse-plugin - 2.9 - - true - true - - - - org.apache.felix - maven-bundle-plugin - - - bundle-manifest - process-classes - - manifest - - - - - - ${project.groupId} - ${project.groupId}.${project.artifactId} - ${project.version} - - - - - com.mycila - license-maven-plugin - 2.11 - - - default-cli - process-classes - - check - - - - - true - true -
LICENSE
- - LICENSE.contributor - - - XML_STYLE - SLASHSTAR_STYLE - SEMICOLON_STYLE - - true - - src/**/*.java - -
-
- - - - - -
- - - - org.apache.felix - maven-bundle-plugin - 2.5.0 - - - -
- - - - - commons-httpclient - commons-httpclient - 3.1 - - - - uk.org.lidalia - slf4j-test - 1.0.0-jdk6 - test - - - - com.google.code.findbugs - annotations - 2.0.3 - provided - - - - org.slf4j - slf4j-api - 1.7.2 - - - - junit - junit - 4.11 - jar - test - - - - org.mockito - mockito-all - 1.9.5 - jar - test - - - com.fasterxml.jackson.core - jackson-databind - 2.1.4 - jar - compile - - - - - http://notnoop.github.com/java-apns - - Library to send Apple Push Notification. - - - - - New BSD License - http://www.opensource.org/licenses/bsd-license.php - repo - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - com.notnoop.apns.internal - - - - org.apache.maven.plugins - maven-jxr-plugin - 2.4 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.17 - - - - - - Github - http://github.com/java-apns/java-apns/issues - - - - - notnoop - Mahmood Ali - - - -
diff --git a/src/main/java/com/notnoop/apns/APNS.java b/src/main/java/com/notnoop/apns/APNS.java deleted file mode 100644 index 9b8f68c0..00000000 --- a/src/main/java/com/notnoop/apns/APNS.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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; - -/** - * The main class to interact with the APNS Service. - * - * Provides an interface to create the {@link ApnsServiceBuilder} and - * {@code ApnsNotification} payload. - * - */ -public final class APNS { - - private APNS() { throw new AssertionError("Uninstantiable class"); } - - /** - * Returns a new Payload builder - */ - public static PayloadBuilder newPayload() { - return new PayloadBuilder(); - } - - /** - * Returns a new APNS Service for sending iPhone notifications - */ - public static ApnsServiceBuilder newService() { - return new ApnsServiceBuilder(); - } -} diff --git a/src/main/java/com/notnoop/apns/ApnsDelegate.java b/src/main/java/com/notnoop/apns/ApnsDelegate.java deleted file mode 100644 index f48738ce..00000000 --- a/src/main/java/com/notnoop/apns/ApnsDelegate.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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; - -/** - * A delegate that gets notified of the status of notification delivery to the - * Apple Server. - * - * The delegate doesn't get notified when the notification actually arrives at - * the phone. - */ -public interface ApnsDelegate { - - /** - * Called when message was successfully sent to the Apple servers - * - * @param message the notification that was sent - * @param resent whether the notification was resent after an error - */ - public void messageSent(ApnsNotification message, boolean resent); - - /** - * Called when the delivery of the message failed for any reason - * - * If message is null, then your notification has been rejected by Apple but - * it has been removed from the cache so it is not possible to identify - * which notification caused the error. In this case subsequent - * notifications may be lost. If this happens you should consider increasing - * your cacheLength value to prevent data loss. - * - * @param message the notification that was attempted to be sent - * @param e the cause and description of the failure - */ - public void messageSendFailed(ApnsNotification message, Throwable e); - - /** - * The connection was closed and/or an error packet was received while - * monitoring was turned on. - * - * @param e the delivery error - * @param messageIdentifier id of the message that failed - */ - public void connectionClosed(DeliveryError e, int messageIdentifier); - - /** - * The resend cache needed a bigger size (while resending messages) - * - * @param newCacheLength new size of the resend cache. - */ - public void cacheLengthExceeded(int newCacheLength); - - /** - * A number of notifications has been queued for resending due to a error-response - * packet being received. - * - * @param resendCount the number of messages being queued for resend - */ - public void notificationsResent(int resendCount); - - /** - * A no operation delegate that does nothing! - */ - public final static ApnsDelegate EMPTY = new ApnsDelegateAdapter(); -} diff --git a/src/main/java/com/notnoop/apns/ApnsDelegateAdapter.java b/src/main/java/com/notnoop/apns/ApnsDelegateAdapter.java deleted file mode 100644 index d10a6755..00000000 --- a/src/main/java/com/notnoop/apns/ApnsDelegateAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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; - -/** - * A no operation delegate that does nothing! - */ -public class ApnsDelegateAdapter implements ApnsDelegate { - - public void messageSent(ApnsNotification message, boolean resent) { - } - - public void messageSendFailed(ApnsNotification message, Throwable e) { - } - - public void connectionClosed(DeliveryError e, int messageIdentifier) { - } - - public void cacheLengthExceeded(int newCacheLength) { - } - - public void notificationsResent(int resendCount) { - } -} diff --git a/src/main/java/com/notnoop/apns/ApnsNotification.java b/src/main/java/com/notnoop/apns/ApnsNotification.java deleted file mode 100644 index 945acccf..00000000 --- a/src/main/java/com/notnoop/apns/ApnsNotification.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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; - -/** - * Represents an APNS notification to be sent to Apple service. - */ -public interface ApnsNotification { - - /** - * Returns the binary representation of the device token. - */ - public byte[] getDeviceToken(); - - /** - * Returns the binary representation of the payload. - * - */ - public byte[] getPayload(); - - /** - * Returns the identifier of the current message. The - * identifier is an application generated identifier. - * - * @return the notification identifier - */ - public int getIdentifier(); - - /** - * Returns the expiry date of the notification, a fixed UNIX - * epoch date expressed in seconds - * - * @return the expiry date of the notification - */ - public int getExpiry(); - - /** - * Returns the binary representation of the message as expected by the - * APNS server. - * - * The returned array can be used to sent directly to the APNS server - * (on the wire/socket) without any modification. - */ - public byte[] marshall(); -} diff --git a/src/main/java/com/notnoop/apns/ApnsService.java b/src/main/java/com/notnoop/apns/ApnsService.java deleted file mode 100644 index 2f34ed78..00000000 --- a/src/main/java/com/notnoop/apns/ApnsService.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 java.util.Collection; -import java.util.Date; -import java.util.Map; - -import com.notnoop.exceptions.NetworkIOException; - -/** - * Represents the connection and interface to the Apple APNS servers. - * - * The service is created by {@link ApnsServiceBuilder} like: - * - *
- *   ApnsService = APNS.newService()
- *                  .withCert("/path/to/certificate.p12", "MyCertPassword")
- *                  .withSandboxDestination()
- *                  .build()
- * 
- */ -public interface ApnsService { - - /** - * Sends a push notification with the provided {@code payload} to the - * iPhone of {@code deviceToken}. - * - * The payload needs to be a valid JSON object, otherwise it may fail - * silently. It is recommended to use {@link PayloadBuilder} to create - * one. - * - * @param deviceToken the destination iPhone device token - * @param payload The payload message - * @throws NetworkIOException if a network error occurred while - * attempting to send the message - */ - ApnsNotification push(String deviceToken, String payload) throws NetworkIOException; - - EnhancedApnsNotification push(String deviceToken, String payload, Date expiry) throws NetworkIOException; - - /** - * Sends a push notification with the provided {@code payload} to the - * iPhone of {@code deviceToken}. - * - * The payload needs to be a valid JSON object, otherwise it may fail - * silently. It is recommended to use {@link PayloadBuilder} to create - * one. - * - * @param deviceToken the destination iPhone device token - * @param payload The payload message - * @throws NetworkIOException if a network error occurred while - * attempting to send the message - */ - ApnsNotification push(byte[] deviceToken, byte[] payload) throws NetworkIOException; - - EnhancedApnsNotification push(byte[] deviceToken, byte[] payload, int expiry) throws NetworkIOException; - - /** - * Sends a bulk push notification with the provided - * {@code payload} to iPhone of {@code deviceToken}s set. - * - * The payload needs to be a valid JSON object, otherwise it may fail - * silently. It is recommended to use {@link PayloadBuilder} to create - * one. - * - * @param deviceTokens the destination iPhone device tokens - * @param payload The payload message - * @throws NetworkIOException if a network error occurred while - * attempting to send the message - */ - Collection push(Collection deviceTokens, String payload) throws NetworkIOException; - Collection push(Collection deviceTokens, String payload, Date expiry) throws NetworkIOException; - - /** - * Sends a bulk push notification with the provided - * {@code payload} to iPhone of {@code deviceToken}s set. - * - * The payload needs to be a valid JSON object, otherwise it may fail - * silently. It is recommended to use {@link PayloadBuilder} to create - * one. - * - * @param deviceTokens the destination iPhone device tokens - * @param payload The payload message - * @throws NetworkIOException if a network error occurred while - * attempting to send the message - */ - Collection push(Collection deviceTokens, byte[] payload) throws NetworkIOException; - Collection push(Collection deviceTokens, byte[] payload, int expiry) throws NetworkIOException; - - /** - * Sends the provided notification {@code message} to the desired - * destination. - * @throws NetworkIOException if a network error occurred while - * attempting to send the message - */ - void push(ApnsNotification message) throws NetworkIOException; - - /** - * Starts the service. - * - * The underlying implementation may prepare its connections or - * data structures to be able to send the messages. - * - * This method is a blocking call, even if the service represents - * a Non-blocking push service. Once the service is returned, it is ready - * to accept push requests. - * - * @throws NetworkIOException if a network error occurred while - * starting the service - */ - void start(); - - /** - * Stops the service and frees any allocated resources it created for this - * service. - * - * The underlying implementation should close all connections it created, - * and possibly stop any threads as well. - */ - void stop(); - - /** - * Returns the list of devices that reported failed-delivery - * attempts to the Apple Feedback services. - * - * The result is map, mapping the device tokens as Hex Strings - * mapped to the timestamp when APNs determined that the - * application no longer exists on the device. - * @throws NetworkIOException if a network error occurred - * while retrieving invalid device connection - */ - Map getInactiveDevices() throws NetworkIOException; - - /** - * Test that the service is setup properly and the Apple servers - * are reachable. - * - * @throws NetworkIOException if the Apple servers aren't reachable - * or the service cannot send notifications for now - */ - void testConnection() throws NetworkIOException; - -} diff --git a/src/main/java/com/notnoop/apns/ApnsServiceBuilder.java b/src/main/java/com/notnoop/apns/ApnsServiceBuilder.java deleted file mode 100644 index abc01f3b..00000000 --- a/src/main/java/com/notnoop/apns/ApnsServiceBuilder.java +++ /dev/null @@ -1,760 +0,0 @@ -/* - * 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: - * - *
- *   ApnsService = APNS.newService()
- *    .withCert("/path/to/certificate.p12", "MyCertPassword")
- *    .withSandboxDestination()
- *    .build()
- * 
- */ -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 - * Bug 6415637. 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 - * Bug 6415637. 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 - * Bug 6415637. 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 - * Bug 6415637. 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 - * Bug 6415637. 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. - * - *

Read the - * Java Networking and Proxies guide to understand the - * proxies complexity. - * - *

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. - * - *

Read the - * Java Networking and Proxies 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 - * - *

Read the - * Java Networking and Proxies 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 waitTimeInSec (set as 5sec) for more request to come - * before executing but not more than maxWaitTimeInSec (set as 10sec) - * - * 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 waitTimeInSec for more request to come - * before executing but not more than maxWaitTimeInSec - * - * 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 waitTimeInSec for more request to come - * before executing but not more than maxWaitTimeInSec - * - * 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 waitTimeInSec for more request to come - * before executing but not more than maxWaitTimeInSec - * - * 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()."); - } -} diff --git a/src/main/java/com/notnoop/apns/DeliveryError.java b/src/main/java/com/notnoop/apns/DeliveryError.java deleted file mode 100644 index 4ba33f64..00000000 --- a/src/main/java/com/notnoop/apns/DeliveryError.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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; - -/** - * Errors in delivery that may get reported by Apple APN servers - */ -public enum DeliveryError { - /** - * Connection closed without any error. - * - * This may occur if the APN service faces an invalid simple - * APNS notification while running in enhanced mode - */ - NO_ERROR(0), - PROCESSING_ERROR(1), - MISSING_DEVICE_TOKEN(2), - MISSING_TOPIC(3), - MISSING_PAYLOAD(4), - INVALID_TOKEN_SIZE(5), - INVALID_TOPIC_SIZE(6), - INVALID_PAYLOAD_SIZE(7), - INVALID_TOKEN(8), - - NONE(255), - UNKNOWN(254); - - private final byte code; - DeliveryError(int code) { - this.code = (byte)code; - } - - /** The status code as specified by Apple */ - public int code() { - return code; - } - - /** - * Returns the appropriate {@code DeliveryError} enum - * corresponding to the Apple provided status code - * - * @param code status code provided by Apple - * @return the appropriate DeliveryError - */ - public static DeliveryError ofCode(int code) { - for (DeliveryError e : DeliveryError.values()) { - if (e.code == code) - return e; - } - - return UNKNOWN; - } -} diff --git a/src/main/java/com/notnoop/apns/EnhancedApnsNotification.java b/src/main/java/com/notnoop/apns/EnhancedApnsNotification.java deleted file mode 100644 index 23fbab4e..00000000 --- a/src/main/java/com/notnoop/apns/EnhancedApnsNotification.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.concurrent.atomic.AtomicInteger; -import com.notnoop.apns.internal.Utilities; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.UnsupportedEncodingException; - -/** - * Represents an APNS notification to be sent to Apple service. - */ -public class EnhancedApnsNotification implements ApnsNotification { - - private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedApnsNotification.class); - private final static byte COMMAND = 1; - private static AtomicInteger nextId = new AtomicInteger(0); - private final int identifier; - private final int expiry; - private final byte[] deviceToken; - private final byte[] payload; - - public static int INCREMENT_ID() { - return nextId.incrementAndGet(); - } - - /** - * The infinite future for the purposes of Apple expiry date - */ - public final static int MAXIMUM_EXPIRY = Integer.MAX_VALUE; - - /** - * Constructs an instance of {@code ApnsNotification}. - * - * The message encodes the payload with a {@code UTF-8} encoding. - * - * @param dtoken The Hex of the device token of the destination phone - * @param payload The payload message to be sent - */ - public EnhancedApnsNotification( - int identifier, int expiryTime, - String dtoken, String payload) { - this.identifier = identifier; - this.expiry = expiryTime; - this.deviceToken = Utilities.decodeHex(dtoken); - this.payload = Utilities.toUTF8Bytes(payload); - } - - /** - * Constructs an instance of {@code ApnsNotification}. - * - * @param dtoken The binary representation of the destination device token - * @param payload The binary representation of the payload to be sent - */ - public EnhancedApnsNotification( - int identifier, int expiryTime, - byte[] dtoken, byte[] payload) { - this.identifier = identifier; - this.expiry = expiryTime; - this.deviceToken = Utilities.copyOf(dtoken); - this.payload = Utilities.copyOf(payload); - } - - /** - * Returns the binary representation of the device token. - * - */ - public byte[] getDeviceToken() { - return Utilities.copyOf(deviceToken); - } - - /** - * Returns the binary representation of the payload. - * - */ - public byte[] getPayload() { - return Utilities.copyOf(payload); - } - - public int getIdentifier() { - return identifier; - } - - public int getExpiry() { - return expiry; - } - - private byte[] marshall; - /** - * Returns the binary representation of the message as expected by the - * APNS server. - * - * The returned array can be used to sent directly to the APNS server - * (on the wire/socket) without any modification. - */ - public byte[] marshall() { - if (marshall == null) { - marshall = Utilities.marshallEnhanced(COMMAND, identifier, - expiry, deviceToken, payload); - } - return marshall.clone(); - } - - /** - * Returns the length of the message in bytes as it is encoded on the wire. - * - * Apple require the message to be of length 255 bytes or less. - * - * @return length of encoded message in bytes - */ - public int length() { - int length = 1 + 4 + 4 + 2 + deviceToken.length + 2 + payload.length; - final int marshalledLength = marshall().length; - assert marshalledLength == length; - return length; - } - - @Override - public int hashCode() { - return (21 - + 31 * identifier - + 31 * expiry - + 31 * Arrays.hashCode(deviceToken) - + 31 * Arrays.hashCode(payload)); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof EnhancedApnsNotification)) - return false; - EnhancedApnsNotification o = (EnhancedApnsNotification)obj; - return (identifier == o.identifier - && expiry == o.expiry - && Arrays.equals(this.deviceToken, o.deviceToken) - && Arrays.equals(this.payload, o.payload)); - } - - @Override - @SuppressFBWarnings("DE_MIGHT_IGNORE") - public String toString() { - String payloadString; - try { - payloadString = new String(payload, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - LOGGER.debug("UTF-8 charset not found on the JRE", ex); - payloadString = "???"; - } - return "Message(Id="+identifier+"; Token="+Utilities.encodeHex(deviceToken)+"; Payload="+payloadString+")"; - } -} diff --git a/src/main/java/com/notnoop/apns/PayloadBuilder.java b/src/main/java/com/notnoop/apns/PayloadBuilder.java deleted file mode 100644 index 798c22a0..00000000 --- a/src/main/java/com/notnoop/apns/PayloadBuilder.java +++ /dev/null @@ -1,535 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.notnoop.apns.internal.Utilities; - -/** - * Represents a builder for constructing Payload requests, as - * specified by Apple Push Notification Programming Guide. - */ -public final class PayloadBuilder { - private static final ObjectMapper mapper = new ObjectMapper(); - - private final Map root; - private final Map aps; - private final Map customAlert; - - /** - * Constructs a new instance of {@code PayloadBuilder} - */ - PayloadBuilder() { - root = new HashMap(); - aps = new HashMap(); - customAlert = new HashMap(); - } - - /** - * Sets the alert body text, the text the appears to the user, - * to the passed value - * - * @param alert the text to appear to the user - * @return this - */ - public PayloadBuilder alertBody(final String alert) { - customAlert.put("body", alert); - return this; - } - - /** - * Sets the alert title text, the text the appears to the user, - * to the passed value. - * - * Used on iOS 8.2, iWatch and also Safari - * - * @param title the text to appear to the user - * @return this - */ - public PayloadBuilder alertTitle(final String title) { - customAlert.put("title", title); - return this; - } - - /** - * The key to a title string in the Localizable.strings file for the current localization. - * - * @param key the localizable message title key - * @return this - */ - public PayloadBuilder localizedTitleKey(final String key) { - customAlert.put("title-loc-key", key); - return this; - } - - /** - * Sets the arguments for the localizable title key. - * - * @param arguments the arguments to the localized alert message - * @return this - */ - public PayloadBuilder localizedTitleArguments(final Collection arguments) { - customAlert.put("title-loc-args", arguments); - return this; - } - - /** - * Sets the arguments for the localizable title key. - * - * @param arguments the arguments to the localized alert message - * @return this - */ - public PayloadBuilder localizedTitleArguments(final String... arguments) { - return localizedTitleArguments(Arrays.asList(arguments)); - } - - /** - * Sets the alert action text - * - * @param action The label of the action button - * @return this - */ - public PayloadBuilder alertAction(final String action) { - customAlert.put("action", action); - return this; - } - - /** - * Sets the "url-args" key that are paired with the placeholders - * inside the urlFormatString value of your website.json file. - * The order of the placeholders in the URL format string determines - * the order of the values supplied by the url-args array. - * - * @param urlArgs the values to be paired with the placeholders inside - * the urlFormatString value of your website.json file. - * @return this - */ - public PayloadBuilder urlArgs(final String... urlArgs){ - aps.put("url-args", urlArgs); - return this; - } - - /** - * Sets the alert sound to be played. - * - * Passing {@code null} disables the notification sound. - * - * @param sound the file name or song name to be played - * when receiving the notification - * @return this - */ - public PayloadBuilder sound(final String sound) { - if (sound != null) { - aps.put("sound", sound); - } else { - aps.remove("sound"); - } - return this; - } - - /** - * Sets the category of the notification for iOS8 notification - * actions. See 13 minutes into "What's new in iOS Notifications" - * - * Passing {@code null} removes the category. - * - * @param category the name of the category supplied to the app - * when receiving the notification - * @return this - */ - public PayloadBuilder category(final String category) { - if (category != null) { - aps.put("category", category); - } else { - aps.remove("category"); - } - return this; - } - - /** - * Sets the notification badge to be displayed next to the - * application icon. - * - * The passed value is the value that should be displayed - * (it will be added to the previous badge number), and - * a badge of 0 clears the badge indicator. - * - * @param badge the badge number to be displayed - * @return this - */ - public PayloadBuilder badge(final int badge) { - aps.put("badge", badge); - return this; - } - - /** - * Requests clearing of the badge number next to the application - * icon. - * - * This is an alias to {@code badge(0)}. - * - * @return this - */ - public PayloadBuilder clearBadge() { - return badge(0); - } - - /** - * Sets the value of action button (the right button to be - * displayed). The default value is "View". - * - * The value can be either the simple String to be displayed or - * a localizable key, and the iPhone will show the appropriate - * localized message. - * - * A {@code null} actionKey indicates no additional button - * is displayed, just the Cancel button. - * - * @param actionKey the title of the additional button - * @return this - */ - public PayloadBuilder actionKey(final String actionKey) { - customAlert.put("action-loc-key", actionKey); - return this; - } - - /** - * Set the notification view to display an action button. - * - * This is an alias to {@code actionKey(null)} - * - * @return this - */ - public PayloadBuilder noActionButton() { - return actionKey(null); - } - - /** - * Sets the notification type to be a 'newstand' notification. - * - * A Newstand Notification targets the Newstands app so that the app - * updates the subscription info and content. - * - * @return this - */ - public PayloadBuilder forNewsstand() { - aps.put("content-available", 1); - return this; - } - - /** - * With iOS7 it is possible to have the application wake up before the user opens the app. - * - * The same key-word can also be used to send 'silent' notifications. With these 'silent' notification - * a different app delegate is being invoked, allowing the app to perform background tasks. - * - * @return this - */ - public PayloadBuilder instantDeliveryOrSilentNotification() { - aps.put("content-available", 1); - return this; - } - - /** - * Set the notification localized key for the alert body - * message. - * - * @param key the localizable message body key - * @return this - */ - public PayloadBuilder localizedKey(final String key) { - customAlert.put("loc-key", key); - return this; - } - - /** - * Sets the arguments for the alert message localizable message. - * - * The iPhone doesn't localize the arguments. - * - * @param arguments the arguments to the localized alert message - * @return this - */ - public PayloadBuilder localizedArguments(final Collection arguments) { - customAlert.put("loc-args", arguments); - return this; - } - - /** - * Sets the arguments for the alert message localizable message. - * - * The iPhone doesn't localize the arguments. - * - * @param arguments the arguments to the localized alert message - * @return this - */ - public PayloadBuilder localizedArguments(final String... arguments) { - return localizedArguments(Arrays.asList(arguments)); - } - - /** - * Sets the launch image file for the push notification - * - * @param launchImage the filename of the image file in the - * application bundle. - * @return this - */ - public PayloadBuilder launchImage(final String launchImage) { - customAlert.put("launch-image", launchImage); - return this; - } - - /** - * Sets any application-specific custom fields. The values - * are presented to the application and the iPhone doesn't - * display them automatically. - * - * This can be used to pass specific values (urls, ids, etc) to - * the application in addition to the notification message - * itself. - * - * @param key the custom field name - * @param value the custom field value - * @return this - */ - public PayloadBuilder customField(final String key, final Object value) { - root.put(key, value); - return this; - } - - public PayloadBuilder mdm(final String s) { - return customField("mdm", s); - } - - /** - * Set any application-specific custom fields. These values - * are presented to the application and the iPhone doesn't - * display them automatically. - * - * This method *adds* the custom fields in the map to the - * payload, and subsequent calls add but doesn't reset the - * custom fields. - * - * @param values the custom map - * @return this - */ - public PayloadBuilder customFields(final Map values) { - root.putAll(values); - return this; - } - - /** - * Returns the length of payload bytes once marshaled to bytes - * - * @return the length of the payload - */ - public int length() { - return copy().buildBytes().length; - } - - /** - * Returns true if the payload built so far is larger than - * the size permitted by Apple (which is 2048 bytes). - * - * @return true if the result payload is too long - */ - public boolean isTooLong() { - return length() > Utilities.MAX_PAYLOAD_LENGTH; - } - - /** - * Shrinks the alert message body so that the resulting payload - * message fits within the passed expected payload length. - * - * This method performs best-effort approach, and its behavior - * is unspecified when handling alerts where the payload - * without body is already longer than the permitted size, or - * if the break occurs within word. - * - * @param payloadLength the expected max size of the payload - * @return this - */ - public PayloadBuilder resizeAlertBody(final int payloadLength) { - return resizeAlertBody(payloadLength, ""); - } - - /** - * Shrinks the alert message body so that the resulting payload - * message fits within the passed expected payload length. - * - * This method performs best-effort approach, and its behavior - * is unspecified when handling alerts where the payload - * without body is already longer than the permitted size, or - * if the break occurs within word. - * - * @param payloadLength the expected max size of the payload - * @param postfix for the truncated body, e.g. "..." - * @return this - */ - public PayloadBuilder resizeAlertBody(final int payloadLength, final String postfix) { - int currLength = length(); - if (currLength <= payloadLength) { - return this; - } - - // now we are sure that truncation is required - String body = (String)customAlert.get("body"); - - final int acceptableSize = Utilities.toUTF8Bytes(body).length - - (currLength - payloadLength - + Utilities.toUTF8Bytes(postfix).length); - body = Utilities.truncateWhenUTF8(body, acceptableSize) + postfix; - - // set it back - customAlert.put("body", body); - - // calculate the length again - currLength = length(); - - if(currLength > payloadLength) { - // string is still too long, just remove the body as the body is - // anyway not the cause OR the postfix might be too long - customAlert.remove("body"); - } - - return this; - } - - /** - * Shrinks the alert message body so that the resulting payload - * message fits within require Apple specification (2048 bytes). - * - * This method performs best-effort approach, and its behavior - * is unspecified when handling alerts where the payload - * without body is already longer than the permitted size, or - * if the break occurs within word. - * - * @return this - */ - public PayloadBuilder shrinkBody() { - return shrinkBody(""); - } - - /** - * Shrinks the alert message body so that the resulting payload - * message fits within require Apple specification (2048 bytes). - * - * This method performs best-effort approach, and its behavior - * is unspecified when handling alerts where the payload - * without body is already longer than the permitted size, or - * if the break occurs within word. - * - * @param postfix for the truncated body, e.g. "..." - * - * @return this - */ - public PayloadBuilder shrinkBody(final String postfix) { - return resizeAlertBody(Utilities.MAX_PAYLOAD_LENGTH, postfix); - } - - /** - * Returns the JSON String representation of the payload - * according to Apple APNS specification - * - * @return the String representation as expected by Apple - */ - public String build() { - if (!root.containsKey("mdm")) { - insertCustomAlert(); - root.put("aps", aps); - } - try { - return mapper.writeValueAsString(root); - } catch (final Exception e) { - throw new RuntimeException(e); - } - } - - private void insertCustomAlert() { - switch (customAlert.size()) { - case 0: - aps.remove("alert"); - break; - case 1: - if (customAlert.containsKey("body")) { - aps.put("alert", customAlert.get("body")); - break; - } - // else follow through - //$FALL-THROUGH$ - default: - aps.put("alert", customAlert); - } - } - - /** - * Returns the bytes representation of the payload according to - * Apple APNS specification - * - * @return the bytes as expected by Apple - */ - public byte[] buildBytes() { - return Utilities.toUTF8Bytes(build()); - } - - @Override - public String toString() { - return build(); - } - - private PayloadBuilder(final Map root, - final Map aps, - final Map customAlert) { - this.root = new HashMap(root); - this.aps = new HashMap(aps); - this.customAlert = new HashMap(customAlert); - } - - /** - * Returns a copy of this builder - * - * @return a copy of this builder - */ - public PayloadBuilder copy() { - return new PayloadBuilder(root, aps, customAlert); - } - - /** - * @return a new instance of Payload Builder - */ - public static PayloadBuilder newPayload() { - return new PayloadBuilder(); - } -} diff --git a/src/main/java/com/notnoop/apns/ReconnectPolicy.java b/src/main/java/com/notnoop/apns/ReconnectPolicy.java deleted file mode 100644 index bff7dd96..00000000 --- a/src/main/java/com/notnoop/apns/ReconnectPolicy.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.ReconnectPolicies; - -/** - * Represents the reconnection policy for the library. - * - * Each object should be used exclusively for one - * {@code ApnsService} only. - */ -public interface ReconnectPolicy { - /** - * Returns {@code true} if the library should initiate a new - * connection for sending the message. - * - * The library calls this method at every message push. - * - * @return true if the library should be reconnected - */ - public boolean shouldReconnect(); - - /** - * Callback method to be called whenever the library - * makes a new connection - */ - public void reconnected(); - - /** - * Returns a deep copy of this reconnection policy, if needed. - * - * Subclasses may return this instance if the object is immutable. - */ - public ReconnectPolicy copy(); - - /** - * Types of the library provided reconnection policies. - * - * This should capture most of the commonly used cases. - */ - public enum Provided { - /** - * Only reconnect if absolutely needed, e.g. when the connection is dropped. - *

- * Apple recommends using a persistent connection. This improves the latency of sending push notification messages. - *

- * The down-side is that once the connection is closed ungracefully (e.g. because Apple server drops it), the library wouldn't - * detect such failure and not warn against the messages sent after the drop before the detection. - */ - NEVER { - @Override - public ReconnectPolicy newObject() { - return new ReconnectPolicies.Never(); - } - }, - - /** - * Makes a new connection if the current connection has lasted for more than half an hour. - *

- * This is the recommended mode. - *

- * This is the sweat-spot in my experiments between dropped connections while minimizing latency. - */ - EVERY_HALF_HOUR { - @Override - public ReconnectPolicy newObject() { - return new ReconnectPolicies.EveryHalfHour(); - } - }, - - /** - * Makes a new connection for every message being sent. - * - * This option ensures that each message is actually - * delivered to Apple. - * - * If you send a lot of messages though, - * Apple may consider your requests to be a DoS attack. - */ - EVERY_NOTIFICATION { - @Override - public ReconnectPolicy newObject() { - return new ReconnectPolicies.Always(); - } - }; - - abstract ReconnectPolicy newObject(); - } -} diff --git a/src/main/java/com/notnoop/apns/SimpleApnsNotification.java b/src/main/java/com/notnoop/apns/SimpleApnsNotification.java deleted file mode 100644 index 464c3bdf..00000000 --- a/src/main/java/com/notnoop/apns/SimpleApnsNotification.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 java.util.Arrays; - -import com.notnoop.apns.internal.Utilities; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.UnsupportedEncodingException; - -/** - * Represents an APNS notification to be sent to Apple service. This is for legacy use only - * and should not be used in new development. - * https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/LegacyFormat.html - * - * This SimpleApnsNotification also only has limited error handling (by the APNS closing the connection - * when a bad message was received) This prevents us from location the malformed notification. - * - * As push messages sent after a malformed notification are discarded by APNS messages will get lost - * and not be delivered with the SimpleApnsNotification. - * - * @deprecated use EnhancedApnsNotification instead. - */ -@SuppressWarnings("deprecation") -@Deprecated -public class SimpleApnsNotification implements ApnsNotification { - - private static final Logger LOGGER = LoggerFactory.getLogger(SimpleApnsNotification.class); - private final static byte COMMAND = 0; - private final byte[] deviceToken; - private final byte[] payload; - - /** - * Constructs an instance of {@code ApnsNotification}. - * - * The message encodes the payload with a {@code UTF-8} encoding. - * - * @param dtoken The Hex of the device token of the destination phone - * @param payload The payload message to be sent - */ - public SimpleApnsNotification(String dtoken, String payload) { - this.deviceToken = Utilities.decodeHex(dtoken); - this.payload = Utilities.toUTF8Bytes(payload); - } - - /** - * Constructs an instance of {@code ApnsNotification}. - * - * @param dtoken The binary representation of the destination device token - * @param payload The binary representation of the payload to be sent - */ - public SimpleApnsNotification(byte[] dtoken, byte[] payload) { - this.deviceToken = Utilities.copyOf(dtoken); - this.payload = Utilities.copyOf(payload); - } - - /** - * Returns the binary representation of the device token. - * - */ - public byte[] getDeviceToken() { - return Utilities.copyOf(deviceToken); - } - - /** - * Returns the binary representation of the payload. - * - */ - public byte[] getPayload() { - return Utilities.copyOf(payload); - } - - private byte[] marshall; - /** - * Returns the binary representation of the message as expected by the - * APNS server. - * - * The returned array can be used to sent directly to the APNS server - * (on the wire/socket) without any modification. - */ - public byte[] marshall() { - if (marshall == null) - marshall = Utilities.marshall(COMMAND, deviceToken, payload); - return marshall.clone(); - } - - /** - * Returns the length of the message in bytes as it is encoded on the wire. - * - * Apple require the message to be of length 255 bytes or less. - * - * @return length of encoded message in bytes - */ - public int length() { - int length = 1 + 2 + deviceToken.length + 2 + payload.length; - final int marshalledLength = marshall().length; - assert marshalledLength == length; - return length; - } - - @Override - public int hashCode() { - return 21 - + 31 * Arrays.hashCode(deviceToken) - + 31 * Arrays.hashCode(payload); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof SimpleApnsNotification)) - return false; - SimpleApnsNotification o = (SimpleApnsNotification)obj; - return Arrays.equals(this.deviceToken, o.deviceToken) - && Arrays.equals(this.payload, o.payload); - } - - public int getIdentifier() { - return -1; - } - - public int getExpiry() { - return -1; - } - - @Override - @SuppressFBWarnings("DE_MIGHT_IGNORE") - public String toString() { - String payloadString; - try { - payloadString = new String(payload, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - LOGGER.debug("UTF-8 charset not found on the JRE", ex); - payloadString = "???"; - } - return "Message(Token="+Utilities.encodeHex(deviceToken)+"; Payload="+payloadString+")"; - } - - -} diff --git a/src/main/java/com/notnoop/apns/StartSendingApnsDelegate.java b/src/main/java/com/notnoop/apns/StartSendingApnsDelegate.java deleted file mode 100644 index cf7d9577..00000000 --- a/src/main/java/com/notnoop/apns/StartSendingApnsDelegate.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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; - -/** - * A delegate that also gets notified just before a notification is being delivered to the - * Apple Server. - */ -public interface StartSendingApnsDelegate extends ApnsDelegate { - - /** - * Called when message is about to be sent to the Apple servers. - * - * @param message the notification that is about to be sent - * @param resent whether the notification is being resent after an error - */ - public void startSending(ApnsNotification message, boolean resent); - -} diff --git a/src/main/java/com/notnoop/apns/internal/AbstractApnsService.java b/src/main/java/com/notnoop/apns/internal/AbstractApnsService.java deleted file mode 100644 index 5f993649..00000000 --- a/src/main/java/com/notnoop/apns/internal/AbstractApnsService.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.internal; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.exceptions.NetworkIOException; - -abstract class AbstractApnsService implements ApnsService { - private ApnsFeedbackConnection feedback; - private AtomicInteger c = new AtomicInteger(); - - public AbstractApnsService(ApnsFeedbackConnection feedback) { - this.feedback = feedback; - } - - public EnhancedApnsNotification push(String deviceToken, String payload) throws NetworkIOException { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), EnhancedApnsNotification.MAXIMUM_EXPIRY, deviceToken, payload); - push(notification); - return notification; - } - - public EnhancedApnsNotification push(String deviceToken, String payload, Date expiry) throws NetworkIOException { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), (int)(expiry.getTime() / 1000), deviceToken, payload); - push(notification); - return notification; - } - - public EnhancedApnsNotification push(byte[] deviceToken, byte[] payload) throws NetworkIOException { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), EnhancedApnsNotification.MAXIMUM_EXPIRY, deviceToken, payload); - push(notification); - return notification; - } - - public EnhancedApnsNotification push(byte[] deviceToken, byte[] payload, int expiry) throws NetworkIOException { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), expiry, deviceToken, payload); - push(notification); - return notification; - } - - public Collection push(Collection deviceTokens, String payload) throws NetworkIOException { - byte[] messageBytes = Utilities.toUTF8Bytes(payload); - List notifications = new ArrayList(deviceTokens.size()); - for (String deviceToken : deviceTokens) { - byte[] dtBytes = Utilities.decodeHex(deviceToken); - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), EnhancedApnsNotification.MAXIMUM_EXPIRY, dtBytes, messageBytes); - notifications.add(notification); - push(notification); - } - return notifications; - } - - public Collection push(Collection deviceTokens, String payload, Date expiry) throws NetworkIOException { - byte[] messageBytes = Utilities.toUTF8Bytes(payload); - List notifications = new ArrayList(deviceTokens.size()); - for (String deviceToken : deviceTokens) { - byte[] dtBytes = Utilities.decodeHex(deviceToken); - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), (int)(expiry.getTime() / 1000), dtBytes, messageBytes); - notifications.add(notification); - push(notification); - } - return notifications; - } - - public Collection push(Collection deviceTokens, byte[] payload) throws NetworkIOException { - List notifications = new ArrayList(deviceTokens.size()); - for (byte[] deviceToken : deviceTokens) { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), EnhancedApnsNotification.MAXIMUM_EXPIRY, deviceToken, payload); - notifications.add(notification); - push(notification); - } - return notifications; - } - - public Collection push(Collection deviceTokens, byte[] payload, int expiry) throws NetworkIOException { - List notifications = new ArrayList(deviceTokens.size()); - for (byte[] deviceToken : deviceTokens) { - EnhancedApnsNotification notification = - new EnhancedApnsNotification(c.incrementAndGet(), expiry, deviceToken, payload); - notifications.add(notification); - push(notification); - } - return notifications; - } - - public abstract void push(ApnsNotification message) throws NetworkIOException; - - public Map getInactiveDevices() throws NetworkIOException { - return feedback.getInactiveDevices(); - } -} diff --git a/src/main/java/com/notnoop/apns/internal/ApnsConnection.java b/src/main/java/com/notnoop/apns/internal/ApnsConnection.java deleted file mode 100644 index 757a81b3..00000000 --- a/src/main/java/com/notnoop/apns/internal/ApnsConnection.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.internal; - -import java.io.Closeable; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.exceptions.NetworkIOException; - -public interface ApnsConnection extends Closeable { - - //Default number of notifications to keep for error purposes - public static final int DEFAULT_CACHE_LENGTH = 100; - - void sendMessage(ApnsNotification m) throws NetworkIOException; - - void testConnection() throws NetworkIOException; - - ApnsConnection copy(); - - void setCacheLength(int cacheLength); - - int getCacheLength(); -} diff --git a/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java b/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java deleted file mode 100644 index 825aea3a..00000000 --- a/src/main/java/com/notnoop/apns/internal/ApnsConnectionImpl.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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.internal; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.Socket; -import java.util.LinkedList; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSocketFactory; -import com.notnoop.apns.ApnsDelegate; -import com.notnoop.apns.StartSendingApnsDelegate; -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.DeliveryError; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.apns.ReconnectPolicy; -import com.notnoop.exceptions.ApnsDeliveryErrorException; -import com.notnoop.exceptions.NetworkIOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ApnsConnectionImpl implements ApnsConnection { - - private static final Logger logger = LoggerFactory.getLogger(ApnsConnectionImpl.class); - - private final SocketFactory factory; - private final String host; - private final int port; - private final int readTimeout; - private final int connectTimeout; - private final Proxy proxy; - private final String proxyUsername; - private final String proxyPassword; - private final ReconnectPolicy reconnectPolicy; - private final ApnsDelegate delegate; - private int cacheLength; - private final boolean errorDetection; - private final ThreadFactory threadFactory; - private final boolean autoAdjustCacheLength; - private final ConcurrentLinkedQueue cachedNotifications, notificationsBuffer; - private Socket socket; - private final AtomicInteger threadId = new AtomicInteger(0); - - public ApnsConnectionImpl(SocketFactory factory, String host, int port) { - this(factory, host, port, new ReconnectPolicies.Never(), ApnsDelegate.EMPTY); - } - - private ApnsConnectionImpl(SocketFactory factory, String host, int port, ReconnectPolicy reconnectPolicy, ApnsDelegate delegate) { - this(factory, host, port, null, null, null, reconnectPolicy, delegate); - } - - private ApnsConnectionImpl(SocketFactory factory, String host, int port, Proxy proxy, String proxyUsername, String proxyPassword, - ReconnectPolicy reconnectPolicy, ApnsDelegate delegate) { - this(factory, host, port, proxy, proxyUsername, proxyPassword, reconnectPolicy, delegate, false, null, - ApnsConnection.DEFAULT_CACHE_LENGTH, true, 0, 0); - } - - public ApnsConnectionImpl(SocketFactory factory, String host, int port, Proxy proxy, String proxyUsername, String proxyPassword, - ReconnectPolicy reconnectPolicy, ApnsDelegate delegate, boolean errorDetection, ThreadFactory tf, int cacheLength, - boolean autoAdjustCacheLength, int readTimeout, int connectTimeout) { - this.factory = factory; - this.host = host; - this.port = port; - this.reconnectPolicy = reconnectPolicy; - this.delegate = delegate == null ? ApnsDelegate.EMPTY : delegate; - this.proxy = proxy; - this.errorDetection = errorDetection; - this.threadFactory = tf == null ? defaultThreadFactory() : tf; - this.cacheLength = cacheLength; - this.autoAdjustCacheLength = autoAdjustCacheLength; - this.readTimeout = readTimeout; - this.connectTimeout = connectTimeout; - this.proxyUsername = proxyUsername; - this.proxyPassword = proxyPassword; - cachedNotifications = new ConcurrentLinkedQueue(); - notificationsBuffer = new ConcurrentLinkedQueue(); - } - - private ThreadFactory defaultThreadFactory() { - return new ThreadFactory() { - ThreadFactory wrapped = Executors.defaultThreadFactory(); - @Override - public Thread newThread( Runnable r ) - { - Thread result = wrapped.newThread(r); - result.setName("MonitoringThread-"+threadId.incrementAndGet()); - result.setDaemon(true); - return result; - } - }; - } - - public synchronized void close() { - Utilities.close(socket); - } - - private void monitorSocket(final Socket socketToMonitor) { - logger.debug("Launching Monitoring Thread for socket {}", socketToMonitor); - - Thread t = threadFactory.newThread(new Runnable() { - final static 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 tempCache = new LinkedList(); - 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(); - } - - private synchronized Socket getOrCreateSocket(boolean resend) throws NetworkIOException { - if (reconnectPolicy.shouldReconnect()) { - logger.debug("Reconnecting due to reconnectPolicy dictating it"); - Utilities.close(socket); - socket = null; - } - - if (socket == null || socket.isClosed()) { - try { - if (proxy == null) { - socket = factory.createSocket(host, port); - logger.debug("Connected new socket {}", socket); - } else if (proxy.type() == Proxy.Type.HTTP) { - TlsTunnelBuilder tunnelBuilder = new TlsTunnelBuilder(); - socket = tunnelBuilder.build((SSLSocketFactory) factory, proxy, proxyUsername, proxyPassword, host, port); - logger.debug("Connected new socket through http tunnel {}", socket); - } else { - boolean success = false; - Socket proxySocket = null; - try { - proxySocket = new Socket(proxy); - proxySocket.connect(new InetSocketAddress(host, port), connectTimeout); - socket = ((SSLSocketFactory) factory).createSocket(proxySocket, host, port, false); - success = true; - } finally { - if (!success) { - Utilities.close(proxySocket); - } - } - logger.debug("Connected new socket through socks tunnel {}", socket); - } - - socket.setSoTimeout(readTimeout); - socket.setKeepAlive(true); - - if (errorDetection) { - monitorSocket(socket); - } - - reconnectPolicy.reconnected(); - logger.debug("Made a new connection to APNS"); - } catch (IOException e) { - logger.error("Couldn't connect to APNS server", e); - // indicate to clients whether this is a resend or initial send - throw new NetworkIOException(e, resend); - } - } - return socket; - } - - int DELAY_IN_MS = 1000; - private static final int RETRIES = 3; - - public synchronized void sendMessage(ApnsNotification m) throws NetworkIOException { - sendMessage(m, false); - drainBuffer(); - } - - private synchronized void sendMessage(ApnsNotification m, boolean fromBuffer) throws NetworkIOException { - logger.debug("sendMessage {} fromBuffer: {}", m, fromBuffer); - - if (delegate instanceof StartSendingApnsDelegate) { - ((StartSendingApnsDelegate) delegate).startSending(m, fromBuffer); - } - - int attempts = 0; - while (true) { - try { - attempts++; - Socket socket = getOrCreateSocket(fromBuffer); - socket.getOutputStream().write(m.marshall()); - socket.getOutputStream().flush(); - cacheNotification(m); - - delegate.messageSent(m, fromBuffer); - - //logger.debug("Message \"{}\" sent", m); - attempts = 0; - break; - } catch (SSLHandshakeException e) { - // No use retrying this, it's dead Jim - throw new NetworkIOException(e); - } catch (IOException e) { - Utilities.close(socket); - if (attempts >= RETRIES) { - logger.error("Couldn't send message after " + RETRIES + " retries." + m, e); - delegate.messageSendFailed(m, e); - Utilities.wrapAndThrowAsRuntimeException(e); - } - // The first failure might be due to closed connection (which in turn might be caused by - // a message containing a bad token), so don't delay for the first retry. - // - // Additionally we don't want to spam the log file in this case, only after the second retry - // which uses the delay. - - if (attempts != 1) { - logger.info("Failed to send message " + m + "... trying again after delay", e); - Utilities.sleep(DELAY_IN_MS); - } - } - } - } - - private synchronized void drainBuffer() { - logger.debug("draining buffer"); - while (!notificationsBuffer.isEmpty()) { - final ApnsNotification notification = notificationsBuffer.poll(); - try { - sendMessage(notification, true); - } - catch (NetworkIOException ex) { - // at this point we are retrying the submission of messages but failing to connect to APNS, therefore - // notify the client of this - delegate.messageSendFailed(notification, ex); - } - } - } - - private void cacheNotification(ApnsNotification notification) { - cachedNotifications.add(notification); - while (cachedNotifications.size() > cacheLength) { - cachedNotifications.poll(); - logger.debug("Removing notification from cache " + notification); - } - } - - public ApnsConnectionImpl copy() { - return new ApnsConnectionImpl(factory, host, port, proxy, proxyUsername, proxyPassword, reconnectPolicy.copy(), delegate, - errorDetection, threadFactory, cacheLength, autoAdjustCacheLength, readTimeout, connectTimeout); - } - - public void testConnection() throws NetworkIOException { - ApnsConnectionImpl testConnection = null; - try { - testConnection = - new ApnsConnectionImpl(factory, host, port, proxy, proxyUsername, proxyPassword, reconnectPolicy.copy(), delegate); - final ApnsNotification notification = new EnhancedApnsNotification(0, 0, new byte[]{0}, new byte[]{0}); - testConnection.sendMessage(notification); - } finally { - if (testConnection != null) { - testConnection.close(); - } - } - } - - public void setCacheLength(int cacheLength) { - this.cacheLength = cacheLength; - } - - public int getCacheLength() { - return cacheLength; - } -} diff --git a/src/main/java/com/notnoop/apns/internal/ApnsFeedbackConnection.java b/src/main/java/com/notnoop/apns/internal/ApnsFeedbackConnection.java deleted file mode 100644 index 26db2086..00000000 --- a/src/main/java/com/notnoop/apns/internal/ApnsFeedbackConnection.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.Socket; -import java.util.Date; -import java.util.Map; -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.notnoop.exceptions.NetworkIOException; - -public class ApnsFeedbackConnection { - private static final Logger logger = LoggerFactory.getLogger(ApnsFeedbackConnection.class); - - private final SocketFactory factory; - private final String host; - private final int port; - private final Proxy proxy; - private final int readTimeout; - private final int connectTimeout; - private final String proxyUsername; - private final String proxyPassword; - - public ApnsFeedbackConnection(final SocketFactory factory, final String host, final int port) { - this(factory, host, port, null, 0, 0, null, null); - } - - public ApnsFeedbackConnection(final SocketFactory factory, final String host, final int port, - final Proxy proxy, int readTimeout, int connectTimeout, final String proxyUsername, final String proxyPassword) { - this.factory = factory; - this.host = host; - this.port = port; - this.proxy = proxy; - this.readTimeout = readTimeout; - this.connectTimeout = connectTimeout; - this.proxyUsername = proxyUsername; - this.proxyPassword = proxyPassword; - } - - int DELAY_IN_MS = 1000; - private static final int RETRIES = 3; - - public Map getInactiveDevices() throws NetworkIOException { - int attempts = 0; - while (true) { - try { - attempts++; - final Map result = getInactiveDevicesImpl(); - - attempts = 0; - return result; - } catch (final Exception e) { - logger.warn("Failed to retrieve invalid devices", e); - if (attempts >= RETRIES) { - logger.error("Couldn't get feedback connection", e); - Utilities.wrapAndThrowAsRuntimeException(e); - } - Utilities.sleep(DELAY_IN_MS); - } - } - } - - public Map getInactiveDevicesImpl() throws IOException { - Socket proxySocket = null; - Socket socket = null; - try { - if (proxy == null) { - socket = factory.createSocket(host, port); - } else if (proxy.type() == Proxy.Type.HTTP) { - TlsTunnelBuilder tunnelBuilder = new TlsTunnelBuilder(); - socket = tunnelBuilder.build((SSLSocketFactory) factory, proxy, proxyUsername, proxyPassword, host, port); - } else { - proxySocket = new Socket(proxy); - proxySocket.connect(new InetSocketAddress(host, port), connectTimeout); - socket = ((SSLSocketFactory) factory).createSocket(proxySocket, host, port, false); - } - socket.setSoTimeout(readTimeout); - socket.setKeepAlive(true); - final InputStream stream = socket.getInputStream(); - return Utilities.parseFeedbackStream(stream); - } finally { - Utilities.close(socket); - Utilities.close(proxySocket); - } - } - -} diff --git a/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java b/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java deleted file mode 100644 index e27d1702..00000000 --- a/src/main/java/com/notnoop/apns/internal/ApnsPooledConnection.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.internal; - -import java.util.concurrent.*; -import com.notnoop.apns.ApnsNotification; -import com.notnoop.exceptions.NetworkIOException; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ApnsPooledConnection implements ApnsConnection { - private static final Logger logger = LoggerFactory.getLogger(ApnsPooledConnection.class); - - private final ApnsConnection prototype; - private final int max; - - private final ExecutorService executors; - private final ConcurrentLinkedQueue prototypes; - - public ApnsPooledConnection(ApnsConnection prototype, int max) { - this(prototype, max, Executors.newFixedThreadPool(max)); - } - - public ApnsPooledConnection(ApnsConnection prototype, int max, ExecutorService executors) { - this.prototype = prototype; - this.max = max; - - this.executors = executors; - this.prototypes = new ConcurrentLinkedQueue(); - } - - private final ThreadLocal uniquePrototype = - new ThreadLocal() { - protected ApnsConnection initialValue() { - ApnsConnection newCopy = prototype.copy(); - prototypes.add(newCopy); - return newCopy; - } - }; - - public void sendMessage(final ApnsNotification m) throws NetworkIOException { - Future future = executors.submit(new Callable() { - public Void call() throws Exception { - uniquePrototype.get().sendMessage(m); - return null; - } - }); - try { - future.get(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - } catch (ExecutionException ee) { - if (ee.getCause() instanceof NetworkIOException) { - throw (NetworkIOException) ee.getCause(); - } - } - } - - public ApnsConnection copy() { - // TODO: Should copy executor properly.... What should copy do - // really?! - return new ApnsPooledConnection(prototype, max); - } - - public void close() { - executors.shutdown(); - try { - executors.awaitTermination(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - logger.warn("pool termination interrupted", e); - } - for (ApnsConnection conn : prototypes) { - Utilities.close(conn); - } - Utilities.close(prototype); - } - - public void testConnection() { - prototype.testConnection(); - } - - public synchronized void setCacheLength(int cacheLength) { - for (ApnsConnection conn : prototypes) { - conn.setCacheLength(cacheLength); - } - } - - @SuppressFBWarnings(value = "UG_SYNC_SET_UNSYNC_GET", justification = "prototypes is a MT-safe container") - public int getCacheLength() { - return prototypes.peek().getCacheLength(); - } -} diff --git a/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java b/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java deleted file mode 100644 index 387f5a8f..00000000 --- a/src/main/java/com/notnoop/apns/internal/ApnsServiceImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.internal; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.exceptions.NetworkIOException; - -public class ApnsServiceImpl extends AbstractApnsService { - private ApnsConnection connection; - - public ApnsServiceImpl(ApnsConnection connection, ApnsFeedbackConnection feedback) { - super(feedback); - this.connection = connection; - } - - @Override - public void push(ApnsNotification msg) throws NetworkIOException { - connection.sendMessage(msg); - } - - public void start() { - } - - public void stop() { - Utilities.close(connection); - } - - public void testConnection() { - connection.testConnection(); - } -} diff --git a/src/main/java/com/notnoop/apns/internal/BatchApnsService.java b/src/main/java/com/notnoop/apns/internal/BatchApnsService.java deleted file mode 100644 index d7dcbefb..00000000 --- a/src/main/java/com/notnoop/apns/internal/BatchApnsService.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.internal; - -import static java.util.concurrent.Executors.defaultThreadFactory; - -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.exceptions.NetworkIOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class BatchApnsService extends AbstractApnsService { - - private static final Logger logger = LoggerFactory.getLogger(BatchApnsService.class); - - /** - * How many seconds to wait for more messages before batch is send. - * Each message reset the wait time - * - * @see #maxBatchWaitTimeInSec - */ - private int batchWaitTimeInSec = 5; - - /** - * How many seconds can be batch delayed before execution. - * This time is not exact amount after which the batch will run its roughly the time - */ - private int maxBatchWaitTimeInSec = 10; - - private long firstMessageArrivedTime; - - private ApnsConnection prototype; - - private Queue batch = new ConcurrentLinkedQueue(); - - private ScheduledExecutorService scheduleService; - private ScheduledFuture taskFuture; - - private Runnable batchRunner = new SendMessagesBatch(); - - public BatchApnsService(ApnsConnection prototype, ApnsFeedbackConnection feedback, int batchWaitTimeInSec, int maxBachWaitTimeInSec, ThreadFactory tf) { - this(prototype, feedback, batchWaitTimeInSec, maxBachWaitTimeInSec, - new ScheduledThreadPoolExecutor(1, - tf != null ? tf : defaultThreadFactory())); - } - - public BatchApnsService(ApnsConnection prototype, ApnsFeedbackConnection feedback, int batchWaitTimeInSec, int maxBachWaitTimeInSec, ScheduledExecutorService executor) { - super(feedback); - this.prototype = prototype; - this.batchWaitTimeInSec = batchWaitTimeInSec; - this.maxBatchWaitTimeInSec = maxBachWaitTimeInSec; - this.scheduleService = executor != null ? executor : new ScheduledThreadPoolExecutor(1, defaultThreadFactory()); - } - - public void start() { - // no code - } - - public void stop() { - Utilities.close(prototype); - if (taskFuture != null) { - taskFuture.cancel(true); - } - scheduleService.shutdownNow(); - } - - public void testConnection() throws NetworkIOException { - prototype.testConnection(); - } - - @Override - public void push(ApnsNotification message) throws NetworkIOException { - if (batch.isEmpty()) { - firstMessageArrivedTime = System.nanoTime(); - } - - long sinceFirstMessageSec = (System.nanoTime() - firstMessageArrivedTime) / 1000 / 1000 / 1000; - - if (taskFuture != null && sinceFirstMessageSec < maxBatchWaitTimeInSec) { - taskFuture.cancel(false); - } - - batch.add(message); - - if (taskFuture == null || taskFuture.isDone()) { - taskFuture = scheduleService.schedule(batchRunner, batchWaitTimeInSec, TimeUnit.SECONDS); - } - } - - class SendMessagesBatch implements Runnable { - public void run() { - ApnsConnection newConnection = prototype.copy(); - try { - ApnsNotification msg; - while ((msg = batch.poll()) != null) { - try { - newConnection.sendMessage(msg); - } catch (NetworkIOException e) { - logger.warn("Network exception sending message msg "+ msg.getIdentifier(), e); - } - } - } finally { - Utilities.close(newConnection); - } - } - } -} diff --git a/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java b/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java deleted file mode 100644 index 21cc8202..00000000 --- a/src/main/java/com/notnoop/apns/internal/QueuedApnsService.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.internal; - -import java.util.Date; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.ApnsService; -import com.notnoop.exceptions.NetworkIOException; - -public class QueuedApnsService extends AbstractApnsService { - - private static final Logger logger = LoggerFactory.getLogger(QueuedApnsService.class); - - private ApnsService service; - private BlockingQueue queue; - private AtomicBoolean started = new AtomicBoolean(false); - - public QueuedApnsService(ApnsService service) { - this(service, null); - } - - public QueuedApnsService(ApnsService service, final ThreadFactory tf) { - super(null); - this.service = service; - this.queue = new LinkedBlockingQueue(); - this.threadFactory = tf == null ? Executors.defaultThreadFactory() : tf; - this.thread = null; - } - - @Override - public void push(ApnsNotification msg) { - if (!started.get()) { - throw new IllegalStateException("service hasn't be started or was closed"); - } - queue.add(msg); - } - - private final ThreadFactory threadFactory; - private Thread thread; - private volatile boolean shouldContinue; - - public void start() { - if (started.getAndSet(true)) { - // I prefer if we throw a runtime IllegalStateException here, - // but I want to maintain semantic backward compatibility. - // So it is returning immediately here - return; - } - - service.start(); - shouldContinue = true; - thread = threadFactory.newThread(new Runnable() { - public void run() { - while (shouldContinue) { - try { - ApnsNotification msg = queue.take(); - service.push(msg); - } catch (InterruptedException e) { - // ignore - } catch (NetworkIOException e) { - // ignore: failed connect... - } catch (Exception e) { - // weird if we reached here - something wrong is happening, but we shouldn't stop the service anyway! - logger.warn("Unexpected message caught... Shouldn't be here", e); - } - } - } - }); - thread.start(); - } - - public void stop() { - started.set(false); - shouldContinue = false; - thread.interrupt(); - service.stop(); - } - - @Override - public Map getInactiveDevices() throws NetworkIOException { - return service.getInactiveDevices(); - } - - public void testConnection() throws NetworkIOException { - service.testConnection(); - } - -} diff --git a/src/main/java/com/notnoop/apns/internal/ReconnectPolicies.java b/src/main/java/com/notnoop/apns/internal/ReconnectPolicies.java deleted file mode 100644 index b6a70eb6..00000000 --- a/src/main/java/com/notnoop/apns/internal/ReconnectPolicies.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.internal; - -import com.notnoop.apns.ReconnectPolicy; - -public final class ReconnectPolicies { - - public static class Never implements ReconnectPolicy { - - public boolean shouldReconnect() { return false; } - public void reconnected() { } - public Never copy() { return this; } - } - - public static class Always implements ReconnectPolicy { - public boolean shouldReconnect() { return true; } - public void reconnected() { } - public Always copy() { return this; } - } - - public static class EveryHalfHour implements ReconnectPolicy { - private static final long PERIOD = 30 * 60 * 1000; - - private long lastRunning = System.currentTimeMillis(); - - public boolean shouldReconnect() { - return System.currentTimeMillis() - lastRunning > PERIOD; - } - - public void reconnected() { - lastRunning = System.currentTimeMillis(); - } - - public EveryHalfHour copy() { - return new EveryHalfHour(); - } - } -} diff --git a/src/main/java/com/notnoop/apns/internal/SSLContextBuilder.java b/src/main/java/com/notnoop/apns/internal/SSLContextBuilder.java deleted file mode 100644 index 5175bfd1..00000000 --- a/src/main/java/com/notnoop/apns/internal/SSLContextBuilder.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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.internal; - -import com.notnoop.exceptions.InvalidSSLConfig; - -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import java.io.IOException; -import java.io.InputStream; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; - -public class SSLContextBuilder { - private String algorithm = "sunx509"; - private KeyManagerFactory keyManagerFactory; - private TrustManager[] trustManagers; - - public SSLContextBuilder withAlgorithm(String algorithm) { - this.algorithm = algorithm; - return this; - } - - public SSLContextBuilder withDefaultTrustKeyStore() throws InvalidSSLConfig { - try { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); - trustManagerFactory.init((KeyStore)null); - trustManagers = trustManagerFactory.getTrustManagers(); - return this; - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } - } - - public SSLContextBuilder withTrustKeyStore(InputStream keyStoreStream, String keyStorePassword, String keyStoreType) throws InvalidSSLConfig { - try { - final KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(keyStoreStream, keyStorePassword.toCharArray()); - return withTrustKeyStore(ks, keyStorePassword); - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } catch (IOException e) { - throw new InvalidSSLConfig(e); - } - - } - public SSLContextBuilder withTrustKeyStore(KeyStore keyStore, String keyStorePassword) throws InvalidSSLConfig { - try { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); - trustManagerFactory.init(keyStore); - trustManagers = trustManagerFactory.getTrustManagers(); - return this; - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } - } - - public SSLContextBuilder withTrustManager(TrustManager trustManager) { - trustManagers = new TrustManager[] { trustManager }; - return this; - } - - public SSLContextBuilder withCertificateKeyStore(InputStream keyStoreStream, String keyStorePassword, String keyStoreType) throws InvalidSSLConfig { - try { - final KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(keyStoreStream, keyStorePassword.toCharArray()); - return withCertificateKeyStore(ks, keyStorePassword); - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } catch (IOException e) { - throw new InvalidSSLConfig(e); - } - } - - public SSLContextBuilder withCertificateKeyStore(InputStream keyStoreStream, String keyStorePassword, String keyStoreType, String keyAlias) throws InvalidSSLConfig { - try { - final KeyStore ks = KeyStore.getInstance(keyStoreType); - ks.load(keyStoreStream, keyStorePassword.toCharArray()); - return withCertificateKeyStore(ks, keyStorePassword, keyAlias); - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } catch (IOException e) { - throw new InvalidSSLConfig(e); - } - } - - public SSLContextBuilder withCertificateKeyStore(KeyStore keyStore, String keyStorePassword) throws InvalidSSLConfig { - try { - keyManagerFactory = KeyManagerFactory.getInstance(algorithm); - keyManagerFactory.init(keyStore, keyStorePassword.toCharArray()); - return this; - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } - } - - public SSLContextBuilder withCertificateKeyStore(KeyStore keyStore, String keyStorePassword, String keyAlias) throws InvalidSSLConfig { - try { - if (!keyStore.containsAlias(keyAlias)) { - throw new InvalidSSLConfig("No key with alias " + keyAlias); - } - KeyStore singleKeyKeyStore = getKeyStoreWithSingleKey(keyStore, keyStorePassword, keyAlias); - return withCertificateKeyStore(singleKeyKeyStore, keyStorePassword); - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } catch (IOException e) { - throw new InvalidSSLConfig(e); - } - } - - /* - * Workaround for keystores containing multiple keys. Java will take the first key that matches - * and this way we can still offer configuration for a keystore with multiple keys and a selection - * based on alias. Also much easier than making a subclass of a KeyManagerFactory - */ - private KeyStore getKeyStoreWithSingleKey(KeyStore keyStore, String keyStorePassword, String keyAlias) - throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException { - KeyStore singleKeyKeyStore = KeyStore.getInstance(keyStore.getType(), keyStore.getProvider()); - final char[] password = keyStorePassword.toCharArray(); - singleKeyKeyStore.load(null, password); - Key key = keyStore.getKey(keyAlias, password); - Certificate[] chain = keyStore.getCertificateChain(keyAlias); - singleKeyKeyStore.setKeyEntry(keyAlias, key, password, chain); - return singleKeyKeyStore; - } - - public SSLContext build() throws InvalidSSLConfig { - if (keyManagerFactory == null) { - throw new InvalidSSLConfig("Missing KeyManagerFactory"); - } - - if (trustManagers == null) { - throw new InvalidSSLConfig("Missing TrustManagers"); - } - - try { - final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagerFactory.getKeyManagers(), trustManagers, null); - return sslContext; - } catch (GeneralSecurityException e) { - throw new InvalidSSLConfig(e); - } - } -} diff --git a/src/main/java/com/notnoop/apns/internal/TlsTunnelBuilder.java b/src/main/java/com/notnoop/apns/internal/TlsTunnelBuilder.java deleted file mode 100644 index 5e0d68cf..00000000 --- a/src/main/java/com/notnoop/apns/internal/TlsTunnelBuilder.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.internal; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.ProtocolException; -import java.net.Proxy; -import java.net.Socket; -import javax.net.ssl.SSLSocketFactory; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import org.apache.commons.httpclient.ConnectMethod; -import org.apache.commons.httpclient.NTCredentials; -import org.apache.commons.httpclient.ProxyClient; -import org.apache.commons.httpclient.UsernamePasswordCredentials; -import org.apache.commons.httpclient.auth.AuthScope; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Establishes a TLS connection using an HTTP proxy. See RFC 2817 5.2. This class does - * not support proxies requiring a "Proxy-Authorization" header. - */ -public final class TlsTunnelBuilder { - - private static final Logger logger = LoggerFactory.getLogger(TlsTunnelBuilder.class); - - public Socket build(SSLSocketFactory factory, Proxy proxy, String proxyUsername, String proxyPassword, String host, int port) - throws IOException { - boolean success = false; - Socket proxySocket = null; - try { - logger.debug("Attempting to use proxy : " + proxy.toString()); - InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address(); - proxySocket = makeTunnel(host, port, proxyUsername, proxyPassword, proxyAddress); - - // Handshake with the origin server. - if(proxySocket == null) { - throw new ProtocolException("Unable to create tunnel through proxy server."); - } - Socket socket = factory.createSocket(proxySocket, host, port, true /* auto close */); - success = true; - return socket; - } finally { - if (!success) { - Utilities.close(proxySocket); - } - } - } - - @SuppressFBWarnings(value = "VA_FORMAT_STRING_USES_NEWLINE", - justification = "use as according to RFC, not platform-linefeed") - Socket makeTunnel(String host, int port, String proxyUsername, - String proxyPassword, InetSocketAddress proxyAddress) throws IOException { - if(host == null || port < 0 || host.isEmpty() || proxyAddress == null){ - throw new ProtocolException("Incorrect parameters to build tunnel."); - } - logger.debug("Creating socket for Proxy : " + proxyAddress.getAddress() + ":" + proxyAddress.getPort()); - Socket socket; - try { - ProxyClient client = new ProxyClient(); - client.getParams().setParameter("http.useragent", "java-apns"); - client.getHostConfiguration().setHost(host, port); - String proxyHost = proxyAddress.getAddress().toString().substring(0, proxyAddress.getAddress().toString().indexOf("/")); - client.getHostConfiguration().setProxy(proxyHost, proxyAddress.getPort()); - - - ProxyClient.ConnectResponse response = client.connect(); - socket = response.getSocket(); - if (socket == null) { - ConnectMethod method = response.getConnectMethod(); - // Read the proxy's HTTP response. - if(method.getStatusLine().getStatusCode() == 407) { - // Proxy server returned 407. We will now try to connect with auth Header - if(proxyUsername != null && proxyPassword != null) { - socket = AuthenticateProxy(method, client,proxyHost, proxyAddress.getPort(), - proxyUsername, proxyPassword); - } else { - throw new ProtocolException("Socket not created: " + method.getStatusLine()); - } - } - } - - } catch (Exception e) { - throw new ProtocolException("Error occurred while creating proxy socket : " + e.toString()); - } - if (socket != null) { - logger.debug("Socket for proxy created successfully : " + socket.getRemoteSocketAddress().toString()); - } - return socket; - } - - private Socket AuthenticateProxy(ConnectMethod method, ProxyClient client, - String proxyHost, int proxyPort, - String proxyUsername, String proxyPassword) throws IOException { - if("ntlm".equalsIgnoreCase(method.getProxyAuthState().getAuthScheme().getSchemeName())) { - // If Auth scheme is NTLM, set NT credentials with blank host and domain name - client.getState().setProxyCredentials(new AuthScope(proxyHost, proxyPort), - new NTCredentials(proxyUsername, proxyPassword,"","")); - } else { - // If Auth scheme is Basic/Digest, set regular Credentials - client.getState().setProxyCredentials(new AuthScope(proxyHost, proxyPort), - new UsernamePasswordCredentials(proxyUsername, proxyPassword)); - } - - ProxyClient.ConnectResponse response = client.connect(); - Socket socket = response.getSocket(); - - if (socket == null) { - method = response.getConnectMethod(); - throw new ProtocolException("Proxy Authentication failed. Socket not created: " - + method.getStatusLine()); - } - return socket; - } - -} - diff --git a/src/main/java/com/notnoop/apns/internal/Utilities.java b/src/main/java/com/notnoop/apns/internal/Utilities.java deleted file mode 100644 index 1849098b..00000000 --- a/src/main/java/com/notnoop/apns/internal/Utilities.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * 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.internal; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.Socket; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Pattern; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManagerFactory; -import com.notnoop.exceptions.InvalidSSLConfig; -import com.notnoop.exceptions.NetworkIOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class Utilities { - private static Logger logger = LoggerFactory.getLogger(Utilities.class); - - public static final String SANDBOX_GATEWAY_HOST = "gateway.sandbox.push.apple.com"; - public static final int SANDBOX_GATEWAY_PORT = 2195; - - public static final String SANDBOX_FEEDBACK_HOST = "feedback.sandbox.push.apple.com"; - public static final int SANDBOX_FEEDBACK_PORT = 2196; - - public static final String PRODUCTION_GATEWAY_HOST = "gateway.push.apple.com"; - public static final int PRODUCTION_GATEWAY_PORT = 2195; - - public static final String PRODUCTION_FEEDBACK_HOST = "feedback.push.apple.com"; - public static final int PRODUCTION_FEEDBACK_PORT = 2196; - - public static final int MAX_PAYLOAD_LENGTH = 2048; - - private Utilities() { throw new AssertionError("Uninstantiable class"); } - - private static final Pattern pattern = Pattern.compile("[ -]"); - public static byte[] decodeHex(final String deviceToken) { - final String hex = pattern.matcher(deviceToken).replaceAll(""); - - final byte[] bts = new byte[hex.length() / 2]; - for (int i = 0; i < bts.length; i++) { - bts[i] = (byte) (charVal(hex.charAt(2 * i)) * 16 + charVal(hex.charAt(2 * i + 1))); - } - return bts; - } - - private static int charVal(final char a) { - if ('0' <= a && a <= '9') { - return (a - '0'); - } else if ('a' <= a && a <= 'f') { - return (a - 'a') + 10; - } else if ('A' <= a && a <= 'F') { - return (a - 'A') + 10; - } else { - throw new RuntimeException("Invalid hex character: " + a); - } - } - - private static final char base[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - public static String encodeHex(final byte[] bytes) { - final char[] chars = new char[bytes.length * 2]; - - for (int i = 0; i < bytes.length; ++i) { - final int b = (bytes[i]) & 0xFF; - chars[2 * i] = base[b >>> 4]; - chars[2 * i + 1] = base[b & 0xF]; - } - - return new String(chars); - } - - public static byte[] toUTF8Bytes(final String s) { - try { - return s.getBytes("UTF-8"); - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - public static byte[] marshall(final byte command, final byte[] deviceToken, final byte[] payload) { - final ByteArrayOutputStream boas = new ByteArrayOutputStream(); - final DataOutputStream dos = new DataOutputStream(boas); - - try { - dos.writeByte(command); - dos.writeShort(deviceToken.length); - dos.write(deviceToken); - dos.writeShort(payload.length); - dos.write(payload); - return boas.toByteArray(); - } catch (final IOException e) { - throw new AssertionError(); - } - } - - public static byte[] marshallEnhanced(final byte command, final int identifier, - final int expiryTime, final byte[] deviceToken, final byte[] payload) { - final ByteArrayOutputStream boas = new ByteArrayOutputStream(); - final DataOutputStream dos = new DataOutputStream(boas); - - try { - dos.writeByte(command); - dos.writeInt(identifier); - dos.writeInt(expiryTime); - dos.writeShort(deviceToken.length); - dos.write(deviceToken); - dos.writeShort(payload.length); - dos.write(payload); - return boas.toByteArray(); - } catch (final IOException e) { - throw new AssertionError(); - } - } - - public static Map parseFeedbackStreamRaw(final InputStream in) { - final Map result = new HashMap(); - - final DataInputStream data = new DataInputStream(in); - - while (true) { - try { - final int time = data.readInt(); - final int dtLength = data.readUnsignedShort(); - final byte[] deviceToken = new byte[dtLength]; - data.readFully(deviceToken); - - result.put(deviceToken, time); - } catch (final EOFException e) { - break; - } catch (final IOException e) { - throw new RuntimeException(e); - } - } - - return result; - } - - public static Map parseFeedbackStream(final InputStream in) { - final Map result = new HashMap(); - - final Map raw = parseFeedbackStreamRaw(in); - for (final Map.Entry entry : raw.entrySet()) { - final byte[] dtArray = entry.getKey(); - final int time = entry.getValue(); // in seconds - - final Date date = new Date(time * 1000L); // in ms - final String dtString = encodeHex(dtArray); - result.put(dtString, date); - } - - return result; - } - - public static void close(final Closeable closeable) { - logger.debug("close {}", closeable); - - try { - if (closeable != null) { - closeable.close(); - } - } catch (final IOException e) { - logger.debug("error while closing resource", e); - } - } - - public static void close(final Socket closeable) { - logger.debug("close {}", closeable); - - try { - if (closeable != null) { - closeable.close(); - } - } catch (final IOException e) { - logger.debug("error while closing socket", e); - } - } - - public static void sleep(final int delay) { - try { - Thread.sleep(delay); - } catch (final InterruptedException e1) { - Thread.currentThread().interrupt(); - } - } - - public static byte[] copyOf(final byte[] bytes) { - final byte[] copy = new byte[bytes.length]; - System.arraycopy(bytes, 0, copy, 0, bytes.length); - return copy; - } - - public static byte[] copyOfRange(final byte[] original, final int from, final int to) { - final int newLength = to - from; - if (newLength < 0) { - throw new IllegalArgumentException(from + " > " + to); - } - final byte[] copy = new byte[newLength]; - System.arraycopy(original, from, copy, 0, - Math.min(original.length - from, newLength)); - return copy; - } - - public static void wrapAndThrowAsRuntimeException(final Exception e) throws NetworkIOException { - if (e instanceof IOException) { - throw new NetworkIOException((IOException)e); - } else if (e instanceof NetworkIOException) { - throw (NetworkIOException)e; - } else if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } else { - throw new RuntimeException(e); - } - } - - @SuppressWarnings({"PointlessArithmeticExpression", "PointlessBitwiseExpression"}) - public static int parseBytes(final int b1, final int b2, final int b3, final int b4) { - return ((b1 << 3 * 8) & 0xFF000000) - | ((b2 << 2 * 8) & 0x00FF0000) - | ((b3 << 1 * 8) & 0x0000FF00) - | ((b4 << 0 * 8) & 0x000000FF); - } - - // @see http://stackoverflow.com/questions/119328/how-do-i-truncate-a-java-string-to-fit-in-a-given-number-of-bytes-once-utf-8-enc - public static String truncateWhenUTF8(final String s, final int maxBytes) { - int b = 0; - for (int i = 0; i < s.length(); i++) { - final char c = s.charAt(i); - - // ranges from http://en.wikipedia.org/wiki/UTF-8 - int skip = 0; - int more; - if (c <= 0x007f) { - more = 1; - } - else if (c <= 0x07FF) { - more = 2; - } else if (c <= 0xd7ff) { - more = 3; - } else if (c <= 0xDFFF) { - // surrogate area, consume next char as well - more = 4; - skip = 1; - } else { - more = 3; - } - - if (b + more > maxBytes) { - return s.substring(0, i); - } - b += more; - i += skip; - } - return s; - } - -} diff --git a/src/main/java/com/notnoop/exceptions/ApnsDeliveryErrorException.java b/src/main/java/com/notnoop/exceptions/ApnsDeliveryErrorException.java deleted file mode 100644 index c9b3bea9..00000000 --- a/src/main/java/com/notnoop/exceptions/ApnsDeliveryErrorException.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package com.notnoop.exceptions; - -import com.notnoop.apns.DeliveryError; - -/** - * - * @author kkirch - */ -public class ApnsDeliveryErrorException extends ApnsException { - - private final DeliveryError deliveryError; - - public ApnsDeliveryErrorException(DeliveryError error) { - this.deliveryError = error; - } - - @Override - public String getMessage() { - return "Failed to deliver notification with error code " + deliveryError.code(); - } - - public DeliveryError getDeliveryError() { - return deliveryError; - } - - -} diff --git a/src/main/java/com/notnoop/exceptions/ApnsException.java b/src/main/java/com/notnoop/exceptions/ApnsException.java deleted file mode 100644 index f1725e5c..00000000 --- a/src/main/java/com/notnoop/exceptions/ApnsException.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.exceptions; - -/** - * Base class for all the exceptions thrown in Apns Library - */ -public abstract class ApnsException extends RuntimeException { - private static final long serialVersionUID = -4756693306121825229L; - - public ApnsException() { super(); } - public ApnsException(String message) { super(message); } - public ApnsException(Throwable cause) { super(cause); } - public ApnsException(String m, Throwable c) { super(m, c); } - -} diff --git a/src/main/java/com/notnoop/exceptions/InvalidSSLConfig.java b/src/main/java/com/notnoop/exceptions/InvalidSSLConfig.java deleted file mode 100644 index be975789..00000000 --- a/src/main/java/com/notnoop/exceptions/InvalidSSLConfig.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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.exceptions; - -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; - -/** - * Signals that the the provided SSL context settings (e.g. - * keystore path, password, encryption type, etc) are invalid - * - * This Exception can be caused by any of the following: - * - *

    - *
  1. {@link KeyStoreException}
  2. - *
  3. {@link NoSuchAlgorithmException}
  4. - *
  5. {@link CertificateException}
  6. - *
  7. {@link IOException}
  8. - *
  9. {@link UnrecoverableKeyException}
  10. - *
  11. {@link KeyManagementException}
  12. - *
- * - */ -public class InvalidSSLConfig extends ApnsException { - private static final long serialVersionUID = -7283168775864517167L; - - public InvalidSSLConfig() { super(); } - public InvalidSSLConfig(String message) { super(message); } - public InvalidSSLConfig(Throwable cause) { super(cause); } - public InvalidSSLConfig(String m, Throwable c) { super(m, c); } - -} diff --git a/src/main/java/com/notnoop/exceptions/NetworkIOException.java b/src/main/java/com/notnoop/exceptions/NetworkIOException.java deleted file mode 100644 index df84eb5e..00000000 --- a/src/main/java/com/notnoop/exceptions/NetworkIOException.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.exceptions; - -import java.io.IOException; - -/** - * Thrown to indicate that that a network operation has failed: - * (e.g. connectivity problems, domain cannot be found, network - * dropped). - */ -public class NetworkIOException extends ApnsException { - private static final long serialVersionUID = 3353516625486306533L; - - private boolean resend; - - public NetworkIOException() { super(); } - public NetworkIOException(String message) { super(message); } - public NetworkIOException(IOException cause) { super(cause); } - public NetworkIOException(String m, IOException c) { super(m, c); } - public NetworkIOException(IOException cause, boolean resend) { - super(cause); - this.resend = resend; - } - - /** - * Identifies whether an exception was thrown during a resend of a - * message or not. In this case a resend refers to whether the - * message is being resent from the buffer of messages internal. - * This would occur if we sent 5 messages quickly to APNS: - * 1,2,3,4,5 and the 3 message was rejected. We would - * then need to resend 4 and 5. If a network exception was - * triggered when doing this, then the resend flag will be - * {@code true}. - * @return {@code true} for an exception trigger during a resend, otherwise {@code false}. - */ - public boolean isResend() { - return resend; - } - -} diff --git a/src/main/java/com/notnoop/exceptions/RuntimeIOException.java b/src/main/java/com/notnoop/exceptions/RuntimeIOException.java deleted file mode 100644 index 1a447bd5..00000000 --- a/src/main/java/com/notnoop/exceptions/RuntimeIOException.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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.exceptions; - -import java.io.IOException; - -/** - * Signals that an I/O exception of some sort has occurred. This - * class is the general class of exceptions produced by failed or - * interrupted I/O operations. - * - * This is a RuntimeException, unlike the java.io.IOException - */ -public class RuntimeIOException extends ApnsException { - private static final long serialVersionUID = 8665285084049041306L; - - public RuntimeIOException() { super(); } - public RuntimeIOException(String message) { super(message); } - public RuntimeIOException(IOException cause) { super(cause); } - public RuntimeIOException(String m, IOException c) { super(m, c); } - -} diff --git a/src/test/java/com/notnoop/apns/APNSTest.java b/src/test/java/com/notnoop/apns/APNSTest.java deleted file mode 100644 index aefe16f2..00000000 --- a/src/test/java/com/notnoop/apns/APNSTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 org.junit.Test; -import static org.junit.Assert.*; -import static org.hamcrest.CoreMatchers.*; - -/** - * Silly Tests - */ -public class APNSTest { - - @Test - public void testInstances() { - assertThat(APNS.newPayload(), isA(PayloadBuilder.class)); - assertThat(APNS.newService(), isA(ApnsServiceBuilder.class)); - } - - @Test - public void payloadShouldGetNewInstances() { - assertNotSame(APNS.newPayload(), APNS.newPayload()); - } - - @Test - public void newServiceGetNewInstances() { - assertNotSame(APNS.newService(), APNS.newService()); - } -} diff --git a/src/test/java/com/notnoop/apns/AbstractApnsServerSocket.java b/src/test/java/com/notnoop/apns/AbstractApnsServerSocket.java deleted file mode 100644 index 7a1efdcd..00000000 --- a/src/test/java/com/notnoop/apns/AbstractApnsServerSocket.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 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(); - } - - @SuppressWarnings("InfiniteLoopStatement") - private void startAccept() { - - try { - while (true) { - Socket accept = serverSocket.accept(); - // Work around JVM deadlock ... https://community.oracle.com/message/10989561#10989561 - accept.setSoLinger(true, 1); - 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; -} diff --git a/src/test/java/com/notnoop/apns/ApnsFeedbackServerSocket.java b/src/test/java/com/notnoop/apns/ApnsFeedbackServerSocket.java deleted file mode 100644 index 254a108d..00000000 --- a/src/test/java/com/notnoop/apns/ApnsFeedbackServerSocket.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 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 inactiveDevices = requestDelegate - .getInactiveDevices(); - DataOutputStream dataStream = new DataOutputStream( - socket.getOutputStream()); - for (Entry 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(); - } -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/ApnsGatewayServerSocket.java b/src/test/java/com/notnoop/apns/ApnsGatewayServerSocket.java deleted file mode 100644 index 3e04d5b0..00000000 --- a/src/test/java/com/notnoop/apns/ApnsGatewayServerSocket.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.Socket; -import java.util.concurrent.ExecutorService; -import javax.net.ssl.SSLContext; - -/** - * Represents the Apple APNS server. This allows testing outside of the Apple - * servers. - */ -@SuppressWarnings("deprecation") -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 e) { - 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]; - final DataInputStream dis = new DataInputStream(inputStream); - dis.readFully(bytes); - return bytes; - } -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/ApnsServerExceptionDelegate.java b/src/test/java/com/notnoop/apns/ApnsServerExceptionDelegate.java deleted file mode 100644 index 431f0d63..00000000 --- a/src/test/java/com/notnoop/apns/ApnsServerExceptionDelegate.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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; - -/** - * 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) { - } - }; -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/ApnsServerService.java b/src/test/java/com/notnoop/apns/ApnsServerService.java deleted file mode 100644 index d7de5a24..00000000 --- a/src/test/java/com/notnoop/apns/ApnsServerService.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 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 getInactiveDevices(); - - public static final ApnsServerService EMPTY = new ApnsServerService() { - @Override - public void messageReceived(ApnsNotification message) throws Exception { - } - - @Override - public Map getInactiveDevices() { - return Collections.emptyMap(); - } - }; -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/ApnsServerSocketBuilder.java b/src/test/java/com/notnoop/apns/ApnsServerSocketBuilder.java deleted file mode 100644 index fc78aad2..00000000 --- a/src/test/java/com/notnoop/apns/ApnsServerSocketBuilder.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * 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 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.SSLContextBuilder; -import com.notnoop.apns.internal.Utilities; -import com.notnoop.exceptions.InvalidSSLConfig; -import com.notnoop.exceptions.RuntimeIOException; -import static com.notnoop.apns.internal.Utilities.*; - -/** - * 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: - * - *
- * ApnsService = APNS.newService()
- * 		.withCert("/path/to/certificate.p12", "MyCertPassword")
- * 		.withSandboxDestination().build()
- * 
- */ -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; - 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 Bug - * 6415637. 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 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 Bug - * 6415637. 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 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(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 Bug - * 6415637. 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 ApnsServerSocketBuilder withCert(KeyStore keyStore, String password) - throws InvalidSSLConfig { - assertPasswordNotEmpty(password); - return withSSLContext(new SSLContextBuilder() - .withAlgorithm(KEY_ALGORITHM) - .withCertificateKeyStore(keyStore, password) - .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 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())); - } - - /** - * 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()."); - } - } -} diff --git a/src/test/java/com/notnoop/apns/ApnsSocketService.java b/src/test/java/com/notnoop/apns/ApnsSocketService.java deleted file mode 100644 index 25378490..00000000 --- a/src/test/java/com/notnoop/apns/ApnsSocketService.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 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(); - } -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/MainClass.java b/src/test/java/com/notnoop/apns/MainClass.java deleted file mode 100644 index e737ce4d..00000000 --- a/src/test/java/com/notnoop/apns/MainClass.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.util.Date; -import java.util.Map; -import java.util.Map.Entry; -import com.notnoop.exceptions.InvalidSSLConfig; - -public class MainClass { - - /** - * @param args Program arguments - * @throws FileNotFoundException - * @throws InvalidSSLConfig - */ - public static void main(final String[] args) throws InvalidSSLConfig, FileNotFoundException { - if (args.length != 4) { - System.err.println("Usage: test \ntest p ./cert abc123 token"); - System.exit(777); - } - - final ApnsDelegate delegate = new ApnsDelegate() { - public void messageSent(final ApnsNotification message, final boolean resent) { - System.out.println("Sent message " + message + " Resent: " + resent); - } - - public void messageSendFailed(final ApnsNotification message, final Throwable e) { - System.out.println("Failed message " + message); - - } - - public void connectionClosed(final DeliveryError e, final int messageIdentifier) { - System.out.println("Closed connection: " + messageIdentifier + "\n deliveryError " + e.toString()); - } - - public void cacheLengthExceeded(final int newCacheLength) { - System.out.println("cacheLengthExceeded " + newCacheLength); - - } - - public void notificationsResent(final int resendCount) { - System.out.println("notificationResent " + resendCount); - } - }; - - final ApnsService svc = APNS.newService() - .withAppleDestination("p".equals(args[0])) - .withCert(new FileInputStream(args[1]), args[2]) - .withDelegate(delegate) - .build(); - - final String goodToken = args[3]; - - final String payload = APNS.newPayload().alertBody("Wrzlmbrmpf dummy alert").build(); - - svc.start(); - System.out.println("Sending message"); - final ApnsNotification goodMsg = svc.push(goodToken, payload); - System.out.println("Message id: " + goodMsg.getIdentifier()); - - System.out.println("Getting inactive devices"); - - final Map inactiveDevices = svc.getInactiveDevices(); - - for (final Entry ent : inactiveDevices.entrySet()) { - System.out.println("Inactive " + ent.getKey() + " at date " + ent.getValue()); - } - System.out.println("Stopping service"); - svc.stop(); - } -} diff --git a/src/test/java/com/notnoop/apns/PayloadBuilderTest.java b/src/test/java/com/notnoop/apns/PayloadBuilderTest.java deleted file mode 100644 index 95ebe28b..00000000 --- a/src/test/java/com/notnoop/apns/PayloadBuilderTest.java +++ /dev/null @@ -1,573 +0,0 @@ -/* - * 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 java.util.Arrays; -import java.util.Map; -import java.util.Random; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.notnoop.apns.internal.Utilities; -import org.junit.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsNot.*; -import static org.hamcrest.core.StringContains.*; -import static org.junit.Assert.*; - -@SuppressWarnings("deprecation") -public class PayloadBuilderTest { - - @Test - public void testEmpty() { - final PayloadBuilder builder = new PayloadBuilder(); - - final String expected = "{\"aps\":{}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void testOneAps() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - - final String expected = "{\"aps\":{\"alert\":\"test\"}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void testTwoAps() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - builder.badge(9); - - final String expected = "{\"aps\":{\"alert\":\"test\",\"badge\":9}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void testSafariAps() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - builder.alertTitle("Test Title"); - builder.actionKey("View"); - builder.urlArgs("arg1", "arg2", "arg3"); - - final String expected = "{\"aps\":{\"alert\":{\"body\":\"test\",\"title\":\"Test Title\",\"action-loc-key\":\"View\"},\"url-args\":[\"arg1\",\"arg2\",\"arg3\"]}}"; - assertEqualsJson(expected, builder.build()); - } - - @Test - public void testTwoApsMultipleBuilds() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - builder.badge(9); - - final String expected = "{\"aps\":{\"alert\":\"test\",\"badge\":9}}"; - assertEqualsJson(expected, builder.build()); - assertEqualsJson(expected, builder.build()); - } - - @Test - public void testIncludeBadge() { - final String badge0 = APNS.newPayload().badge(0).toString(); - final String badgeNo = APNS.newPayload().clearBadge().toString(); - - final String expected = "{\"aps\":{\"badge\":0}}"; - assertEqualsJson(expected, badge0); - assertEqualsJson(expected, badgeNo); - } - - @Test - public void localizedTitleKeyAndArguments() { - final PayloadBuilder builder = new PayloadBuilder() - .localizedTitleKey("GAME_PLAY_REQUEST_FORMAT") - .localizedTitleArguments("Jenna", "Frank"); - builder.sound("chime"); - - final String expected = "{\"aps\":{\"sound\":\"chime\",\"alert\":{\"title-loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"title-loc-args\":[\"Jenna\",\"Frank\"]}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void localizedOneWithArray() { - final PayloadBuilder builder = new PayloadBuilder() - .localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank"); - builder.sound("chime"); - - final String expected = "{\"aps\":{\"sound\":\"chime\",\"alert\":{\"loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"loc-args\":[\"Jenna\",\"Frank\"]}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void localizedOneWithVarargs() { - final PayloadBuilder builder = new PayloadBuilder() - .localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank"); - builder.sound("chime"); - - final String expected = "{\"aps\":{\"sound\":\"chime\",\"alert\":{\"loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"loc-args\":[\"Jenna\",\"Frank\"]}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void localizedTwo() { - final PayloadBuilder builder = - new PayloadBuilder() - .sound("chime") - .localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank"); - - final String expected = "{\"aps\":{\"sound\":\"chime\",\"alert\":{\"loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"loc-args\":[\"Jenna\",\"Frank\"]}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void customFieldSimple() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - - builder.customField("ache1", "what"); - builder.customField("ache2", 2); - - final String expected = "{\"ache1\":\"what\",\"ache2\":2,\"aps\":{\"alert\":\"test\"}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void customFieldArray() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - - builder.customField("ache1", Arrays.asList("a1", "a2")); - builder.customField("ache2", new int[] { 1, 2 } ); - - final String expected = "{\"ache1\":[\"a1\",\"a2\"],\"ache2\":[1,2],\"aps\":{\"alert\":\"test\"}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void customBody() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("what").actionKey("Cancel"); - - final String expected = "{\"aps\":{\"alert\":{\"action-loc-key\":\"Cancel\",\"body\":\"what\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void multipleBuildCallsWithCustomBody() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("what").actionKey("Cancel"); - - final String expected = "{\"aps\":{\"alert\":{\"action-loc-key\":\"Cancel\",\"body\":\"what\"}}}"; - assertEqualsJson(expected, builder.build()); - assertEqualsJson(expected, builder.build()); - } - - @Test - public void customBodyReverseOrder() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.actionKey("Cancel").alertBody("what"); - - final String expected = "{\"aps\":{\"alert\":{\"action-loc-key\":\"Cancel\",\"body\":\"what\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void alertNoView() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.actionKey(null).alertBody("what"); - - final String expected = "{\"aps\":{\"alert\":{\"action-loc-key\":null,\"body\":\"what\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void alertNoViewSimpler() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.noActionButton().alertBody("what"); - - final String expected = "{\"aps\":{\"alert\":{\"action-loc-key\":null,\"body\":\"what\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void alertWithImageOnly() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.launchImage("/test"); - - final String expected = "{\"aps\":{\"alert\":{\"launch-image\":\"/test\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void alertWithImageAndText() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.launchImage("/test").alertBody("hello"); - - final String expected = "{\"aps\":{\"alert\":{\"launch-image\":\"/test\",\"body\":\"hello\"}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void emptyApsWithFields() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.customField("achme2", new int[] { 5, 8 } ); - - final String expected = "{\"achme2\":[5,8],\"aps\":{}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void abitComplicated() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.customField("achme", "foo"); - builder.sound("chime"); - builder.localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank"); - - final String expected = "{\"achme\":\"foo\",\"aps\":{\"sound\":\"chime\",\"alert\":{\"loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"loc-args\":[\"Jenna\",\"Frank\"]}}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } - - @Test - public void multipleBuildAbitComplicated() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.customField("achme", "foo"); - builder.sound("chime"); - builder.localizedKey("GAME_PLAY_REQUEST_FORMAT") - .localizedArguments("Jenna", "Frank"); - - final String expected = "{\"achme\":\"foo\",\"aps\":{\"sound\":\"chime\",\"alert\":{\"loc-key\":\"GAME_PLAY_REQUEST_FORMAT\",\"loc-args\":[\"Jenna\",\"Frank\"]}}}"; - assertEqualsJson(expected, builder.build()); - assertEqualsJson(expected, builder.build()); - } - - @Test - public void copyReturnsNewInstance() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.sound("chime"); - final PayloadBuilder copy = builder.copy(); - copy.badge(5); - - assertNotSame(builder, copy); - - final String expected = "{\"aps\":{\"sound\":\"chime\"}}"; - assertEqualsJson(expected, builder.build()); - - final String copyExpected = "{\"aps\":{\"sound\":\"chime\",\"badge\":5}}"; - assertEqualsJson(copyExpected, copy.build()); - } - - @Test - public void simpleEnglishLength() { - final PayloadBuilder builder = new PayloadBuilder().alertBody("test"); - final String expected = "{\"aps\":{\"alert\":\"test\"}}"; - assertEqualsJson(expected, builder.build()); - final int actualLength = Utilities.toUTF8Bytes(expected).length; - assertEquals(actualLength, builder.length()); - assertFalse(builder.isTooLong()); - } - - @Test - public void abitComplicatedEnglishLength() { - final byte[] dtBytes = new byte[32]; - new Random().nextBytes(dtBytes); - - final String deviceToken = Utilities.encodeHex(dtBytes); - final PayloadBuilder builder = new PayloadBuilder().alertBody("test"); - - final SimpleApnsNotification fromString = new SimpleApnsNotification(deviceToken, builder.build()); - final SimpleApnsNotification fromBytes = new SimpleApnsNotification(dtBytes, Utilities.toUTF8Bytes(builder.build())); - - final String expected = "{\"aps\":{\"alert\":\"test\"}}"; - final int actualPacketLength = 1 + 2 + dtBytes.length + 2 + /* payload length = */ Utilities.toUTF8Bytes(expected).length; - assertEquals(actualPacketLength, fromString.length()); - assertEquals(actualPacketLength, fromBytes.length()); - assertEquals(expected.length(), fromString.getPayload().length); - assertArrayEquals(fromString.marshall(), fromBytes.marshall()); - assertFalse(builder.isTooLong()); - } - - private String strOfLen(final int l) { - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < l; ++i) { - sb.append('c'); - } - return sb.toString(); - } - - private PayloadBuilder payloadOf(final int l) { - return APNS.newPayload().alertBody(strOfLen(l)); - } - - @Test - public void detectingLongMessages() { - final String basic = "{\"aps\":{\"alert\":\"\"}}"; - final int wrapperOverhead = basic.length(); - final int cutoffForAlert = 2048 - wrapperOverhead; - - final PayloadBuilder wayShort = payloadOf(1); - assertFalse(wayShort.isTooLong()); - assertTrue(wayShort.length() == wrapperOverhead + 1); - - final PayloadBuilder bitShort = payloadOf(cutoffForAlert - 1); - assertFalse(bitShort.isTooLong()); - assertTrue(bitShort.length() == wrapperOverhead + cutoffForAlert - 1); - - final PayloadBuilder border = payloadOf(cutoffForAlert); - assertFalse(border.isTooLong()); - assertTrue(border.length() == wrapperOverhead + cutoffForAlert); - assertTrue(border.length() == 2048); - - final PayloadBuilder abitLong = payloadOf(cutoffForAlert + 1); - assertTrue(abitLong.isTooLong()); - assertTrue(abitLong.length() == wrapperOverhead + cutoffForAlert + 1); - - final PayloadBuilder tooLong = payloadOf(cutoffForAlert + 1000); - assertTrue(tooLong.isTooLong()); - assertTrue(tooLong.length() == wrapperOverhead + cutoffForAlert + 1000); - } - - @Test - public void shrinkLongMessages() { - final String basic = "{\"aps\":{\"alert\":\"\"}}"; - final int wrapperOverhead = basic.length(); - final int cutoffForAlert = 2048 - wrapperOverhead; - final int max_length = 2048; - - final PayloadBuilder wayShort = payloadOf(1); - wayShort.shrinkBody(); // NOOP - assertFalse(wayShort.isTooLong()); - assertTrue(wayShort.length() == wrapperOverhead + 1); - - final PayloadBuilder bitShort = payloadOf(cutoffForAlert - 1); - bitShort.shrinkBody(); // NOOP - assertFalse(bitShort.isTooLong()); - assertTrue(bitShort.length() == wrapperOverhead + cutoffForAlert - 1); - - final PayloadBuilder border = payloadOf(cutoffForAlert); - assertFalse(border.isTooLong()); // NOOP - assertTrue(border.length() == max_length); - - final PayloadBuilder abitLong = payloadOf(cutoffForAlert + 1); - abitLong.shrinkBody(); - assertFalse(abitLong.isTooLong()); - assertTrue(abitLong.length() == max_length); - - final PayloadBuilder tooLong = payloadOf(cutoffForAlert + 1000); - tooLong.shrinkBody(); - assertFalse(tooLong.isTooLong()); - assertTrue(tooLong.length() == max_length); - } - - @Test - public void shrinkLongMessagesWithOtherthigns() { - final String basic = "{\"aps\":{\"alert\":\"\"}}"; - final int wrapperOverhead = basic.length(); - final int cutoffForAlert = 2048 - wrapperOverhead; - final int max_length = 2048; - - final PayloadBuilder wayShort = payloadOf(1).sound("default"); - assertFalse(wayShort.isTooLong()); - assertTrue(wayShort.length() <= max_length); - - final PayloadBuilder bitShort = payloadOf(cutoffForAlert - 1).sound("default"); - bitShort.shrinkBody(); // NOOP - assertFalse(bitShort.isTooLong()); - assertTrue(bitShort.length() <= max_length); - - final PayloadBuilder border = payloadOf(cutoffForAlert).sound("default"); - border.shrinkBody(); - assertFalse(border.isTooLong()); // NOOP - assertTrue(border.length() == max_length); - - final PayloadBuilder abitLong = payloadOf(cutoffForAlert + 1).sound("default"); - abitLong.shrinkBody(); - assertFalse(abitLong.isTooLong()); - assertTrue(abitLong.length() == max_length); - - final PayloadBuilder tooLong = payloadOf(cutoffForAlert + 1000).sound("default"); - tooLong.shrinkBody(); - assertFalse(tooLong.isTooLong()); - assertTrue(tooLong.length() == max_length); - } - - @Test - public void removeAlertIfSooLong() { - final PayloadBuilder tooLong = - APNS.newPayload() - .customField("test", strOfLen(2048)) - .alertBody("what"); - tooLong.shrinkBody(); - final String payload = tooLong.build(); - assertThat(payload, not(containsString("alert"))); - - } - - private void assertEqualsJson(final String expected, final String actual) { - final ObjectMapper mapper = new ObjectMapper(); - try { - @SuppressWarnings("unchecked") - final - Map exNode = mapper.readValue(expected, Map.class), - acNode = mapper.readValue(actual, Map.class); - assertEquals(exNode, acNode); - } catch (final Exception e) { - throw new IllegalStateException(e); - } - } - - @Test - public void supportsMDM() { - final String mdm = APNS.newPayload().mdm("213").toString(); - - final String expected = "{\"mdm\":\"213\"}"; - assertEqualsJson(expected, mdm); - } - - @Test - public void supportsNewsstand() { - final String news = APNS.newPayload().forNewsstand().toString(); - - final String expected = "{\"aps\":{\"content-available\":1}}"; - assertEqualsJson(expected, news); - } - - @Test - public void tooLongWithCustomFields() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("12345678"); - - builder.customField("ache1", "what"); - builder.customField("ache2", 2); - - final String s1 = builder.toString(); - assertThat(s1, containsString("12345678")); - final String s2 = builder.toString(); - assertThat(s2, containsString("12345678")); - - assertEqualsJson(s1, s2); - } - - @Test - public void trimWorksWithLongFields() { - final PayloadBuilder builder = new PayloadBuilder(); - final String toolong = - "1234567890123456789012345678901234567890" + - "1234567890123456789012345678901234567890" + - "1234567890123456789012345678901234567890" + - "1234567890123456789012345678901234567890" + - "1234567890123456789012345678901234567890" + - "1234567890123456789012345678901234567890"; - - builder.alertBody(toolong); - builder.actionKey("OK"); - - builder.shrinkBody(); - - final String s2 = builder.toString(); - assertThat(s2, containsString("12345678")); - } - - - @Test - public void utf8Encoding() { - final String str = "esem�ny"; - - final PayloadBuilder builder = new PayloadBuilder(); - final String s1 = builder.alertBody(str).toString(); - - assertThat(s1, containsString(str)); - } - - @Test - public void utf8EncodingEscaped() { - final String str = "esem\u00E9ny"; - - final PayloadBuilder builder = new PayloadBuilder(); - final String s1 = builder.alertBody(str).toString(); - - assertThat(s1, containsString(str)); - } - - @Test - public void silentPingMessage() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.instantDeliveryOrSilentNotification(); - - final String expected = "{\"aps\":{\"content-available\":1}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - - } - - @Test - public void silentPingMessageWithCustomKey() { - final PayloadBuilder builder = new PayloadBuilder(); - - builder.instantDeliveryOrSilentNotification(); - builder.customField("ache1", "what"); - - final String expected = "{\"aps\":{\"content-available\":1},\"ache1\":\"what\"}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - - } - - @Test - public void instantMessageWithAlert() { - final PayloadBuilder builder = new PayloadBuilder(); - builder.alertBody("test"); - builder.instantDeliveryOrSilentNotification(); - - final String expected = "{\"aps\":{\"alert\":\"test\",\"content-available\":1}}"; - final String actual = builder.toString(); - assertEqualsJson(expected, actual); - } -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsConnectionCacheTest.java b/src/test/java/com/notnoop/apns/integration/ApnsConnectionCacheTest.java deleted file mode 100644 index 79aa042e..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsConnectionCacheTest.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * 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 java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; -import com.notnoop.apns.APNS; -import com.notnoop.apns.ApnsDelegate; -import com.notnoop.apns.StartSendingApnsDelegate; -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.DeliveryError; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.apns.SimpleApnsNotification; -import com.notnoop.apns.utils.ApnsServerStub; -import com.notnoop.apns.utils.FixedCertificates; -import org.junit.*; - -import static com.notnoop.apns.utils.FixedCertificates.*; - -@SuppressWarnings("deprecation") -public class ApnsConnectionCacheTest { - - private ApnsServerStub server; - private static SimpleApnsNotification msg1 = new SimpleApnsNotification("a87d8878d878a79", "{\"aps\":{}}"); - private static SimpleApnsNotification msg2 = new SimpleApnsNotification("a87d8878d878a88", "{\"aps\":{}}"); - private static EnhancedApnsNotification eMsg1 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - private static EnhancedApnsNotification eMsg2 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - private static EnhancedApnsNotification eMsg3 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - - @Before - public void startup() { - } - - @After - public void tearDown() { - server.stop(); - server = null; - } - - - /** - * 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()); - - } - - /** - * Test2 to make sure that after rejected notification - * in-flight notifications are re-transmitted - * - * @throws InterruptedException - */ - @Test(timeout = 5000) - public void handleReTransmissionError1Good1Bad2Good() throws InterruptedException { - server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory()); - final CountDownLatch sync = new CountDownLatch(6); - final AtomicInteger numResent = new AtomicInteger(); - final AtomicInteger numSent = new AtomicInteger(); - final AtomicInteger numStartSend = new AtomicInteger(); - int EXPECTED_RESEND_COUNT = 2; - int EXPECTED_SEND_COUNT = 3; - 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(msg1.length() * 3 + eMsg2.length() * 2); - service.push(msg1); - service.push(eMsg2); - service.push(eMsg1); - service.push(msg2); - - 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()); - - } - - /** - * Test to make sure single rejected notifications are returned - * - * @throws InterruptedException - */ - @Test(timeout = 5000) - public void handleReTransmissionError1Bad() throws InterruptedException { - - server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory()); - final CountDownLatch sync = new CountDownLatch(1); - final AtomicInteger numError = new AtomicInteger(); - final AtomicInteger numStartSend = new AtomicInteger(); - int EXPECTED_ERROR_COUNT = 1; - 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) { - } - - public void messageSendFailed(ApnsNotification message, Throwable e) { - numError.incrementAndGet(); - sync.countDown(); - } - - public void connectionClosed(DeliveryError e, int messageIdentifier) { - } - - public void cacheLengthExceeded(int newCacheLength) { - } - - public void notificationsResent(int resendCount) { - } - }) - .build(); - server.stopAt(eMsg1.length()); - service.push(eMsg1); - - server.sendError(8, eMsg1.getIdentifier()); - server.getWaitForError().release(); - server.getMessages().acquire(); - - sync.await(); - - Assert.assertEquals(EXPECTED_ERROR_COUNT, numError.get()); - Assert.assertEquals(EXPECTED_ERROR_COUNT, numStartSend.get()); - } - - /** - * Test to make sure that after rejected notification - * in-flight notifications are re-transmitted with a queued connection - * - * @throws InterruptedException - */ - @Ignore("Fails because old ApnsServerStub does not accept() on the connection socket for all the time.") - @Test(timeout = 10000) - public void handleTransmissionErrorInQueuedConnection() throws InterruptedException { - server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory()); - final AtomicInteger sync = new AtomicInteger(138); - final AtomicInteger numResent = new AtomicInteger(); - final AtomicInteger numSent = new AtomicInteger(); - server.getWaitForError().acquire(); - server.start(); - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .asQueued() - .withDelegate(new ApnsDelegate() { - public void messageSent(ApnsNotification message, boolean resent) { - if (!resent) { - numSent.incrementAndGet(); - } - sync.getAndDecrement(); - } - - public void messageSendFailed(ApnsNotification message, Throwable e) { - numSent.decrementAndGet(); - sync.incrementAndGet(); - } - - public void connectionClosed(DeliveryError e, int messageIdentifier) { - } - - public void cacheLengthExceeded(int newCacheLength) { - } - - public void notificationsResent(int resendCount) { - numResent.set(resendCount); - sync.getAndAdd(resendCount); - } - }) - .build(); - server.stopAt(eMsg3.length() * 50 + msg1.length() * 3 - + eMsg2.length() * 2 + eMsg1.length() * 85); - for (int i = 0; i < 50; ++i) { - service.push(eMsg3); - } - service.push(msg1); - service.push(eMsg2); - service.push(eMsg1); - service.push(msg2); - for (int i = 0; i < 85; ++i) { - service.push(eMsg1); - } - - server.sendError(8, eMsg2.getIdentifier()); - server.getWaitForError().release(); - server.getMessages().acquire(); - - while(sync.get() != 0) { - Thread.yield(); - } - } - - /** - * Test to make sure that if the cache length is violated we get - * a notification - * - * @throws InterruptedException - */ - @Test(timeout = 5000) - public void cacheLengthNotification() throws InterruptedException { - - server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory()); - final CountDownLatch sync = new CountDownLatch(1); - int ORIGINAL_CACHE_LENGTH = 100; - final AtomicInteger modifiedCacheLength = new AtomicInteger(); - server.getWaitForError().acquire(); - server.start(); - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withDelegate(new ApnsDelegate() { - public void messageSent(ApnsNotification message, boolean resent) { - - } - - public void messageSendFailed(ApnsNotification message, Throwable e) { - } - - public void connectionClosed(DeliveryError e, int messageIdentifier) { - } - - public void cacheLengthExceeded(int newCacheLength) { - modifiedCacheLength.set(newCacheLength); - sync.countDown(); - } - - public void notificationsResent(int 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 < 101; ++i) { - service.push(eMsg3); - } - - server.sendError(8, eMsg2.getIdentifier()); - - server.getWaitForError().release(); - server.getMessages().acquire(); - - sync.await(); - - - Assert.assertTrue(ORIGINAL_CACHE_LENGTH < modifiedCacheLength.get()); - } - -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsConnectionResendTest.java b/src/test/java/com/notnoop/apns/integration/ApnsConnectionResendTest.java deleted file mode 100644 index f826146f..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsConnectionResendTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 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\":{}}"); - } - -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsConnectionTest.java b/src/test/java/com/notnoop/apns/integration/ApnsConnectionTest.java deleted file mode 100644 index d366e91c..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsConnectionTest.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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.ApnsService; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.apns.SimpleApnsNotification; -import com.notnoop.apns.utils.ApnsServerStub; -import com.notnoop.apns.utils.junit.DumpThreadsOnErrorRule; -import com.notnoop.apns.utils.junit.Repeat; -import com.notnoop.apns.utils.junit.RepeatRule; -import com.notnoop.exceptions.NetworkIOException; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import static com.notnoop.apns.utils.FixedCertificates.LOCALHOST; -import static com.notnoop.apns.utils.FixedCertificates.clientContext; -import static com.notnoop.apns.utils.FixedCertificates.clientMultiKeyContext; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - - -@SuppressWarnings("ALL") -public class ApnsConnectionTest { - - @Rule - public TestName testName = new TestName(); - - @Rule - public RepeatRule rr = new RepeatRule(); - - @Rule - public DumpThreadsOnErrorRule dumpRule = new DumpThreadsOnErrorRule(); - - ApnsServerStub server; - static SimpleApnsNotification msg1 = new SimpleApnsNotification("a87d8878d878a79", "{\"aps\":{}}"); - static SimpleApnsNotification msg2 = new SimpleApnsNotification("a87d8878d878a88", "{\"aps\":{}}"); - static EnhancedApnsNotification eMsg1 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - static EnhancedApnsNotification eMsg2 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - static EnhancedApnsNotification eMsg3 = new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), - 1, "a87d8878d878a88", "{\"aps\":{}}"); - private int gatewayPort; - - @Before - public void startup() { - System.out.println("****** "+testName.getMethodName()); - server = ApnsServerStub.prepareAndStartServer(); - gatewayPort = server.getEffectiveGatewayPort(); - } - - @After - public void tearDown() { - server.stop(); - server = null; - } - - @Repeat(count = 50) - @Test(timeout = 2000) - public void sendOneSimple() throws InterruptedException { - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, gatewayPort) - .build(); - server.stopAt(msg1.length()); - service.push(msg1); - server.getMessages().acquire(); - - assertArrayEquals(msg1.marshall(), server.getReceived().toByteArray()); - } - - @Repeat(count = 50) - @Test(timeout = 2000) - public void sendOneQueued() throws InterruptedException { - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, gatewayPort) - .asQueued() - .build(); - server.stopAt(msg1.length()); - service.push(msg1); - server.getMessages().acquire(); - - assertArrayEquals(msg1.marshall(), server.getReceived().toByteArray()); - } - - - @Test - public void sendOneSimpleWithoutTimeout() throws InterruptedException { - server.getToWaitBeforeSend().set(2000); - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, gatewayPort) - .withReadTimeout(5000) - .build(); - server.stopAt(msg1.length()); - service.push(msg1); - server.getMessages().acquire(); - - assertArrayEquals(msg1.marshall(), server.getReceived().toByteArray()); - } - - /** - * Unlike in the feedback case, push messages won't expose the socket timeout, - * as the read is done in a separate monitoring thread. - * - * Therefore, normal behavior is expected in this test. - * - * @throws InterruptedException - */ - @Test - public void sendOneSimpleWithTimeout() throws InterruptedException { - server.getToWaitBeforeSend().set(5000); - ApnsService service = - APNS.newService().withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, gatewayPort) - .withReadTimeout(1000) - .build(); - server.stopAt(msg1.length()); - service.push(msg1); - server.getMessages().acquire(); - - assertArrayEquals(msg1.marshall(), server.getReceived().toByteArray()); - } - - @Test(timeout = 2000) - public void sendOneSimpleMultiKey() throws InterruptedException { - ApnsService service = - APNS.newService().withSSLContext(clientMultiKeyContext("notnoop-client")) - .withGatewayDestination(LOCALHOST, gatewayPort) - .build(); - server.stopAt(msg1.length()); - service.push(msg1); - server.getMessages().acquire(); - - assertArrayEquals(msg1.marshall(), server.getReceived().toByteArray()); - } - - @Test(timeout = 2000) - public void sendOneSimpleClientCertFail() throws InterruptedException { - ApnsService service = - APNS.newService().withSSLContext(clientMultiKeyContext("notused")) - .withGatewayDestination(LOCALHOST, gatewayPort) - .build(); - server.stopAt(msg1.length()); - try { - service.push(msg1); - fail(); - } catch (NetworkIOException e) { - assertTrue("Expected bad_certifcate exception", e.getMessage().contains("bad_certificate")); - } - } - -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsDelegateRecorder.java b/src/test/java/com/notnoop/apns/integration/ApnsDelegateRecorder.java deleted file mode 100644 index 1d3cdfba..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsDelegateRecorder.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.ApnsDelegate; -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.DeliveryError; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.assertEquals; - -public class ApnsDelegateRecorder implements ApnsDelegate { - - private List sent = new ArrayList(); - private List failed = new ArrayList(); - - @Override - public void messageSent(ApnsNotification message, boolean resent) { - sent.add(new MessageSentRecord(message, resent)); - } - - @Override - public void messageSendFailed(ApnsNotification message, Throwable e) { - failed.add(new MessageSentFailedRecord(message, e)); - } - - @Override - public void connectionClosed(DeliveryError e, int messageIdentifier) { - // not stubbed - } - - @Override - public void cacheLengthExceeded(int newCacheLength) { - // not stubbed - } - - @Override - public void notificationsResent(int resendCount) { - // not stubbed - } - - public List getSent() { - return Collections.unmodifiableList(sent); - } - - public List getFailed() { - return Collections.unmodifiableList(failed); - } - - public static class MessageSentRecord { - private final ApnsNotification notification; - private final boolean resent; - - public MessageSentRecord(ApnsNotification notification, boolean resent) { - this.notification = notification; - this.resent = resent; - } - - public ApnsNotification getNotification() { - return notification; - } - - public boolean isResent() { - return resent; - } - } - - public static class MessageSentFailedRecord { - private final ApnsNotification notification; - private final Throwable ex; - - public MessageSentFailedRecord(ApnsNotification notification, Throwable ex) { - this.notification = notification; - this.ex = ex; - } - - public ApnsNotification getNotification() { - return notification; - } - - @SuppressWarnings("unchecked") - public T getException() { - return (T) ex; - } - - public void assertRecord(ApnsNotification notification, Throwable ex) { - assertEquals(notification, getNotification()); - assertEquals(ex.getClass(), this.ex.getClass()); - } - } - -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorLongRunningTest.java b/src/test/java/com/notnoop/apns/integration/ApnsSimulatorLongRunningTest.java deleted file mode 100644 index ec7ceaf1..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorLongRunningTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@SuppressWarnings("deprecation") -public class ApnsSimulatorLongRunningTest extends ApnsSimulatorTestBase { - - final Logger logger = LoggerFactory.getLogger(ApnsSimulatorLongRunningTest.class); - - @Test - public void multipleTokensBad_issue145() throws InterruptedException { - final int rounds = 15; - for (int i = 0; i < rounds; ++i) { - logger.debug("*********** "+i); - send(8, 0); - assertNumberReceived(2); - } - - } - -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTest.java b/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTest.java deleted file mode 100644 index 194f8127..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.DeliveryError; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.Timeout; -import org.mockito.Matchers; -import uk.org.lidalia.slf4jext.Level; -import uk.org.lidalia.slf4jtest.LoggingEvent; -import uk.org.lidalia.slf4jtest.TestLoggerFactory; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.CoreMatchers.hasItem; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -@SuppressWarnings("deprecation") -public class ApnsSimulatorTest extends ApnsSimulatorTestBase { - - // final Logger logger = LoggerFactory.getLogger(ApnsSimulatorTest.class); - - //@Rule - //public DumpThreadsOnErrorRule dump = new DumpThreadsOnErrorRule(); - - @Rule - public Timeout timeout = new Timeout(5000); - - - @Test - public void sendOne() throws InterruptedException { - send(0); - server.getQueue().poll(5, TimeUnit.SECONDS); - assertIdle(); - assertDelegateSentCount(1); - } - - @Test - public void sendThree() throws InterruptedException { - sendCount(3, 0); - assertNumberReceived(3); - assertDelegateSentCount(3); - } - - @Test - public void sendThousand() throws InterruptedException { - TestLoggerFactory.getInstance().setPrintLevel(Level.INFO); - sendCount(1000, 0); - assertNumberReceived(1000); - assertDelegateSentCount(1000); - } - - - @Test - public void sendDelay() throws InterruptedException { - send(-3); - server.getQueue().poll(5, TimeUnit.SECONDS); - assertIdle(); - assertDelegateSentCount(1); - } - - @Test - public void testConnectionClose() throws InterruptedException { - send(8); - assertNumberReceived(1); - assertDelegateSentCount(1); - verify(delegate, times(1)).connectionClosed(Matchers.any(DeliveryError.class), Matchers.anyInt()); - } - - @Test - public void handleRetransmissionWithSeveralOutstandingMessages() throws InterruptedException { - send(-1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1); - assertNumberReceived(13); - assertDelegateSentCount(13 + 7); // Initially sending all 13 notifications, then resend the last 7 ones - verify(delegate, times(1)).connectionClosed(Matchers.any(DeliveryError.class), Matchers.anyInt()); - } - - - @Test - public void testClientDoesNotResendMessagesWhenServerClosesSocketWithoutErrorPacket() throws InterruptedException { - send(-1, -1, -1, -1, -1, -100, -1, -1, -1, -1, -1, -1, -1); - assertNumberReceived(6); - } - - @Ignore - @Test - public void RaceCondition() { - // TODO implement test & decide if fix is necessary afterwards. - Assert.fail("Assumption: monitoring thread crashes in read() when the sender thread closes the connection first."); - // Thus the last feedback message gets lost, thus we lose messages. - } - - @Test - public void abortNoWait() throws InterruptedException { - send(8, 0); - assertNumberReceived(2); - } - - @Test - public void doNotSpamLogWhenConnectionClosesBetweenFeedbackPackets() throws InterruptedException { - // Don't spam a lot of information into the log when the socket closes at a "legal" location. (Just before - // or after a feedback packet) - send(-1, 8, -1); - assertNumberReceived(3); - final List allLoggingEvents = TestLoggerFactory.getAllLoggingEvents(); - assertThat(allLoggingEvents, not(hasItem(eventContains("Exception while waiting for error code")))); - } - - @Test - public void firstTokenBad_issue145() throws InterruptedException { - // Test for Issue #145 - send(8, 0); - assertNumberReceived(2); - } -} diff --git a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTestBase.java b/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTestBase.java deleted file mode 100644 index 5461924a..00000000 --- a/src/test/java/com/notnoop/apns/integration/ApnsSimulatorTestBase.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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.*; -import com.notnoop.apns.internal.Utilities; -import com.notnoop.apns.utils.FixedCertificates; -import com.notnoop.apns.utils.Simulator.ApnsServerSimulator; -import com.notnoop.apns.utils.Simulator.FailingApnsServerSimulator; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.TestName; -import org.mockito.Matchers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import uk.org.lidalia.slf4jext.Level; -import uk.org.lidalia.slf4jtest.LoggingEvent; -import uk.org.lidalia.slf4jtest.TestLoggerFactory; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -import static com.notnoop.apns.utils.FixedCertificates.LOCALHOST; -import static com.notnoop.apns.utils.FixedCertificates.clientContext; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class ApnsSimulatorTestBase { - final Logger logger = LoggerFactory.getLogger(ApnsSimulatorTestBase.class); - - private static final String payload = "{\"aps\":{}}"; - @Rule - public TestName name = new TestName(); - protected FailingApnsServerSimulator server; - protected ApnsDelegate delegate; - private ApnsService service; - private Random random; - - @Before - public void startup() { - // http://projects.lidalia.org.uk/slf4j-test/ - TestLoggerFactory.getInstance().setPrintLevel(Level.DEBUG); - TestLoggerFactory.clearAll(); - - logger.info("\n\n\n\n\n"); - logger.info("********* Test: {}", name.getMethodName()); - - server = new FailingApnsServerSimulator(FixedCertificates.serverContext().getServerSocketFactory()); - server.start(); - delegate = ApnsDelegate.EMPTY; - delegate = mock(ApnsDelegate.class); - service = APNS.newService() - .withSSLContext(clientContext()) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .withDelegate(delegate).build(); - random = new Random(); - } - - @After - public void tearDown() { - server.stop(); - server = null; - } - - protected Matcher eventContains(final String subString) { - return new BaseMatcher() { - @Override - public boolean matches(final Object item) { - final String message = ((LoggingEvent) item).getMessage(); - return message.contains(subString); - } - - @Override - public void describeTo(final Description description) { - description.appendText("substring [" + subString + "]"); - } - }; - } - - protected void send(final int... codes) { - for (int code : codes) { - send(code); - } - } - - protected void send(final int code) { - - ApnsNotification notification = makeNotification(code); - service.push(notification); - - } - - /** - * Create an APNS notification that creates specified "error" behaviour in the - * {@link com.notnoop.apns.utils.Simulator.FailingApnsServerSimulator} - * - * @param code A code specifying the FailingApnsServer's behaviour. - *
    - *
  • -100: Drop connection
  • - *
  • below zero: wait (-code) number times a tenth of a second
  • - *
  • above zero: send code as APNS error message then drop connection
  • - *
- * @return an APNS notification - */ - private EnhancedApnsNotification makeNotification(final int code) { - byte[] deviceToken = new byte[32]; - random.nextBytes(deviceToken); - if (code == 0) { - deviceToken[0] = 42; - } else { - deviceToken[0] = (byte) 0xff; - deviceToken[1] = (byte) 0xff; - - if (code < -100) { - // Drop connection - deviceToken[2] = (byte) 2; - } else if (code < 0) { - // Sleep - deviceToken[2] = (byte) 1; - deviceToken[3] = (byte) -code; - } else { - // Send APNS error response then drop connection - assert code > 0; - deviceToken[2] = (byte) 0; - deviceToken[3] = (byte) code; - - } - - } - return new EnhancedApnsNotification(EnhancedApnsNotification.INCREMENT_ID(), 1, deviceToken, Utilities.toUTF8Bytes(payload)); - } - - protected void sendCount(final int count, final int code) { - for (int i = 0; i < count; ++i) { - send(code); - } - } - - protected void assertNumberReceived(final int count) throws InterruptedException { - logger.debug("assertNumberReceived {}", count); - int i = 0; - try { - for (; i < count; ++i) { - ApnsServerSimulator.Notification notification = server.getQueue().poll(5, TimeUnit.SECONDS); - logger.debug("Polled notification {}", notification); - if (notification == null) - throw new RuntimeException("poll timed out"); - } - } catch (RuntimeException re) { - logger.error("Exception in assertNumberReceived, took {}", i); - throw re; - } - logger.debug("assertNumberReceived - successfully took {}", count); - assertIdle(); - } - - protected void assertIdle() throws InterruptedException { - logger.info("assertIdle"); - Thread.sleep(1000); - assertThat(server.getQueue().size(), equalTo(0)); - } - - protected void assertDelegateSentCount(final int count) { - logger.info("assertDelegateSentCount {}", count); - verify(delegate, times(count)).messageSent(Matchers.any(ApnsNotification.class), Matchers.anyBoolean()); - } -} diff --git a/src/test/java/com/notnoop/apns/integration/FeedbackTest.java b/src/test/java/com/notnoop/apns/integration/FeedbackTest.java deleted file mode 100644 index 706bee06..00000000 --- a/src/test/java/com/notnoop/apns/integration/FeedbackTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 java.io.IOException; -import java.net.SocketTimeoutException; -import javax.net.ssl.SSLContext; -import com.notnoop.apns.APNS; -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.utils.ApnsServerStub; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import static com.notnoop.apns.internal.ApnsFeedbackParsingUtils.*; -import static com.notnoop.apns.utils.FixedCertificates.*; -import static org.junit.Assert.*; - -public class FeedbackTest { - - ApnsServerStub server; - SSLContext clientContext = clientContext(); - - - @Before - public void startup() { - server = ApnsServerStub.prepareAndStartServer(); - } - - @After - public void tearDown() { - server.stop(); - server = null; - } - - @Test - public void simpleFeedback() throws IOException { - server.getToSend().write(simple); - - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .build(); - - checkParsedSimple(service.getInactiveDevices()); - } - - @Test - public void simpleFeedbackWithoutTimeout() throws IOException { - server.getToSend().write(simple); - server.getToWaitBeforeSend().set(2000); - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .withReadTimeout(3000) - .build(); - - checkParsedSimple(service.getInactiveDevices()); - } - - @Test() - public void simpleFeedbackWithTimeout() throws IOException { - server.getToSend().write(simple); - server.getToWaitBeforeSend().set(5000); - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .withReadTimeout(1000) - .build(); - try { - service.getInactiveDevices(); - fail("RuntimeException expected"); - } - catch(RuntimeException e) { - assertEquals("Socket timeout exception expected", - SocketTimeoutException.class, e.getCause().getClass() ); - } - } - - @Test - public void threeFeedback() throws IOException { - server.getToSend().write(three); - - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .build(); - - checkParsedThree(service.getInactiveDevices()); - } - - @Test - public void simpleQueuedFeedback() throws IOException { - server.getToSend().write(simple); - - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .asQueued() - .build(); - - checkParsedSimple(service.getInactiveDevices()); - } - - @Test - public void threeQueuedFeedback() throws IOException { - server.getToSend().write(three); - - ApnsService service = - APNS.newService().withSSLContext(clientContext) - .withGatewayDestination(LOCALHOST, server.getEffectiveGatewayPort()) - .withFeedbackDestination(LOCALHOST, server.getEffectiveFeedbackPort()) - .asQueued() - .build(); - - checkParsedThree(service.getInactiveDevices()); - } - -} diff --git a/src/test/java/com/notnoop/apns/internal/ApnsConnectionTest.java b/src/test/java/com/notnoop/apns/internal/ApnsConnectionTest.java deleted file mode 100644 index e9964589..00000000 --- a/src/test/java/com/notnoop/apns/internal/ApnsConnectionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.internal; - -import java.io.ByteArrayOutputStream; -import javax.net.SocketFactory; -import com.notnoop.apns.SimpleApnsNotification; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import static com.notnoop.apns.internal.MockingUtils.*; - - -@SuppressWarnings("deprecation") -public class ApnsConnectionTest { - private SimpleApnsNotification msg = new SimpleApnsNotification ("a87d8878d878a79", "{\"aps\":{}}"); - - @Test - public void simpleSocket() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SocketFactory factory = mockSocketFactory(baos, null); - packetSentRegardless(factory, baos); - } - - @Test - @Ignore - public void closedSocket() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SocketFactory factory = mockClosedThenOpenSocket(baos, null, true, 1); - packetSentRegardless(factory, baos); - } - - @Test - public void errorOnce() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SocketFactory factory = mockClosedThenOpenSocket(baos, null, false, 1); - packetSentRegardless(factory, baos); - } - - @Test - public void errorTwice() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SocketFactory factory = mockClosedThenOpenSocket(baos, null, false, 2); - packetSentRegardless(factory, baos); - } - - /** - * Connection fails after three retries - */ - @Test(expected = Exception.class) - public void errorThrice() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SocketFactory factory = mockClosedThenOpenSocket(baos, null, false, 3); - packetSentRegardless(factory, baos); - } - - private void packetSentRegardless(SocketFactory sf, ByteArrayOutputStream baos) { - ApnsConnectionImpl connection = new ApnsConnectionImpl(sf, "localhost", 80); - connection.DELAY_IN_MS = 0; - connection.sendMessage(msg); - Assert.assertArrayEquals(msg.marshall(), baos.toByteArray()); - connection.close(); - } -} diff --git a/src/test/java/com/notnoop/apns/internal/ApnsFeedbackConnectionTest.java b/src/test/java/com/notnoop/apns/internal/ApnsFeedbackConnectionTest.java deleted file mode 100644 index 537ddd22..00000000 --- a/src/test/java/com/notnoop/apns/internal/ApnsFeedbackConnectionTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.internal; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import javax.net.SocketFactory; - -import org.junit.Test; - -import static com.notnoop.apns.internal.ApnsFeedbackParsingUtils.*; -import static com.notnoop.apns.internal.MockingUtils.mockClosedThenOpenSocket; - -public class ApnsFeedbackConnectionTest { - - InputStream simpleStream = new ByteArrayInputStream(simple); - InputStream threeStream = new ByteArrayInputStream(three); - - /** Simple Parsing **/ - @Test - public void rowParseOneDevice() { - checkRawSimple(Utilities.parseFeedbackStreamRaw(simpleStream)); - } - - @Test - public void threeParseTwoDevices() { - checkRawThree(Utilities.parseFeedbackStreamRaw(threeStream)); - } - - @Test - public void parsedSimple() { - checkParsedSimple(Utilities.parseFeedbackStream(simpleStream)); - } - - @Test - public void parsedThree() { - checkParsedThree(Utilities.parseFeedbackStream(threeStream)); - } - - /** With Connection **/ - @Test - public void connectionParsedOne() { - SocketFactory sf = MockingUtils.mockSocketFactory(null, simpleStream); - ApnsFeedbackConnection connection = new ApnsFeedbackConnection(sf, "localhost", 80); - checkParsedSimple(connection.getInactiveDevices()); - } - - @Test - public void connectionParsedThree() { - SocketFactory sf = MockingUtils.mockSocketFactory(null, threeStream); - ApnsFeedbackConnection connection = new ApnsFeedbackConnection(sf, "localhost", 80); - checkParsedThree(connection.getInactiveDevices()); - } - - /** Check error recover **/ - @Test - public void feedbackWithClosedSocket() { - SocketFactory sf = mockClosedThenOpenSocket(null, simpleStream, true, 1); - ApnsFeedbackConnection connection = new ApnsFeedbackConnection(sf, "localhost", 80); - connection.DELAY_IN_MS = 0; - checkParsedSimple(connection.getInactiveDevices()); - } - - @Test - public void feedbackWithErrorOnce() { - SocketFactory sf = mockClosedThenOpenSocket(null, simpleStream, true, 2); - ApnsFeedbackConnection connection = new ApnsFeedbackConnection(sf, "localhost", 80); - connection.DELAY_IN_MS = 0; - checkParsedSimple(connection.getInactiveDevices()); - } - - /** - * Connection fails after three retries - */ - @Test(expected = Exception.class) - public void feedbackWithErrorTwice() { - SocketFactory sf = mockClosedThenOpenSocket(null, simpleStream, true, 3); - ApnsFeedbackConnection connection = new ApnsFeedbackConnection(sf, "localhost", 80); - connection.DELAY_IN_MS = 0; - checkParsedSimple(connection.getInactiveDevices()); - } - -} diff --git a/src/test/java/com/notnoop/apns/internal/ApnsFeedbackParsingUtils.java b/src/test/java/com/notnoop/apns/internal/ApnsFeedbackParsingUtils.java deleted file mode 100644 index 40603b6f..00000000 --- a/src/test/java/com/notnoop/apns/internal/ApnsFeedbackParsingUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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.internal; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.Map; -import java.util.Random; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - -public class ApnsFeedbackParsingUtils { - static byte[] pack(byte[]... args) { - int total = 0; - for (byte[] arg : args) - total += arg.length; - - byte[] result = new byte[total]; - - int index = 0; - for (byte[] arg : args) { - System.arraycopy(arg, 0, result, index, arg.length); - index += arg.length; - } - return result; - } - - static byte[] simpleDevice = new byte[32]; - static byte[] firstDevice = new byte[32]; - static byte[] secondDevice = new byte[32]; - static byte[] thirdDevice = new byte[32]; - static { - Random random = new Random(); - do { - random.nextBytes(simpleDevice); - random.nextBytes(firstDevice); - random.nextBytes(secondDevice); - random.nextBytes(thirdDevice); - } while (Arrays.equals(firstDevice, secondDevice) - || (Arrays.equals(secondDevice, thirdDevice)) - || (Arrays.equals(firstDevice, thirdDevice))); - } - - static int simpleDate; - public static byte[] simple = pack( - /* time_t */ new byte[] {0, 0, 0, 0}, - /* length */ new byte[] { 0, 32 }, - /* device token */ simpleDevice - ); - - static int firstDate = 10; - static int secondDate = 1 << 8; - static int thirdDate = secondDate; - public static byte[] three = pack( - /* first message */ - /* time_t */ new byte[] {0, 0, 0, 10}, - /* length */ new byte[] { 0, 32 }, - /* device token */ firstDevice, - - /* second device */ - /* time_t */ new byte[] {0, 0, 1, 0}, - /* length */ new byte[] { 0, 32 }, - /* device token */ secondDevice, - - /* Duplicate time */ - /* time_t */ new byte[] {0, 0, 1, 0}, - /* length */ new byte[] { 0, 32 }, - /* device token */ thirdDevice - ); - - protected static void checkRawSimple(Map simpleParsed) { - assertEquals(1, simpleParsed.size()); - assertThat(simpleParsed.keySet(), hasItem(simpleDevice)); - - for (Map.Entry e : simpleParsed.entrySet()) { - byte[] device = e.getKey(); - Integer date = e.getValue(); - if (Arrays.equals(simpleDevice, device)) { - assertEquals(simpleDate, (int)date); - } else { - fail("Unexpected value in collection"); - } - } - } - - protected static void checkRawThree(Map threeParsed) { - assertEquals(3, threeParsed.size()); - Collection devices = threeParsed.keySet(); - assertThat(devices, hasItems(firstDevice, secondDevice, thirdDevice)); - - for (Map.Entry e : threeParsed.entrySet()) { - byte[] device = e.getKey(); - Integer date = e.getValue(); - if (Arrays.equals(firstDevice, device)) { - assertEquals(firstDate, (int)date); - } else if (Arrays.equals(secondDevice, device)) { - assertEquals(secondDate, (int)date); - } else if (Arrays.equals(thirdDevice, device)) { - assertEquals(thirdDate, (int)date); - } else { - fail("Unexpected value in collection"); - } - } - - } - - public static void checkParsedSimple(Map simpleParsed) { - Date sd = new Date(simpleDate * 1000L); - String deviceToken = Utilities.encodeHex(simpleDevice); - - assertEquals(1, simpleParsed.size()); - assertThat(simpleParsed.keySet(), hasItem(deviceToken)); - assertEquals(sd, simpleParsed.get(deviceToken)); - } - - public static void checkParsedThree(Map threeParsed) { - Date d1 = new Date(firstDate * 1000L); - String dt1 = Utilities.encodeHex(firstDevice); - - Date d2 = new Date(secondDate * 1000L); - String dt2 = Utilities.encodeHex(secondDevice); - - Date d3 = new Date(thirdDate * 1000L); - String dt3 = Utilities.encodeHex(thirdDevice); - - assertEquals(3, threeParsed.size()); - assertThat(threeParsed.keySet(), hasItems(dt1, dt2, dt3)); - assertEquals(d1, threeParsed.get(dt1)); - assertEquals(d2, threeParsed.get(dt2)); - assertEquals(d3, threeParsed.get(dt3)); - } - -} diff --git a/src/test/java/com/notnoop/apns/internal/ApnsPooledConnectionTest.java b/src/test/java/com/notnoop/apns/internal/ApnsPooledConnectionTest.java deleted file mode 100644 index 7071a85b..00000000 --- a/src/test/java/com/notnoop/apns/internal/ApnsPooledConnectionTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.internal; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.exceptions.NetworkIOException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static org.mockito.Mockito.*; - -public class ApnsPooledConnectionTest { - - private ApnsConnection errorPrototype; - private ApnsConnection prototype; - - private ExecutorService executorService; - - @Before - public void setup() { - errorPrototype = mock(ApnsConnection.class); - when(errorPrototype.copy()).thenReturn(errorPrototype); - doThrow(NetworkIOException.class).when(errorPrototype).sendMessage(any(ApnsNotification.class)); - - prototype = mock(ApnsConnection.class); - when(prototype.copy()).thenReturn(prototype); - } - - @After - public void cleanup() { - if (executorService != null) { - executorService.shutdownNow(); - } - } - - @Test(expected = NetworkIOException.class) - public void testSendMessage() throws Exception { - ApnsPooledConnection conn = new ApnsPooledConnection(errorPrototype, 1, getSingleThreadExecutor()); - conn.sendMessage(mock(ApnsNotification.class)); - } - - @Test - public void testCopyCalls() throws Exception { - ApnsPooledConnection conn = new ApnsPooledConnection(prototype, 1, getSingleThreadExecutor()); - for (int i = 0; i < 10; i++) { - conn.sendMessage(mock(ApnsNotification.class)); - } - verify(prototype, times(1)).copy(); - } - - @Test - public void testCloseCalls() throws Exception { - ApnsPooledConnection conn = new ApnsPooledConnection(prototype, 1, getSingleThreadExecutor()); - conn.sendMessage(mock(ApnsNotification.class)); - conn.close(); - // should be closed twice because of the thread local copy - verify(prototype, times(2)).close(); - } - - private ExecutorService getSingleThreadExecutor() { - executorService = Executors.newSingleThreadExecutor(); - return executorService; - } -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/internal/ApnsServiceImplTest.java b/src/test/java/com/notnoop/apns/internal/ApnsServiceImplTest.java deleted file mode 100644 index e4b60ebf..00000000 --- a/src/test/java/com/notnoop/apns/internal/ApnsServiceImplTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.internal; - -import org.junit.Test; -import static org.mockito.Mockito.*; - -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.EnhancedApnsNotification; - -public class ApnsServiceImplTest { - - EnhancedApnsNotification notification = new EnhancedApnsNotification(1, - EnhancedApnsNotification.MAXIMUM_EXPIRY, "2342", "{}"); - - @Test - public void pushEventually() { - ApnsConnection connection = mock(ApnsConnection.class); - ApnsService service = newService(connection, null); - - service.push(notification); - - verify(connection, times(1)).sendMessage(notification); - } - - @Test - public void pushEventuallySample() { - ApnsConnection connection = mock(ApnsConnection.class); - ApnsService service = newService(connection, null); - - service.push("2342", "{}"); - - verify(connection, times(1)).sendMessage(notification); - } - - protected ApnsService newService(ApnsConnection connection, ApnsFeedbackConnection feedback) { - return new ApnsServiceImpl(connection, null); - } -} diff --git a/src/test/java/com/notnoop/apns/internal/BatchApnsServiceTest.java b/src/test/java/com/notnoop/apns/internal/BatchApnsServiceTest.java deleted file mode 100644 index 70b62030..00000000 --- a/src/test/java/com/notnoop/apns/internal/BatchApnsServiceTest.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.internal; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.util.concurrent.Executors; - -import org.junit.Before; -import org.junit.Test; - -import com.notnoop.apns.ApnsNotification; - -public class BatchApnsServiceTest { - - private ApnsConnection prototype; - private BatchApnsService service; - - private int delayTimeInSec = 2; - private int delayTimeInSec_millis = delayTimeInSec * 1000; /* 2000 */ - private int delayTimeInSec1_2_millis = delayTimeInSec * 1000 / 2; /* 1000 */ - private int delayTimeInSec1_4_millis = delayTimeInSec * 1000 / 4; /* 500 */ - private int maxDelayTimeInSec = 2 * delayTimeInSec; - - @Before - public void setup() { - prototype = mock(ApnsConnection.class); - when(prototype.copy()).thenReturn(prototype); - - service = new BatchApnsService(prototype, null, delayTimeInSec, maxDelayTimeInSec, Executors.defaultThreadFactory()); - } - - @Test - public void simpleBatchWait_one() throws IOException, InterruptedException { - // send message - ApnsNotification message = service.push("1234", "{}"); - - // make sure no message was send yet - verify(prototype, times(0)).copy(); - verify(prototype, times(0)).sendMessage(message); - verify(prototype, times(0)).close(); - - Thread.sleep(delayTimeInSec_millis + /* for sure */250); - - // verify batch sends and close the connection - verify(prototype, times(1)).copy(); - verify(prototype, times(1)).sendMessage(message); - verify(prototype, times(1)).close(); - } - - @Test - public void simpleBatchWait_multiple() throws IOException, InterruptedException { - // send message - ApnsNotification message1 = service.push("1234", "{}"); - Thread.sleep(delayTimeInSec1_2_millis); - ApnsNotification message2 = service.push("4321", "{}"); - - // make sure no message was send yet - verify(prototype, times(0)).copy(); - verify(prototype, times(0)).sendMessage(message1); - verify(prototype, times(0)).sendMessage(message2); - verify(prototype, times(0)).close(); - - Thread.sleep(delayTimeInSec1_4_millis * 3); - - // still no send - verify(prototype, times(0)).copy(); - verify(prototype, times(0)).sendMessage(message1); - verify(prototype, times(0)).sendMessage(message2); - verify(prototype, times(0)).close(); - - Thread.sleep(delayTimeInSec1_4_millis + /* for sure */250); - - // verify batch sends and close the connection - verify(prototype, times(1)).copy(); - verify(prototype, times(1)).sendMessage(message1); - verify(prototype, times(1)).sendMessage(message2); - verify(prototype, times(1)).close(); - } - - @Test - public void simpleBatchWait_maxDelay() throws IOException, InterruptedException { - // send message - ApnsNotification message1 = service.push("1234", "{}"); - Thread.sleep(delayTimeInSec1_4_millis * 3); - ApnsNotification message2 = service.push("4321", "{}"); - Thread.sleep(delayTimeInSec1_4_millis * 3); - ApnsNotification message3 = service.push("4321", "{}"); - Thread.sleep(delayTimeInSec1_4_millis * 3); - ApnsNotification message4 = service.push("4321", "{}"); - - // make sure no message was send yet - verify(prototype, times(0)).copy(); - verify(prototype, times(0)).sendMessage(message1); - verify(prototype, times(0)).sendMessage(message2); - verify(prototype, times(0)).sendMessage(message3); - verify(prototype, times(0)).sendMessage(message4); - verify(prototype, times(0)).close(); - - Thread.sleep(delayTimeInSec1_4_millis + /* for sure */250); - - // verify batch sends and close the connection - verify(prototype, times(1)).copy(); - verify(prototype, times(1)).sendMessage(message1); - verify(prototype, times(1)).sendMessage(message2); - verify(prototype, times(1)).sendMessage(message3); - verify(prototype, times(1)).sendMessage(message4); - verify(prototype, times(1)).close(); - } - -} diff --git a/src/test/java/com/notnoop/apns/internal/MockingUtils.java b/src/test/java/com/notnoop/apns/internal/MockingUtils.java deleted file mode 100644 index 0b1b3e81..00000000 --- a/src/test/java/com/notnoop/apns/internal/MockingUtils.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.internal; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import javax.net.SocketFactory; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.mockito.stubbing.OngoingStubbing; - -import static org.mockito.Mockito.*; - -public class MockingUtils { - - static SocketFactory mockSocketFactory(OutputStream out, InputStream in) { - try { - Socket socket = mock(Socket.class); - when(socket.getOutputStream()).thenReturn(out); - when(socket.getInputStream()).thenReturn(in); - - SocketFactory factory = mock(SocketFactory.class); - when(factory.createSocket()).thenReturn(socket); - when(factory.createSocket(anyString(), anyInt())).thenReturn(socket); - - return factory; - } catch (Exception e) { - e.printStackTrace(); - throw new AssertionError("Cannot be here!"); - } - } - - static SocketFactory mockClosedThenOpenSocket(OutputStream out, InputStream in, boolean isClosed, int failedTries) { - try { - List socketMocks = new ArrayList(failedTries + 1); - - for (int i = 0; i < failedTries; ++i) { - Socket socket = mock(Socket.class); - if (isClosed) { - mockSocketClosed(socket); - } else { - when(socket.getOutputStream()).thenThrow( - new IOException("simulated IOException")); - doAnswer(new DynamicMockSocketClosed(socket)).when(socket).close(); - } - socketMocks.add(socket); - } - - Socket socket = mock(Socket.class); - when(socket.getOutputStream()).thenReturn(out); - when(socket.getInputStream()).thenReturn(in); - when(socket.isConnected()).thenReturn(true); - socketMocks.add(socket); - - SocketFactory factory = mock(SocketFactory.class); - OngoingStubbing stubbing = when(factory.createSocket(anyString(), anyInt())); - for (Socket t : socketMocks) - stubbing = stubbing.thenReturn(t); - - return factory; - } catch (Exception e) { - e.printStackTrace(); - throw new AssertionError("Cannot be here!"); - } - } - - private static void mockSocketClosed(final Socket socket) throws IOException { - when(socket.isClosed()).thenReturn(true); - when(socket.isConnected()).thenReturn(false); - when(socket.getOutputStream()).thenThrow( - new AssertionError("Should have checked for closed connection")); - } - - /** - * Change a mock socket's behaviour to be closed. Dynamically used from close() - */ - private static class DynamicMockSocketClosed implements Answer { - private final Socket socket; - - public DynamicMockSocketClosed(final Socket socket) { - this.socket = socket; - } - - @Override - public Void answer(final InvocationOnMock invocation) throws Throwable { - mockSocketClosed(socket); - return null; - } - } -} diff --git a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTCTest.java b/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTCTest.java deleted file mode 100644 index d7374649..00000000 --- a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTCTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.internal; - -import static org.junit.Assert.*; - -import org.junit.Test; -import static org.mockito.Mockito.*; - -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.SimpleApnsNotification; -import com.notnoop.apns.internal.QueuedApnsServiceTest.ConnectionStub; - -public class QueuedApnsServiceTCTest { - - @Test(expected=IllegalStateException.class) - public void sendWithoutStarting() { - QueuedApnsService service = new QueuedApnsService(null); - service.push(notification); - } - - SimpleApnsNotification notification = new SimpleApnsNotification("2342", "{}"); - - @Test - public void pushEventually() { - ConnectionStub connection = spy(new ConnectionStub(0, 1)); - ApnsService service = newService(connection, null); - - service.push(notification); - connection.semaphore.acquireUninterruptibly(); - - verify(connection, times(1)).sendMessage(notification); - } - - @Test - public void doNotBlock() { - final int delay = 10000; - ConnectionStub connection = spy(new ConnectionStub(delay, 2)); - QueuedApnsService queued = - new QueuedApnsService(new ApnsServiceImpl(connection, null)); - queued.start(); - long time1 = System.currentTimeMillis(); - queued.push(notification); - queued.push(notification); - long time2 = System.currentTimeMillis(); - assertTrue("queued.push() blocks", (time2 - time1) < delay); - - connection.interrupt(); - connection.semaphore.acquireUninterruptibly(); - verify(connection, times(2)).sendMessage(notification); - - queued.stop(); - } - - protected ApnsService newService(ApnsConnection connection, ApnsFeedbackConnection feedback) { - ApnsService service = new ApnsServiceImpl(connection, null); - ApnsService queued = new QueuedApnsService(service); - queued.start(); - return queued; - } -} diff --git a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java b/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java deleted file mode 100644 index 846a9a6f..00000000 --- a/src/test/java/com/notnoop/apns/internal/QueuedApnsServiceTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.internal; - -import static org.junit.Assert.*; - -import java.io.IOException; -import java.util.concurrent.Semaphore; - -import org.junit.Test; -import static org.mockito.Mockito.*; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.ApnsService; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.exceptions.NetworkIOException; - -public class QueuedApnsServiceTest { - - @Test(expected = IllegalStateException.class) - public void sendWithoutStarting() { - QueuedApnsService service = new QueuedApnsService(null); - service.push(notification); - } - EnhancedApnsNotification notification = new EnhancedApnsNotification(1, - EnhancedApnsNotification.MAXIMUM_EXPIRY, "2342", "{}"); - - @Test - public void pushEventually() { - ConnectionStub connection = spy(new ConnectionStub(0, 1)); - ApnsService service = newService(connection, null); - - service.push(notification); - connection.semaphore.acquireUninterruptibly(); - - verify(connection, times(1)).sendMessage(notification); - } - - @Test - public void pushEventuallySample() { - ConnectionStub connection = spy(new ConnectionStub(0, 1)); - ApnsService service = newService(connection, null); - - service.push("2342", "{}"); - connection.semaphore.acquireUninterruptibly(); - - verify(connection, times(1)).sendMessage(notification); - } - - @Test - public void doNotBlock() { - final int delay = 10000; - ConnectionStub connection = spy(new ConnectionStub(delay, 2)); - QueuedApnsService queued = - new QueuedApnsService(new ApnsServiceImpl(connection, null)); - queued.start(); - long time1 = System.currentTimeMillis(); - queued.push(notification); - queued.push(notification); - long time2 = System.currentTimeMillis(); - assertTrue("queued.push() blocks", (time2 - time1) < delay); - - connection.interrupt(); - connection.semaphore.acquireUninterruptibly(); - verify(connection, times(2)).sendMessage(notification); - - queued.stop(); - } - - protected ApnsService newService(ApnsConnection connection, ApnsFeedbackConnection feedback) { - ApnsService service = new ApnsServiceImpl(connection, null); - ApnsService queued = new QueuedApnsService(service); - queued.start(); - return queued; - } - - static class ConnectionStub implements ApnsConnection { - - Semaphore semaphore; - int delay; - - public ConnectionStub(int delay, int expectedCalls) { - this.semaphore = new Semaphore(1 - expectedCalls); - this.delay = delay; - } - volatile boolean stop; - - public synchronized void sendMessage(ApnsNotification m) { - long time = System.currentTimeMillis(); - while (!stop && (System.currentTimeMillis() < time + delay)) { - // WTF? Here was a busy wait for up to 10 seconds or until "stop" fired. Added: A tiny amount of sleep every round. - try { - Thread.sleep(2); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - semaphore.release(); - } - - protected void interrupt() { - stop = true; - } - - public ApnsConnection copy() { - throw new RuntimeException("Not implemented"); - } - - public void close() throws IOException { - } - - public void testConnection() throws NetworkIOException { - } - - public void setCacheLength(int cacheLength) { - } - - public int getCacheLength() { - return -1; - } - } -} diff --git a/src/test/java/com/notnoop/apns/internal/SillyTests.java b/src/test/java/com/notnoop/apns/internal/SillyTests.java deleted file mode 100644 index f51da395..00000000 --- a/src/test/java/com/notnoop/apns/internal/SillyTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.internal; - -import java.util.Collections; -import java.util.Date; -import java.util.Map; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - -public class SillyTests { - - @Test - public void testFeedbackCalls() { - Map map = Collections.singletonMap("Test", new Date()); - ApnsFeedbackConnection feed = mock(ApnsFeedbackConnection.class); - when(feed.getInactiveDevices()).thenReturn(map); - - ApnsServiceImpl service = new ApnsServiceImpl(null, feed); - assertEquals(map, service.getInactiveDevices()); - - // The feedback should be called once at most - // Otherwise, we need to verify that the results of both - // calls are accounted for - verify(feed, times(1)).getInactiveDevices(); - } - - @Test - public void testQueuedFeedbackCalls() { - Map map = Collections.singletonMap("Test", new Date()); - ApnsFeedbackConnection feed = mock(ApnsFeedbackConnection.class); - when(feed.getInactiveDevices()).thenReturn(map); - - ApnsServiceImpl service = new ApnsServiceImpl(null, feed); - QueuedApnsService queued = new QueuedApnsService(service); - assertEquals(map, queued.getInactiveDevices()); - - // The feedback should be called once at most - // Otherwise, we need to verify that the results of both - // calls are accounted for - verify(feed, times(1)).getInactiveDevices(); - } - -} diff --git a/src/test/java/com/notnoop/apns/internal/SimpleApnsNotificationTest.java b/src/test/java/com/notnoop/apns/internal/SimpleApnsNotificationTest.java deleted file mode 100644 index 29660941..00000000 --- a/src/test/java/com/notnoop/apns/internal/SimpleApnsNotificationTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.internal; - -import static org.junit.Assert.*; - -import org.junit.experimental.theories.*; -import org.junit.runner.RunWith; - -import com.notnoop.apns.PayloadBuilder; -import com.notnoop.apns.SimpleApnsNotification; -import com.notnoop.apns.internal.Utilities; - -import static com.notnoop.apns.PayloadBuilder.*; -import static com.notnoop.apns.internal.Utilities.*; - -@SuppressWarnings("deprecation") -@RunWith(Theories.class) -public class SimpleApnsNotificationTest { - - // Device Tokens - @DataPoints public static String[] deviceTokens = - { - "298893742908AB98C", - "98234098203BACCCC93284092" - }; - - // Messages - @DataPoints public static PayloadBuilder[] payloaders = - { - newPayload().alertBody("test").sound("default"), - newPayload().sound("chimes").actionKey("Cancel"), - newPayload().customField("notice", "this") - }; - - @Theory - public void lengthConsistency(String deviceToken, PayloadBuilder payload) { - SimpleApnsNotification msg = new SimpleApnsNotification(deviceToken, payload.build()); - assertEquals(msg.marshall().length, msg.length()); - } - - @Theory - public void commandIsZero(String deviceToken, PayloadBuilder payload) { - SimpleApnsNotification msg = new SimpleApnsNotification(deviceToken, payload.build()); - byte[] bytes = msg.marshall(); - assertEquals(0, /*command part*/ bytes[0]); - } - - @Theory - public void deviceTokenPart(String deviceToken, PayloadBuilder payload) { - SimpleApnsNotification msg = new SimpleApnsNotification(deviceToken, payload.build()); - byte[] bytes = msg.marshall(); - - byte[] dt = decodeHex(deviceToken); - assertEquals(dt.length, /* found length */ ((bytes[1] & 0xff) << 8) + (bytes[2]& 0xff)); - - // verify the device token part - assertArrayEquals(dt, Utilities.copyOfRange(bytes, 3, 3 + dt.length)); - } - - @Theory - public void payloadPart(String deviceToken, PayloadBuilder payload) { - String payloadString = payload.build(); - SimpleApnsNotification msg = new SimpleApnsNotification(deviceToken, payloadString); - byte[] bytes = msg.marshall(); - - byte[] pl = toUTF8Bytes(payloadString); - - // in reverse - int plBegin = bytes.length - pl.length; - - /// verify the payload part - assertArrayEquals(pl, Utilities.copyOfRange(bytes, plBegin, bytes.length)); - assertEquals(pl.length, ((bytes[plBegin - 2] & 0xff) << 8) + (bytes[plBegin - 1] & 0xff)); - } - - @Theory - public void allPartsLength(String deviceToken, PayloadBuilder payload) { - String payloadString = payload.build(); - SimpleApnsNotification msg = new SimpleApnsNotification(deviceToken, payloadString); - byte[] bytes = msg.marshall(); - - int expectedLength = 1 - + 2 + decodeHex(deviceToken).length - + 2 + toUTF8Bytes(payloadString).length; - assertEquals(expectedLength, bytes.length); - } -} diff --git a/src/test/java/com/notnoop/apns/internal/TlsTunnelBuilderTest.java b/src/test/java/com/notnoop/apns/internal/TlsTunnelBuilderTest.java deleted file mode 100644 index 833bed55..00000000 --- a/src/test/java/com/notnoop/apns/internal/TlsTunnelBuilderTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.internal; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -//import java.net.InetSocketAddress; -//import java.net.Proxy; -//import java.net.Socket; -import org.junit.Test; -import static org.junit.Assert.fail; - -public class TlsTunnelBuilderTest { - - @Test - public void makeTunnelSuccess() throws IOException { - /* Uncomment this test to verify with your proxy settings */ - /*try { - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.mydomain.com", 8080)); - - InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address(); - Socket proxySocket = new Socket(proxyAddress.getAddress(), proxyAddress.getPort()); - InetSocketAddress destAddress = new InetSocketAddress("myhost.com", 2195); - - new TlsTunnelBuilder().makeTunnel(destAddress.getAddress().toString(), - destAddress.getPort(), - "proxy-username", "proxy-password", - proxyAddress); - } catch (IOException ex){ - fail(); - }*/ - - } - - @Test - public void invalidProxyParams() throws IOException { - try { - new TlsTunnelBuilder().makeTunnel("origin.example.com", 9876, null, null, null); - fail(); - } catch (IOException expected) { - // No operation - } - } - - private InputStream inputStream(String content) throws IOException { - return new ByteArrayInputStream(content.getBytes("UTF-8")); - } -} diff --git a/src/test/java/com/notnoop/apns/internal/UtilitiesTest.java b/src/test/java/com/notnoop/apns/internal/UtilitiesTest.java deleted file mode 100644 index 251dc139..00000000 --- a/src/test/java/com/notnoop/apns/internal/UtilitiesTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.internal; - -import org.junit.Assert; -import org.junit.Test; - -public class UtilitiesTest { - - @Test - public void testEncodeAndDecode() { - String encodedHex = "a1b2d4"; - - byte[] decoded = Utilities.decodeHex(encodedHex); - String encoded = Utilities.encodeHex(decoded); - - Assert.assertEquals(encodedHex.toLowerCase(), encoded.toLowerCase()); - } - - @Test - public void testParsingBytes() { - Assert.assertEquals(0xFF00FF00, Utilities.parseBytes(0xFF, 0, 0xFF, 0)); - Assert.assertEquals(0x00FF00FF, Utilities.parseBytes(0, 0xFF, 0, 0xFF)); - Assert.assertEquals(0xDEADBEEF, Utilities.parseBytes(0xDE, 0xAD, 0xBE, 0xEF)); - Assert.assertEquals(0x0EADBEEF, Utilities.parseBytes(0x0E, 0xAD, 0xBE, 0xEF)); - - Assert.assertTrue(Utilities.parseBytes(0xF0, 0,0,0) < 0); - Assert.assertTrue(Utilities.parseBytes(0x80, 0,0,0) < 0); - Assert.assertTrue(Utilities.parseBytes(0x70, 0,0,0) > 0); - } - - @Test - public void testEncodingUTF8() { - String m = "esemény"; - - // See http://en.wikipedia.org/wiki/Unicode_equivalence#Example - // - // Oh the joy as different platforms choose to normalize Unicode differently ... but both are valid. - // - // This is intended to fix a problem under jdk 6, I was not able to reproduce it with jdk 7u51 on OSX Mavericks - // (Java seems to also use expected_NFC here). - byte[] expected_NFC = { - 'e', 's', 'e', 'm', (byte)0x00C3, (byte)0x00A9, 'n', 'y' - }; - - byte[] expected_NFD = { - 'e', 's', 'e', 'm', (byte)0x00cc, (byte)0x0081, (byte)0x0061, 'n', 'y' - }; - - final byte[] bytes = Utilities.toUTF8Bytes(m); - - if (bytes.length == 8) { - Assert.assertArrayEquals(expected_NFC, bytes); - } else { - Assert.assertArrayEquals(expected_NFD, bytes); - } - - } -} diff --git a/src/test/java/com/notnoop/apns/utils/ApnsServerStub.java b/src/test/java/com/notnoop/apns/utils/ApnsServerStub.java deleted file mode 100644 index 95d24f38..00000000 --- a/src/test/java/com/notnoop/apns/utils/ApnsServerStub.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * 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.utils; - -import javax.net.ServerSocketFactory; -import javax.net.ssl.SSLServerSocket; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ApnsServerStub { - - /** - * Create an ApnsServerStub - * - * @param gatePort port for the gateway stub server - * @param feedPort port for the feedback stub server - * @return an ApnsServerStub - * @deprecated use prepareAndStartServer() without port numbers and query the port numbers from the server using - * ApnsServerStub.getEffectiveGatewayPort() and ApnsServerStub.getEffectiveFeedbackPort() - */ - @Deprecated - public static ApnsServerStub prepareAndStartServer(int gatePort, int feedPort) { - ApnsServerStub server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory(), gatePort, feedPort); - server.start(); - return server; - } - - /** - * Create an ApnsServerStub that uses any free port for gateway and feedback. - * - * @return the server stub. Use getEffectiveGatewayPort() and getEffectiveFeedbackPort() to ask for ports. - */ - public static ApnsServerStub prepareAndStartServer() { - ApnsServerStub server = new ApnsServerStub(FixedCertificates.serverContext().getServerSocketFactory()); - server.start(); - return server; - } - - private static final Logger LOGGER = LoggerFactory.getLogger(ApnsServerStub.class); - private final AtomicInteger toWaitBeforeSend = new AtomicInteger(0); - private final ByteArrayOutputStream received; - private final ByteArrayOutputStream toSend; - private final Semaphore messages = new Semaphore(0); - private final Semaphore startUp = new Semaphore(0); - private final Semaphore gatewayOutLock = new Semaphore(0); - private final Semaphore waitForError = new Semaphore(1); - private final ServerSocketFactory sslFactory; - private final int gatewayPort; - private final int feedbackPort; - private int effectiveGatewayPort; - private int effectiveFeedbackPort; - private OutputStream gatewayOutputStream; - - public ApnsServerStub(ServerSocketFactory sslFactory) { - this(sslFactory, 0, 0); - } - - public ApnsServerStub(ServerSocketFactory sslFactory, int gatewayPort, int feedbackPort) { - this.sslFactory = sslFactory; - this.gatewayPort = gatewayPort; - this.feedbackPort = feedbackPort; - - this.received = new ByteArrayOutputStream(); - this.toSend = new ByteArrayOutputStream(); - } - - Thread gatewayThread; - Thread feedbackThread; - SSLServerSocket gatewaySocket; - SSLServerSocket feedbackSocket; - - public void start() { - gatewayThread = new GatewayRunner(); - feedbackThread = new FeedbackRunner(); - gatewayThread.start(); - feedbackThread.start(); - startUp.acquireUninterruptibly(2); - } - - @SuppressWarnings("deprecation") - public void stop() { - try { - if (gatewaySocket != null) { - gatewaySocket.close(); - } - } catch (IOException e) { - LOGGER.warn("Can not close gatewaySocket properly", e); - } - try { - if (feedbackSocket != null) { - feedbackSocket.close(); - } - } catch (IOException e) { - LOGGER.warn("Can not close feedbackSocket properly", e); - } - - if (gatewayThread != null) { - gatewayThread.stop(); - } - - if (feedbackThread != null) { - feedbackThread.stop(); - } - - } - - public void sendError(int err, int id) { - ByteBuffer buf = ByteBuffer.allocate(6); - buf.put((byte) 8).put((byte) err).putInt(id); - try { - gatewayOutLock.acquire(); - gatewayOutputStream.write(buf.array()); - gatewayOutputStream.flush(); - } catch (Exception ex) { - LOGGER.warn("An error occured with accessing gateway", ex); - } - } - - public int getEffectiveGatewayPort() { - return effectiveGatewayPort; - } - - public int getEffectiveFeedbackPort() { - return effectiveFeedbackPort; - } - - public AtomicInteger getToWaitBeforeSend() { - return toWaitBeforeSend; - } - - public ByteArrayOutputStream getReceived() { - return received; - } - - public ByteArrayOutputStream getToSend() { - return toSend; - } - - public Semaphore getMessages() { - return messages; - } - - public Semaphore getWaitForError() { - return waitForError; - } - - private class GatewayRunner extends Thread { - - @SuppressWarnings("InfiniteLoopStatement") - public void run() { - Thread.currentThread().setName("GatewayThread"); - try { - gatewaySocket = (SSLServerSocket)sslFactory.createServerSocket(gatewayPort); - gatewaySocket.setNeedClientAuth(true); - } catch (IOException e) { - messages.release(); - throw new RuntimeException(e); - } - - InputStream in = null; - effectiveGatewayPort = gatewaySocket.getLocalPort(); - - try { - // Listen for connections - startUp.release(); - while (true) { - Socket socket = gatewaySocket.accept(); - // Work around JVM deadlock ... https://community.oracle.com/message/10989561#10989561 - socket.setSoLinger(true, 1); - - // Create streams to securely send and receive data to the client - in = socket.getInputStream(); - gatewayOutputStream = socket.getOutputStream(); - gatewayOutLock.release(); - - // Read from in and write to out... - byte[] read = readFully(in); - - waitBeforeSend(); - received.write(read); - messages.release(); - - - waitForError.acquire(); - - // Close the socket - in.close(); - gatewayOutputStream.close(); - } - } catch (Throwable e) { - try { - if (in != null) { - in.close(); - } - } catch (IOException ioex) { - LOGGER.warn("Can not close socket properly", ioex); - } - try { - if (gatewayOutputStream != null) { - gatewayOutputStream.close(); - } - } catch (IOException ioex) { - LOGGER.warn("Can not close gatewayOutputStream properly", ioex); - } - messages.release(); - } - } - - } - - private class FeedbackRunner extends Thread { - - public void run() { - Thread.currentThread().setName("FeedbackThread"); - try { - feedbackSocket = (SSLServerSocket)sslFactory.createServerSocket(feedbackPort); - feedbackSocket.setNeedClientAuth(true); - } catch (IOException e) { - e.printStackTrace(); - messages.release(); - throw new RuntimeException(e); - } - - effectiveFeedbackPort = feedbackSocket.getLocalPort(); - try { - // Listen for connections - startUp.release(); - Socket socket = feedbackSocket.accept(); - // Work around JVM deadlock ... https://community.oracle.com/message/10989561#10989561 - socket.setSoLinger(true, 1); - - // Create streams to securely send and receive data to the client - InputStream in = socket.getInputStream(); - OutputStream out = socket.getOutputStream(); - - waitBeforeSend(); - // Read from in and write to out... - toSend.writeTo(out); - - // Close the socket - in.close(); - out.close(); - } catch (SocketException se) { - // Ignore closed socket. - } catch (IOException ioex) { - ioex.printStackTrace(); - } - messages.release(); - } - } - - AtomicInteger readLen = new AtomicInteger(); - - public void stopAt(int length) { - readLen.set(length); - } - - public byte[] readFully(InputStream st) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - - int read; - try { - while (readLen.getAndDecrement() > 0 && (read = st.read()) != -1) { - stream.write(read); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - - - return stream.toByteArray(); - } - - /** - * Introduces a waiting time, used to trigger read timeouts. - */ - private void waitBeforeSend() { - int wait = toWaitBeforeSend.get(); - if (wait != 0) { - try { - Thread.sleep(wait); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - -} diff --git a/src/test/java/com/notnoop/apns/utils/FixedCertificates.java b/src/test/java/com/notnoop/apns/utils/FixedCertificates.java deleted file mode 100644 index d9f69d33..00000000 --- a/src/test/java/com/notnoop/apns/utils/FixedCertificates.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.utils; - -import com.notnoop.apns.internal.SSLContextBuilder; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.X509TrustManager; -import java.io.InputStream; - -public class FixedCertificates { - - public static final String CLIENT_STORE = "clientStore.p12"; - public static final String CLIENT_PASSWORD = "123456"; - - public static final String CLIENT_MULTI_KEY_STORE = "clientStore.jks"; - public static final String CLIENT_MULTI_KEY_PASSWORD = "123456"; - - public static final String SERVER_STORE = "serverStore.p12"; - public static final String SERVER_PASSWORD = "123456"; - - public static final String SERVER_TRUST_STORE = "serverTrustStore.p12"; - public static final String SERVER_TRUST_PASSWORD = "123456"; - - public static final String LOCALHOST = "localhost"; - - public static SSLContext serverContext() { - try { - InputStream stream = FixedCertificates.class.getResourceAsStream("/" + SERVER_STORE); - InputStream trustStream = FixedCertificates.class.getResourceAsStream("/" + SERVER_TRUST_STORE); - assert stream != null; - return new SSLContextBuilder() - .withAlgorithm("sunx509") - .withCertificateKeyStore(stream, SERVER_PASSWORD, "PKCS12") - .withTrustKeyStore(trustStream, SERVER_TRUST_PASSWORD, "PKCS12") - .build(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static SSLContext clientContext() { - try { - InputStream stream = FixedCertificates.class.getResourceAsStream("/" + CLIENT_STORE); - assert stream != null; - return new SSLContextBuilder() - .withAlgorithm("sunx509") - .withCertificateKeyStore(stream, CLIENT_PASSWORD, "PKCS12") - .withTrustManager(new X509TrustManagerTrustAll()) - .build(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static SSLContext clientMultiKeyContext(String keyAlias) { - try { - InputStream stream = FixedCertificates.class.getResourceAsStream("/" + CLIENT_MULTI_KEY_STORE); - assert stream != null; - return new SSLContextBuilder() - .withAlgorithm("sunx509") - .withCertificateKeyStore(stream, CLIENT_MULTI_KEY_PASSWORD, "JKS", keyAlias) - .withTrustManager(new X509TrustManagerTrustAll()) - .build(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static String clientCertPath() { - return ClassLoader.getSystemResource(CLIENT_STORE).getPath(); - } - - static class X509TrustManagerTrustAll implements X509TrustManager { - public boolean checkClientTrusted(java.security.cert.X509Certificate[] chain){ - return true; - } - - public boolean isServerTrusted(java.security.cert.X509Certificate[] chain){ - return true; - } - - public boolean isClientTrusted(java.security.cert.X509Certificate[] chain){ - return true; - } - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {} - - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {} - } - -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/Action.java b/src/test/java/com/notnoop/apns/utils/Simulator/Action.java deleted file mode 100644 index fdaddb6e..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/Action.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.utils.Simulator; - -public enum Action { - DO_NOTHING, RETURN_ERROR, RETURN_ERROR_AND_SHUTDOWN -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsInputStream.java b/src/test/java/com/notnoop/apns/utils/Simulator/ApnsInputStream.java deleted file mode 100644 index 189ca3a4..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsInputStream.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.utils.Simulator; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -public class ApnsInputStream extends DataInputStream { - public ApnsInputStream(final InputStream inputStream) { - super(inputStream); - } - - byte[] readBlob() throws IOException { - int length = readUnsignedShort(); - byte[] blob = new byte[length]; - readFully(blob); - return blob; - } - - ApnsInputStream readFrame() throws IOException { - int length = readInt(); - byte[] buffer = new byte[length]; - readFully(buffer); - final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer); - return new ApnsInputStream(byteArrayInputStream); - } - - public Item readItem() throws IOException { - byte itemId = readByte(); - byte[] blob = readBlob(); - return new Item(itemId, blob); - } - - public static class Item { - public final static byte ID_DEVICE_TOKEN = 1; - public final static byte ID_PAYLOAD = 2; - public final static byte ID_NOTIFICATION_IDENTIFIER = 3; - public final static byte ID_EXPIRATION_DATE = 4; - public final static byte ID_PRIORITY = 5; - public final static Item DEFAULT = new Item((byte)0, new byte[0]); - - private final byte itemId; - private final byte[] blob; - - public Item(final byte itemId, final byte[] blob) { - - this.itemId = itemId; - this.blob = blob; - } - - public byte getItemId() { - return itemId; - } - - public byte[] getBlob() { - return blob.clone(); - } - - public int getInt() { return blob.length < 4 ? 0 : ByteBuffer.wrap(blob).getInt(); } - - public byte getByte() { return blob.length < 1 ? 0 : blob[0]; } - } -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsNotificationWithAction.java b/src/test/java/com/notnoop/apns/utils/Simulator/ApnsNotificationWithAction.java deleted file mode 100644 index b05bc6ef..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsNotificationWithAction.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.utils.Simulator; - -import com.notnoop.apns.utils.Simulator.ApnsServerSimulator.Notification; - -public class ApnsNotificationWithAction { - private final Notification notification; - private final ApnsResponse response; - - public ApnsNotificationWithAction(Notification notification) { - this(notification, ApnsResponse.doNothing()); - } - - public ApnsNotificationWithAction(Notification notification, ApnsResponse response) { - if (notification == null) - { - throw new NullPointerException("notification cannot be null"); - } - this.notification = notification; - if (response == null) - { - throw new NullPointerException("response cannot be null"); - } - this.response = response; - } - - public Notification getNotification() { - return notification; - } - - public int getId() { - return notification.getIdentifier(); - } - - public ApnsResponse getResponse() { - return response; - } - -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsResponse.java b/src/test/java/com/notnoop/apns/utils/Simulator/ApnsResponse.java deleted file mode 100644 index 8fcc7d9d..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsResponse.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.utils.Simulator; - -import com.notnoop.apns.ApnsNotification; -import com.notnoop.apns.DeliveryError; - -public class ApnsResponse { - - private final Action action; - private final DeliveryError error; - private final int errorId; - - private ApnsResponse(Action action, DeliveryError error, int errorId) { - this.action = action; - this.error = error; - this.errorId = errorId; - } - - public boolean isDoNothing() { - return action == Action.DO_NOTHING; - } - - public Action getAction() { - return action; - } - - public DeliveryError getError() { - return error; - } - - public int getErrorId() { - return errorId; - } - - public static ApnsResponse doNothing() { - return new ApnsResponse(Action.DO_NOTHING, null, 0); - } - - public static ApnsResponse returnError(DeliveryError error, int errorId) { - return new ApnsResponse(Action.RETURN_ERROR, error, errorId); - } - - public static ApnsResponse returnErrorAndShutdown(DeliveryError error, int errorId) { - return new ApnsResponse(Action.RETURN_ERROR_AND_SHUTDOWN, error, errorId); - } - - public static ApnsResponse returnErrorAndShutdown(DeliveryError error, ApnsNotification notification) { - return new ApnsResponse(Action.RETURN_ERROR_AND_SHUTDOWN, error, notification.getIdentifier()); - } - -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsServerSimulator.java b/src/test/java/com/notnoop/apns/utils/Simulator/ApnsServerSimulator.java deleted file mode 100644 index 0e93068c..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsServerSimulator.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * 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.utils.Simulator; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.net.ServerSocket; -import java.net.SocketException; -import java.nio.*; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicInteger; -import javax.net.ServerSocketFactory; -import com.notnoop.apns.internal.Utilities; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class ApnsServerSimulator { - - private static final Logger logger = LoggerFactory.getLogger(ApnsServerSimulator.class); - private static AtomicInteger threadNameCount = new AtomicInteger(0); - - private final Semaphore startUp = new Semaphore(0); - private final ServerSocketFactory sslFactory; - - private int effectiveGatewayPort; - private int effectiveFeedbackPort; - - public ApnsServerSimulator(ServerSocketFactory sslFactory) { - this.sslFactory = sslFactory; - } - - Thread gatewayThread; - Thread feedbackThread; - ServerSocket gatewaySocket; - ServerSocket feedbackSocket; - - public void start() { - logger.debug("Starting APNSServerSimulator"); - gatewayThread = new GatewayListener(); - feedbackThread = new FeedbackRunner(); - gatewayThread.start(); - feedbackThread.start(); - startUp.acquireUninterruptibly(2); - } - - public void stop() { - logger.debug("Stopping APNSServerSimulator"); - try { - if (gatewaySocket != null) { - gatewaySocket.close(); - } - } catch (IOException e) { - logger.warn("Can not close gatewaySocket properly", e); - } - try { - if (feedbackSocket != null) { - feedbackSocket.close(); - } - } catch (IOException e) { - logger.warn("Can not close feedbackSocket properly", e); - } - - if (gatewayThread != null) { - gatewayThread.interrupt(); - } - - if (feedbackThread != null) { - feedbackThread.interrupt(); - } - logger.debug("Stopped - APNSServerSimulator"); - - } - - public int getEffectiveGatewayPort() { - return effectiveGatewayPort; - } - - public int getEffectiveFeedbackPort() { - return effectiveFeedbackPort; - } - - private class GatewayListener extends Thread { - - private GatewayListener() { - super(new ThreadGroup("GatewayListener" + threadNameCount.incrementAndGet()), ""); - setName(getThreadGroup().getName()); - } - - public void run() { - logger.debug("Launched " + Thread.currentThread().getName()); - try { - - try { - gatewaySocket = sslFactory.createServerSocket(0); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - effectiveGatewayPort = gatewaySocket.getLocalPort(); - - // Listen for connections - startUp.release(); - - while (!isInterrupted()) { - try { - handleGatewayConnection(new InputOutputSocket(gatewaySocket.accept())); - } catch (SocketException ex) { - logger.warn("Interruption while handling gateway connection", ex); - interrupt(); - } catch (IOException ioe) { - logger.warn("An error occured while handling gateway connection", ioe); - } - } - } finally { - logger.debug("Terminating " + Thread.currentThread().getName()); - getThreadGroup().list(); - getThreadGroup().interrupt(); - } - } - - private void handleGatewayConnection(final InputOutputSocket inputOutputSocket) throws IOException { - Thread gatewayConnectionTread = new Thread() { - @Override - public void run() { - try { - parseNotifications(inputOutputSocket); - } finally { - inputOutputSocket.close(); - } - } - }; - gatewayConnectionTread.start(); - } - - private void parseNotifications(final InputOutputSocket inputOutputSocket) { - logger.debug("Running parseNotifications {}", inputOutputSocket.getSocket()); - while (!Thread.interrupted()) { - try { - final ApnsInputStream inputStream = inputOutputSocket.getInputStream(); - byte notificationType = inputStream.readByte(); - logger.debug("Received Notification (type {})", notificationType); - switch (notificationType) { - case 0: - readLegacyNotification(inputOutputSocket); - break; - case 1: - readEnhancedNotification(inputOutputSocket); - break; - case 2: - readFramedNotifications(inputOutputSocket); - break; - } - } catch (IOException ioe) { - logger.warn("An error occured while reading notification", ioe); - Thread.currentThread().interrupt(); - } - } - } - - private void readFramedNotifications(final InputOutputSocket inputOutputSocket) throws IOException { - - Map map = new HashMap(); - - ApnsInputStream frameStream = inputOutputSocket.getInputStream().readFrame(); - try { - while (!Thread.currentThread().isInterrupted()) { - final ApnsInputStream.Item item = frameStream.readItem(); - map.put(item.getItemId(), item); - } - } catch (EOFException eof) { - // Done reading. - } - - byte[] deviceToken = get(map, ApnsInputStream.Item.ID_DEVICE_TOKEN).getBlob(); - byte[] payload = get(map, ApnsInputStream.Item.ID_PAYLOAD).getBlob(); - int identifier = get(map, ApnsInputStream.Item.ID_NOTIFICATION_IDENTIFIER).getInt(); - int expiry = get(map, ApnsInputStream.Item.ID_EXPIRATION_DATE).getInt(); - byte priority = get(map, ApnsInputStream.Item.ID_PRIORITY).getByte(); - - final Notification notification = new Notification(2, identifier, expiry, deviceToken, payload, priority); - logger.debug("Read framed notification {}", notification); - onNotification(notification, inputOutputSocket); - - } - - private ApnsInputStream.Item get(final Map map, final byte idDeviceToken) { - ApnsInputStream.Item item = map.get(idDeviceToken); - if (item == null) { - item = ApnsInputStream.Item.DEFAULT; - } - return item; - } - - private void readEnhancedNotification(final InputOutputSocket inputOutputSocket) throws IOException { - ApnsInputStream inputStream = inputOutputSocket.getInputStream(); - - int identifier = inputStream.readInt(); - int expiry = inputStream.readInt(); - final byte[] deviceToken = inputStream.readBlob(); - final byte[] payload = inputStream.readBlob(); - final Notification notification = new Notification(1, identifier, expiry, deviceToken, payload); - logger.debug("Read enhanced notification {}", notification); - onNotification(notification, inputOutputSocket); - } - - private void readLegacyNotification(final InputOutputSocket inputOutputSocket) throws IOException { - ApnsInputStream inputStream = inputOutputSocket.getInputStream(); - - final byte[] deviceToken = inputStream.readBlob(); - final byte[] payload = inputStream.readBlob(); - final Notification notification = new Notification(0, deviceToken, payload); - logger.debug("Read legacy notification {}", notification); - onNotification(notification, inputOutputSocket); - - } - - @Override - public void interrupt() { - logger.debug("Interrupt, closing socket"); - super.interrupt(); - try { - gatewaySocket.close(); - } catch (IOException e) { - logger.warn("Can not close gatewaySocket properly", e); - } - } - } - - protected void fail(final byte status, final int identifier, final InputOutputSocket inputOutputSocket) throws IOException { - logger.debug("FAIL {} {}", status, identifier); - - // Here comes the fun ... we need to write the feedback packet as one single packet - // or the client will notice the connection to be closed before it read the complete packet. - // But - only on linux, however. (I was not able to see that problem on Windows 7 or OS X) - // What also helped was inserting a little sleep between the flush and closing the connection. - // - // I believe this is irregular (writing to a tcp socket then closing it should result in ALL data - // being visible at the client) but interestingly in Netty there is (was) a similar problem: - // https://github.com/netty/netty/issues/1952 - // - // Funnily that appeared as somebody ported this library to use netty. - // - // - // - ByteBuffer bb = ByteBuffer.allocate(6); - bb.put((byte) 8); - bb.put(status); - bb.putInt(identifier); - inputOutputSocket.syncWrite(bb.array()); - inputOutputSocket.close(); - logger.debug("FAIL - closed"); - } - - private class FeedbackRunner extends Thread { - - private FeedbackRunner() { - super(new ThreadGroup("FeedbackRunner" + threadNameCount.incrementAndGet()), ""); - setName(getThreadGroup().getName()); - } - - public void run() { - try { - logger.debug("Launched " + Thread.currentThread().getName()); - try { - feedbackSocket = sslFactory.createServerSocket(0); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - effectiveFeedbackPort = feedbackSocket.getLocalPort(); - - startUp.release(); - - while (!isInterrupted()) { - try { - handleFeedbackConnection(new InputOutputSocket(feedbackSocket.accept())); - } catch (SocketException ex) { - logger.warn("Interruption while handling feedback connection", ex); - interrupt(); - } catch (IOException ioe) { - logger.warn("An error occured while handling feedback connection", ioe); - } - } - } finally { - logger.debug("Terminating " + Thread.currentThread().getName()); - getThreadGroup().list(); - getThreadGroup().interrupt(); - } - } - - private void handleFeedbackConnection(final InputOutputSocket inputOutputSocket) { - Thread feedbackConnectionTread = new Thread() { - @Override - public void run() { - try { - logger.debug("Feedback connection sending feedback"); - sendFeedback(inputOutputSocket); - } catch (IOException ioe) { - // An exception is unexpected here. Close the current connection and bail out. - logger.warn("An error occured while sending feedback", ioe); - } finally { - inputOutputSocket.close(); - } - - } - }; - feedbackConnectionTread.start(); - } - - private void sendFeedback(final InputOutputSocket inputOutputSocket) throws IOException { - List badTokens = getBadTokens(); - - for (byte[] token : badTokens) { - writeFeedback(inputOutputSocket, token); - } - } - - private void writeFeedback(final InputOutputSocket inputOutputSocket, final byte[] token) throws IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(os); - final int unixTime = (int) (new Date().getTime() / 1000); - dos.writeInt(unixTime); - dos.writeShort((short) token.length); - dos.write(token); - dos.close(); - inputOutputSocket.syncWrite(os.toByteArray()); - } - } - - - @SuppressWarnings("UnusedDeclaration") - public class Notification { - private final int type; - private final int identifier; - private final int expiry; - private final byte[] deviceToken; - private final byte[] payload; - private final byte priority; - - public Notification(final int type, final byte[] deviceToken, final byte[] payload) { - this(type, 0, 0, deviceToken, payload); - } - - public Notification(final int type, final int identifier, final int expiry, final byte[] deviceToken, final byte[] payload) { - this(type, identifier, expiry, deviceToken, payload, (byte) 10); - - } - - public Notification(final int type, final int identifier, final int expiry, final byte[] deviceToken, final byte[] payload, - final byte priority) { - this.priority = priority; - this.type = type; - this.identifier = identifier; - this.expiry = expiry; - this.deviceToken = deviceToken; - this.payload = payload; - } - - public byte[] getPayload() { - return payload.clone(); - } - - public byte[] getDeviceToken() { - return deviceToken.clone(); - } - - public int getType() { - return type; - } - - public int getExpiry() { - return expiry; - } - - public int getIdentifier() { - return identifier; - } - - public byte getPriority() { - return priority; - } - - @Override - public String toString() { - return "Notification{" + - "type=" + type + - ", identifier=" + identifier + - ", expiry=" + expiry + - ", deviceToken=" + Utilities.encodeHex(deviceToken) + - //", payload=" + Utilities.encodeHex(payload) + - ", priority=" + priority + - '}'; - } - } - - protected void onNotification(final Notification notification, final InputOutputSocket inputOutputSocket) throws IOException { - } - - - protected List getBadTokens() { - return new ArrayList(); - } -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsSimulatorWithVerification.java b/src/test/java/com/notnoop/apns/utils/Simulator/ApnsSimulatorWithVerification.java deleted file mode 100644 index b5e591bc..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/ApnsSimulatorWithVerification.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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.utils.Simulator; - -import com.google.common.base.Strings; -import com.notnoop.apns.EnhancedApnsNotification; -import com.notnoop.apns.internal.Utilities; - -import javax.net.ServerSocketFactory; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; - -/** - * Provides a simulator that receives connection over TCP as per a real APNS server. This class allows verification - * and prior configuration of responses in a manner similar to a mocking framework. - */ -public class ApnsSimulatorWithVerification extends ApnsServerSimulator { - - private List receivedNotifications; - private Queue expectedWithResponses; - private List unexpected; - - public ApnsSimulatorWithVerification(ServerSocketFactory sslFactory) { - super(sslFactory); - receivedNotifications = new ArrayList(); - expectedWithResponses = new ConcurrentLinkedQueue(); - unexpected = new ArrayList(); - } - - public void reset() { - receivedNotifications.clear(); - expectedWithResponses.clear(); - unexpected.clear(); - } - - public List getReceivedNotifications() { - return Collections.unmodifiableList(receivedNotifications); - } - - private void addExpected(ApnsNotificationWithAction notificationWithAction) { - expectedWithResponses.add(notificationWithAction); - } - - protected void onNotification(final Notification notification, final InputOutputSocket inOutSocket) - throws IOException { - receivedNotifications.add(notification); - pollExpectedResponses(notification, inOutSocket); - } - - protected void pollExpectedResponses(Notification notification, InputOutputSocket inOutSocket) throws IOException { - final ApnsNotificationWithAction withAction = expectedWithResponses.poll(); - if (withAction == null) { - unexpected.add(notification); - } else if (!matchNotificationWithExpected(withAction, notification)) { - unexpected.add(notification); - } else { - handleNotificationWithAction(withAction, inOutSocket); - } - } - - protected void handleNotificationWithAction(ApnsNotificationWithAction notificationWithAction, - InputOutputSocket inOutSocket) throws IOException { - final ApnsResponse response = notificationWithAction.getResponse(); - if (!response.isDoNothing()) { - if (response.getAction() == Action.RETURN_ERROR_AND_SHUTDOWN) { - // have to stop first before sending out the error - stop(); - sendError(response, inOutSocket); - } else { - sendError(response, inOutSocket); - } - } - } - - private boolean matchNotificationWithExpected(ApnsNotificationWithAction withAction, Notification found) { - if (withAction.getId() != found.getIdentifier()) { - return false; - } - return matchDeviceToken(withAction.getNotification().getDeviceToken(), found.getDeviceToken()); - } - - private boolean matchDeviceToken(byte[] expected, byte[] found) { - return Arrays.equals(expected, found); - } - - protected void sendError(ApnsResponse response, InputOutputSocket inOutSocket) throws IOException { - final byte status = (byte) response.getError().code(); - fail(status, response.getErrorId(), inOutSocket); - } - - public DoResponse when(Notification notification) { - return new DoResponse(notification); - } - - public DoResponse when(EnhancedApnsNotification notification) { - return new DoResponse(buildNotification(notification)); - } - - public void verify() { - final int size = expectedWithResponses.size(); - if (size > 0) { - final String error = String.format("[%d] Expected notification(s) were not received, first id was: [%d] ", - size, expectedWithResponses.poll().getId()); - throw new IllegalStateException(error); - } - verifyUnexpected(); - } - - public void verifyAndWait(int waitSecs) { - verifyUnexpected(); - - long timeRemaining = TimeUnit.SECONDS.toMillis(waitSecs); - final long sleepForMs = 250; - while (!expectedWithResponses.isEmpty() && timeRemaining > 0) { - sleep(sleepForMs < timeRemaining ? sleepForMs : timeRemaining); - timeRemaining -= sleepForMs; - } - verify(); - } - - private void verifyUnexpected() { - if (!unexpected.isEmpty()) { - final Notification firstUnexpected = this.unexpected.get(0); - throw new IllegalStateException(String.format("Unexpected notifications received, count: [%d]. First" + - " notification is for id: [%d], deviceToken: [%s]", unexpected.size(), - firstUnexpected.getIdentifier(), Utilities.encodeHex(firstUnexpected.getDeviceToken()))); - } - } - - private void sleep(long ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - } - - public Notification buildNotification(EnhancedApnsNotification notification) { - return new Notification(1, notification.getIdentifier(), notification.getExpiry(), - notification.getDeviceToken(), notification.getPayload()); - } - - public class DoResponse { - private final Notification expected; - - public DoResponse(Notification notification) { - this.expected = notification; - } - - public ApnsSimulatorWithVerification thenRespond(ApnsResponse response) { - addExpected(new ApnsNotificationWithAction(expected, response)); - return ApnsSimulatorWithVerification.this; - } - - public ApnsSimulatorWithVerification thenDoNothing() { - addExpected(new ApnsNotificationWithAction(expected, ApnsResponse.doNothing())); - return ApnsSimulatorWithVerification.this; - } - } - - public static String deviceTokenForId(int id) - { - final String right = Integer.toHexString(id).toUpperCase(); - final int zeroedLength = 64 - right.length(); - return Strings.repeat("0", zeroedLength) + right; - } -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/FailingApnsServerSimulator.java b/src/test/java/com/notnoop/apns/utils/Simulator/FailingApnsServerSimulator.java deleted file mode 100644 index dda34b56..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/FailingApnsServerSimulator.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.utils.Simulator; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ServerSocketFactory; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * A Server simulator that can simulate some failure modes. - */ -public class FailingApnsServerSimulator extends ApnsServerSimulator { - - private static final Logger logger = LoggerFactory.getLogger(FailingApnsServerSimulator.class); - - - private BlockingQueue queue = new LinkedBlockingQueue(); - - public FailingApnsServerSimulator(final ServerSocketFactory sslFactory) { - super(sslFactory); - } - - @Override - protected void onNotification(final ApnsServerSimulator.Notification notification, final InputOutputSocket inputOutputSocket) - throws IOException { - logger.debug("Queueing notification " + notification); - queue.add(notification); - final byte[] token = notification.getDeviceToken(); - if (token.length == 32 && token[0] == (byte)0xff && token[1] == (byte)0xff) { - switch (token[2]) { - case 0: - fail(token[3], notification.getIdentifier(), inputOutputSocket); - break; - - case 1: - try { - final int millis = token[3] * 100; - Thread.sleep(millis); - } catch (InterruptedException e) { - Thread.interrupted(); - } - break; - - case 2: - default: - inputOutputSocket.close(); - break; - } - - } - } - - protected List getBadTokens() { - return super.getBadTokens(); - } - - public BlockingQueue getQueue() { - return queue; - } - -} diff --git a/src/test/java/com/notnoop/apns/utils/Simulator/InputOutputSocket.java b/src/test/java/com/notnoop/apns/utils/Simulator/InputOutputSocket.java deleted file mode 100644 index 8170c523..00000000 --- a/src/test/java/com/notnoop/apns/utils/Simulator/InputOutputSocket.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.utils.Simulator; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.Socket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Wrap some of the boilerplate code using socket, enable passing around a socket together with its streams. - */ -public class InputOutputSocket { - private static final Logger LOGGER = LoggerFactory.getLogger(InputOutputSocket.class); - private final Socket socket; - private final ApnsInputStream inputStream; - private final DataOutputStream outputStream; - - public InputOutputSocket(final Socket socket) throws IOException { - if (socket == null) { - throw new NullPointerException("socket may not be null"); - } - - this.socket = socket; - - // Hack, work around JVM deadlock ... https://community.oracle.com/message/10989561#10989561 - socket.setSoLinger(true, 1); - outputStream = new DataOutputStream(socket.getOutputStream()); - inputStream = new ApnsInputStream(socket.getInputStream()); - } - - public Socket getSocket() { - return socket; - } - - public ApnsInputStream getInputStream() { - return inputStream; - } - - /* - public DataOutputStream getOutputStream() { - return outputStream; - } - */ - - - - public synchronized void close() { - try { - inputStream.close(); - } catch (IOException e) { - LOGGER.warn("Can not close inputStream properly", e); - } - - try { - outputStream.close(); - } catch (IOException e) { - LOGGER.warn("Can not close outputStream properly", e); - } - - try { - socket.close(); - } catch (IOException e) { - LOGGER.warn("Can not close socket properly", e); - } - } - - /** - * Write data to the output stream while synchronized against close(). This hopefully fixes - * sporadic test failures caused by a deadlock of write() and close() - * @param bytes The data to write - * @throws IOException if an error occurs - */ - public void syncWrite(byte[] bytes) throws IOException { - outputStream.write(bytes); - outputStream.flush(); - } -} diff --git a/src/test/java/com/notnoop/apns/utils/StringTruncationTest.java b/src/test/java/com/notnoop/apns/utils/StringTruncationTest.java deleted file mode 100644 index 275e50e8..00000000 --- a/src/test/java/com/notnoop/apns/utils/StringTruncationTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.utils; - -import org.junit.Assert; - -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; - -import com.notnoop.apns.internal.Utilities; - -// Test inspired by http://stackoverflow.com/questions/119328/how-do-i-truncate-a-java-string-to-fit-in-a-given-number-of-bytes-once-utf-8-enc -@RunWith(Theories.class) -public class StringTruncationTest { - @DataPoints public static Object[][] dataPoints = { - {"abcd", 0, 0}, - {"abcd", 1, 1}, - {"abcd", 2, 2}, - {"abcd", 3, 3}, - {"abcd", 4, 4}, - {"abcd", 5, 4}, - - {"a\u0080b", 0, 0}, - {"a\u0080b", 1, 1}, - {"a\u0080b", 2, 1}, - {"a\u0080b", 3, 3}, - {"a\u0080b", 4, 4}, - {"a\u0080b", 5, 4}, - - {"a\u0800b", 0, 0}, - {"a\u0800b", 1, 1}, - {"a\u0800b", 2, 1}, - {"a\u0800b", 3, 1}, - {"a\u0800b", 4, 4}, - {"a\u0800b", 5, 5}, - {"a\u0800b", 6, 5}, - }; - - @DataPoints public static Object[][] surrogatePairs = { - {"\uD834\uDD1E", 0, 0}, - {"\uD834\uDD1E", 1, 0}, - {"\uD834\uDD1E", 2, 0}, - {"\uD834\uDD1E", 3, 0}, - {"\uD834\uDD1E", 4, 4}, - {"\uD834\uDD1E", 5, 4} - }; - - @Theory - public void truncateUTF8Properly(Object[] p) { - String str = (String)p[0]; - int maxBytes = (Integer)p[1]; - int expectedBytes = (Integer)p[2]; - - String result = Utilities.truncateWhenUTF8(str, maxBytes); - byte[] utf8 = Utilities.toUTF8Bytes(result); - - Assert.assertEquals(expectedBytes, utf8.length); - } -} diff --git a/src/test/java/com/notnoop/apns/utils/junit/DumpThreadsOnErrorRule.java b/src/test/java/com/notnoop/apns/utils/junit/DumpThreadsOnErrorRule.java deleted file mode 100644 index 9ac5565d..00000000 --- a/src/test/java/com/notnoop/apns/utils/junit/DumpThreadsOnErrorRule.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.utils.junit; - - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.Map; - -public class DumpThreadsOnErrorRule implements TestRule { - - @Override - public Statement apply(Statement base, Description description) { - return new DumpThreadsStatement(base); - } - - private static class DumpThreadsStatement extends Statement { - - private final Statement base; - - private DumpThreadsStatement(Statement base) { - this.base = base; - } - - @Override - public void evaluate() throws Throwable { - try { - base.evaluate(); - } catch (Throwable t) { - dumpAllThreads(); - throw t; - } - } - - private void dumpAllThreads() { - Map liveThreads = Thread.getAllStackTraces(); - for (Object o : liveThreads.keySet()) { - Thread key = (Thread) o; - System.err.println("\nThread " + key.getName()); - StackTraceElement[] trace = (StackTraceElement[]) liveThreads.get(key); - for (StackTraceElement aTrace : trace) { - System.err.println("\tat " + aTrace); - } - } - } - } -} \ No newline at end of file diff --git a/src/test/java/com/notnoop/apns/utils/junit/Repeat.java b/src/test/java/com/notnoop/apns/utils/junit/Repeat.java deleted file mode 100644 index 382ace30..00000000 --- a/src/test/java/com/notnoop/apns/utils/junit/Repeat.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.utils.junit; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target({java.lang.annotation.ElementType.METHOD}) -public @interface Repeat { - public abstract int count(); -} diff --git a/src/test/java/com/notnoop/apns/utils/junit/RepeatRule.java b/src/test/java/com/notnoop/apns/utils/junit/RepeatRule.java deleted file mode 100644 index eeababe9..00000000 --- a/src/test/java/com/notnoop/apns/utils/junit/RepeatRule.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.utils.junit; - - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -public class RepeatRule implements TestRule { - - @Override - public Statement apply(Statement base, Description description) { - Repeat repeat = description.getAnnotation(Repeat.class); - if (repeat != null) { - return new RepeatStatement(repeat.count(), base); - } - return base; - } - - private static class RepeatStatement extends Statement { - - private final int count; - private final Statement base; - - private RepeatStatement(int count, Statement base) { - this.count = count; - this.base = base; - } - - @Override - public void evaluate() throws Throwable { - for (int i = count; i > 0; i--) { - base.evaluate(); - } - } - } -} \ No newline at end of file diff --git a/src/test/resources/clientStore.jks b/src/test/resources/clientStore.jks deleted file mode 100644 index 8564ff4a..00000000 Binary files a/src/test/resources/clientStore.jks and /dev/null differ diff --git a/src/test/resources/clientStore.p12 b/src/test/resources/clientStore.p12 deleted file mode 100644 index ef8243e1..00000000 Binary files a/src/test/resources/clientStore.p12 and /dev/null differ diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml deleted file mode 100644 index a48239f3..00000000 --- a/src/test/resources/logback-test.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/test/resources/serverStore.p12 b/src/test/resources/serverStore.p12 deleted file mode 100644 index 80806269..00000000 Binary files a/src/test/resources/serverStore.p12 and /dev/null differ diff --git a/src/test/resources/serverTrustStore.p12 b/src/test/resources/serverTrustStore.p12 deleted file mode 100644 index 458b8b63..00000000 Binary files a/src/test/resources/serverTrustStore.p12 and /dev/null differ diff --git a/stresstest.sh b/stresstest.sh deleted file mode 100755 index 5410b6eb..00000000 --- a/stresstest.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -rm builds.logs -while mvn clean install; do date >>builds.log; done diff --git a/template.mf b/template.mf deleted file mode 100644 index aca149c3..00000000 --- a/template.mf +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: com.notnoop.apns -Bundle-ManifestVersion: 2 -Import-Template: - org.slf4j.*;version="0", - com.fasterxml.jackson.*;version="0", - javax.net.*;version="0", - edu.umd.cs.findbugs.annotations.*;version="0", - org.apache.*;version="0" diff --git a/tools/generate_test_stores.sh b/tools/generate_test_stores.sh deleted file mode 100644 index 2d3899b5..00000000 --- a/tools/generate_test_stores.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -KEYSIZE=1024 -VALIDITY=1460 -rm -f serverStore.p12 serverTrustStore.p12 caKey.pem caCert.pem request.crs clientStore.p12 clientCert.crt clientStore.jks - -echo ---=== Generating Certificates ===--- -echo This tools will request several parameters. -echo The names are free, but it is recommended -echo to enter a name that identifies the certificate -echo for example TestServer, TestCA, TestClient, TestInvalidClient -echo Enter 123456 when asked for a password -echo - -# Server Cert -echo --== Generating Server Certificate ==-- -keytool -genkey -keyalg RSA -alias notnoop-server \ - -keystore serverStore.p12 -storepass 123456 -storetype PKCS12 \ - -validity ${VALIDITY} -keysize ${KEYSIZE} -keytool -exportcert -alias notnoop-server \ - -keystore serverStore.p12 -storepass 123456 -storetype PKCS12 | keytool -printcert - -# Client Cert CA -echo --== Generating Client CA Certificate ==-- -keytool -genkey -keyalg RSA -alias notnoop-ca \ - -keystore serverTrustStore.p12 -storetype pkcs12 -storepass 123456 \ - -validity ${VALIDITY} -keysize ${KEYSIZE} -keytool -exportcert -alias notnoop-ca \ - -keystore serverTrustStore.p12 -storepass 123456 -storetype PKCS12 | keytool -printcert - -openssl pkcs12 -in serverTrustStore.p12 -nocerts -out caKey.pem -openssl pkcs12 -in serverTrustStore.p12 -clcerts -nokeys -out caCert.pem - -echo --== Generating Client Certificate ==-- -keytool -genkey -keyalg RSA -keystore clientStore.p12 -storepass 123456 -storetype PKCS12 -alias notnoop-client \ - -validity ${VALIDITY} -keysize ${KEYSIZE} -keytool -certreq -keystore clientStore.p12 -storepass 123456 -storetype PKCS12 -alias notnoop-client -file request.crs - -echo --== Signing Client Certificate with CA ==-- -openssl x509 -req -CA CACert.pem -CAkey CAKey.pem -in request.crs -out clientCert.crt \ - -days ${VALIDITY} -CAcreateserial -outform PEM -set_serial 1 -cat caCert.pem >> clientCert.crt -keytool -import -keystore clientStore.p12 -storepass 123456 -storetype PKCS12 -file clientCert.crt -alias notnoop-client -keytool -exportcert -alias notnoop-client \ - -keystore clientStore.p12 -storepass 123456 -storetype PKCS12 | keytool -printcert - -echo --== Generating JKS Client Keystore with Client Certificate ==-- -keytool -importkeystore \ - -srckeystore clientStore.p12 -srcstorepass 123456 -srcstoretype PKCS12 -srcalias notnoop-client \ - -destkeystore clientStore.jks -deststorepass 123456 -deststoretype JKS -destalias notnoop-client - -echo --== Generating Invalid Client Certificate ==-- -keytool -genkeypair -keyalg RSA -keystore clientStore.jks -storepass 123456 -storetype JKS -alias notused - -keytool -list -keystore clientStore.jks -storepass 123456 -storetype JKS - -rm caKey.pem caCert.pem request.crs clientCert.crt \ No newline at end of file