Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,20 @@ To send a notification, you can do it in two steps:
.build();
String subscriptionUri = "https://..../"
service.push(subscriptionUri, notification);

3. Create a ssl connection

MpnsService service = MPNS.newService()
.withCert(WIN_MPNS_KEYSTORE_PATH, WIN_MPNS_CERTIFICATE_PASS, "JKS", KeyManagerFactory.getDefaultAlgorithm())
.build();
The common name of certificate used in creating the keystore should be used during registering for the windows push service
(opening the push channel) by the windows native app.

That's it!

Features In the Making
---------------------------
* Authenticated Connections
* Authenticated Connections(DONE)
* Auto retries (exponential back-off feature)
* More testing!

Expand Down
58 changes: 56 additions & 2 deletions src/main/java/com/notnoop/mpns/MpnsServiceBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,30 @@
*/
package com.notnoop.mpns;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import com.notnoop.mpns.internal.*;
import com.notnoop.mpns.exceptions.InvalidSSLConfig;
import com.notnoop.mpns.exceptions.RuntimeIOException;
import com.notnoop.mpns.internal.AbstractMpnsService;
import com.notnoop.mpns.internal.MpnsPooledService;
import com.notnoop.mpns.internal.MpnsQueuedService;
import com.notnoop.mpns.internal.MpnsServiceImpl;
import com.notnoop.mpns.internal.Utilities;

/**
* The class is used to create instances of {@link MpnsService}.
Expand All @@ -58,6 +71,9 @@
* </pre>
*/
public class MpnsServiceBuilder {

private SSLContext sslContext;

private int pooledMax = 1;
private ExecutorService executor = null;

Expand All @@ -72,6 +88,36 @@ public class MpnsServiceBuilder {
* Constructs a new instance of {@code MpnsServiceBuilder}
*/
public MpnsServiceBuilder() { }

public MpnsServiceBuilder withCert(String fileName, String password, String ksType, String kAlgo)
throws RuntimeIOException, InvalidSSLConfig
{
FileInputStream stream = null;
try {
stream = new FileInputStream(fileName);
return withCert(stream, password, ksType, kAlgo);
} catch (FileNotFoundException e) {
throw new RuntimeIOException(e);
} finally {
Utilities.close(stream);
}
}

public MpnsServiceBuilder withCert(InputStream stream, String password, String ksType, String kAlgo)
throws InvalidSSLConfig
{
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("Passwords must be specified." +
"Oracle Java SDK does not support passwordless p12 certificates");
}

return withSSLContext(Utilities.newSSLContext(stream, password,ksType, kAlgo));
}

public MpnsServiceBuilder withSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}

/**
* Specify the address of the HTTP proxy the connection should
Expand Down Expand Up @@ -182,9 +228,17 @@ public MpnsService build() {
if (httpClient != null) {
client = httpClient;
} else if (pooledMax == 1) {
client = new DefaultHttpClient();
client = new DefaultHttpClient();

SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext);
Scheme sch = new Scheme("https", 443, socketFactory);
client.getConnectionManager().getSchemeRegistry().register(sch);
} else {
client = new DefaultHttpClient(Utilities.poolManager(pooledMax));

SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext);
Scheme sch = new Scheme("https", 443, socketFactory);
client.getConnectionManager().getSchemeRegistry().register(sch);
}

if (proxy != null) {
Expand Down
101 changes: 91 additions & 10 deletions src/main/java/com/notnoop/mpns/internal/Utilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,33 @@
*/
package com.notnoop.mpns.internal;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.util.Enumeration;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.notnoop.mpns.MpnsDelegate;
import com.notnoop.mpns.MpnsNotification;
import com.notnoop.mpns.MpnsResponse;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import com.notnoop.mpns.exceptions.InvalidSSLConfig;

public final class Utilities {
private static Logger logger = LoggerFactory.getLogger(Utilities.class);
private Utilities() { throw new AssertionError("Uninstantiable class"); }

/**
Expand Down Expand Up @@ -115,7 +128,7 @@ public static MpnsResponse logicalResponseFor(HttpResponse response) {
}

if (r.getDeviceConnectionStatus() != null
&& !r.getNotificationStatus().equals(headerValue(response, "X-DeviceConnectionStatus"))) {
&& !r.getDeviceConnectionStatus().equals(headerValue(response, "X-DeviceConnectionStatus"))) {
continue;
}

Expand All @@ -131,16 +144,84 @@ public static MpnsResponse logicalResponseFor(HttpResponse response) {
assert false;
return null;
}

public static void close(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException e) {
logger.debug("error while closing resource", e);
}
}

public static void fireDelegate(MpnsNotification message, HttpResponse response, MpnsDelegate delegate) {
if (delegate != null) {
MpnsResponse r = Utilities.logicalResponseFor(response);

if (r.isSuccessful()) {
delegate.messageSent(message, r);
} else {
delegate.messageFailed(message, r);
if(r!=null) {
if (r.isSuccessful()) {
delegate.messageSent(message, r);
} else {
delegate.messageFailed(message, r);
}
}
}
}

public static SSLSocketFactory newSSLSocketFactory(InputStream cert, String password,
String ksType, String ksAlgorithm) throws InvalidSSLConfig
{
SSLContext context = newSSLContext(cert, password, ksType, ksAlgorithm);
return context.getSocketFactory();
}

// Create a trust manager that does not validate certificate chains
private static TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{}

public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{}
}
};

public static SSLContext newSSLContext(InputStream cert, String password,
String ksType, String ksAlgorithm) throws InvalidSSLConfig
{
try {
KeyStore ks = KeyStore.getInstance(ksType);
ks.load(cert, password.toCharArray());

// Get a KeyManager and initialize it
KeyManagerFactory kmf = KeyManagerFactory.getInstance(ksAlgorithm);
kmf.init(ks, password.toCharArray());

// Get a TrustManagerFactory and init with KeyStore
TrustManagerFactory tmf = TrustManagerFactory.getInstance(ksAlgorithm);
tmf.init(ks);

// Get the SSLContext to help create SSLSocketFactory
SSLContext sslc = SSLContext.getInstance("TLS");
sslc.init(kmf.getKeyManagers(), trustAllCerts, null);

logger.debug("SSL context read with following properties:");
logger.debug("Aliases");
String alias;
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
alias = aliases.nextElement();
logger.debug("Alias:"+alias+" with certificate:"+ks.getCertificate(alias)+" certType:"+ks.getCertificate(alias).getType());
}
return sslc;
} catch (Exception e) {
throw new InvalidSSLConfig(e);
}
}
}