Merge "Remove unnecessary type parameter and cast"
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
index e4eafb5..f1a14e7 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/Router.java
@@ -143,7 +143,7 @@
     }
 
     /**
-     * Starts the Router.
+     * Starts the router.
      */
     public void start() {
         bgpUpdatesExecutor.execute(new Runnable() {
@@ -161,6 +161,14 @@
         });
     }
 
+    /**
+     * Shuts the router down.
+     */
+    public void shutdown() {
+        bgpUpdatesExecutor.shutdownNow();
+        bgpIntentsSynchronizerExecutor.shutdownNow();
+    }
+
     //@Override TODO hook this up to something
     public void leaderChanged(boolean isLeader) {
         log.debug("Leader changed: {}", isLeader);
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
index 0f6e38a..4abefa7 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/SdnIp.java
@@ -90,6 +90,9 @@
 
     @Deactivate
     protected void deactivate() {
+        bgpSessionManager.shutDown();
+        router.shutdown();
+
         log.info("Stopped");
     }
 
diff --git a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
index cde65c6..2fcd1fe 100644
--- a/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
+++ b/apps/sdnip/src/main/java/org/onlab/onos/sdnip/config/SdnIpConfigReader.java
@@ -16,6 +16,7 @@
 package org.onlab.onos.sdnip.config;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Map;
@@ -40,12 +41,8 @@
 
     private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json";
     private String configFileName = DEFAULT_CONFIG_FILE;
-    //private Map<String, Interface> interfaces;
-    // We call the BGP routers in our SDN network the BGP speakers, and call
-    // the BGP routers outside our SDN network the BGP peers.
-    private Map<String, BgpSpeaker> bgpSpeakers;
-    private Map<IpAddress, BgpPeer> bgpPeers;
-    //private InvertedRadixTree<Interface> interfaceRoutes;
+    private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>();
+    private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>();
 
     /**
      * Reads the info contained in the configuration file.
@@ -58,78 +55,25 @@
 
         try {
             Configuration config = mapper.readValue(gatewaysFile, Configuration.class);
-            /*interfaces = new ConcurrentHashMap<>();
-            for (Interface intf : config.getInterfaces()) {
-                interfaces.put(intf.getName(), intf);
-            }*/
-            bgpSpeakers = new ConcurrentHashMap<>();
             for (BgpSpeaker speaker : config.getBgpSpeakers()) {
                 bgpSpeakers.put(speaker.name(), speaker);
             }
-            bgpPeers = new ConcurrentHashMap<>();
             for (BgpPeer peer : config.getPeers()) {
                 bgpPeers.put(peer.ipAddress(), peer);
             }
+        } catch (FileNotFoundException e) {
+            log.warn("Configuration file not found: {}", configFileName);
         } catch (IOException e) {
             log.error("Error reading JSON file", e);
-            //throw new ConfigurationRuntimeException("Error in JSON file", e);
         }
-
-        // Populate the interface InvertedRadixTree
-        /*for (Interface intf : interfaces.values()) {
-            Ip4Prefix prefix = intf.getIp4Prefix();
-            String binaryString = RouteEntry.createBinaryString(prefix);
-            interfaceRoutes.put(binaryString, intf);
-        }*/
     }
 
-    /*
-     * To find the Interface which has longest matchable IP prefix (sub-network
-     *  prefix) to next hop IP address.
-     *
-     * @param address the IP address of next hop router
-     * @return the Interface which has longest matchable IP prefix
-     */
-    /*private Interface longestInterfacePrefixMatch(IpAddress address) {
-        Ip4Prefix prefixToSearchFor =
-            new Ip4Prefix(address, (short) Ip4Address.BIT_LENGTH);
-        String binaryString = RouteEntry.createBinaryString(prefixToSearchFor);
-
-        Iterator<Interface> it =
-            interfaceRoutes.getValuesForKeysPrefixing(binaryString).iterator();
-        Interface intf = null;
-        // Find the last prefix, which will be the longest prefix
-        while (it.hasNext()) {
-            intf = it.next();
-        }
-
-        return intf;
-    }*/
-
-    /*@Override
-    public Interface getOutgoingInterface(IpAddress dstIpAddress) {
-        return longestInterfacePrefixMatch(dstIpAddress);
-    }*/
-
     public void init() {
-        //interfaceRoutes = new ConcurrentInvertedRadixTree<>(
-                //new DefaultByteArrayNodeFactory());
-
-        // Reading config values
-        /*String configFilenameParameter = context.getConfigParams(this).get("configfile");
-        if (configFilenameParameter != null) {
-            currentConfigFilename = configFilenameParameter;
-        }*/
         log.debug("Config file set to {}", configFileName);
 
         readConfiguration(configFileName);
     }
 
-    /*@Override
-    public Map<String, Interface> getInterfaces() {
-        return Collections.unmodifiableMap(interfaces);
-    }*/
-
     @Override
     public Map<String, BgpSpeaker> getBgpSpeakers() {
         return Collections.unmodifiableMap(bgpSpeakers);
diff --git a/web/gui/src/main/webapp/index2.html b/web/gui/src/main/webapp/index2.html
index 8c7cec5..95c4e4d 100644
--- a/web/gui/src/main/webapp/index2.html
+++ b/web/gui/src/main/webapp/index2.html
@@ -81,6 +81,7 @@
     <!-- TODO: replace with template marker and inject refs server-side -->
     <script src="sample2.js"></script>
     <script src="sampleAlt2.js"></script>
+    <script src="sampleRadio.js"></script>
 
     <!-- finally, build the UI-->
     <script type="text/javascript">
diff --git a/web/gui/src/main/webapp/mast2.css b/web/gui/src/main/webapp/mast2.css
index 4502776..7f094b3 100644
--- a/web/gui/src/main/webapp/mast2.css
+++ b/web/gui/src/main/webapp/mast2.css
@@ -48,12 +48,8 @@
 }
 
 #mast span.radio {
-    color: darkslateblue;
     font-size: 10pt;
-}
-
-#mast span.radio {
-    margin: 4px 0;
+    margin: 4px 2px;
     border: 1px dotted #222;
     padding: 1px 6px;
     color: #eee;
diff --git a/web/gui/src/main/webapp/module-div-template.js b/web/gui/src/main/webapp/module-div-template.js
new file mode 100644
index 0000000..09ea569
--- /dev/null
+++ b/web/gui/src/main/webapp/module-div-template.js
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/*
+ Module template file for DIV based view.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+    'use strict';
+
+    var list,
+        data = [ 'foo', 'bar', 'baz' ];
+
+    // invoked only the first time the view is loaded
+    function preload(view, ctx) {
+        // NOTE: view.$div is a D3 selection of the view's div
+        list = view.$div.append('ul');
+    }
+
+    // invoked just prior to loading the view
+    function reset(view) {
+
+    }
+
+    // invoked when the view is loaded
+    function load(view, ctx) {
+        list.selectAll('li')
+            .data(data)
+            .enter()
+            .append('li')
+            .text(function (d) { return d; })
+    }
+
+    // invoked when the view is resized
+    function resize(view, ctx) {
+
+    }
+
+    // == register the view here, with links to lifecycle callbacks
+
+    onos.ui.addView('myViewId', {
+        preload: preload,
+        reset: reset,
+        load: load,
+        // unload: unload,
+        // error: error
+        resize: resize
+    });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/module-svg-template.js b/web/gui/src/main/webapp/module-svg-template.js
new file mode 100644
index 0000000..66da760
--- /dev/null
+++ b/web/gui/src/main/webapp/module-svg-template.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/*
+ Module template file for SVG based view.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+    'use strict';
+
+    var svg;
+
+    // set the size of the SVG layer to match that of the view
+    function sizeSvg(view) {
+        svg.attr({
+            width: view.width(),
+            height: view.height()
+        });
+    }
+
+    // invoked only the first time the view is loaded
+    function preload(view, ctx) {
+        // NOTE: view.$div is a D3 selection of the view's div
+        svg = view.$div.append('svg');
+        sizeSvg(view);
+        // ... further code to initialize the SVG view ...
+
+    }
+
+    // invoked just prior to loading the view
+    function reset(view) {
+
+    }
+
+    // invoked when the view is loaded
+    function load(view, ctx) {
+
+    }
+
+    // invoked when the view is resized
+    function resize(view, ctx) {
+        sizeSvg(view);
+        // ... further code to handle resize of view ...
+
+    }
+
+    // == register the view here, with links to lifecycle callbacks
+
+    onos.ui.addView('myViewId', {
+        preload: preload,
+        reset: reset,
+        load: load,
+        // unload: unload,
+        // error: error
+        resize: resize
+    });
+
+}(ONOS));
diff --git a/web/gui/src/main/webapp/module-template.js b/web/gui/src/main/webapp/module-template.js
deleted file mode 100644
index 3de7d79..0000000
--- a/web/gui/src/main/webapp/module-template.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2014 Open Networking Laboratory
- *
- * 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.
- */
-
-/*
- Module template file.
-
- @author Simon Hunt
- */
-
-(function (onos) {
-    'use strict';
-
-    var api = onos.api;
-
-    // == define your functions here.....
-
-
-    // == register views here, with links to lifecycle callbacks
-
-//    api.addView('view-id', {/* callbacks */});
-
-
-}(ONOS));
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
index c35b56d..85aa617 100644
--- a/web/gui/src/main/webapp/onos2.js
+++ b/web/gui/src/main/webapp/onos2.js
@@ -51,7 +51,8 @@
             errorCount = 0;
 
         // DOM elements etc.
-        var $view;
+        var $view,
+            $mastRadio;
 
 
         // ..........................................................
@@ -204,6 +205,9 @@
             // the incoming view, then unload it...
             if (current.view && (current.view.vid !== view.vid)) {
                 current.view.unload();
+                // detach radio buttons, if they were there..
+                $('#mastRadio').children().detach();
+
             }
 
             // cache new view and context
@@ -223,6 +227,61 @@
             view.load(current.ctx);
         }
 
+        // generate 'unique' id by prefixing view id
+        function uid(view, id) {
+            return view.vid + '-' + id;
+        }
+
+        // restore id by removing view id prefix
+        function unUid(view, uid) {
+            var re = new RegExp('^' + view.vid + '-');
+            return uid.replace(re, '');
+        }
+
+        function setRadioButtons(vid, btnSet, callback) {
+            var view = views[vid],
+                btnG;
+
+            // lazily create the buttons...
+            if (!(btnG = view.radioButtons)) {
+                btnG = d3.select(document.createElement('div'));
+
+                btnSet.forEach(function (btn, i) {
+                    var bid = btn.id || 'b' + i,
+                        txt = btn.text || 'Button #' + i,
+                        b = btnG.append('span')
+                        .attr({
+                            id: uid(view, bid),
+                            class: 'radio'
+                        })
+                        .text(txt);
+                    if (i === 0) {
+                        b.classed('active', true);
+                    }
+                });
+
+                btnG.selectAll('span')
+                    .on('click', function (d) {
+                        var btn = d3.select(this),
+                            bid = btn.attr('id'),
+                            act = btn.classed('active');
+
+                        if (!act) {
+                            $mastRadio.selectAll('span')
+                                .classed('active', false);
+                            btn.classed('active', true);
+
+                            callback(view.token(), unUid(view, bid));
+                        }
+                    });
+
+                view.radioButtons = btnG;
+            }
+
+            // attach the buttons to the masthead
+            $mastRadio.node().appendChild(btnG.node());
+        }
+
         function resize(e) {
             d3.selectAll('.onosView').call(setViewDimensions);
             // allow current view to react to resize event...
@@ -256,8 +315,9 @@
             if (validateViewArgs(vid)) {
                 this.nid = nid;     // explicit navitem id (can be null)
                 this.cb = $.isPlainObject(cb) ? cb : {};    // callbacks
-                this.$div = null;   // view not yet added to DOM
-                this.ok = true;     // valid view
+                this.$div = null;               // view not yet added to DOM
+                this.radioButtons = null;       // no radio buttons yet
+                this.ok = true;                 // valid view
             }
 
         }
@@ -289,7 +349,8 @@
 
                     // functions
                     width: this.width,
-                    height: this.height
+                    height: this.height,
+                    setRadio: this.setRadio
                 }
             },
 
@@ -368,8 +429,11 @@
 
             height: function () {
                 return $(this.$div.node()).height();
-            }
+            },
 
+            setRadio: function (btnSet, cb) {
+                setRadioButtons(this.vid, btnSet, cb);
+            }
 
             // TODO: consider schedule, clearTimer, etc.
         };
@@ -465,6 +529,7 @@
             built = true;
 
             $view = d3.select('#view');
+            $mastRadio = d3.select('#mastRadio');
 
             $(window).on('hashchange', hash);
             $(window).on('resize', resize);
@@ -487,4 +552,4 @@
         };
     };
 
-}(jQuery));
\ No newline at end of file
+}(jQuery));
diff --git a/web/gui/src/main/webapp/sampleRadio.js b/web/gui/src/main/webapp/sampleRadio.js
new file mode 100644
index 0000000..4d362a9
--- /dev/null
+++ b/web/gui/src/main/webapp/sampleRadio.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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.
+ */
+
+/*
+ Sample view to illustrate radio buttons.
+
+ @author Simon Hunt
+ */
+
+(function (onos) {
+    'use strict';
+
+    var data = [ 'Yo, radio button set...', 'Time to shine' ],
+        btnSet = [
+            { id: 'b1', text: 'First Button' },
+            { id: 'b2', text: 'Second Button' },
+            { id: 'b3', text: 'Third Button' }
+        ],
+        btnLookup = {};
+
+    btnSet.forEach(function (b) {
+        btnLookup[b.id] = b;
+    });
+
+    // invoked when the view is loaded
+    function load(view, ctx) {
+        view.setRadio(btnSet, doRadio);
+
+        view.$div.selectAll('p')
+            .data(data)
+            .enter()
+            .append('p')
+            .text(function (d) { return d; })
+            .style('padding', '2px 8px');
+    }
+
+    function doRadio(view, id) {
+        view.$div.append('p')
+            .text('You pressed the ' + btnLookup[id].text)
+            .style({
+                'font-size': '10pt',
+                color: 'green',
+                padding: '0 20px',
+                margin: '2px'
+            });
+    }
+
+    // == register the view here, with links to lifecycle callbacks
+
+    onos.ui.addView('sampleRadio', {
+        reset: true,    // empty the div on reset
+        load: load
+    });
+
+}(ONOS));