Walkthrough: Deploy a JavaScript JMS App as a Hybrid Android App

In this walkthrough, you will learn how to deploy an existing JavaScript JMS web app built with the Kaazing WebSocket Gateway JavaScript JMS libraries as a hybrid app for Android. This topic walks you through the following subjects:

  1. What You Will Accomplish
  2. Before You Begin
  3. Install the Gateway, Android SDK, and Cordova 6.0
  4. Build the Hybrid Android App Using Cordova
  5. Configure and Run the Gateway and Apache ActiveMQ
  6. Test the Hybrid Android App
  7. Summary
Notes:
  • A hybrid Android app is a hybrid of a browser-based application and a native Android app. A native Android app is built using the Android SDK. A hybrid Android app is built using HTML, CSS, and JavaScript like a typical browser-based application, and then packaged in a framework that allows it to be converted into an app that runs on Android devices much like a native app. Hybrid Android apps enable web developers to leverage their web experience to create apps for Android. For information on native Android development, see Android Develop.
  • A hybrid Android app is also different from a browser-based application that is intended for viewing in a browser on Android, but not downloaded through the Android app store. If you have an existing Kaazing WebSocket Gateway JavaScript JMS browser-based application, you can use it to create a hybrid Android app or you can modify its web content to make it compatible with browsers on Android.

What You Will Accomplish

At the end of this walkthrough, a JavaScript JMS demo created using the Kaazing WebSocket Gateway JavaScript JMS libraries runs as a hybrid Android app on Android, connects to the Apache ActiveMQ broker via the Gateway, and sends and receives JMS messages using a native or emulated WebSocket connection. Users can run the hybrid Android app on any Android device and connect via the Gateway to the Apache ActiveMQ broker.

This walkthrough uses the JavaScript JMS demo that ships with the Gateway as the example app, but the steps outlined in this walkthrough are the same for other JavaScript JMS client applications built with the Gateway.

Note: For this walkthrough, you can use any JMS-compliant message broker. By default, the Gateway is configured to connect to the server on tcp://localhost:61613. You can configure the connect URL in the file GATEWAY_HOME/conf/gateway-config.xml. See About Integrating Kaazing WebSocket Gateway and JMS-Compliant Message Brokers for more information.

Before You Begin

Before starting this walkthrough you need the following:

Learn about supported browsers, operating systems, and platform versions in Release Notes.

Note: Steps for installing and configuring Android Studio, Android SDK, and Cordova 6.0 are included in this walkthrough.

Install the Gateway, Android Studio, Android SDK, and Cordova 6.0

The following steps take you through the installation of the software required for deploying a hybrid Android app. If you have already have this software installed, you can simply note the locations of the installed software for later use.

  1. Install the Gateway as described in Setting Up Kaazing WebSocket Gateway.
  2. Download and install Android Studio from http://developer.android.com/sdk/installing/index.html?pkg=studio.
  3. Launch Android Studio.
  4. In Welcome to Android Studio, click Configure, and then click SDK Manager.
  5. Select the Android 2.2 and the Android 4.1 packages and then click Install packages.
  6. Download and install Cordova 6.0 as described in Installing the Cordova CLI from Cordova.

Build the Hybrid Android App Using Cordova

The following steps will create the project directory for the hybrid Android app, and populate the directory with the web app files and JavaScript JMS library files required to communicate with the Gateway.

Note: The general steps for creating an Android app using Cordova are documented in the Apache Cordova Documentation, Create the App.

  1. Open a Terminal at the folder where you want to save the app.
  2. To create the app and populate it with the JavaScript demo, enter the following:

    cordova create myAndroidApp com.example.myapp myAndroidApp --template GATEWAY_HOME/demo/cordova/www

    The GATEWAY_HOME/demo/cordova/www points to the JavaScript demo files included with the Gateway.

  3. In the myAndroidApp folder created by Cordova, open the HTML file that contains the HTML GUI and JavaScript code of your app. It is located here: myAndroidApp/www/index.html.
  4. Replace the contents of index.html with the following:

    <!DOCTYPE html>
    <html class="no-js">
    <head>
    
      <link rel="stylesheet" href="resources/css/normalize.css">
      <link rel="stylesheet" href="resources/css/dev.css">
      <link rel="stylesheet" href="resources/css/demo.css">
    
      <style>
    
        body {
          margin: 2px;
          margin-top: 0;
          background-color: whitesmoke;
        }
    
        h1 {
          font-size: 24px;
          margin-top: 0;
        }
    
        div.setting {
          margin-top: 5px;
        }
        div.setting:first-of-type {
          margin-top: 0;
        }
        div.setting label {
          width: 80px;
        }
        div.setting input {
          width: 180px;
          margin-bottom: 0;
        }
        div.setting button {
          width: 85px;
        }
    
        #console {
          position: relative;
          float: left;
          overflow-y: scroll;
          width: 100%;
          height: 200px;
          border: solid 1px #dddddd;
          background-color: white; }
    
        #console pre {
          font-family: monospace; }
    
        #console > div {
          border-bottom: dashed 1px #dddddd;
          padding-top: 5px;
          padding-bottom: 5px; }
    
        #console div {
          font-family: monospace;
          padding-left: 5px;
          font-size: 15px;
          line-height: 18px; }
    
        #console > div:nth-child(even) {
          background-color: #fafafa; }
    
        #console div div {
          line-height: 15px; }
    
        #console .error {
          color: red; }
    
        #console > div > div {
          display: block;
          margin-left: 15px;
          font-size: smaller; }
    
        #console .sendMessage {
          color: #336633; }
    
        #console .sendMessage .destination {
          color: #577557; }
    
        #console .sendMessage .properties {
          color: #45ad45; }
    
        #console .sendMessage .headers {
          color: #8aad45; }
    
        #console.hidden .headers {
          /*    display: none; */
          background-color: red; }
    
        #console .receiveMessage {
          color: #0000ff; }
    
        #console .receiveMessage .destination {
          color: #6666ff; }
    
        #console .receiveMessage .properties {
          color: #4791ff; }
    
        #console .receiveMessage .headers {
          color: #7193eb; }
    
        #console .txSendMessage {
          color: #606; }
    
        #console .txSendMessage > div {
          color: #9e4d9e; }
    
        #subscriptions {
          border: 0;
          font-size: smaller;
          width: 100%;
          margin: 0;
        }
    
        #subscriptions th, #subscriptions td {
          background-color: whitesmoke;
          border: 0;
          text-align: left;
          padding-left: 5px;
          padding-right: 5px;
          line-height: 150%;
    /*      padding-top: 3px;
          padding-bottom: 3px;
    */    }
    
        #subscriptions th {
          padding-top: 2px;
          padding-bottom: 2px;
          line-height: 15px;
          font-size: 14px;
        }
    
        #subscriptions > thead, #subscriptions > tbody > tr:nth-child(even) {
          background-color: #fbfbfb; }
    
        #subscriptions th.destination, #subscriptions td.destination {
          white-space: nowrap;
    /*      min-width: 125px; */
          /*line-height: 15px;*/
        }
    
        #subscriptions th.unsubscribe, #subscriptions td.unsubscribe {
          width: 100%; }
    
        #subscriptions button {
          font-size: smaller; }
    
        #console_div label {
          width: auto;
          margin-left: 5px; }
    
        #middle .subscriptionTag {
          color: #aaaaaa; }
    
        div.section-heading {
          font-weight: bold;
        }
    
        div.section-heading-left {
          float: left;
        }
        div.toggle {
          float: right;
        }
    
        input[type=checkbox] {
          width: 20px;
        }
    
        label[for=toggleJmsHeadersCb] {
          width: 140px;
          float: none;
          font-weight: normal;
        }
    
        button#clear {
          float: right;
        }
    
        img#logo {
          width: 166px;
          margin-left: 3px;
        }
    
      </style>
    
      <script src="resources/js/jquery-1.9.1.min.js"></script>
      <script src="resources/js/modernizr.js"></script>
    
    </head>
    <body>
    
    <!--  Kaazing scripts -->
    <script type="text/javascript" language="javascript" src="WebSocket.js"></script>
    <script type="text/javascript" language="javascript" src="JmsClient.js"></script>
    
    <script type="text/javascript">
    
    var connection;
    var session;
    
    /* UI Elements */
    var logConsole, url, connectBut;
    var destination, message, subscribe, send;
    var rollback, clear;
    var receivedMessageCount, receivedMessageCounter = 0;
    var subscriptionsTable;
    var destinationCounter = 1;
    var toggleJmsHeadersCb;
    var logoHeight;
    
    function clearLog() {
      while (logConsole.childNodes.length > 0) {
        logConsole.removeChild(logConsole.lastChild);
      }
    }
    
    // Log a string message
    function log(message) {
      var div = document.createElement("div");
      div.className = "logMessage"
      div.innerHTML = message;
      logDiv(div);
    }
    
    function logDiv(div) {
      logConsole.appendChild(div);
      toggleJmsHeaders(); // Hide the headers if that's what the user specified
      // Make sure the last line is visible.
      logConsole.scrollTop = logConsole.scrollHeight;
      while (logConsole.childNodes.length > 20) {
        // Delete two rows to preserved the alternate background colors.
        logConsole.removeChild(logConsole.firstChild);
        logConsole.removeChild(logConsole.firstChild);
      }
    }
    
    function updateConnectionButtons(connected) {
      if (connected) {
        connectBut.innerHTML = "Disconnect";
      } else {
        connectBut.innerHTML = "Connect";
      }
      subscribe.disabled = !connected;
      send.disabled = !connected;
    }
    
    function createDestination(name, session) {
      if (name.indexOf("/topic/") == 0) {
        return session.createTopic(name);
      }
      else if (name.indexOf("/queue/") == 0) {
        return session.createQueue(name);
      }
      else {
        throw new Error("Destination must start with /topic/ or /queue/");
      }
    }
    
    function handleConnectBut() {
      if (connectBut.innerHTML == "Connect") {
        doConnect();
      } else {
        doDisconnect();
      }
    }
    
    function doConnect() {
      log("CONNECT: " + url.value);
    
      var jmsConnectionFactory = new JmsConnectionFactory(url.value);
    
      //setup challenge handler
      setupSSO(jmsConnectionFactory.getWebSocketFactory());
      try {
        var connectionFuture =
        jmsConnectionFactory.createConnection(null, null, function () {
          if (!connectionFuture.exception) {
            try {
              connection = connectionFuture.getValue();
              connection.setExceptionListener(handleException);
    
              log("CONNECTED");
    
              session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
              transactedSession = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
    
              connection.start(function () {
                updateConnectionButtons(true);
              });
            }
            catch (e) {
              handleException(e);
            }
          }
          else {
            handleException(connectionFuture.exception);
          }
        });
      }
      catch (e) {
        handleException(e);
      }
    }
    
    function handleException(e) {
      log("<span class='error'>EXCEPTION: " + e+"</span>");
    
      if (e.type == "ConnectionDisconnectedException") {
        updateConnectionButtons(false);
      }
    }
    
    function doDisconnect() {
      // Clear any subscriptions.
      if (document.getElementsByClassName) {
        var subscriptions = document.getElementsByClassName("unsubscribeButton");
        while (subscriptions[0]) {
          subscriptions[0].click();
        }
      } else {
        // The IE way.
        var unsubscribeButtons = subscriptionsTable.getElementsByTagName("button");
        while (unsubscribeButtons.length > 0) {
          var b = unsubscribeButtons[0];
          if (b.className == "unsubscribeButton") {
            b.click();
          }
        }
      }
    
      log("CLOSE");
      try {
        connection.close(function () {
          log("CONNECTION CLOSED");
          updateConnectionButtons(false);
        });
      }
      catch (e) {
        handleException(e);
      }
    }
    
    function handleSubscribe() {
      var name = destination.value;
    
      var destinationId = destinationCounter++;
    
      log("SUBSCRIBE: " + name + " <span class=\"subscriptionTag\">[#"+destinationId+"]</span>");
    
      var dest = createDestination(name, session);
    
      var consumer;
    
      consumer = session.createConsumer(dest);
    
      consumer.setMessageListener(function(message) {
        handleMessage(name, destinationId, message);
      });
    
      // Add a row to the subscriptions table.
      //
    
      var tBody = subscriptionsTable.tBodies[0];
    
      var rowCount = tBody.rows.length;
      var row = tBody.insertRow(rowCount);
    
      var destinationCell = row.insertCell(0);
      destinationCell.className = "destination";
      destinationCell.appendChild(document.createTextNode(name+" "));
      var destNode = document.createElement("span");
      destNode.className = "subscriptionTag";
      destNode.innerHTML = "[#"+destinationId+"]";
      destinationCell.appendChild(destNode);
    
      var unsubscribeCell = row.insertCell(1);
      unsubscribeCell.className = "unsubscribe";
      var unsubscribeButton = document.createElement("button");
      unsubscribeButton.className = "unsubscribeButton";
      unsubscribeButton.innerHTML = "Unsubscribe";
      unsubscribeButton.addEventListener('click', function(event) {
        var targ;
        if (event.target) {
          targ = event.target;
        } else {
          targ=event.srcElement; // The wonders of IE
        }
        log("UNSUBSCRIBE: " + name + " <span class=\"subscriptionTag\">[#"+destinationId+"]</span>");
        if (consumer) {
          consumer.close(null);
        }
        var rowIndex = targ.parentElement.parentElement.rowIndex
        subscriptionsTable.deleteRow(rowIndex);
          setLogPaneHeight();
      }, false);
      unsubscribeCell.appendChild(unsubscribeButton);
    
      setLogPaneHeight();
    }
    
    function handleMessage(destination, destinationId, message) {
      var content = "";
    
      if (message instanceof TextMessage) {
        content = "RECEIVED TextMessage: " + message.getText();
      }
      else if (message instanceof BytesMessage) {
        var body = [];
        message.readBytes(body);
        content = "RECEIVED BytesMessage: " + body;
      }
      else if (message instanceof MapMessage) {
        var keys = message.getMapNames();
        content = "RECEIVED MapMessage: <br/>";
    
        for (var i=0; i<keys.length; i++) {
          var key = keys[i];
          var value = message.getObject(key);
          var type;
          if (value == null) {
            type = "";
          }
          else if (value instanceof String) {
            type = "String";
          }
          else if (value instanceof Number) {
            type = "Number";
          }
          else if (value instanceof Boolean) {
            type = "Boolean";
          }
          else if (value instanceof Array) {
            type = "Array";
          }
          content += key + ": " + value;
          if(type != "") {
            content += " (" + type + ")"
          }
          content += "<br />";
        }
      }
      else {
        content = "RECEIVED UNKNOWN MESSAGE";
      }
    
      var div = document.createElement("div");
      div.className = "logMessage receiveMessage"
      div.innerHTML = content;
    
      div.appendChild(buildDestinationDiv("Destination", destination));
    
      div.appendChild(buildPropertiesDiv(message));
    
      div.appendChild(buildJMSHeadersDiv(message, true));
    
      logDiv(div);
    
      receivedMessageCount.innerHTML = ++receivedMessageCounter;
    }
    
    var logMessageSend = function(classname, prefix, destination, messageStr, message) {
      var div = document.createElement("div");
      div.className = "logMessage "+classname
      div.innerHTML = prefix + messageStr;
    
      div.appendChild(buildPropertiesDiv(message));
    
      div.appendChild(buildJMSHeadersDiv(message, false));
    
      div.appendChild(buildDestinationDiv("Destination", destination));
    
      logDiv(div);
    }
    
    var buildDestinationDiv = function(label, destName, destId) {
      var destinationDiv = document.createElement("div");
      destinationDiv.className = "destination";
      var destIdStr = "";
      if (destId != undefined) {
        destIdStr = " [#"+destId+"]";
      }
      destinationDiv.innerHTML += label + ": " + destName+destIdStr;
      return destinationDiv;
    }
    
    var buildPropertiesDiv = function(message) {
      var propsDiv = document.createElement("div");
      propsDiv.className = "properties";
      var props = message.getPropertyNames();
      while (props.hasMoreElements()) {
        var propName = props.nextElement();
        var propValue = message.getStringProperty(propName);
        propsDiv.innerHTML += "Property: "+propName+"="+propValue+"<br>";
      }
      return propsDiv;
    }
    
    var buildJMSHeadersDiv = function(message, receive) {
      var headersDiv = document.createElement("div");
      headersDiv.className = "headers";
      var deliveryModeStr;
      switch (message.getJMSDeliveryMode()) {
      case DeliveryMode.NON_PERSISTENT:
        deliveryModeStr = "NON_PERSISTENT";
        break;
      case DeliveryMode.PERSISTENT:
        deliveryModeStr = "PERSISTENT";
        break;
      default:
        deliveryModeStr = "UNKNOWN";
      }
    
      var jmsDestination = message.getJMSDestination();
      var destinationName = (jmsDestination instanceof Queue) ? jmsDestination.getQueueName()
      : jmsDestination.getTopicName();
      headersDiv.innerHTML += "JMSDestination: " + destinationName +"<br>";
    
      if (receive) {
        headersDiv.innerHTML += "JMSRedelivered: " + message.getJMSRedelivered()+"<br>";
      }
    
      headersDiv.innerHTML += "JMSDeliveryMode: "+message.getJMSDeliveryMode()+" ("+deliveryModeStr+")<br>";
      headersDiv.innerHTML += "JMSPriority: "+message.getJMSPriority()+"<br>";
      headersDiv.innerHTML += "JMSMessageID: "+message.getJMSMessageID()+"<br>";
      headersDiv.innerHTML += "JMSTimestamp: "+message.getJMSTimestamp()+"<br>";
      headersDiv.innerHTML += "JMSCorrelationID: "+message.getJMSCorrelationID()+"<br>";
      headersDiv.innerHTML += "JMSType: "+message.getJMSType()+"<br>";
      headersDiv.innerHTML += "JMSReplyTo: "+message.getJMSReplyTo()+"<br>";
      return headersDiv;
    }
    
    var addProperties = function(message) {
      var i = 1;
      var propName;
      while (propName = document.getElementById("propName"+i)) {
        if (propName.value.length > 0) {
          var propValue = document.getElementById("propValue"+i);
          message.setStringProperty(propName.value, propValue.value);
        }
        i++;
      }
    }
    
    function handleSend() {
      var name = destination.value;
      var dest = createDestination(name, session);
      var producer = session.createProducer(dest);
    
      var textMsg = session.createTextMessage(message.value);
    
      addProperties(textMsg);
    
      try {
        var future = producer.send(textMsg, function(){
          if (future.exception) {
            handleException(future.exception);
          }
        });
      } catch (e) {
        handleException(e);
      }
    
      logMessageSend("sendMessage", "SEND TextMessage: ", destination.value, message.value, textMsg);
    
      producer.close();
    }
    
    var toggleJmsHeaders = function(event) {
      $('div.headers').toggleClass('hidden', !toggleJmsHeadersCb.checked);
    }
    
    $( document ).ready(function() {
    
      // Initialize UI elements
      url = document.getElementById("url");
    
      connectBut = document.getElementById("connectBut");
    
      logConsole = document.getElementById("console")
      receivedMessageCount = document.getElementById("receivedMessageCount");
      toggleJmsHeadersCb = document.getElementById("toggleJmsHeadersCb");
    
      destination = document.getElementById("destination");
      message = document.getElementById("message");
      subscribe = document.getElementById("subscribe");
      send = document.getElementById("send");
    
      clear = document.getElementById("clear");
    
      subscriptionsTable = document.getElementById("subscriptions");
    
      // default the location
      url.value = "ws://10.0.2.2:8888/jms";
    
      updateConnectionButtons(false);
    
      connectBut.onclick = handleConnectBut;
      subscribe.onclick = handleSubscribe;
      send.onclick = handleSend;
    
      clear.onclick = clearLog;
      toggleJmsHeadersCb.onclick = toggleJmsHeaders;
    
      // initialize the disabled states
      connectBut.disabled = null;
    
      logoHeight = $("#logo").height();
    
      setLogPaneHeight();
    
    
    });
    
      // Grow the log pane to fit to the bottom of the window, if there is
      // space available.
    function setLogPaneHeight() {
      var windowHeight = $(window).height();
      var console = $("#console");
      if (console.position().top < windowHeight - logoHeight) {
        console.css("height", windowHeight - console.position().top - (console.outerHeight() - console.height()) - logoHeight);
      }
    }
    
    function setupSSO(webSocketFactory) {
      /* Respond to authentication challenges with popup login dialog */
      var basicHandler = new BasicChallengeHandler();
      basicHandler.loginHandler = function(callback) {
        popupLoginDialog(callback);
      }
      webSocketFactory.setChallengeHandler(basicHandler);
    }
    
    
    function popupLoginDialog(callback) {
      //popup dialog to get credentials
      var popup = document.getElementById("sso_logindiv");
      popup.style.display = "block";
      var login = document.getElementById("sso_login");
      var cancel = document.getElementById("sso_cancel");
    
      //"OK" button was clicked, invoke callback function with credential to login
      login.onclick = function() {
        var username = document.getElementById("sso_username");
        var password = document.getElementById("sso_password");
        var credentials = new PasswordAuthentication(username.value, password.value);
        //clear user input
        username.value = "";
        password.value = "";
        //hide popup
        popup.style.display = "none";
        callback(credentials);
      }
      //"Cancel" button has been clicked, invoke callback function with null argument to cancel login
      cancel.onclick = function() {
        var username = document.getElementById("sso_username");
        var password = document.getElementById("sso_password");
        //clear user input
        username.value = "";
        password.value = "";
        //hide popup
        popup.style.display = "none";
        callback(null);
      }
    }
    
    </script>
    
      <h1>Cordova JMS Messaging Demo</h1>
    
      <div class="setting">
      <label for="url">Location</label>
      <input id="url" />
      <button id="connectBut">Connect</button>
      <!-- Login dialog for testing authentication -->
      <div id="sso_logindiv" style="margin-left: 2px; position: absolute; border: 1px solid black; border-radius:10px; display: none; height: 190px; width: 318px; background-color:#d0e7fd; z-index: 999;">
        <div style="margin-left: 20px; height: 35px; margin-top: 20px; font-weight: bold;">Login</div>
        <div style="height: 124px; width: 296px; border: 1px solid black; border-radius:10px; background-color:white; margin-left: 10px;">
          <div style="margin-left:10px; margin-top: 10px;">
            <span style="width: 60px; font-size:11pt;">Username:</span><input id="sso_username" size="12" style="width: 180px" value="" />
          </div>
          <div style="margin-left:10px">
            <span style="width: 60px;  font-size:11pt;">Password:</span><input id="sso_password" type="password" size="12" style="width: 180px" value="" />
          </div>
          <div style="margin-left:45px">
            <button id="sso_login" style="margin-left:25px; width: 60px;">OK</button>
            <button id="sso_cancel"  style="margin-left:25px; width: 60px;">Cancel</button>
          </div>
        </div>
      </div>
      <!-- Login dialog for testing authentication -->
      </div>
    
      <div class="setting">
      <label for="destination">Destination</label>
      <input id="destination" value="/topic/destination">
      <button id="subscribe">Subscribe</button>
      </div>
    
      <div class="setting">
      <label for="message">Message</label>
      <input id="message" value="Hello, message">
      <button id="send">Send</button>
      </div>
    
      <hr />
    
      <div class="section-heading">Subscriptions</div>
      <table id="subscriptions">
    
        <tbody>
    
        </tbody>
    
      </table>
    
      <hr />
    
      <div class="section-heading section-heading-left">Log</div>
      <div class="toggle">
      <input type="checkbox" id="toggleJmsHeadersCb" class="cb">
      <label for="toggleJmsHeadersCb">Show JMS headers</label>
      </div>
      <div style="clear: both;"></div>
    
      Messages received : <span id="receivedMessageCount">0</span>
      <button id="clear">Clear Log</button>
      <div style="clear: both;"></div>
    
      <div id="console"></div>
      <div style="clear: both;"></div>
    
      <img id="logo" src="resources/images/nav-logo-kaazing.png" />
    
    </body>
    </html>
    

    Note: This walkthrough uses the out of the box JavaScript JMS demo as an example web app, but when you create your own JavaScript JMS app, you can create it in your favorite IDE and then copy it to this www directory. You must name the HTML file for your app index.html and you must ensure that links in index.html point to the JavaScript WebSocket library (by default, GATEWAY_HOME/lib/client/javascript) and the JavaScript JMS library (by default, GATEWAY_HOME/lib/client/javascript/jms), for example:

    <script src="WebSocket.js" type="text/javascript" language="javascript"></script>
    <script src="JmsClient.js" type="text/javascript" language="javascript"></script>
    
  5. Save index.html.
  6. In Terminal, navigate to the folder for your new app (cd myAndroidApp).
  7. To specify the target platform as Android, enter the following command: cordova platform add android. Note: To see a list of the current platforms available, enter: cordova platforms ls.
  8. To build the project, enter the following command: cordova build android. The build process might take some time. When it is finsihed, Cordova generates the platform-specific project within the project's platforms subdirectory that you can open with Android Studio (for example, myAndroidApp/platforms/android).

    On Mac or Linux, if you receive the error Error: Android SDK not found then open your .bash_profile file and enter the following (replace JohnSmith with your user name):

    export ANDROID_HOME=/Users/JohnSmith/Library/Android/sdk
    export PATH=$ANDROID_HOME/platform-tools:$PATH
    export PATH=$ANDROID_HOME/tools:$PATH

    For more info, https://cordova.apache.org/docs/en/dev/guide/platforms/android/.

Configure and Run the Gateway and Apache ActiveMQ

Now that you've set up the project for your Android app, you can start Apache ActiveMQ and the Gateway. The following steps start the Apache ActiveMQ service that is included with the Gateway, configure the Gateway to work with the hybrid Android app, and then run the Gateway.

  1. Start Apache ActiveMQ. For steps on starting Apache ActiveMQ, see the setup information for Apache ActiveMQ. Typically, open a Terminal in ACTIVEMQ_HOME/bin and enter: ./activemq start.
  2. Open the GATEWAY_HOME/conf/gateway-config.xml file in a text editor. You need to modify this file to use the Android Emulator loopback IP address (10.0.2.2) and you need to change the cross site constraint to allow any origin in order to run the app locally. 10.0.2.2 is the special alias to your host loopback interface (127.0.0.1 on your development machine). For more information about Android Emulator networking, see Emulator Networking from Android.
  3. Modify the jms service to use the Android loopback IP address and the cross site constraint to allow any origin:

    <service>
      <name>JMS Service</name>
      <description>JMS Service</description>
      <accept>ws://10.0.2.2:8888/jms</accept>
    
      <type>jms</type>
    
      <properties>
        <connection.factory.name>ConnectionFactory</connection.factory.name>
        <context.lookup.topic.format>dynamicTopics/%s</context.lookup.topic.format>
        <context.lookup.queue.format>dynamicQueues/%s</context.lookup.queue.format>
        <env.java.naming.factory.initial>org.apache.activemq.jndi.ActiveMQInitialContextFactory</env.java.naming.factory.initial>
        <env.java.naming.provider.url>tcp://localhost:61616</env.java.naming.provider.url>
      </properties>
    
        <accept-options>
          <ws.bind>localhost:8888</ws.bind>
        </accept-options>
    
      <realm-name>demo</realm-name>
    
      <cross-site-constraint>
        <allow-origin>*</allow-origin>
      </cross-site-constraint>
    </service>
    
  4. Save the gateway-config.xml file.
  5. Invoke the gateway.start command by navigating to the GATEWAY_HOME/bin directory where you installed the Gateway and then enter the following to run the gateway.start script:

    ./gateway.start (Mac and Linux)

    gateway.start.bat (Windows)

    The output from the command will display that the Gateway is listening on:

    ws://10.0.2.2:8888/jms @ localhost:8888

Test the Hybrid Android App

The following steps use the Android Emulator to test the hybrid Android app that you created.

Notes:
  • Both the Gateway and Apache ActiveMQ must be running before testing the hybrid Android app.
  • When testing on Android devices: In order to connect the hybrid Android app on the Android device to the Gateway and Apache ActiveMQ, the Android device must be able to resolve the host name of computer running the Gateway on the network. This is the host name that you will use in the Location field of the hybrid Android app. For example, ws://host_name:8001/jms. You can also configure the Android device to use the IP Address of the computer running the Gateway (for example, ws://192.168.4.86:8001/jms). Using the IP Address is often the easiest method during testing.
  1. In Terminal, ensure that you are in the root of the project directory (for example, myAndroidApp), and enter the following to see a list of all available Simulator targets: cordova run android --list. For this walkthrough, we will use Nexus_5_API_22_x86.
  2. To run the app in the Android Simulator, enter the following: cordova run android --target="Nexus_5_API_22_x86". The Android Simulator opens and displays the hybrid Android app. It might take several minutes for the Android Simulator to launch and display the app. The app icon might appear on the Android Simulator screen for a while before displaying the GUI of the app. You might need to relaunch the app within the Android Simulator.
  3. In the hybrid app in the Android Simulator, click in the Location field, and enter the jms service URI ws://10.0.2.2:8888/jms, and click Connect.

    The Log messages field reports a successful connection. If the app does not connect, ensure that you have the Gateway and Apache ActiveMQ running.

  4. Play with hybrid app to see the full functionality.
  5. For information on testing your hybrid Android app on an Android device, see Setting up a Device for Development and OEM USB Drivers in the Android developer documentation (http://developer.android.com).

Summary

In this walkthrough, you learned how to turn one of the out of the box Kaazing WebSocket Gateway JavaScript JMS demos into a hybrid Android app. To learn how to build your own Kaazing WebSocket Gateway JMS apps, refer to the documentation.

Notes

Clients built using Kaazing WebSocket Gateway 3.x libraries will work against Kaazing WebSocket Gateway 4.x. If you wish to upgrade your 3.x client to the 4.x libraries, please note that the 3.x clients used a single JMS library and 4.x clients include and use separate WebSocket and JMS libraries. Update your client library file and code references to include both the WebSocket and JMS libraries, as described in the 4.x documentation. For more migration information, see Migrate JavaScript Applications to Kaazing WebSocket Gateway 4.x.

TOP