Developing a server-side push application using Java

This topic shows you how to create a push request in the PAP push format or the BlackBerry push format. You can also look at the sample server-side application to see how to send a push request to a BlackBerry Java Application or a BlackBerry WebWorks application.

PAP push format

A PAP push request sends the pushed data to the BlackBerry MDS Connection Service as a multipart message which contains an XML control entity and the data to push. If the request is a cancellation or status-query request, only an XML control entity is sent. Delivery parameters are specified in the XML control entity.

  1. Generate a random PushID for the push request. You must include the PushID when you build the push request.
    String push id="_donepushID:13522321608214"+random.nextInt();
  2. Construct the URL to which the push request is posted.
    URL mdsUrl = new URL("http", mdsHost, MDS_PORT, "/pap");
  3. Open a connection to the BlackBerry Enterprise Service. Invoke openConnection() on the push URL, and then cast the returned object as an HttpURLConnection. An HttpURLConnection represents a connection to a remote object.
    HttpURLConnection mdsConn = (HttpURLConnection)
      mdsUrl.openConnection();
  4. Set the HTTP headers for the request. In a PAP push request, the HTTP headers specify content type of the push request (multipart/related), the Application ID with which the listener application sends data to the port on which the listener application is waiting, and the HTTP request method.
    String boundary = "";
    boundary = "asdlfkjiurwghasf";
    mdsConn.setRequestProperty("Content-Type", "multipart/related; 
      type=\"application/xml\"; boundary=" + boundary);
    mdsConn.setRequestProperty("X-Wap-Application-Id", "/");
    mdsConn.setRequestProperty("X-Rim-Push-Dest-Port","100");
    mdsConn.setRequestMethod("POST");
  5. Set doOutput(Boolean) to true to indicate that the application intends to send data to the URL connection.
    mdsConn.setDoOutput(true);
  6. To receive responses and result notifications from the BlackBerry MDS Connection Service, set doInput(Boolean) to true to indicate that the application will read incoming data from the URL connection.
    mdsConn.setDoInput(true);
  7. Construct the MIME multipart message. In this sample, the PAP control entity is constructed by reading a template multipart message and replacing the template data with real values.
    String output = requestTemplate.replaceAll("\\$\\(pushid\\)", 
      pushId); 
    output = output.replaceAll("\\$\\(boundary\\)", boundary); 
    output = output.replaceAll("\\$\\(notifyURL\\)", "" + notifyURL); 
    output = output.replaceAll("\\$\\(pin\\)", "" + _pinField.
      getText()); 
    
    String deliveryMethod = "confirmed"; 
    
    output = output.replaceAll("\\$\\(deliveryMethod\\)", 
      deliveryMethod); 
    output = output.replaceAll("\\$\\(headers\\)", "Content-Type: 
      text/plain"); 
    output = output.replaceAll("\\$\\(content\\)", data); 
    output = output.replaceAll("\r\n", "EOL"); 
    output = output.replaceAll("\n", "EOL"); 
    output = output.replaceAll("EOL", "\r\n");
  8. Write the push request data to the server connection. Invoke getOutputStream() to access an output stream, write to the output stream, and then close it.
    System.out.println(output);
    OutputStream outs = mdsConn.getOutputStream(); 
    copyStreams(new ByteArrayInputStream(output.getBytes()), outs);
  9. Read the BlackBerry MDS Connection Service response by invoking getInputStream() to access an input stream.
    copyStreams(mdsConn.getInputStream(), response);
     
    int httpCode = mdsConn.getResponseCode();
     
    if (httpCode != HttpURLConnection.HTTP_ACCEPTED) 
    { 
        throw new Exception("MDS returned HTTP status: " + httpCode);
    } 

BlackBerry push format

A BlackBerry push request sends the pushed data to the BlackBerry MDS Connection Service as a byte stream. Delivery parameters are specified in HTTP headers sent as part of the byte stream.

  1. Generate a random PushID for the push request. You must include the PushID when you build the push request.
    String push id="_donepushID:13522321607216"+random.nextInt();
  2. Construct the URL to which the server-side push application sends the push request.
    URL _pushURL = null;
    
    _pushURL = new URL("http", "localhost", MDS_PORT, 
      "/push?DESTINATION="+ DevicePin +"&PORT="+DEVICE_PORT+
      "&REQUESTURI=localhost");
  3. Open a connection to the BlackBerry Enterprise Service. Invoke openConnection() on the push URL, and then cast the returned object as an HttpURLConnection object. An HttpURLConnection represents a connection to a remote object.
    HttpURLConnection conn =(HttpURLConnection)url.openConnection();
  4. Set the HTTP headers for the request. The HTTP headers specify the delivery parameters, such as reliability level or delivery time stamp, for the push request.
    conn.setRequestMethod("POST");
    conn.setRequestProperty("X-RIM-PUSH-ID", pushId);            
    conn.setRequestProperty("X-RIM-Push-NotifyURL", 
      notifyURL);            
    conn.setRequestProperty("X-RIM-Push-Reliability-Mode",
      "APPLICATION");
  5. Set doOutput(Boolean) to true to indicate that the application intends to send data to the URL connection.
    conn.setDoOutput(true);
  6. To receive responses and result notifications from the BlackBerry MDS Connection Service, set doInput(Boolean) to true to indicate that the application will read incoming data from the URL connection.
    conn.setDoInput(true);
  7. Write the push request data to the server connection. Invoke getOutputStream() to access an output stream, write the data to the output stream, and then close the output stream.
    OutputStream out = conn.getOutputStream(); 
    out.write(data); 
    out.close();
  8. Read the BlackBerry MDS Connection Service response. Invoke getInputStream() to access an input stream. Determine the size of the response data and, if the data size is nonzero, open a data input stream, and then read in the data.
    InputStream ins = conn.getInputStream(); 
    int contentLength = conn.getContentLength(); 
    if (contentLength > 0) 
    { 
        byte[] someArray = new byte [contentLength]; 
        DataInputStream dins = new DataInputStream(ins); 
        dins.readFully(someArray); 
        System.out.println(new String(someArray)); 
    } 
    ins.close();
  9. Close the connection by invoking disconnect() to indicate that the application plans to make no further requests to the server.
    conn.disconnect();

Server-side sample application

You can use the following code sample to submit push requests that are delivered to a BlackBerry Java Application or a BlackBerry WebWorks that is listening on a port. A dialog box allows you to specify the BlackBerry MDS Connection Service information, and whether to use the BlackBerry push format or the PAP push format for the push request.

package com.rim.samples.server.httppushdemo;

import java.io.*;
import javax.swing.*;
import javax.swing.border.*;
import java.net.*;
import java.util.*;
import java.awt.Color;

/**
 * <p>The HTTPPushDemo class provides a simple PUSH server sample.
 * <p>This program will send text to a listening device. The associated 
 * client demo is HTTPPushDemo in the com.rim.samples.device package. Start up both 
 * the device simulator and MDS before executing this program. For reliable push, 
 * append the port that you are pushing to in rimpublic.property file 
 * (push.application.reliable.ports): <code>push.application.reliable.
 * ports=7874,<b>100</b></code
 *
 * <p> The general form of the URL for posting (pushing) data to the device is:
 * http://&lt;host&gt;:&lt;port&gt;/push?DESTINATION=&lt;device pin&gt;&amp;PORT=&lt;device_port&gt;&REQUESTURI=&lt;post uri&gt;
 */
public class HTTPPushDemo extends javax.swing.JFrame {

    //constants -----------------------------------------------------------------
    private static final String RESOURCE_PATH = "com/rim/samples/server/httppushdemo/resources";
    private static final String DEVICE_PIN = "2100000A";
    private static final String DEVICE_PORT = "100";
    private static final int MDS_PORT = 8080;

    private static final String IMAGE_TYPE = ".png";
    private String requestTemplate;
    private String notifyURL="http://localhost:7778";
    private Random random= new Random();
    private Thread notificationThread;

    //statics -------------------------------------------------------------------
    private static ResourceBundle _resources = java.util.ResourceBundle.getBundle(RESOURCE_PATH);

    //constructors --------------------------------------------------------------
    /** Creates a new HTTPPushDemo instance*/
    public HTTPPushDemo() {

        initComponents ();
        pack ();

        //sizing code for the main frame
        setSize(_panel.getWidth(), _panel.getHeight());
        setLocation(100,100);
        notificationThread= new NotificationThread();
    }

    private URL getPushURL(String DevicePin)
    {
        /**
         * The format of the URL is:
         * http://<host>:<port>/push?DESTINATION=<device pin>&PORT=<device_port>&REQUESTURI=<post uri>
         */
        URL _pushURL = null;
        try {
            if ((DevicePin == null) || (DevicePin.length() == 0))
            {
                DevicePin = DEVICE_PIN;
            }
            _pushURL = new URL("http", "localhost", MDS_PORT, "/push?DESTINATION="+ DevicePin +"&PORT="+DEVICE_PORT+"&REQUESTURI=localhost");
            
        } catch (MalformedURLException e) {
            System.err.println(e.toString());
        }
        return _pushURL;
    }

    /** 
     * This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the FormEditor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        _panel = new javax.swing.JPanel();
        _textField = new javax.swing.JTextField();
        _textArea= new javax.swing.JTextArea();
        _pinField = new javax.swing.JTextField(DEVICE_PIN);
        _label = new javax.swing.JTextArea();
        _notification=new javax.swing.JTextArea();
        _rimButton= new javax.swing.JRadioButton("rim");
        _papButton= new javax.swing.JRadioButton("pap");
        _buttonGroup= new javax.swing.ButtonGroup();
        _buttonGroup.add(_rimButton);
        _buttonGroup.add(_papButton);

        _sendButton = new javax.swing.JButton();
        getContentPane().setLayout(null);
        setTitle(_resources.getString("HTTPPushDemo.title"));
        setResizable(false);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                exitForm(evt);
            }
        });

        _panel.setLayout(null);
        _panel.setPreferredSize(getSize());

        _textArea.setToolTipText(_resources.getString("HTTPPushDemo._textField.toolTipText"));
        _panel.add(_textArea);
        _textArea.setBounds(10, 50, 270, 100);
        _textArea.setBorder(new LineBorder(Color.BLACK));


        _pinField.setToolTipText(_resources.getString("HTTPPushDemo._pinField.toolTipText"));
        _panel.add(_pinField);
        _pinField.setBounds(10, 170, 150, 30);

        _panel.add(_rimButton);
        _panel.add(_papButton);
        _rimButton.setBounds(170, 170, 50, 30);
        _papButton.setBounds(240, 170, 50, 30);

        _label.setWrapStyleWord(true);
        _label.setLineWrap(true);
        _label.setEditable(false);
        _label.setText(_resources.getString("HTTPPushDemo._label.text"));
        _label.setBackground((java.awt.Color) javax.swing.UIManager.getDefaults ().get ("Button.background"));
        _panel.add(_label);
        _label.setBounds(10, 10, 270, 40);

        _sendButton.setLabel(_resources.getString("HTTPPushDemo._sendButton.label"));
        _sendButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                sendButtonMouseClicked(evt);
            }
        });

        _panel.add(_sendButton);
        _sendButton.setLocation(10, 210);
        _sendButton.setSize(_sendButton.getPreferredSize());

        JScrollPane _scrollPane = new javax.swing.JScrollPane(_notification);
        _scrollPane.setVerticalScrollBarPolicy(
                        JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

        _panel.add(_scrollPane);
        _scrollPane.setBounds(10,250,270, 150);


        getContentPane().add(_panel);
        _panel.setBounds(0, 0, 300, 450);

    }//GEN-END:initComponents

    private void sendButtonMouseClicked(java.awt.event.MouseEvent evt) 
    {//GEN-FIRST:event_sendButtonMouseClicked

        String text =_textArea.getText();

        if(_rimButton.isSelected()) postData(text);
        else if(_papButton.isSelected()) papPush(text);

    }//GEN-LAST:event_sendButtonMouseClicked

   /**
    * <p>posts the specified data to the device
    * <p>The URL is hardcoded for the purposes of this demo, and takes the form:
    * http://&lt;host&gt;:&lt;port&gt;/push?DESTINATION=&ltdevice pin&gt;&PORT=&lt;device_port&gt;&REQUESTURI=&lt;post uri&gt;
    * param data the data to post
    *
    */
    private void postData(String data)
    {

        String push id="_donepushID:13522321629133"+random.nextInt();

        setupNotifyThread();
        
        try {
            URL url = getPushURL(_pinField.getText());
            System.out.println(_resources.getString("HTTPPushDemo.status.sendingToString") + url.toString());

            //open the connection using the static member...
            HttpURLConnection conn =(HttpURLConnection)url.openConnection();
            conn.setDoInput(true);//For receiving the confirmation
            conn.setDoOutput(true);//For sending the data
            conn.setRequestMethod("POST");//Post the data to the proxy
            conn.setRequestProperty("X-RIM-PUSH-ID", pushId);
            conn.setRequestProperty("X-RIM-Push-NotifyURL", notifyURL);
            conn.setRequestProperty("X-RIM-Push-Reliability-Mode","APPLICATION");
            //Write the data
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.close();
            InputStream ins =conn.getInputStream();
            int contentLength =conn.getContentLength();
            System.out.println( _resources.getString("HTTPPushDemo.status.contentLengthDescription")+ contentLength);
            if (contentLength > 0)
            {
                byte[] someArray = new byte [contentLength];
                DataInputStream dins = new DataInputStream(ins);
                dins.readFully(someArray);
                System.out.println(new String(someArray));
            }

            conn.disconnect();

        } catch (IOException e) {
            System.err.println(e);
        }
    }

    private void readPapTemplate()
    {

        try {
            String papFilename =  "com/rim/samples/server/httppushdemo/pap_push.txt";
	      	InputStream ins = new BufferedInputStream(new FileInputStream(papFilename));            
            ByteArrayOutputStream bouts = new ByteArrayOutputStream();
            copyStreams(ins, bouts);
            this.requestTemplate = new String(bouts.toByteArray());
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    
    private void setupNotifyThread()
    {
        if( !notificationThread.isAlive() )
        {
            notificationThread = new NotificationThread();
            notificationThread.start(); 
        }
    }   

    private void papPush(String data)
    {
        String push id="_donepushID:13522321628822"+random.nextInt();
        
        setupNotifyThread();
        
        readPapTemplate();
        String errorCode = null;
        try {
            String mdsHost = "localhost";
            URL mdsUrl = new URL("http", mdsHost, MDS_PORT, "/pap");
            System.out.println(" sending PAP request to " + mdsUrl.toString() + "; pushId = " + pushId);

            HttpURLConnection mdsConn = (HttpURLConnection)mdsUrl.openConnection();

            String boundary = "";
            boundary = "asdlfkjiurwghasf";
            mdsConn.setRequestProperty("Content-Type", "multipart/related; type=\"application/xml\"; boundary=" + boundary);
            mdsConn.setRequestProperty("X-Wap-Application-Id", "/");
            mdsConn.setRequestProperty("X-Rim-Push-Dest-Port","100");
            mdsConn.setRequestMethod("POST");

            mdsConn.setAllowUserInteraction(false);
            mdsConn.setDoInput(true);
            mdsConn.setDoOutput(true);

            String output = requestTemplate.replaceAll("\\$\\(pushid\\)", pushId);
            output = output.replaceAll("\\$\\(boundary\\)", boundary);
            output = output.replaceAll("\\$\\(notifyURL\\)", "" + notifyURL);
            output = output.replaceAll("\\$\\(pin\\)", "" + _pinField.getText());

            String   deliveryMethod = "confirmed";

            output = output.replaceAll("\\$\\(deliveryMethod\\)", deliveryMethod);


            output = output.replaceAll("\\$\\(headers\\)", "Content-Type: text/plain");
            output = output.replaceAll("\\$\\(content\\)", data);

            output = output.replaceAll("\r\n", "EOL");
            output = output.replaceAll("\n", "EOL");
            output = output.replaceAll("EOL", "\r\n");


            System.out.println(output);
            OutputStream outs = mdsConn.getOutputStream();
            copyStreams(new ByteArrayInputStream(output.getBytes()), outs);

            mdsConn.connect();

            ByteArrayOutputStream response = new ByteArrayOutputStream();
            copyStreams(mdsConn.getInputStream(), response);

            int httpCode = mdsConn.getResponseCode();

            if (httpCode != HttpURLConnection.HTTP_ACCEPTED) {
                throw new Exception("MDS returned HTTP status: " + httpCode);
            }

        } catch (Exception exception) {
            if (errorCode == null)
            {
                errorCode = exception.getClass().getName();
            }

            System.out.println(" encountered error on submission: " + exception.toString());
        }
    }

    public void copyStreams(InputStream ins, OutputStream outs) throws IOException
    {
            int maxRead = 1024;
            byte [] buffer = new byte[1024];
            int bytesRead;

            for(;;)
            {
                bytesRead = ins.read(buffer);
                System.out.println(buffer);
                if (bytesRead <= 0) break; 
                outs.write(buffer, 0, bytesRead);
            }
    }

    /** Exit the Application */
    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
        System.exit (0);
    }//GEN-LAST:event_exitForm

    /**
    * @param args the command line arguments
    */
    public static void main (String args[]) {
        new HTTPPushDemo ().show ();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel _panel;
    private javax.swing.JTextField _textField;
    private javax.swing.JTextArea _textArea;
    private javax.swing.JTextField _pinField;
    private javax.swing.JTextArea _label;
    private javax.swing.JTextArea _notification;
    private javax.swing.JButton _sendButton;
    private javax.swing.JRadioButton _rimButton;
    private javax.swing.JRadioButton _papButton;
    private javax.swing.ButtonGroup _buttonGroup;
    private javax.swing.JScrollPane _scrollPane;

    // End of variables declaration//GEN-END:variables

    public class NotificationThread extends Thread {

        public void run() 
        {
            try {
                System.out.println("Waiting for notification on port " + 7778 + "...");
                while (true)
                {
                    ServerSocket serverSocket = new ServerSocket(7778);
                    serverSocket.setSoTimeout(120000);
                    try {
                        Socket clientSocket = serverSocket.accept();
                        _notification.setText("Received notification:");
                        InputStream input = clientSocket.getInputStream();
                        StringBuffer str= new StringBuffer();
                        int byteRead = input.read();
                        while ((byteRead != -1) && (input.available() > 0))
                        {
                            str.append((char)byteRead);
                            byteRead = input.read();
                        }
                        _notification.append(str.toString());
                        PrintWriter output = new PrintWriter(clientSocket.getOutputStream());
                        output.close();
                        clientSocket.close();
                    } catch (SocketTimeoutException ste) {
                        System.out.println("Notification connection timeout. Restarting...");
                    }               
                    serverSocket.close();
                }
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }
}

XML control entity template

The server-side sample application uses the following template to create the XML control entity when the PAP push format is used. The sample Java application reads the template, then replaces the template data with real values.

--$(boundary)
Content-Type: application/xml; charset=UTF-8

<?xml version="1.0"?>
<!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 2.0//EN" 
        "http://www.wapforum.org/DTD/pap_2.0.dtd" 
        [<?wap-pap-ver supported-versions="2.0,1.*"?>]>
<pap>
    <push-message push-id="_done$(pushid)13522321621189" 
          source-reference="$(username)"
          deliver-before-timestamp="2009-10-10T19:00:00Z"
          ppg-notify-requested-to="$(notifyURL)">
       <address address-value="$(pin)"/>
       <quality-of-service delivery-method="$(deliveryMethod)"/>
    </push-message>
</pap>
--$(boundary)
$(headers)

$(content)
--$(boundary)--