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

Change-Id: Ib88929a8825b7adf136d2b6b90d66db10549c165
diff --git a/protocols/bmv2/src/main/thrift/control_plane.thrift b/protocols/bmv2/src/main/thrift/control_plane.thrift
new file mode 100644
index 0000000..0885097
--- /dev/null
+++ b/protocols/bmv2/src/main/thrift/control_plane.thrift
@@ -0,0 +1,12 @@
+namespace java org.p4.bmv2.thrift
+namespace cpp cpservice
+
+service ControlPlaneService {
+
+   bool ping(),
+
+   oneway void packetIn(1: i32 port, 2: i64 reason, 3: i32 tableId, 4: i32 contextId, 5: binary packet),
+
+   oneway void hello(1: i32 thriftServerPort, 2: i32 deviceId)
+
+}
\ No newline at end of file
diff --git a/protocols/bmv2/src/main/thrift/simple_pre.thrift b/protocols/bmv2/src/main/thrift/simple_pre.thrift
new file mode 100644
index 0000000..b727162
--- /dev/null
+++ b/protocols/bmv2/src/main/thrift/simple_pre.thrift
@@ -0,0 +1,84 @@
+namespace java org.p4.bmv2.thrift
+/* Copyright 2013-present Barefoot Networks, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Antonin Bas (antonin@barefootnetworks.com)
+ *
+ */
+
+namespace cpp bm_runtime.simple_pre
+namespace py bm_runtime.simple_pre
+
+typedef i32 BmMcMgrp
+typedef i32 BmMcRid
+typedef i32 BmMcMgrpHandle
+typedef i32 BmMcL1Handle
+typedef string BmMcPortMap // string of 0s and 1s
+
+enum McOperationErrorCode {
+  TABLE_FULL = 1,
+  INVALID_HANDLE = 2,
+  INVALID_MGID = 3,
+  INVALID_L1_HANDLE = 4,
+  INVALID_L2_HANLDE = 5,
+  ERROR = 6
+}
+
+exception InvalidMcOperation {
+  1:McOperationErrorCode code
+}
+
+service SimplePre {
+
+  BmMcMgrpHandle bm_mc_mgrp_create(
+    1:i32 cxt_id,
+    2:BmMcMgrp mgrp
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_mgrp_destroy(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  BmMcL1Handle bm_mc_node_create(
+    1:i32 cxt_id,
+    2:BmMcRid rid
+    3:BmMcPortMap port_map
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_associate(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle,
+    3:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_dissociate(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle,
+    3:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_destroy(
+    1:i32 cxt_id,
+    2:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_update(
+    1:i32 cxt_id,
+    2:BmMcL1Handle l1_handle,
+    3:BmMcPortMap port_map
+  ) throws (1:InvalidMcOperation ouch),
+}
diff --git a/protocols/bmv2/src/main/thrift/simple_pre_lag.thrift b/protocols/bmv2/src/main/thrift/simple_pre_lag.thrift
new file mode 100644
index 0000000..1d0200d
--- /dev/null
+++ b/protocols/bmv2/src/main/thrift/simple_pre_lag.thrift
@@ -0,0 +1,95 @@
+namespace java org.p4.bmv2.thrift
+/* Copyright 2013-present Barefoot Networks, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Srikrishna Gopu (krishna@barefootnetworks.com)
+ * Antonin Bas (antonin@barefootnetworks.com)
+ *
+ */
+
+namespace cpp bm_runtime.simple_pre_lag
+namespace py bm_runtime.simple_pre_lag
+
+typedef i32 BmMcMgrp
+typedef i32 BmMcRid
+typedef i32 BmMcMgrpHandle
+typedef i32 BmMcL1Handle
+typedef i16 BmMcLagIndex
+typedef string BmMcPortMap // string of 0s and 1s
+typedef string BmMcLagMap // string of 0s and 1s
+
+enum McOperationErrorCode {
+  TABLE_FULL = 1,
+  INVALID_HANDLE = 2,
+  INVALID_MGID = 3,
+  INVALID_L1_HANDLE = 4,
+  INVALID_L2_HANLDE = 5,
+  ERROR = 6
+}
+
+exception InvalidMcOperation {
+  1:McOperationErrorCode code
+}
+
+service SimplePreLAG {
+
+  BmMcMgrpHandle bm_mc_mgrp_create(
+    1:i32 cxt_id,
+    2:BmMcMgrp mgrp
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_mgrp_destroy(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  BmMcL1Handle bm_mc_node_create(
+    1:i32 cxt_id,
+    2:BmMcRid rid,
+    3:BmMcPortMap port_map,
+    4:BmMcLagMap lag_map
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_associate(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle,
+    3:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_dissociate(
+    1:i32 cxt_id,
+    2:BmMcMgrpHandle mgrp_handle,
+    3:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_destroy(
+    1:i32 cxt_id,
+    2:BmMcL1Handle l1_handle
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_node_update(
+    1:i32 cxt_id,
+    2:BmMcL1Handle l1_handle,
+    3:BmMcPortMap port_map,
+    4:BmMcLagMap lag_map
+  ) throws (1:InvalidMcOperation ouch),
+
+  void bm_mc_set_lag_membership(
+    1:i32 cxt_id,
+    2:BmMcLagIndex lag_index,
+    3:BmMcPortMap port_map
+  ) throws (1:InvalidMcOperation ouch),
+}
diff --git a/protocols/bmv2/src/main/thrift/simple_switch.thrift b/protocols/bmv2/src/main/thrift/simple_switch.thrift
new file mode 100644
index 0000000..87b5e49
--- /dev/null
+++ b/protocols/bmv2/src/main/thrift/simple_switch.thrift
@@ -0,0 +1,38 @@
+namespace java org.p4.bmv2.thrift
+/* Copyright 2013-present Barefoot Networks, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Antonin Bas (antonin@barefootnetworks.com)
+ *
+ */
+
+namespace cpp sswitch_runtime
+namespace py sswitch_runtime
+
+service SimpleSwitch {
+
+  i32 mirroring_mapping_add(1:i32 mirror_id, 2:i32 egress_port);
+  i32 mirroring_mapping_delete(1:i32 mirror_id);
+  i32 mirroring_mapping_get_egress_port(1:i32 mirror_id);
+
+  i32 set_egress_queue_depth(1:i32 depth_pkts);
+  i32 set_egress_queue_rate(1:i64 rate_pps);
+
+  oneway void push_packet(1:i32 port, 2:binary packet);
+
+  bool ping();
+
+}
diff --git a/protocols/bmv2/src/main/thrift/standard.thrift b/protocols/bmv2/src/main/thrift/standard.thrift
new file mode 100644
index 0000000..7268139
--- /dev/null
+++ b/protocols/bmv2/src/main/thrift/standard.thrift
@@ -0,0 +1,479 @@
+namespace java org.p4.bmv2.thrift
+/* Copyright 2013-present Barefoot Networks, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Antonin Bas (antonin@barefootnetworks.com)
+ *
+ */
+
+namespace cpp bm_runtime.standard
+namespace py bm_runtime.standard
+
+typedef i64 BmEntryHandle
+typedef list<binary> BmActionData
+
+typedef i32 BmMemberHandle
+typedef i32 BmGroupHandle
+
+typedef i32 BmLearningListId
+typedef i64 BmLearningBufferId
+typedef i32 BmLearningSampleId
+
+enum BmMatchParamType {
+  EXACT = 0,
+  LPM = 1,
+  TERNARY = 2,
+  VALID = 3
+}
+
+struct BmMatchParamExact {
+  1:binary key
+}
+
+struct BmMatchParamLPM {
+  1:binary key,
+  2:i32 prefix_length
+}
+
+struct BmMatchParamTernary {
+  1:binary key,
+  2:binary mask
+}
+
+struct BmMatchParamValid {
+  1:bool key
+}
+
+# Thrift union sucks in C++, the following is much better
+struct BmMatchParam {
+  1:BmMatchParamType type,
+  2:optional BmMatchParamExact exact,
+  3:optional BmMatchParamLPM lpm,
+  4:optional BmMatchParamTernary ternary,
+  5:optional BmMatchParamValid valid
+}
+
+typedef list<BmMatchParam> BmMatchParams
+
+struct BmAddEntryOptions {
+  1:optional i32 priority
+}
+
+struct BmCounterValue {
+  1:i64 bytes;
+  2:i64 packets;
+}
+
+struct BmMeterRateConfig {
+  1:double units_per_micros;
+  2:i32 burst_size;
+}
+
+enum TableOperationErrorCode {
+  TABLE_FULL = 1,
+  INVALID_HANDLE = 2,
+  EXPIRED_HANDLE = 3,
+  COUNTERS_DISABLED = 4,
+  METERS_DISABLED = 5,
+  AGEING_DISABLED = 6,
+  INVALID_TABLE_NAME = 7,
+  INVALID_ACTION_NAME = 8,
+  WRONG_TABLE_TYPE = 9,
+  INVALID_MBR_HANDLE = 10,
+  MBR_STILL_USED = 11,
+  MBR_ALREADY_IN_GRP = 12,
+  MBR_NOT_IN_GRP = 13,
+  INVALID_GRP_HANDLE = 14,
+  GRP_STILL_USED = 15,
+  EMPTY_GRP = 16,
+  DUPLICATE_ENTRY = 17,
+  BAD_MATCH_KEY = 18,
+  INVALID_METER_OPERATION = 19,
+  ERROR = 20,
+}
+
+exception InvalidTableOperation {
+  1:TableOperationErrorCode code
+}
+
+enum CounterOperationErrorCode {
+  INVALID_COUNTER_NAME = 1,
+  INVALID_INDEX = 2,
+  ERROR = 3,
+}
+
+exception InvalidCounterOperation {
+  1:CounterOperationErrorCode code
+}
+
+enum SwapOperationErrorCode {
+  CONFIG_SWAP_DISABLED = 1,
+  ONGOING_SWAP = 2,
+  NO_ONGOING_SWAP = 3
+}
+
+exception InvalidSwapOperation {
+  1:SwapOperationErrorCode code
+}
+
+enum MeterOperationErrorCode {
+  INVALID_INDEX = 1,
+  BAD_RATES_LIST = 2,
+  INVALID_INFO_RATE_VALUE = 3,
+  INVALID_BURST_SIZE_VALUE = 4,
+  ERROR = 5
+}
+
+exception InvalidMeterOperation {
+ 1:MeterOperationErrorCode code
+}
+
+typedef i64 BmRegisterValue
+
+enum RegisterOperationErrorCode {
+  INVALID_INDEX = 1,
+  ERROR = 2
+}
+
+exception InvalidRegisterOperation {
+ 1:RegisterOperationErrorCode code
+}
+
+enum LearnOperationErrorCode {
+  INVALID_LIST_ID = 1,
+  ERROR = 2
+}
+
+exception InvalidLearnOperation {
+ 1:LearnOperationErrorCode code
+}
+
+// TODO
+enum DevMgrErrorCode {
+  ERROR = 1
+}
+
+exception InvalidDevMgrOperation {
+ 1:DevMgrErrorCode code
+}
+
+struct DevMgrPortInfo {
+  1:i32 port_num;
+  2:string iface_name;
+  3:bool is_up;
+  4:map<string, string> extra;
+}
+
+service Standard {
+	
+  // table operations
+
+  BmEntryHandle bm_mt_add_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMatchParams match_key,
+    4:string action_name,
+    5:BmActionData action_data,
+    6:BmAddEntryOptions options
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_set_default_action(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:string action_name,
+    4:BmActionData action_data
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_delete_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_modify_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle,
+    4:string action_name,
+    5:BmActionData action_data
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_set_entry_ttl(
+    1:i32 cxt_id,
+    2:string table_name
+    3:BmEntryHandle entry_handle,
+    4:i32 timeout_ms
+  ) throws (1:InvalidTableOperation ouch),
+
+  // indirect tables
+
+  BmMemberHandle bm_mt_indirect_add_member(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:string action_name,
+    4:BmActionData action_data
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_delete_member(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMemberHandle mbr_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_modify_member(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMemberHandle mbr_handle,
+    4:string action_name,
+    5:BmActionData action_data
+  ) throws (1:InvalidTableOperation ouch),
+
+  BmEntryHandle bm_mt_indirect_add_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMatchParams match_key,
+    4:BmMemberHandle mbr_handle,
+    5:BmAddEntryOptions options
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_modify_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle,
+    4:BmMemberHandle mbr_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_delete_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_set_entry_ttl(
+    1:i32 cxt_id,
+    2:string table_name
+    3:BmEntryHandle entry_handle,
+    4:i32 timeout_ms
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_set_default_member(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMemberHandle mbr_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  // indirect tables with selector
+
+  BmGroupHandle bm_mt_indirect_ws_create_group(
+    1:i32 cxt_id,
+    2:string table_name
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_ws_delete_group(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmGroupHandle grp_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_ws_add_member_to_group(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMemberHandle mbr_handle,
+    4:BmGroupHandle grp_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_ws_remove_member_from_group(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMemberHandle mbr_handle,
+    4:BmGroupHandle grp_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  BmEntryHandle bm_mt_indirect_ws_add_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmMatchParams match_key,
+    4:BmGroupHandle grp_handle
+    5:BmAddEntryOptions options
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_ws_modify_entry(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle,
+    4:BmGroupHandle grp_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_indirect_ws_set_default_group(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmGroupHandle grp_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  BmCounterValue bm_mt_read_counter(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_reset_counters(
+    1:i32 cxt_id,
+    2:string table_name
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_write_counter(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle,
+    4:BmCounterValue value
+  ) throws (1:InvalidTableOperation ouch),
+
+  void bm_mt_set_meter_rates(
+    1:i32 cxt_id,
+    2:string table_name,
+    3:BmEntryHandle entry_handle,
+    4:list<BmMeterRateConfig> rates
+  ) throws (1:InvalidTableOperation ouch),
+
+  // indirect counters
+
+  BmCounterValue bm_counter_read(
+    1:i32 cxt_id,
+    2:string counter_name,
+    3:i32 index
+  ) throws (1:InvalidCounterOperation ouch),
+
+  void bm_counter_reset_all(
+    1:i32 cxt_id,
+    2:string counter_name
+  ) throws (1:InvalidCounterOperation ouch),
+
+  void bm_counter_write(
+    1:i32 cxt_id,
+    2:string counter_name,
+    3:i32 index,
+    4:BmCounterValue value
+  ) throws (1:InvalidCounterOperation ouch),
+
+  // learning acks
+
+  void bm_learning_ack(
+    1:i32 cxt_id,
+    2:BmLearningListId list_id,
+    3:BmLearningBufferId buffer_id,
+    4:list<BmLearningSampleId> sample_ids
+  ) throws (1:InvalidLearnOperation ouch),
+
+  void bm_learning_ack_buffer(
+    1:i32 cxt_id,
+    2:BmLearningListId list_id,
+    3:BmLearningBufferId buffer_id
+  ) throws (1:InvalidLearnOperation ouch),
+
+  void bm_learning_set_timeout(
+    1:i32 cxt_id,
+    2:BmLearningListId list_id,
+    3:i32 timeout_ms
+  ) throws (1:InvalidLearnOperation ouch),
+
+  void bm_learning_set_buffer_size(
+    1:i32 cxt_id,
+    2:BmLearningListId list_id,
+    3:i32 nb_samples
+  ) throws (1:InvalidLearnOperation ouch),
+
+  // swap configs
+
+  void bm_load_new_config(
+    1:string config_str
+  ) throws (1:InvalidSwapOperation ouch),
+
+  void bm_swap_configs() throws (1:InvalidSwapOperation ouch),
+  
+  // meters
+
+  void bm_meter_array_set_rates(
+    1:i32 cxt_id,
+    2:string meter_array_name,
+    3:list<BmMeterRateConfig> rates
+  ) throws (1:InvalidMeterOperation ouch)
+
+  void bm_meter_set_rates(
+    1:i32 cxt_id,
+    2:string meter_array_name,
+    3:i32 index,
+    4:list<BmMeterRateConfig> rates
+  ) throws (1:InvalidMeterOperation ouch)
+
+
+  // registers
+
+  BmRegisterValue bm_register_read(
+    1:i32 cxt_id,
+    2:string register_array_name,
+    3:i32 idx
+  ) throws (1:InvalidRegisterOperation ouch)
+
+  void bm_register_write(
+    1:i32 cxt_id,
+    2:string register_array_name,
+    3:i32 index,
+    4:BmRegisterValue value
+  ) throws (1:InvalidRegisterOperation ouch)
+
+  void bm_register_write_range(
+    1:i32 cxt_id,
+    2:string register_array_name,
+    3:i32 start_index,
+    4:i32 end_index,
+    5:BmRegisterValue value
+  ) throws (1:InvalidRegisterOperation ouch)
+
+  void bm_register_reset(
+    1:i32 cxt_id,
+    2:string register_array_name
+  ) throws (1:InvalidRegisterOperation ouch)
+
+
+  // device manager
+
+  void bm_dev_mgr_add_port(
+    1:string iface_name,
+    2:i32 port_num,
+    3:string pcap_path // optional
+  ) throws (1:InvalidDevMgrOperation ouch)
+
+  void bm_dev_mgr_remove_port(
+    1:i32 port_num
+  ) throws (1:InvalidDevMgrOperation ouch)
+
+  list<DevMgrPortInfo> bm_dev_mgr_show_ports(
+  ) throws (1:InvalidDevMgrOperation ouch)
+
+  // debug functions
+
+  string bm_dump_table(
+    1:i32 cxt_id,
+    2:string table_name
+  )
+
+  void bm_reset_state()
+
+  string bm_get_config()
+  string bm_get_config_md5()
+
+  string bm_serialize_state()
+}
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"
   }
 }