Enabling UI live-reload for apps not in the ONOS source code

Change-Id: Ib88929a8825b7adf136d2b6b90d66db10549c165
diff --git a/web/gui/src/main/webapp/README.md b/web/gui/src/main/webapp/README.md
index cbf3160..4d51023 100644
--- a/web/gui/src/main/webapp/README.md
+++ b/web/gui/src/main/webapp/README.md
@@ -26,4 +26,17 @@
 [BS] Watching files...
 ```
 
-To open ONOS visit the local URL (eg: `http://localhost:3000`) plus `/onos/ui` (eg: `http://localhost:3000/onos/ui`)
\ No newline at end of file
+To open ONOS visit the local URL (eg: `http://localhost:3000`) plus `/onos/ui`
+(eg: `http://localhost:3000/onos/ui`)
+
+## Loading files from external applications
+
+The UI development environment provide the ability to serve UI files
+from an external forlder that can be specified with:
+`ONOS_EXTERNAL_APP_DIRS="appName:path-to-the-first-folder" npm start`
+
+Eg:
+`ONOS_EXTERNAL_APP_DIRS="sampleCustom:../../meow/sample/meowster-sample/" npm start`
+
+_Note that `ONOS_EXTERNAL_APP_DIRS` is an environment variable,so it can be set with_
+_`export ONOS_EXTERNAL_APP_DIRS="sampleCustom:../../meow/sample/meowster-sample/"`_
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/bs-config.js b/web/gui/src/main/webapp/bs-config.js
index e7419ac..210560a 100644
--- a/web/gui/src/main/webapp/bs-config.js
+++ b/web/gui/src/main/webapp/bs-config.js
@@ -19,15 +19,38 @@
   target: 'http://localhost:8182',
 });
 
+if (process.env.ONOS_EXTERNAL_APP_DIRS) {
+    var external_apps = process.env.ONOS_EXTERNAL_APP_DIRS.replace(/\s/,'').split(',');
+    external_apps = external_apps.reduce(function(dict, app){
+        var pieces = app.split(':');
+        var appName = pieces[0];
+        var appPath = pieces[1];
+        dict[appName] = appPath;
+        return dict;
+    }, {});
+}
+
 var defaultViews = fs.readdirSync('./app/view/');
 var viewNameMatcher = new RegExp(/\/onos\/ui\/app\/view\/(.+)\/.+\.(?:js|css|html)/);
 
+var checkExternalApp = function (url) {
+    if(external_apps){
+        for(var i = 0; i < Object.keys(external_apps).length; i++){
+            var key = Object.keys(external_apps)[i];
+            if(url.indexOf(key) !== -1){
+                return key;
+            };
+        }
+    }
+    return false;
+};
+
 proxy.on('upgrade', function (req, socket, head) {
   console.log('[WS]: ', head);
   proxy.ws(req, socket, head);
 });
 
-proxy.on('error', function(error, req, res) {
+proxy.on('error', function (error, req, res) {
   res.writeHead(500, {
     'Content-Type': 'text/plain'
   });
@@ -50,21 +73,29 @@
   proxy: {
     target: "http://localhost:8181",
     ws: true,
-    middleware: function(req, res, next){
-
+    middleware: function (req, res, next) {
 
       var viewName = viewNameMatcher.exec(req.url);
-      if(!!viewName && defaultViews.indexOf(viewName[1]) === -1){
-        // in this case it is an external application that extend the view
+      var isExternalApp = checkExternalApp(req.url);
+
+      if (!!viewName && !isExternalApp && defaultViews.indexOf(viewName[1]) === -1) {
+        // in this case it is an application that extend the view
         // so we redirect the request to the app folder in case of .js, .css, .html
         req.url = req.url.replace('/onos/ui/', '/apps/' + viewName[1] + '/app/src/main/resources/');
         proxy.web(req, res);
       }
+      else if (isExternalApp) {
+        // in this case it is an external application (not in ONOS source code) that extend the view
+        // so we redirect the request to the app folder in case of .js, .css, .html
+        req.url = req.url.replace('/onos/ui/', '/src/main/resources/');
+        proxy.web(req, res);
+      }
       // NOTE onos.js and index.html should not be proxied (require server side injection)
-      else if(req.url.match(/(?:js|css|html)/) && req.url !== '/onos/ui/onos.js' && req.url !== '/onos/ui/index.html' && req.url !== '/onos/ui/nav.html'){
+      else if (req.url.match(/(?:js|css|html)/) && req.url !== '/onos/ui/onos.js' && 
+               req.url !== '/onos/ui/index.html' && req.url !== '/onos/ui/nav.html') {
         // redirect onos base js files to the source folder
         req.url = req.url.replace('/onos/ui/', '/web/gui/src/main/webapp/');
-        proxy.web(req, res); 
+        proxy.web(req, res);
       }
       else {
         return next();
diff --git a/web/gui/src/main/webapp/dev_server.js b/web/gui/src/main/webapp/dev_server.js
index 513d37a..2ded0ff 100644
--- a/web/gui/src/main/webapp/dev_server.js
+++ b/web/gui/src/main/webapp/dev_server.js
@@ -1,10 +1,8 @@
 'use strict';
 
-var http = require('http');
-// var httpProxy = require('http-proxy');
-var connect = require('connect');
-var serveStatic = require('serve-static');
 var path = require('path');
+var express = require('express');
+var app = express();
 
 var conf = {
   paths: {
@@ -13,17 +11,31 @@
   port: '8182'
 }
 
-var httpProxyInit = function (baseDir) {
+if (process.env.ONOS_EXTERNAL_APP_DIRS) {
+    var external_apps = process.env.ONOS_EXTERNAL_APP_DIRS.replace(/\s/,'').split(',');
+    external_apps.forEach(function(a, i){
+        let [appName, appPath] = a.split(':');
+        conf.paths[appName] = appPath;
+    });
+}
 
-  var app = connect();
+var httpProxyInit = function (baseDirs) {
 
-  app.use(serveStatic(path.join(__dirname, baseDir)));
+  Object.keys(baseDirs).forEach(dir => {
+    var d = path.isAbsolute(baseDirs[dir]) ? baseDirs[dir] : path.join(__dirname, baseDirs[dir]);
+    app.use(express.static(d));
+  });
 
-  var server = http.createServer(app);
+  app.get('/', function (req, res) {
+    res.send('Hello World!');
+  });
 
-  server.listen(conf.port, function(){
-    console.log('Dev server is up and listening on http://localhost:', conf.port);
+  app.listen(conf.port, function () {
+    console.log(`Dev server is up and listening on http://localhost:${conf.port}!`);
   });
 };
 
-httpProxyInit(conf.paths.root);
\ No newline at end of file
+httpProxyInit(conf.paths);
+
+
+
diff --git a/web/gui/src/main/webapp/package.json b/web/gui/src/main/webapp/package.json
index c4f2cb7..fb7cf86 100644
--- a/web/gui/src/main/webapp/package.json
+++ b/web/gui/src/main/webapp/package.json
@@ -7,17 +7,16 @@
     "test": "tests"
   },
   "scripts": {
-    "bs": "browser-sync start --config bs-config.js",
-    "dev-server": "node dev_server.js",
+    "bs": "browser-sync start  --config bs-config.js",
+    "dev-server": "node --harmony_destructuring dev_server.js",
     "start": "parallelshell \"npm run dev-server\" \"npm run bs\""
   },
   "author": "ON.Lab",
   "license": "Apache 2.0",
   "devDependencies": {
     "browser-sync": "^2.12.8",
-    "connect": "^3.4.1",
-    "http-proxy": "^1.13.2",
     "parallelshell": "^2.0.0",
-    "serve-static": "^1.10.2"
+    "serve-static": "^1.10.2",
+    "express": "^4.14.0"
   }
 }