Permalink
Browse files

add a simple delegate

  • Loading branch information...
1 parent 773378c commit ecc1d2a44eee10c235a7097d7418351497df2c85 @notnoop committed Jun 25, 2011
View
35 src/main/java/com/notnoop/mpns/MpnsDelegate.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011, 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.mpns;
+
+public interface MpnsDelegate {
+ public void messageSent(String messageId, MpnsResponse response);
+}
View
144 src/main/java/com/notnoop/mpns/MpnsResponse.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2011, 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.mpns;
+
+/**
+ * Represents the logical response of MpnsService
+ */
+public enum MpnsResponse {
+ /*
+ * The notification request was accepted and queued for delivery.
+ */
+ RECEIVED(200, "Received", "Connected", "Active"),
+
+ /**
+ * The notification request was accepted and queued for delivery. However,
+ * the device is temporarily disconnected.
+ */
+ QUEUED(200, "Received", "Temporarily Disconnected", "Active"),
+
+ /**
+ * Queue overflow. The web service should re-send the notification later.
+ * A best practice is to use an exponential backoff algorithm in minute
+ * increments.
+ */
+ QUEUE_FULL(200, "QueueFull", null, "Active"),
+
+ /**
+ * The push notification was received and dropped by the Push Notification
+ * Service. The Suppressed status can occur if the notification channel
+ * was configured to suppress push notifications for a particular push
+ * notification class.
+ */
+ SUPPRESSED(200, "Suppressed", null, "Active"),
+
+ /**
+ * This error occurs when the web service sends a notification request
+ * with a bad XML document or malformed notification URI.
+ */
+ BAD_REQUEST(400, null, null, null),
+
+ /**
+ * Sending this notification is unauthorized. This error can occur for one
+ * of the following reasons:
+ */
+ UNAUTHORIZED(401, null, null, null),
+
+ /**
+ * The subscription is invalid and is not present on the Push Notification
+ * Service. The web service should stop sending new notifications to this
+ * subscription, and drop the subscription state for its corresponding
+ * application session.
+ */
+ EXPIRED(404, "Dropped", null, "Expired"),
+
+ /**
+ * Invalid method (PUT, DELETE, CREATE). Only POST is allowed when sending
+ * a notification request.
+ */
+ METHOD_NOT_ALLOWED(405, null, null, null),
+
+ /**
+ * This error occurs when an unauthenticated web service has reached the
+ * per day throttling limit for a subscription. The web service can try to
+ * re-send the push notification every one hour after receiving this
+ * error. The web service may need to wait up to 24 hours before normal
+ * notification flow will resume.
+ */
+ OVER_LIMIT(406, "Dropped", null, "Active"),
+
+ /**
+ * The device is in an inactive state. The web service may re-attempt
+ * sending the request one time per hour at maximum after receiving this
+ * error. If the web service violates the maximum of one re-attempt per
+ * hour, the Push Notification Service will de-register or permanently
+ * block the web service.
+ */
+ INACTIVATE_STATE(412, "Dropped", "Inactive", null),
+
+ /**
+ * The Push Notification Service is unable to process the request. The web
+ * service should re-send the notification later. A best practice is to
+ * use an exponential backoff algorithm in minute increments.
+ */
+ SERVICE_UNAVAILABLE(503, null, null, null);
+
+ //// Response Code,NotificationStatus,DeviceConnectionStatus,SubscriptionStatus,Comments
+ private final int responseCode;
+ private final String notificationStatus;
+ private final String deviceConnectionStatus;
+ private final String subscriptionStatus;
+
+ MpnsResponse(int responseCode, String notificationStatus,
+ String deviceConnectionStatus,
+ String subscriptionStatus) {
+ this.responseCode = responseCode;
+ this.notificationStatus = notificationStatus;
+ this.deviceConnectionStatus = deviceConnectionStatus;
+ this.subscriptionStatus = subscriptionStatus;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ public String getNotificationStatus() {
+ return notificationStatus;
+ }
+
+ public String getDeviceConnectionStatus() {
+ return deviceConnectionStatus;
+ }
+
+ public String getSubscriptionStatus() {
+ return subscriptionStatus;
+ }
+}
View
11 src/main/java/com/notnoop/mpns/MpnsServiceBuilder.java
@@ -66,6 +66,8 @@
private HttpClient httpClient = null;
private int timeout = -1;
+ private MpnsDelegate delegate;
+
/**
* Constructs a new instance of {@code MpnsServiceBuilder}
*/
@@ -161,6 +163,11 @@ public MpnsServiceBuilder timeout(int timeout) {
return this;
}
+ public MpnsServiceBuilder delegate(MpnsDelegate delegate) {
+ this.delegate = delegate;
+ return this;
+ }
+
/**
* Returns a fully initialized instance of {@link MpnsService},
* according to the requested settings.
@@ -193,9 +200,9 @@ public MpnsService build() {
// Configure service
AbstractMpnsService service;
if (pooledMax == 1) {
- service = new MpnsServiceImpl(client);
+ service = new MpnsServiceImpl(client, delegate);
} else {
- service = new MpnsPooledService(client, executor);
+ service = new MpnsPooledService(client, executor, delegate);
}
if (isQueued) {
View
12 src/main/java/com/notnoop/mpns/internal/MpnsPooledService.java
@@ -37,15 +37,19 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;
+import com.notnoop.mpns.MpnsDelegate;
+import com.notnoop.mpns.MpnsResponse;
import com.notnoop.mpns.MpnsService;
public class MpnsPooledService extends AbstractMpnsService implements MpnsService {
private final HttpClient httpClient;
private final ExecutorService executor;
+ private final MpnsDelegate delegate;
- public MpnsPooledService(HttpClient httpClient, ExecutorService executor) {
+ public MpnsPooledService(HttpClient httpClient, ExecutorService executor, MpnsDelegate delegate) {
this.httpClient = httpClient;
this.executor = executor;
+ this.delegate = delegate;
}
@Override
@@ -54,6 +58,12 @@ protected void push(final HttpPost request) {
public void run() {
try {
HttpResponse response = httpClient.execute(request);
+ if (delegate != null) {
+ MpnsResponse r = Utilities.logicalResponseFor(response);
+ String messageId = Utilities.messageIdOf(response);
+
+ delegate.messageSent(messageId, r);
+ }
EntityUtils.consume(response.getEntity());
} catch (Exception e) {
throw new RuntimeException(e);
View
14 src/main/java/com/notnoop/mpns/internal/MpnsServiceImpl.java
@@ -38,20 +38,30 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;
+import com.notnoop.mpns.MpnsDelegate;
+import com.notnoop.mpns.MpnsResponse;
import com.notnoop.mpns.MpnsService;
import com.notnoop.mpns.exceptions.NetworkIOException;
public class MpnsServiceImpl extends AbstractMpnsService implements MpnsService {
- protected final HttpClient httpClient;
+ private final HttpClient httpClient;
+ private final MpnsDelegate delegate;
- public MpnsServiceImpl(HttpClient httpClient) {
+ public MpnsServiceImpl(HttpClient httpClient, MpnsDelegate delegate) {
this.httpClient = httpClient;
+ this.delegate = delegate;
}
@Override
protected void push(HttpPost request) {
try {
HttpResponse response = httpClient.execute(request);
+ if (delegate != null) {
+ MpnsResponse r = Utilities.logicalResponseFor(response);
+ String messageId = Utilities.messageIdOf(response);
+
+ delegate.messageSent(messageId, r);
+ }
EntityUtils.consume(response.getEntity());
} catch (ClientProtocolException e) {
throw new RuntimeException(e);
View
44 src/main/java/com/notnoop/mpns/internal/Utilities.java
@@ -30,8 +30,12 @@
*/
package com.notnoop.mpns.internal;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import com.notnoop.mpns.MpnsResponse;
+
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
@@ -109,4 +113,44 @@ public static String escapeXml(String value) {
throw new AssertionError("The world is coming to an end! No UTF-8 support");
}
}
+
+ private static String headerValue(HttpResponse response, String header) {
+ Header[] headers = response.getHeaders(header);
+
+ return headers.length == 0 ? null : headers[0].getValue();
+ }
+
+ private static MpnsResponse[] logicalResponses = MpnsResponse.values();
+ public static MpnsResponse logicalResponseFor(HttpResponse response) {
+ for (MpnsResponse r: logicalResponses) {
+ if (r.getResponseCode() != response.getStatusLine().getStatusCode()) {
+ continue;
+ }
+
+ if (r.getNotificationStatus() != null
+ && !r.getNotificationStatus().equals(headerValue(response, "X-NotificationStatus"))) {
+ continue;
+ }
+
+ if (r.getDeviceConnectionStatus() != null
+ && !r.getNotificationStatus().equals(headerValue(response, "X-DeviceConnectionStatus"))) {
+ continue;
+ }
+
+ if (r.getSubscriptionStatus() != null
+ && !r.getSubscriptionStatus().equals(headerValue(response, "X-SubscriptionStatus"))) {
+ continue;
+ }
+
+ return r;
+ }
+
+ // Didn't find anything
+ assert false;
+ return null;
+ }
+
+ public static String messageIdOf(HttpResponse response) {
+ return headerValue(response, "X-MessageID");
+ }
}
View
4 src/test/java/com/notnoop/mpns/internal/XMLEscapingTest.java
@@ -42,7 +42,7 @@
public void escapesNull() {
assertThat(escapeXml(null), is((String)null));
}
-
+
@Test
public void escapeEmpty() {
assertThat(escapeXml(""), is(""));
@@ -58,7 +58,7 @@ public void escapeNonEntities() {
public void escapeAmpt() {
assertThat(escapeXml("test&"), is("test&"));
}
-
+
@Test
public void escapeAllValues() {
assertThat(escapeXml("AT&T called \"Johns's father\" > 2 hours ago < 5 years ago."),

0 comments on commit ecc1d2a

Please sign in to comment.