Package blackberry.push

Source Code of blackberry.push.PushListener

/*
* Copyright 2010-2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package blackberry.push;

import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.io.ConnectionNotFoundException;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

import net.rim.device.api.io.http.PushInputStream;
import net.rim.device.api.script.ScriptableFunction;
import net.rim.device.api.util.DataBuffer;
import blackberry.common.push.PushData;
import blackberry.push.data.PushDataObject;

/**
*
* Separate push listening thread to handle all traffic for a particular port.
*
*/
public class PushListener extends Thread {

    private static final int CHUNK_SIZE = 256;
    private static final String URL_PREFIX = "http://:";
    private static final String MDS_SUFFIX = ";deviceside=false;ConnectionType=mds-public";

    private boolean _stop;
    private StreamConnectionNotifier _notify;
    private int _port;
    private ScriptableFunction _callback;
    private Object _callBackLock;
    private Vector _messageQueue;
    private boolean _isSuspended;
    private int _maxQueueCap;
    private Thread _listenThread;

    /**
     * Constructor
     *
     * @param port
     *            the port that this object will listen on.
     * @param callback
     *            the scriptable callback function to invoke when a new push message is received.
     * @param maxQueue
     *            the max amount of pushed messages to queue
     */
    public PushListener( int port, ScriptableFunction callback, int maxQueue ) {
        _port = port;
        _callback = callback;
        _callBackLock = new Object();
        _maxQueueCap = maxQueue;
        if( _maxQueueCap > 0 ) {
            _messageQueue = new Vector( _maxQueueCap );
        } else {
            _messageQueue = new Vector();
        }
    }

    /**
     *
     * Suspend the listener - preventing it from invoking callback functions.
     *
     * Messages pushed to the port are still received and queued
     *
     */
    public void suspend() {
        setSuspended( true );
    }

    /**
     * Updates this listener's callback to a new one. Also, if the listener was previously suspended - it will resume.
     *
     * @param callback
     *            the new callback function to invoke.
     */
    public void updateCallback( ScriptableFunction callback ) {
        synchronized( _callBackLock ) {
            _callback = callback;
        }
        setSuspended( false );
    }

    /**
     * Stops this listener - all connections are closd, all threads are finished. The push listener cannot be restarted once it is
     * stopped - a new instance, listening on the same port, must be created.
     */
    public synchronized void stop() {
        _stop = true;

        // wake the message processor
        synchronized( _messageQueue ) {
            _messageQueue.notify();
        }

        try {
            if( _notify != null ) {
                _notify.close();
            }
        } catch( IOException e ) {
        }
    }

    /**
     * @see java.lang.Thread
     */
    public void run() {
        _listenThread = Thread.currentThread();

        // Create message processing thread, and run it
        MessageProcessor messageProcessor = new MessageProcessor();
        messageProcessor.start();

        while( !_stop ) {
            StreamConnection stream = null;
            InputStream input = null;
            DataBuffer db = null;
            try {
                /**
                 * Does this need to be synch'd? This should be a single threaded instance that runs autonomously on a port.
                 *
                 * Might want to call this in the the PushService class to catch IOPortAlreadyBoundException so we can properly
                 * throw this back up via JavaScript so the developer realizes that the port they tried to open is unavailable.
                 */
                synchronized( this ) {
                    _notify = (StreamConnectionNotifier) Connector.open( getURL() );
                }

                while( !_stop ) {
                    // block for data
                    stream = _notify.acceptAndOpen();
                    try {
                        // create input stream
                        input = stream.openInputStream();

                        // extract the data from the input stream
                        db = new DataBuffer();
                        byte[] data = new byte[ CHUNK_SIZE ];
                        int chunk = 0;
                        while( -1 != ( chunk = input.read( data ) ) ) {
                            db.write( data, 0, chunk );
                        }

                        // trim the array - the buffer in DataBuffer grows
                        // past the size and fills with empty chars
                        db.trim();

                        // synchronize this block to avoid race conditions with the queue
                        synchronized( _messageQueue ) {
                            // Create push object, add to queue for callback thread
                            PushData pd = new PushData( stream, input, db.getArray() );
                            _messageQueue.addElement( pd );
                            _messageQueue.notify();
                        }
                    } catch( IOException e1 ) {
                        // a problem occurred with the input stream
                        // however, the original StreamConnectionNotifier is still valid
                        if( input != null ) {
                            try {
                                input.close();
                            } catch( IOException e2 ) {
                            }
                        }
                        if( stream != null ) {
                            try {
                                stream.close();
                            } catch( IOException e2 ) {
                            }
                        }
                    }
                }

            } catch( ConnectionNotFoundException cnfe ) {
                // stop this listener, it's not going to run
                stop();
            } catch( IOException ioe ) {
                // likely the stream was closed
            } finally {
                if( _notify != null ) {
                    try {
                        _notify.close();
                        _notify = null;
                    } catch( IOException e ) {
                    }
                }
            }
        }
    }

    /*
     * When set to true, the listener is suspended - preventing it from invoking callback functions. Messages pushed to the port
     * are still received and queued. When set to false, the listener resumes normal functions - receiving push messages and
     * invoking callbacks.
     *
     * isSuspended - flag indicating whether listener is suspended.
     */
    private void setSuspended( boolean isSuspended ) {
        synchronized( _messageQueue ) {
            _isSuspended = isSuspended;
            _messageQueue.notifyAll();
        }
    }

    /**
     * Private class that creates a thread to process push messages already received by listener thread.
     */
    private final class MessageProcessor extends Thread {
        public void run() {
            while( !_stop ) {
                // check for messages
                synchronized( _messageQueue ) {
                    if( _messageQueue.size() == 0 || _isSuspended ) {
                        try {
                            _messageQueue.wait();
                        } catch( Exception e ) {
                            // ignore - check loop
                        }
                        continue;
                    }
                }
                // process message - one at a time
                PushData message = (PushData) _messageQueue.elementAt( 0 );
                processMessage( message );

                // remove message from queue
                synchronized( _messageQueue ) {
                    _messageQueue.removeElementAt( 0 );
                }
            }

            // clean-up any messages that were not processed
            // sync up with listen thread to make sure all messages
            // are in the queue so everything is cleaned up
            try {
                _listenThread.join();
            } catch( Exception e ) {
                // ignore, proceed with clean-up
            }
            Enumeration messagesList = _messageQueue.elements();
            PushData message;
            while( messagesList.hasMoreElements() ) {
                message = (PushData) messagesList.nextElement();
                message.discard();
            }
        }

        /**
         * Processes a created net.rim.device.api.web.jse.toolkit.push.PushData message
         *
         * @param message
         *            PushData message.
         */
        void processMessage( PushData message ) {
            // Create PushDataObject Scriptable Object
            PushDataObject pushDataScriptable = new PushDataObject( message );

            try {
                // Invoke callback method
                Object result = null;
                synchronized( _callBackLock ) {
                    result = _callback.invoke( null, new Object[] { pushDataScriptable } );
                }

                // Accept/Decline push message based on return from callback
                if( result instanceof Integer ) {
                    int callbackReply = ( (Integer) result ).intValue();
                    switch( callbackReply ) {
                        case PushDataObject.ACCEPT:
                            message.accept();
                            break;
                        case PushDataObject.DECLINE_USERDCR:
                            message.decline( PushInputStream.DECLINE_REASON_USERDCR );
                            break;
                        case PushDataObject.DECLINE_USERPND:
                            message.decline( PushInputStream.DECLINE_REASON_USERPND );
                            break;
                        case PushDataObject.DECLINE_USERREQ:
                            message.decline( PushInputStream.DECLINE_REASON_USERREQ );
                            break;
                        case PushDataObject.DECLINE_USERRFS:
                            message.decline( PushInputStream.DECLINE_REASON_USERRFS );
                            break;
                        case PushDataObject.DECLINE_USERDCU:
                        default:
                            // should not get here
                            message.decline( PushInputStream.DECLINE_REASON_USERDCU );
                            break;
                    }
                } else {
                    // By default, we'll decline the message
                    message.decline( PushInputStream.DECLINE_REASON_USERDCU );
                }
            } catch( Exception e ) {
                try {
                    // Decline message
                    message.decline( PushInputStream.DECLINE_REASON_USERDCU );
                } catch( Exception ex ) {
                }
            }
            // discard message?
        }
    }

    private String getURL() {
        String result = URL_PREFIX + _port + MDS_SUFFIX;
        return result;
    }
}
TOP

Related Classes of blackberry.push.PushListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.