Converting builtin applications to be delivered in a self-contained manner via OAR files.

Change-Id: I5b7c6939aacc263248868fac2e0f69124c5f3609
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
index 7f4f9b2..d19c987 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
@@ -18,9 +18,11 @@
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import org.onosproject.app.ApplicationAdminService;
 import org.onosproject.app.ApplicationService;
 import org.onosproject.app.ApplicationState;
 import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
 
 import java.util.Arrays;
 import java.util.List;
@@ -37,11 +39,20 @@
      * Creates a new message handler for the application messages.
      */
     protected ApplicationViewMessageHandler() {
-        super(ImmutableSet.of("appDataRequest"));
+        super(ImmutableSet.of("appDataRequest", "appManagementRequest"));
     }
 
     @Override
     public void process(ObjectNode message) {
+        String type = string(message, "event", "unknown");
+        if (type.equals("appDataRequest")) {
+            sendAppList(message);
+        } else if (type.equals("appManagementRequest")) {
+            processManagementCommand(message);
+        }
+    }
+
+    private void sendAppList(ObjectNode message) {
         ObjectNode payload = payload(message);
         String sortCol = string(payload, "sortCol", "id");
         String sortDir = string(payload, "sortDir", "asc");
@@ -58,6 +69,24 @@
         connection().sendMessage("appDataResponse", 0, rootNode);
     }
 
+    private void processManagementCommand(ObjectNode message) {
+        ObjectNode payload = payload(message);
+        String action = string(payload, "action");
+        String name = string(payload, "name");
+        if (action != null && name != null) {
+            ApplicationAdminService service = get(ApplicationAdminService.class);
+            ApplicationId appId = service.getId(name);
+            if (action.equals("activate")) {
+                service.activate(appId);
+            } else if (action.equals("deactivate")) {
+                service.deactivate(appId);
+            } else if (action.equals("uninstall")) {
+                service.uninstall(appId);
+            }
+            sendAppList(message);
+        }
+    }
+
     private TableRow[] generateTableRows(ApplicationService service) {
         List<TableRow> list = service.getApplications().stream()
                 .map(application -> new ApplicationTableRow(service, application))
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index 3cc12b3..3be244e 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -628,7 +628,9 @@
         public void event(MastershipEvent event) {
             sendAllInstances("updateInstance");
             Device device = deviceService.getDevice(event.subject());
-            sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
+            if (device != null) {
+                sendMessage(deviceMessage(new DeviceEvent(DEVICE_UPDATED, device)));
+            }
         }
     }
 
diff --git a/web/gui/src/main/webapp/WEB-INF/web.xml b/web/gui/src/main/webapp/WEB-INF/web.xml
index e6807fa..f83fbad 100644
--- a/web/gui/src/main/webapp/WEB-INF/web.xml
+++ b/web/gui/src/main/webapp/WEB-INF/web.xml
@@ -160,7 +160,6 @@
         <url-pattern>/websock/*</url-pattern>
     </servlet-mapping>
 
-
     <servlet>
         <servlet-name>Legacy Web Socket Service</servlet-name>
         <servlet-class>org.onosproject.ui.impl.GuiWebSocketServlet</servlet-class>
diff --git a/web/gui/src/main/webapp/app/view/app/app.css b/web/gui/src/main/webapp/app/view/app/app.css
index 481708d..a44b219 100644
--- a/web/gui/src/main/webapp/app/view/app/app.css
+++ b/web/gui/src/main/webapp/app/view/app/app.css
@@ -37,25 +37,45 @@
     cursor: pointer;
 }
 
-.light .ctrl-btns g.icon use {
-    fill: #fff;
+
+/* Inactive */
+.light .ctrl-btns div g.icon rect,
+.light .ctrl-btns div:hover g.icon rect {
+    fill: #eee;
 }
-.dark .ctrl-btns g.icon use {
+.dark .ctrl-btns div g.icon rect,
+.dark .ctrl-btns div:hover g.icon rect {
+    fill: #222;
+}
+
+.light .ctrl-btns div g.icon use {
+    fill: #ddd;
+}
+.dark .ctrl-btns div g.icon use {
     fill: #333;
 }
 
-.light .ctrl-btns g.icon rect {
-    fill: #bbb;
-}
-.dark .ctrl-btns g.icon rect {
-    fill: #444;
+/* Active hover */
+.light .ctrl-btns div.active:hover g.icon rect {
+    fill: #800;
 }
 
-/* Inactive */
-.light .ctrl-btns .disabled g.icon rect {
+.dark .ctrl-btns div.active:hover g.icon rect {
+    fill: #CE5650;
+}
+
+/* Active */
+.light .ctrl-btns div.active g.icon use {
+    fill: #fff;
+}
+.dark .ctrl-btns div.active g.icon use {
     fill: #eee;
 }
-.dark .ctrl-btns .disabled g.icon rect {
-    fill: #111;
+
+.light .ctrl-btns div.active g.icon rect {
+    fill: #bbb;
+}
+.dark .ctrl-btns div.active g.icon rect {
+    fill: #444;
 }
 
diff --git a/web/gui/src/main/webapp/app/view/app/app.html b/web/gui/src/main/webapp/app/view/app/app.html
index ced8ec2..e1e1647 100644
--- a/web/gui/src/main/webapp/app/view/app/app.html
+++ b/web/gui/src/main/webapp/app/view/app/app.html
@@ -3,11 +3,16 @@
     <div class="tabular-header">
         <h2>Applications ({{ctrl.tableData.length}} total)</h2>
         <div class="ctrl-btns">
-            <div icon icon-size="36" icon-id="plus"></div>
-            <div icon icon-size="36" icon-id="minus" class="disabled"></div>
-            <div icon icon-size="36" icon-id="play" class="disabled"></div>
-            <div icon icon-size="36" icon-id="stop" class="disabled"></div>
+            <div id="app-install"    icon icon-size="36" icon-id="plus" class="active"></div>
+            <div id="app-activate"   icon icon-size="36" icon-id="play"></div>
+            <div id="app-deactivate" icon icon-size="36" icon-id="stop"></div>
+            <div id="app-uninstall"  icon icon-size="36" icon-id="minus"></div>
         </div>
+
+        <form id="app-form" method="POST" action="/onos/v1/applications/upload" enctype="multipart/form-data" style="display:none">
+            <input type="file" id="file" accept=".oar">
+            <button type="submit" id="app-upload">Upload</button>
+        </form>
     </div>
 
     <table class="summary-list"
@@ -20,7 +25,7 @@
                 <th colId="id" sortable>App ID </th>
                 <th colId="version" sortable>Version </th>
                 <th colId="origin" sortable>Origin </th>
-                <th colId="desc" col-width="400px">Description </th>
+                <th colId="desc" col-width="640px">Description </th>
             </tr>
         </thead>
 
diff --git a/web/gui/src/main/webapp/app/view/app/app.js b/web/gui/src/main/webapp/app/view/app/app.js
index 1d46471..236099d 100644
--- a/web/gui/src/main/webapp/app/view/app/app.js
+++ b/web/gui/src/main/webapp/app/view/app/app.js
@@ -1,14 +1,14 @@
 /*
  * Copyright 2015 Open Networking Laboratory
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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,
+ * 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.
@@ -21,19 +21,49 @@
 (function () {
     'use strict';
 
-    var selRow;
+    var selRow, selection;
 
     angular.module('ovApp', [])
     .controller('OvAppCtrl',
-        ['$log', '$scope', 'TableBuilderService',
+        ['$log', '$scope', 'TableBuilderService', 'WebSocketService',
 
-    function ($log, $scope, tbs) {
+    function ($log, $scope, tbs, wss) {
         function selCb($event, row) {
             selRow = angular.element($event.currentTarget);
-            // adjust which toolbar buttons are selected
+            selection = row;
             $log.debug('Got a click on:', row);
+            // adjust which toolbar buttons are selected
+            d3.select('#app-activate').classed('active', row && row.state === 'INSTALLED');
+            d3.select('#app-deactivate').classed('active', row && row.state === 'ACTIVE');
+            d3.select('#app-uninstall').classed('active', row);
         }
 
+        d3.select('#app-install').on('click', function () {
+            $log.debug('Initiating install');
+            var evt = document.createEvent("HTMLEvents");
+            evt.initEvent("click", true, true);
+            document.getElementById('file').dispatchEvent(evt);
+        });
+
+        function appAction(action) {
+            if (selection) {
+                $log.debug('Initiating uninstall of', selection);
+                wss.sendEvent('appManagementRequest', {action: action, name: selection.id});
+            }
+        }
+
+        d3.select('#file').on('change', function () {
+            var file = document.getElementById('file').value.replace('C:\\fakepath\\', '');
+            $log.info('Handling file', file);
+            var evt = document.createEvent("HTMLEvents");
+            evt.initEvent("click", true, true);
+            document.getElementById('app-upload').dispatchEvent(evt);
+        });
+
+        d3.select('#app-uninstall').on('click', function () { appAction('uninstall'); });
+        d3.select('#app-activate').on('click', function () { appAction('activate'); });
+        d3.select('#app-deactivate').on('click', function () { appAction('deactivate'); });
+
         tbs.buildTable({
             self: this,
             scope: $scope,