Skip to content

Commit

Permalink
add a simple delegate
Browse files Browse the repository at this point in the history
  • Loading branch information
notnoop committed Jun 25, 2011
1 parent 773378c commit ecc1d2a
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 7 deletions.
35 changes: 35 additions & 0 deletions 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);
}
144 changes: 144 additions & 0 deletions 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;
}
}
11 changes: 9 additions & 2 deletions src/main/java/com/notnoop/mpns/MpnsServiceBuilder.java
Expand Up @@ -66,6 +66,8 @@ public class MpnsServiceBuilder {
private HttpClient httpClient = null;
private int timeout = -1;

private MpnsDelegate delegate;

/**
* Constructs a new instance of {@code MpnsServiceBuilder}
*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/com/notnoop/mpns/internal/MpnsPooledService.java
Expand Up @@ -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
Expand All @@ -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);
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/notnoop/mpns/internal/MpnsServiceImpl.java
Expand Up @@ -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);
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/com/notnoop/mpns/internal/Utilities.java
Expand Up @@ -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;
Expand Down Expand Up @@ -109,4 +113,44 @@ public static byte[] toUTF8(String content) {
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");
}
}
4 changes: 2 additions & 2 deletions src/test/java/com/notnoop/mpns/internal/XMLEscapingTest.java
Expand Up @@ -42,7 +42,7 @@ public class XMLEscapingTest {
public void escapesNull() {
assertThat(escapeXml(null), is((String)null));
}

@Test
public void escapeEmpty() {
assertThat(escapeXml(""), is(""));
Expand All @@ -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."),
Expand Down

0 comments on commit ecc1d2a

Please sign in to comment.