/** * CommunicationController.java * * Copyright � 1998-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. * * Note: For the sake of simplicity, this sample application may not leverage * resource bundles and resource strings. However, it is STRONGLY recommended * that application developers make use of the localization features available * within the BlackBerry development platform to ensure a seamless application * experience across a variety of languages and geographies. For more information * on localizing your application, please refer to the BlackBerry Java Development * Environment Development Guide associated with this release. */ package com.rim.samples.device.communicationapidemo; import java.io.ByteArrayInputStream; import java.util.Hashtable; import net.rim.device.api.io.URI; import net.rim.device.api.io.messaging.BlockingReceiverDestination; import net.rim.device.api.io.messaging.BlockingSenderDestination; import net.rim.device.api.io.messaging.BpsSubscriptionMessageBuilder; import net.rim.device.api.io.messaging.ByteMessage; import net.rim.device.api.io.messaging.Context; import net.rim.device.api.io.messaging.CredentialsCollector; import net.rim.device.api.io.messaging.Destination; import net.rim.device.api.io.messaging.DestinationFactory; import net.rim.device.api.io.messaging.FireAndForgetDestination; import net.rim.device.api.io.messaging.HttpMessage; import net.rim.device.api.io.messaging.InboundDestinationConfiguration; import net.rim.device.api.io.messaging.InboundDestinationConfigurationFactory; import net.rim.device.api.io.messaging.Message; import net.rim.device.api.io.messaging.MessageFailureException; import net.rim.device.api.io.messaging.MessageListener; import net.rim.device.api.io.messaging.NonBlockingReceiverDestination; import net.rim.device.api.io.messaging.NonBlockingSenderDestination; import net.rim.device.api.io.messaging.SenderDestination; import net.rim.device.api.io.messaging.StreamMessage; import net.rim.device.api.io.messaging.UsernamePasswordCredentials; import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.component.Dialog; import com.rim.samples.device.communicationapidemo.util.Utils; /** * Contains methods which demonstrate Communication API use cases */ public final class CommunicationController { // URI of a simple echo server which responds to http // get requests sent from the application by sending // back requested resource (xml, text, json, etc). public static String ECHO_SERVER_URI; public static int TIMEOUT; // Response timeout private static UiApplication _app; private static int _listenerId = 1; // Used to generate id private final Context _context; private NonBlockingSenderDestination _cancellationDest; /** * Creates a new CommunicationController object */ public CommunicationController() { _context = new Context("MyContext"); _app = UiApplication.getUiApplication(); try { // Read settings from config.xml ECHO_SERVER_URI = Utils.getEchoServerUri(); TIMEOUT = Utils.getTimeout(); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Displays a pop-up dialog to the user with a given message * * @param message * The text to display */ public static void alertDialog(final String message) { _app.invokeLater(new Runnable() { public void run() { Dialog.alert(message); } }); } /** * Sends get request message using non-blocking destination and listens for * the response in the created listener. * * @param uriStr * Uri where the request is sent to * @param releaseDestination * Release destination on completion of the method * @param callback * Callback object responsible for updating UI upon message * delivery / request timeout */ public void sendNonBlocking(final String uriStr, final boolean releaseDestination, final ResponseCallback callback) { MonitoredDestinationListener responseListener; try { final URI uri = URI.create(uriStr); responseListener = new MonitoredDestinationListener( "SendNonBlocking MessageListener [id: " + _listenerId++ + "]"); final SenderDestination senderDestination = DestinationFactory.createNonBlockingSenderDestination( _context, uri, responseListener); final TimeoutMonitor timeoutThread = new TimeoutMonitor(senderDestination, releaseDestination, callback, responseListener); final Thread t1 = new Thread(timeoutThread, "monitor"); t1.start(); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Sends get request message to the URI and blocks until response received * or we timed out. * * @param uriSenderStr * Sender destination URI * @param listener * Listener for callback */ public void sendBlocking(final String uriSenderStr, final CommunicationControllerListener listener) { new Thread(new Runnable() { /** * @see Runnable#run() */ public void run() { try { final SendBlockingThread ts = new SendBlockingThread(uriSenderStr); ts.start(); // Blocking this thread until message received or timeout for (int i = 0; i < TIMEOUT; i++) { if (ts.isCompleted()) { listener.onWaitTimerCompleted(); return; // Thread finished execution } Thread.sleep(1000); // Sleep 1 sec listener.onWaitTimerCounterChanged(i + 1); } if (ts.getResponse() == null && ts.isAlive()) // Timeout { ts.interrupt(); // Force thread to stop listener.onWaitTimerCompleted(); alertDialog("Timed out after " + TIMEOUT + " sec"); return; } } catch (final Exception e) { alertDialog(e.toString()); } } }).start(); } /** * Sends message to the URI using FireAndForget destination * * @param uriSenderStr * Sender destination URI */ public void sendFireForget(final String uriSenderStr) { FireAndForgetDestination fireForgetDest = null; try { fireForgetDest = (FireAndForgetDestination) DestinationFactory .getSenderDestination(_context.getName(), URI .create(uriSenderStr)); if (fireForgetDest == null) { fireForgetDest = DestinationFactory.createFireAndForgetDestination( _context, URI.create(uriSenderStr)); } final int msgId = fireForgetDest.sendNoResponse(); alertDialog("Message [id:" + msgId + "] has been sent!"); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Creates receiver destination (BES) for the URI and registers a listener * * @param uriStr * URI of the receiver destination * @param autoStartEnabled * Auto start application on incoming messages */ public void startNonBlockingReceiverBES(final String uriStr, final boolean autoStartEnabled) { NonBlockingReceiverDestination nonBlockRecvDest = null; try { nonBlockRecvDest = (NonBlockingReceiverDestination) DestinationFactory .getReceiverDestination(URI.create(uriStr)); if (nonBlockRecvDest == null) // Not registered yet { final MessageListener responseListener = new DestinationListener("NonBlockingReceiverBES", true); // Prepare the in-bound destination for incoming messages // (BES/http) final InboundDestinationConfiguration config = InboundDestinationConfigurationFactory .createBESConfiguration(autoStartEnabled, true, false); nonBlockRecvDest = DestinationFactory .createNonBlockingReceiverDestination(config, URI.create(uriStr), responseListener); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Creates blocking receiver destination (BES) in a separate thread which * blocks till message arrives or times out. This method should be called * every time you expect a new message, since after receiving one message * the thread listening for message completes. * * @param uriStr * Receiver destination URI * @param autoStartEnabled * Auto-start application on incoming messages * @param timeout * Timeout for receiving the incoming push message */ public void startBlockingReceiverBES(final String uriStr, final boolean autoStartEnabled, final int timeout) { // Create and start thread final ReceiveBlockingThread t = new ReceiveBlockingThread(uriStr, InboundDestinationConfiguration.CONFIG_TYPE_BES, autoStartEnabled, timeout); t.start(); } /** * Creates non-blocking receiver destination (IPC) for the URI and registers * a listener. * * @param uriStr * @param autoStartEnabled * Auto-start application on incoming messages */ public void startNonBlockingReceiverIPC(final String uriStr, final boolean autoStartEnabled) { NonBlockingReceiverDestination nonBlockRecvDest = null; try { nonBlockRecvDest = (NonBlockingReceiverDestination) DestinationFactory .getReceiverDestination(URI.create(uriStr)); if (nonBlockRecvDest == null) // Not registered yet { final SimpleListener responseListener = new SimpleListener("NonBlockingReceiverIPC"); // Prepare the inbound destination for incoming // messages (Inter Process Communication). final InboundDestinationConfiguration config = InboundDestinationConfigurationFactory .createIPCConfiguration(autoStartEnabled, true, false); nonBlockRecvDest = DestinationFactory .createNonBlockingReceiverDestination(config, URI.create(uriStr), null, responseListener); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Creates blocking receiver destination (IPC) in a separate thread which * blocks till message arrives or times out. This method should be called * every time you expect a new message, since after receiving one message * the thread listening for the message completes. * * @param uriStr * Receiver destination URI * @param autoStartEnabled * Auto-start application on incoming messages * @param timeout * Timeout for receiving the incoming push message */ public void startBlockingReceiverIPC(final String uriStr, final boolean autoStartEnabled, final int timeout) { // create and start thread final ReceiveBlockingThread t = new ReceiveBlockingThread(uriStr, InboundDestinationConfiguration.CONFIG_TYPE_IPC, autoStartEnabled, timeout); t.start(); } /** * Pauses destination from receiving incoming push messages */ public void pauseReceiver(final String uriStr) { try { final Destination dest = DestinationFactory.getReceiverDestination(URI .create(uriStr)); if (dest != null) { dest.pause(); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Resumes delivery of messages to destination specified by provided URI */ public void resumeReceiver(final String uriStr) { try { final Destination dest = DestinationFactory.getReceiverDestination(URI .create(uriStr)); if (dest != null) { dest.resume(); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Releases destination * * @see Destination#release() */ public void releaseReceiver(final String uriStr) { try { final Destination dest = DestinationFactory.getReceiverDestination(URI .create(uriStr)); if (dest != null) { dest.release(); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Destroys destination - should be executed when the app is uninstalled */ public void destroyReceiver(final String uriStr) { try { final Destination dest = DestinationFactory.getReceiverDestination(URI .create(uriStr)); if (dest != null) { dest.destroy(); } } catch (final Exception e) { alertDialog(e.toString()); } } /** * Sets the destination. Used by message cancellation test. */ public void setNonBlockingSenderDestination(final String uriStr) { final MessageListener responseListener = new DestinationListener("Cancellation Listener", true); try { final URI uri = URI.create(uriStr); SenderDestination destination = DestinationFactory.getSenderDestination(_context.getName(), uri); if (destination == null) { // Destination not registered yet destination = DestinationFactory.createNonBlockingSenderDestination( _context, uri, responseListener); } _cancellationDest = (NonBlockingSenderDestination) destination; } catch (final Exception e) { alertDialog(e.toString()); } } /** * Sends cancellable/non-cancellable message and tries to cancel using * destination. * * @param msgString * Message string to be sent with the message * @param isCancellable * True if message can be canceled, otherwise false */ public void testMessageCancellable(final String msgString, final boolean isCancellable) { if (_cancellationDest == null) { // Check if destination is created alertDialog("Please register destination first!"); return; } final ByteMessage msg = _cancellationDest.createByteMessage(); try { msg.setStringPayload(msgString); msg.setCancellable(isCancellable); // Cancellable final int msgId = _cancellationDest.send(msg); _cancellationDest.cancel(msgId); } catch (final Exception e) { alertDialog(e.toString()); } } /* * Sends non-cancellable and cancellable messages, then tries to cancel them * on best effort. */ public void testCancelAllCancellable() { try { if (_cancellationDest == null) // check if destination is created { alertDialog("Please register destination first!"); return; } final int numMsg = 5; // number of messages to be sent final ByteMessage[] messages = new ByteMessage[numMsg]; final int[] msgIds = new int[numMsg]; // Create messages for (int i = 0; i < messages.length; i++) { messages[i] = _cancellationDest.createByteMessage(); messages[i].setCancellable(true); messages[i].setStringPayload("Message " + i + "[cancellable]"); } // Reset two first messages as non-cancellable messages[0].setCancellable(false); messages[0].setStringPayload("Message 0 [non-cancellable]"); messages[1].setCancellable(false); messages[1].setStringPayload("Message 1 [non-cancellable]"); // Send messages and read IDs for (int i = 0; i < messages.length; i++) { msgIds[i] = _cancellationDest.send(messages[i]); } // Use another method to cancel all _cancellationDest.cancelAllCancellable(); // NOTE: If a message has already been sent, then it // can not be canceled. } catch (final Exception e) { alertDialog(e.toString()); } } /** * Gets response from a URI which requires credentials * * @param callback * Callback object responsible for updating UI upon message * delivery / request timeout */ public void authenticate(final String uriStr, final String username, final String password, final ResponseCallback callback) { MonitoredDestinationListener responseListener; try { final URI uri = URI.create(uriStr); final String user = username; final String pass = password; final CredentialsCollector credentialsCollector = new CredentialsCollector() { public UsernamePasswordCredentials getBasicAuthenticationCredentials( final String authenticatedEntityID, final Hashtable properties) { return new UsernamePasswordCredentials(user, pass); } }; final Context context = new Context("Authenticate", credentialsCollector); responseListener = new MonitoredDestinationListener( "Authentification MessageListener [id: " + _listenerId++ + "]"); final SenderDestination senderDestination = DestinationFactory.createNonBlockingSenderDestination( context, uri, responseListener); final TimeoutMonitor timeoutThread = new TimeoutMonitor(senderDestination, true, callback, responseListener); final Thread t1 = new Thread(timeoutThread, "monitor"); t1.start(); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Registers/subscribes the application for receiving BPS pushes */ public void registerBPSPush(final String appId, final String BPDSUri, final String contentUri, final String listenUri) { NonBlockingReceiverDestination listenDest = null; NonBlockingSenderDestination bpsServerDest = null; try { final MessageListener bpsListener = new DestinationListener("BPS", true); final InboundDestinationConfiguration config1 = InboundDestinationConfigurationFactory .createBPSConfiguration(true, true, false, appId, BPDSUri); // serverUrl listenDest = DestinationFactory.createNonBlockingReceiverDestination( config1, URI.create(listenUri), bpsListener); final MessageListener subscriptionListener = new SubscriptionResponseListener("Subscription"); bpsServerDest = DestinationFactory.createNonBlockingSenderDestination( _context, URI.create(contentUri), subscriptionListener); final ByteMessage subscrMsg = BpsSubscriptionMessageBuilder .createByteSubscriptionMessage(bpsServerDest, listenDest, "user", "pwd"); ((HttpMessage) subscrMsg).setQueryParam("field1", "xxxxx"); ((HttpMessage) subscrMsg).setQueryParam("osversion", "6.0"); bpsServerDest.send(subscrMsg); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Uploads data in the form of streams to a server * * @param callback * Callback object responsible for updating UI upon message * delivery / request timeout */ public void uploadStream(final String uploadUri, final ResponseCallback callback) { MonitoredDestinationListener responseListener; try { final URI uri = URI.create(uploadUri + ";deviceSide=true"); responseListener = new MonitoredDestinationListener( "StreamData MessageListener [id: " + _listenerId++ + "]"); final SenderDestination senderDestination = DestinationFactory.createNonBlockingSenderDestination( _context, uri, responseListener); // Create a byte array of size 10000 final byte[] input = new byte[10000]; for (int i = 0; i < input.length; i++) { input[i] = '5'; } final ByteArrayInputStream bais = new ByteArrayInputStream(input); final StreamMessage message = senderDestination.createStreamMessage(); ((HttpMessage) message).setMethod(HttpMessage.POST); message.setStreamPayload(bais); message.setPriority(2); message.setTransportHeader("Transfer-Encoding", "chunked"); final TimeoutMonitor timeoutThread = new TimeoutMonitor(senderDestination, true, callback, responseListener, message); final Thread t1 = new Thread(timeoutThread, "monitor"); t1.start(); } catch (final Exception e) { alertDialog(e.toString()); } } /** * Sender thread to sent a message using BlockingSenderDestination */ private final class SendBlockingThread extends Thread { String _uriStr; Message _response; boolean _completed; /** * Creates new SendBlockingThread object * * @param uriStr * URI to create BlockingSenderDestination */ public SendBlockingThread(final String uriStr) { _uriStr = uriStr; _response = null; } /** * Gets the response received on get request sent using * BlockingSenderDestination. * * @return Response message */ public Message getResponse() { return _response; } /** * Sends get request and receives response using * BlockingSenderDestination * * @see Thread#run() */ public void run() { BlockingSenderDestination blockSendDest = null; try { blockSendDest = (BlockingSenderDestination) DestinationFactory .getSenderDestination(_context.getName(), URI .create(_uriStr)); if (blockSendDest == null) { blockSendDest = DestinationFactory.createBlockingSenderDestination( _context, URI.create(_uriStr)); } // Send message and wait for response _response = blockSendDest.sendReceive(); if (_response != null) { _completed = true; // For "http" final String alertString = "received Message [id:" + _response.getMessageId() + "]\n" + "Response Code: " + ((HttpMessage) _response) .getResponseCode(); alertDialog(alertString); } } catch (final Exception e) { _completed = true; alertDialog(e.toString()); } finally { if (blockSendDest != null) { blockSendDest.release(); } } } public boolean isCompleted() { return _completed; } } private static final class ReceiveBlockingThread extends Thread { private Message _response; private final String _uriStr; private final int _configType; private final int _timeout; private final boolean _autoStartEnabled; /** * Create new ReceiveBlockingThread object * * @param uriStr * URI of the receiver * @param configType * Inbound destination configuration type * @param autoStartEnabled * Enable auto-start of the application when message received * and application is closed * @param timeout * Response timeout */ public ReceiveBlockingThread(final String uriStr, final int configType, final boolean autoStartEnabled, final int timeout) { _uriStr = uriStr; _configType = configType; _autoStartEnabled = autoStartEnabled; _timeout = timeout; _response = null; } /** * @see Thread#run() */ public void run() { BlockingReceiverDestination blockRecvDest = null; try { blockRecvDest = (BlockingReceiverDestination) DestinationFactory .getReceiverDestination(URI.create(_uriStr)); if (blockRecvDest == null) // Not registered yet { InboundDestinationConfiguration config; // Prepare the inbound destination for incoming messages if (_configType == InboundDestinationConfiguration.CONFIG_TYPE_BES) { config = InboundDestinationConfigurationFactory .createBESConfiguration( _autoStartEnabled, true, false); } else if (_configType == InboundDestinationConfiguration.CONFIG_TYPE_IPC) { config = InboundDestinationConfigurationFactory .createIPCConfiguration( _autoStartEnabled, true, false); } else { throw new IllegalArgumentException( "Invalid InboundDestinationConfiguration type! Implemented support of IPC and BES only."); } blockRecvDest = DestinationFactory .createBlockingReceiverDestination(config, URI.create(_uriStr)); } String alertString = ""; _response = blockRecvDest.receive(_timeout * 1000); if (_response != null) { alertString = "RECEIVED[id: " + _response.getMessageId() + "]:"; final String stringPayload = ((ByteMessage) _response).getStringPayload(); if (stringPayload != null) { alertString += "\n" + stringPayload; } } else { // No response received alertString = "No message has been received during timeout of " + _timeout + " sec."; } alertDialog(alertString); } catch (final Exception e) { alertDialog(e.toString()); } } } /** * Message Listener to be used with non-blocking destinations for * asynchronous message processing. */ private static class DestinationListener implements MessageListener { private final String _name; private boolean _onMessage = false; private Message _response = null; private final boolean _alertOnMessage; /** * Create new DestinationListener object * * @param name * Name of the listener */ private DestinationListener(final String name) { this(name, false); } /** * Creates a new DestinationListener object * * @param name * Name of the listener * @param alertOnMessage * Display alert dialog when message is received */ private DestinationListener(final String name, final boolean alertOnMessage) { _name = name; _alertOnMessage = alertOnMessage; } /** * @see MessageListener#onMessage(Destination, Message) */ public void onMessage(final Destination destination, final Message message) { _response = message; _onMessage = true; if (_alertOnMessage) { String alertString = _name + " :RECEIVED[id: " + message.getMessageId() + "]:"; final String stringPayload = ((ByteMessage) message).getStringPayload(); if (stringPayload != null) { alertString += "\n" + stringPayload; } alertDialog(alertString); } } /** * @see MessageListener#onMessageCancelled(Destination, int) */ public void onMessageCancelled(final Destination destination, final int cancelledMessageId) { _onMessage = true; alertDialog("CANCELLED - " + destination.getUri().toString() + " id: " + cancelledMessageId); } /** * @see MessageListener#onMessageFailed(Destination, * MessageFailureException) */ public void onMessageFailed(final Destination destination, final MessageFailureException exception) { _onMessage = true; alertDialog("FAILED - " + exception.toString()); } /** * Checks if message event methods were called * * @return <code>true</code> if one of the message events happened */ public boolean isOnMessage() { return _onMessage; } /** * Gets response message * * @return message Response */ public Message getResponse() { return _response; } } /** * Message Listener for processing subscription responses using BPS */ private final class SubscriptionResponseListener extends DestinationListener { /** * Creates a new SubscriptionResponseListener object * * @param name * Name of the listener */ private SubscriptionResponseListener(final String name) { super(name); } /** * @see DestinationListener#onMessage(Destination, Message) */ public void onMessage(final Destination destination, final Message message) { super.onMessage(destination, message); final String s = ((ByteMessage) message).getStringPayload(); if (s.equalsIgnoreCase("rc=200")) { alertDialog("Subscription successful."); } else { alertDialog("Subscription failed."); } } } /** * MessageListener to be used with destinations - displays information about * message when message arrives, fails or is canceled. */ private static final class SimpleListener implements MessageListener { private final String _name; /** * Creates a new SimpleListener object * * @param name * Name of the listener */ private SimpleListener(final String name) { _name = name; } /** * @see MessageListener#onMessage(Destination, Message) */ public void onMessage(final Destination destination, final Message message) { String alertString = _name + " :RECEIVED[id: " + message.getMessageId() + "]:"; // Read payload if (message instanceof ByteMessage) { final String stringPayload = ((ByteMessage) message).getStringPayload(); if (stringPayload != null) { alertString += "\n" + stringPayload; } } alertDialog(alertString); } /** * @see MessageListener#onMessageCancelled(Destination, int) */ public void onMessageCancelled(final Destination destination, final int cancelledMessageId) { alertDialog("CANCELLED - " + destination.getUri().toString() + " id: " + cancelledMessageId); } /** * @see MessageListener#onMessageFailed(Destination, * MessageFailureException) */ public void onMessageFailed(final Destination destination, final MessageFailureException exception) { alertDialog("FAILED - " + exception.toString()); } } private final class MonitoredDestinationListener extends DestinationListener { private final Object _lock; /** * Creates a new DestinationListener object * * @param name * Name of the listener */ private MonitoredDestinationListener(final String name) { super(name); _lock = "str_lock"; } public Object getLock() { return _lock; } public void onMessage(final Destination destination, final Message message) { super.onMessage(destination, message); synchronized (_lock) { _lock.notifyAll(); } } } private static final class TimeoutMonitor implements Runnable { private final SenderDestination _destination; private final MonitoredDestinationListener _responseListener; private final boolean _releaseDestination; private final ResponseCallback _responseCallback; private Message _message; public TimeoutMonitor(final SenderDestination destination, final boolean releaseDestination, final ResponseCallback callback, final MonitoredDestinationListener responseListener) { _destination = destination; _releaseDestination = releaseDestination; _responseCallback = callback; _responseListener = responseListener; } public TimeoutMonitor(final SenderDestination destination, final boolean releaseDestination, final ResponseCallback callback, final MonitoredDestinationListener responseListener, final Message message) { this(destination, releaseDestination, callback, responseListener); _message = message; } public void run() { try { final Object lock = _responseListener.getLock(); // Send message to retrieve the response if (_message == null) { ((NonBlockingSenderDestination) _destination).send(); } else { ((NonBlockingSenderDestination) _destination) .send(_message); } // Wait till a response is received by the message listener synchronized (lock) { try { lock.wait(TIMEOUT * 1000); } catch (final InterruptedException ex) { // } } if (_responseListener.isOnMessage()) { final Message responseMsg = _responseListener.getResponse(); _responseCallback.updateUI(responseMsg); } else { _responseCallback.timeoutDialog(TIMEOUT); } } catch (final Exception e) { alertDialog(e.toString()); } finally { if (_releaseDestination && _destination != null) { _destination.release(); } } } } }