be.mjosoft.ios.apn.APNService Maven / Gradle / Ivy
package be.mjosoft.ios.apn;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
/**
* This class is used to send message to the Apple Push Notification Service.
* @author mj
*/
public class APNService
{
protected static String host = "gateway.push.apple.com";
protected static int port = 2195;
protected static String hostDevel = "gateway.sandbox.push.apple.com";
protected static int portDevel = 2195;
protected boolean develMode;
protected SSLSocketFactory sslSocketFactory;
protected Socket socket;
protected int sequence;
/**
* Constructor.
* Switches into production mode.
*/
public APNService()
{
this(false);
}
/**
* Constructor.
* @param develMode true switches into development mode, false switches into production mode.
*/
public APNService(boolean develMode)
{
this.develMode = develMode;
}
/**
* Initialises the permanent connection with the Apple Push Notification Service.
* @param certificateFile the certificate file.
* @param certificatePWD the certificate password.
* @param certificateType the certificate type.
* @throws APNException if an error occured.
*/
public void initConnexion(String certificateFile, String certificatePWD, String certificateType) throws APNException
{
try{
initConnexion(new FileInputStream(certificateFile), certificatePWD, certificateType);
}catch(FileNotFoundException ex){
throw new APNException(ex);
}
}
/**
* Initialises the permanent connection with the Apple Push Notification Service.
* @param certificateFile the certificate file.
* @param certificatePWD the certificate password.
* @param certificateType the certificate type.
* @throws APNException if an error occured.
*/
public void initConnexion(File certificateFile, String certificatePWD, String certificateType) throws APNException
{
try{
initConnexion(new FileInputStream(certificateFile), certificatePWD, certificateType);
}catch(FileNotFoundException ex){
throw new APNException(ex);
}
}
/**
* Initialises the permanent connection with the Apple Push Notification Service.
* @param certificateStream the certificate file stream.
* @param certificatePWD the certificate password.
* @param certificateType the certificate type.
* @throws APNException if an error occured.
*/
public void initConnexion(InputStream certificateStream, String certificatePWD, String certificateType) throws APNException
{
try
{
KeyStore keyStore = KeyStore.getInstance(certificateType);
keyStore.load(certificateStream, certificatePWD.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("sunx509");
keyManagerFactory.init(keyStore, certificatePWD.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("sunx509");
trustManagerFactory.init(keyStore);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(keyManagerFactory.getKeyManagers(), null, null);
sslSocketFactory = sslCtx.getSocketFactory();
connect();
}
catch(Exception ex)
{
throw new APNException(ex);
}
}
/**
* Connects to the Apple Push Notification Service.
* @throws APNException if an error occured.
*/
protected void connect() throws APNException
{
try
{
if(develMode){
socket = (SSLSocket)sslSocketFactory.createSocket(hostDevel, portDevel);
}else{
socket = (SSLSocket)sslSocketFactory.createSocket(host, port);
}
socket.setSoTimeout(1000);
sequence = 0;
}
catch(IOException ex)
{
throw new APNException(ex);
}
}
/**
* Ends the connection to the Apple Push Notification Service.
*/
public void endConnection()
{
try{
socket.close();
}catch(Exception ex){}
}
/**
* Sends a message to a device.
* @param deviceToken the token of the device to send the message.
* @param message the message to send.
* @throws APNException if an error occured.
*/
public void sendAPN(String deviceToken, APNMessage message) throws APNException
{
sequence++;
deviceToken = deviceToken == null ? "" : deviceToken.replaceAll(" ", "").replaceAll("<", "").replaceAll(">", "");
byte binary[] = createBinary(deviceToken, message);
try
{
OutputStream out = socket.getOutputStream();
out.write(binary, 0, binary.length);
out.flush();
Thread readThread = new Thread()
{
public void run()
{
byte[] response = new byte[6];
try
{
InputStream in = socket.getInputStream();
int read = in.read(response, 0, 6);
if (read > - 1)
{
if (response[1] != 0)
{
endConnection();
try {
connect();
} catch (Exception ex)
{}
}
}
}
catch (SocketTimeoutException ex)
{
}
catch (Exception ex)
{
}
}
};
readThread.start();
readThread.join(2000);
}
catch (Exception ex)
{
endConnection();
try {
connect();
} catch (Exception exx)
{}
}
}
/**
* Create the binary message to send to the Apple Push Notification Service.
* @param deviceToken the token of the device to which send the message.
* @param message the message to send.
* @return the binary representation of the message to send.
*/
protected byte[] createBinary(String deviceToken, APNMessage message)
{
// Prepare the token part.
deviceToken = deviceToken.toUpperCase();
byte deviceTokenBinary[] = new byte[deviceToken.length() / 2];
for(int i = 0, k = 0; i < deviceToken.length(); i += 2)
{
String byteS = deviceToken.substring(i, i + 2);
int byteInt = Integer.parseInt(byteS, 16);
deviceTokenBinary[k++] = (byte)byteInt;
}
byte[] messageBytes = null;
try{
messageBytes = message.toString().getBytes("UTF-8");
}catch(Exception ex){
ex.printStackTrace();
}
List bytes = new ArrayList();
// Device token
bytes.add((byte)1);
// Token length (2 bytes) (big endian)
bytes.add((byte)((byte)(deviceTokenBinary.length & 0xff00) >> 8));
bytes.add((byte)((byte)(deviceTokenBinary.length & 0xff)));
// Token (32 bytes)
for(byte b : deviceTokenBinary) {
bytes.add(b);
}
// Payload
bytes.add((byte)2);
// Payload length (2 bytes) (bid endian)
bytes.add((byte)((byte)(messageBytes.length & 0xff00) >> 8));
bytes.add((byte)((byte)(messageBytes.length & 0xff)));
// Payload (message)
for(byte b : messageBytes) {
bytes.add(b);
}
// Identifier (4 bytes)
bytes.add((byte)3);
// Identifier length (2 bytes) (big endian)
bytes.add((byte)((byte)(4 & 0xff00) >> 8));
bytes.add((byte)((byte)(4 & 0xff)));
// Identifier (4 bytes)
int identifier = sequence;
for (int i = 0; i < 4; i++)
{
bytes.add((new Integer(identifier >>> 24)).byteValue());
identifier <<= 8;
}
// Expires (4 bytes)
bytes.add((byte)4);
// Expires length (2 bytes) (big endian)
bytes.add((byte)((byte)(4 & 0xff00) >> 8));
bytes.add((byte)((byte)(4 & 0xff)));
// Expires (4 bytes)
if(message.getExpiresAfter() == 0) {
bytes.add((byte)0);
bytes.add((byte)0);
bytes.add((byte)0);
bytes.add((byte)0);
}
else
{
int expires = (int) (System.currentTimeMillis() / 1000) + message.getExpiresAfter();
for (int i = 0; i < 4; i++)
{
bytes.add((new Integer(expires >>> 24)).byteValue());
expires <<= 8;
}
}
// Priority (1 bytes)
bytes.add((byte)5);
// Priority length (2 bytes) (big endian)
bytes.add((byte)1);
// Priority
bytes.add((byte)message.getPriority());
// Writes the bytes.
int nbBytes = 1 + 4 + bytes.size();
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(nbBytes);
bytesOut.write((byte)2);
try {
bytesOut.write(htonl(bytes.size()));
} catch (IOException ex)
{}
for(Byte b : bytes) {
bytesOut.write((byte)b);
}
return bytesOut.toByteArray();
}
byte[] htonl(int x)
{
byte[] res = new byte[4];
for (int i = 0; i < 4; i++)
{
res[i] = (new Integer(x >>> 24)).byteValue();
x <<= 8;
}
return res;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy