Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add a simple delegate

  • Loading branch information...
commit ecc1d2a44eee10c235a7097d7418351497df2c85 1 parent 773378c
Mahmood Ali authored
35 src/main/java/com/notnoop/mpns/MpnsDelegate.java
... ... @@ -0,0 +1,35 @@
  1 +/*
  2 + * Copyright 2011, Mahmood Ali.
  3 + * All rights reserved.
  4 + *
  5 + * Redistribution and use in source and binary forms, with or without
  6 + * modification, are permitted provided that the following conditions are
  7 + * met:
  8 + *
  9 + * * Redistributions of source code must retain the above copyright
  10 + * notice, this list of conditions and the following disclaimer.
  11 + * * Redistributions in binary form must reproduce the above
  12 + * copyright notice, this list of conditions and the following disclaimer
  13 + * in the documentation and/or other materials provided with the
  14 + * distribution.
  15 + * * Neither the name of Mahmood Ali. nor the names of its
  16 + * contributors may be used to endorse or promote products derived from
  17 + * this software without specific prior written permission.
  18 + *
  19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23 + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30 + */
  31 +package com.notnoop.mpns;
  32 +
  33 +public interface MpnsDelegate {
  34 + public void messageSent(String messageId, MpnsResponse response);
  35 +}
144 src/main/java/com/notnoop/mpns/MpnsResponse.java
... ... @@ -0,0 +1,144 @@
  1 +/*
  2 + * Copyright 2011, Mahmood Ali.
  3 + * All rights reserved.
  4 + *
  5 + * Redistribution and use in source and binary forms, with or without
  6 + * modification, are permitted provided that the following conditions are
  7 + * met:
  8 + *
  9 + * * Redistributions of source code must retain the above copyright
  10 + * notice, this list of conditions and the following disclaimer.
  11 + * * Redistributions in binary form must reproduce the above
  12 + * copyright notice, this list of conditions and the following disclaimer
  13 + * in the documentation and/or other materials provided with the
  14 + * distribution.
  15 + * * Neither the name of Mahmood Ali. nor the names of its
  16 + * contributors may be used to endorse or promote products derived from
  17 + * this software without specific prior written permission.
  18 + *
  19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23 + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30 + */
  31 +package com.notnoop.mpns;
  32 +
  33 +/**
  34 + * Represents the logical response of MpnsService
  35 + */
  36 +public enum MpnsResponse {
  37 + /*
  38 + * The notification request was accepted and queued for delivery.
  39 + */
  40 + RECEIVED(200, "Received", "Connected", "Active"),
  41 +
  42 + /**
  43 + * The notification request was accepted and queued for delivery. However,
  44 + * the device is temporarily disconnected.
  45 + */
  46 + QUEUED(200, "Received", "Temporarily Disconnected", "Active"),
  47 +
  48 + /**
  49 + * Queue overflow. The web service should re-send the notification later.
  50 + * A best practice is to use an exponential backoff algorithm in minute
  51 + * increments.
  52 + */
  53 + QUEUE_FULL(200, "QueueFull", null, "Active"),
  54 +
  55 + /**
  56 + * The push notification was received and dropped by the Push Notification
  57 + * Service. The Suppressed status can occur if the notification channel
  58 + * was configured to suppress push notifications for a particular push
  59 + * notification class.
  60 + */
  61 + SUPPRESSED(200, "Suppressed", null, "Active"),
  62 +
  63 + /**
  64 + * This error occurs when the web service sends a notification request
  65 + * with a bad XML document or malformed notification URI.
  66 + */
  67 + BAD_REQUEST(400, null, null, null),
  68 +
  69 + /**
  70 + * Sending this notification is unauthorized. This error can occur for one
  71 + * of the following reasons:
  72 + */
  73 + UNAUTHORIZED(401, null, null, null),
  74 +
  75 + /**
  76 + * The subscription is invalid and is not present on the Push Notification
  77 + * Service. The web service should stop sending new notifications to this
  78 + * subscription, and drop the subscription state for its corresponding
  79 + * application session.
  80 + */
  81 + EXPIRED(404, "Dropped", null, "Expired"),
  82 +
  83 + /**
  84 + * Invalid method (PUT, DELETE, CREATE). Only POST is allowed when sending
  85 + * a notification request.
  86 + */
  87 + METHOD_NOT_ALLOWED(405, null, null, null),
  88 +
  89 + /**
  90 + * This error occurs when an unauthenticated web service has reached the
  91 + * per day throttling limit for a subscription. The web service can try to
  92 + * re-send the push notification every one hour after receiving this
  93 + * error. The web service may need to wait up to 24 hours before normal
  94 + * notification flow will resume.
  95 + */
  96 + OVER_LIMIT(406, "Dropped", null, "Active"),
  97 +
  98 + /**
  99 + * The device is in an inactive state. The web service may re-attempt
  100 + * sending the request one time per hour at maximum after receiving this
  101 + * error. If the web service violates the maximum of one re-attempt per
  102 + * hour, the Push Notification Service will de-register or permanently
  103 + * block the web service.
  104 + */
  105 + INACTIVATE_STATE(412, "Dropped", "Inactive", null),
  106 +
  107 + /**
  108 + * The Push Notification Service is unable to process the request. The web
  109 + * service should re-send the notification later. A best practice is to
  110 + * use an exponential backoff algorithm in minute increments.
  111 + */
  112 + SERVICE_UNAVAILABLE(503, null, null, null);
  113 +
  114 + //// Response Code,NotificationStatus,DeviceConnectionStatus,SubscriptionStatus,Comments
  115 + private final int responseCode;
  116 + private final String notificationStatus;
  117 + private final String deviceConnectionStatus;
  118 + private final String subscriptionStatus;
  119 +
  120 + MpnsResponse(int responseCode, String notificationStatus,
  121 + String deviceConnectionStatus,
  122 + String subscriptionStatus) {
  123 + this.responseCode = responseCode;
  124 + this.notificationStatus = notificationStatus;
  125 + this.deviceConnectionStatus = deviceConnectionStatus;
  126 + this.subscriptionStatus = subscriptionStatus;
  127 + }
  128 +
  129 + public int getResponseCode() {
  130 + return responseCode;
  131 + }
  132 +
  133 + public String getNotificationStatus() {
  134 + return notificationStatus;
  135 + }
  136 +
  137 + public String getDeviceConnectionStatus() {
  138 + return deviceConnectionStatus;
  139 + }
  140 +
  141 + public String getSubscriptionStatus() {
  142 + return subscriptionStatus;
  143 + }
  144 +}
11 src/main/java/com/notnoop/mpns/MpnsServiceBuilder.java
@@ -66,6 +66,8 @@
66 66 private HttpClient httpClient = null;
67 67 private int timeout = -1;
68 68
  69 + private MpnsDelegate delegate;
  70 +
69 71 /**
70 72 * Constructs a new instance of {@code MpnsServiceBuilder}
71 73 */
@@ -161,6 +163,11 @@ public MpnsServiceBuilder timeout(int timeout) {
161 163 return this;
162 164 }
163 165
  166 + public MpnsServiceBuilder delegate(MpnsDelegate delegate) {
  167 + this.delegate = delegate;
  168 + return this;
  169 + }
  170 +
164 171 /**
165 172 * Returns a fully initialized instance of {@link MpnsService},
166 173 * according to the requested settings.
@@ -193,9 +200,9 @@ public MpnsService build() {
193 200 // Configure service
194 201 AbstractMpnsService service;
195 202 if (pooledMax == 1) {
196   - service = new MpnsServiceImpl(client);
  203 + service = new MpnsServiceImpl(client, delegate);
197 204 } else {
198   - service = new MpnsPooledService(client, executor);
  205 + service = new MpnsPooledService(client, executor, delegate);
199 206 }
200 207
201 208 if (isQueued) {
12 src/main/java/com/notnoop/mpns/internal/MpnsPooledService.java
@@ -37,15 +37,19 @@
37 37 import org.apache.http.client.methods.HttpPost;
38 38 import org.apache.http.util.EntityUtils;
39 39
  40 +import com.notnoop.mpns.MpnsDelegate;
  41 +import com.notnoop.mpns.MpnsResponse;
40 42 import com.notnoop.mpns.MpnsService;
41 43
42 44 public class MpnsPooledService extends AbstractMpnsService implements MpnsService {
43 45 private final HttpClient httpClient;
44 46 private final ExecutorService executor;
  47 + private final MpnsDelegate delegate;
45 48
46   - public MpnsPooledService(HttpClient httpClient, ExecutorService executor) {
  49 + public MpnsPooledService(HttpClient httpClient, ExecutorService executor, MpnsDelegate delegate) {
47 50 this.httpClient = httpClient;
48 51 this.executor = executor;
  52 + this.delegate = delegate;
49 53 }
50 54
51 55 @Override
@@ -54,6 +58,12 @@ protected void push(final HttpPost request) {
54 58 public void run() {
55 59 try {
56 60 HttpResponse response = httpClient.execute(request);
  61 + if (delegate != null) {
  62 + MpnsResponse r = Utilities.logicalResponseFor(response);
  63 + String messageId = Utilities.messageIdOf(response);
  64 +
  65 + delegate.messageSent(messageId, r);
  66 + }
57 67 EntityUtils.consume(response.getEntity());
58 68 } catch (Exception e) {
59 69 throw new RuntimeException(e);
14 src/main/java/com/notnoop/mpns/internal/MpnsServiceImpl.java
@@ -38,20 +38,30 @@
38 38 import org.apache.http.client.methods.HttpPost;
39 39 import org.apache.http.util.EntityUtils;
40 40
  41 +import com.notnoop.mpns.MpnsDelegate;
  42 +import com.notnoop.mpns.MpnsResponse;
41 43 import com.notnoop.mpns.MpnsService;
42 44 import com.notnoop.mpns.exceptions.NetworkIOException;
43 45
44 46 public class MpnsServiceImpl extends AbstractMpnsService implements MpnsService {
45   - protected final HttpClient httpClient;
  47 + private final HttpClient httpClient;
  48 + private final MpnsDelegate delegate;
46 49
47   - public MpnsServiceImpl(HttpClient httpClient) {
  50 + public MpnsServiceImpl(HttpClient httpClient, MpnsDelegate delegate) {
48 51 this.httpClient = httpClient;
  52 + this.delegate = delegate;
49 53 }
50 54
51 55 @Override
52 56 protected void push(HttpPost request) {
53 57 try {
54 58 HttpResponse response = httpClient.execute(request);
  59 + if (delegate != null) {
  60 + MpnsResponse r = Utilities.logicalResponseFor(response);
  61 + String messageId = Utilities.messageIdOf(response);
  62 +
  63 + delegate.messageSent(messageId, r);
  64 + }
55 65 EntityUtils.consume(response.getEntity());
56 66 } catch (ClientProtocolException e) {
57 67 throw new RuntimeException(e);
44 src/main/java/com/notnoop/mpns/internal/Utilities.java
@@ -30,8 +30,12 @@
30 30 */
31 31 package com.notnoop.mpns.internal;
32 32
  33 +import org.apache.http.Header;
  34 +import org.apache.http.HttpResponse;
33 35 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
34 36
  37 +import com.notnoop.mpns.MpnsResponse;
  38 +
35 39 import java.io.InputStream;
36 40 import java.io.InputStreamReader;
37 41 import java.io.Reader;
@@ -109,4 +113,44 @@ public static String escapeXml(String value) {
109 113 throw new AssertionError("The world is coming to an end! No UTF-8 support");
110 114 }
111 115 }
  116 +
  117 + private static String headerValue(HttpResponse response, String header) {
  118 + Header[] headers = response.getHeaders(header);
  119 +
  120 + return headers.length == 0 ? null : headers[0].getValue();
  121 + }
  122 +
  123 + private static MpnsResponse[] logicalResponses = MpnsResponse.values();
  124 + public static MpnsResponse logicalResponseFor(HttpResponse response) {
  125 + for (MpnsResponse r: logicalResponses) {
  126 + if (r.getResponseCode() != response.getStatusLine().getStatusCode()) {
  127 + continue;
  128 + }
  129 +
  130 + if (r.getNotificationStatus() != null
  131 + && !r.getNotificationStatus().equals(headerValue(response, "X-NotificationStatus"))) {
  132 + continue;
  133 + }
  134 +
  135 + if (r.getDeviceConnectionStatus() != null
  136 + && !r.getNotificationStatus().equals(headerValue(response, "X-DeviceConnectionStatus"))) {
  137 + continue;
  138 + }
  139 +
  140 + if (r.getSubscriptionStatus() != null
  141 + && !r.getSubscriptionStatus().equals(headerValue(response, "X-SubscriptionStatus"))) {
  142 + continue;
  143 + }
  144 +
  145 + return r;
  146 + }
  147 +
  148 + // Didn't find anything
  149 + assert false;
  150 + return null;
  151 + }
  152 +
  153 + public static String messageIdOf(HttpResponse response) {
  154 + return headerValue(response, "X-MessageID");
  155 + }
112 156 }
4 src/test/java/com/notnoop/mpns/internal/XMLEscapingTest.java
@@ -42,7 +42,7 @@
42 42 public void escapesNull() {
43 43 assertThat(escapeXml(null), is((String)null));
44 44 }
45   -
  45 +
46 46 @Test
47 47 public void escapeEmpty() {
48 48 assertThat(escapeXml(""), is(""));
@@ -58,7 +58,7 @@ public void escapeNonEntities() {
58 58 public void escapeAmpt() {
59 59 assertThat(escapeXml("test&"), is("test&"));
60 60 }
61   -
  61 +
62 62 @Test
63 63 public void escapeAllValues() {
64 64 assertThat(escapeXml("AT&T called \"Johns's father\" > 2 hours ago < 5 years ago."),

0 comments on commit ecc1d2a

Please sign in to comment.
Something went wrong with that request. Please try again.