All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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