GUI -- Refactoring of server-side message handlers (Part One).

Change-Id: I895cef0545f7ba4b78a2adfa2bad9d889ca0104a
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java
deleted file mode 100644
index 66db400..0000000
--- a/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTableRow.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-package org.onosproject.ui.impl;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import java.util.HashMap;
-import java.util.Map;
-
-
-/**
- * Provides a partial implementation of {@link TableRow}.
- */
-public abstract class AbstractTableRow implements TableRow {
-
-    private static final ObjectMapper MAPPER = new ObjectMapper();
-
-    private final Map<String, String> data = new HashMap<>();
-
-    @Override
-    public String get(String key) {
-        return data.get(key);
-    }
-
-    @Override
-    public ObjectNode toJsonNode() {
-        ObjectNode result = MAPPER.createObjectNode();
-        for (String id : columnIds()) {
-            result.put(id, data.get(id));
-        }
-        return result;
-    }
-
-    /**
-     * Subclasses must provide the list of column IDs.
-     *
-     * @return array of column IDs
-     */
-    protected abstract String[] columnIds();
-
-    /**
-     * Add a column ID to value binding.
-     *
-     * @param id the column ID
-     * @param value the cell value
-     */
-    protected void add(String id, String value) {
-        data.put(id, value);
-    }
-}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTabularViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTabularViewMessageHandler.java
deleted file mode 100644
index 494ad42..0000000
--- a/web/gui/src/main/java/org/onosproject/ui/impl/AbstractTabularViewMessageHandler.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-package org.onosproject.ui.impl;
-
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import org.onosproject.ui.UiMessageHandler;
-
-import java.util.Set;
-
-/**
- * Base message handler for tabular views.
- */
-public abstract class AbstractTabularViewMessageHandler extends UiMessageHandler {
-
-    /**
-     * Creates a new tabular view message handler.
-     *
-     * @param messageTypes set of message types
-     */
-    protected AbstractTabularViewMessageHandler(Set<String> messageTypes) {
-        super(messageTypes);
-    }
-
-    /**
-     * Produces JSON from the specified array of rows.
-     *
-     * @param rows table rows
-     * @return JSON array
-     */
-    protected ArrayNode generateArrayNode(TableRow[] rows) {
-        ArrayNode array = mapper.createArrayNode();
-        for (TableRow r : rows) {
-            array.add(r.toJsonNode());
-        }
-        return array;
-    }
-
-    // TODO: possibly convert this into just a toolbox class
-    // TODO: extract and generalize other table constructs
-}
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 d19c987..08408dc 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
@@ -15,7 +15,6 @@
  */
 package org.onosproject.ui.impl;
 
-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;
@@ -23,6 +22,11 @@
 import org.onosproject.app.ApplicationState;
 import org.onosproject.core.Application;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.Arrays;
 import java.util.List;
@@ -33,7 +37,7 @@
 /**
  * Message handler for application view related messages.
  */
-public class ApplicationViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class ApplicationViewMessageHandler extends UiMessageHandler {
 
     /**
      * Creates a new message handler for the application messages.
@@ -44,7 +48,7 @@
 
     @Override
     public void process(ObjectNode message) {
-        String type = string(message, "event", "unknown");
+        String type = eventType(message);
         if (type.equals("appDataRequest")) {
             sendAppList(message);
         } else if (type.equals("appManagementRequest")) {
@@ -54,17 +58,13 @@
 
     private void sendAppList(ObjectNode message) {
         ObjectNode payload = payload(message);
-        String sortCol = string(payload, "sortCol", "id");
-        String sortDir = string(payload, "sortDir", "asc");
+        RowComparator rc = TableUtils.createRowComparator(payload);
 
         ApplicationService service = get(ApplicationService.class);
         TableRow[] rows = generateTableRows(service);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode applications = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("apps", applications);
+        rootNode.set("apps", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("appDataResponse", 0, rootNode);
     }
@@ -95,7 +95,8 @@
     }
 
     /**
-     * TableRow implementation for {@link org.onosproject.core.Application applications}.
+     * TableRow implementation for
+     * {@link org.onosproject.core.Application applications}.
      */
     private static class ApplicationTableRow extends AbstractTableRow {
 
@@ -118,10 +119,10 @@
             ApplicationState state = service.getState(app.id());
             String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE;
 
-            add(STATE, state.toString());
+            add(STATE, state);
             add(STATE_IID, iconId);
             add(ID, app.id().name());
-            add(VERSION, app.version().toString());
+            add(VERSION, app.version());
             add(ORIGIN, app.origin());
             add(DESC, app.description());
         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
index ac3fb92..55592e3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ClusterViewMessageHandler.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.ui.impl;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.joda.time.DateTime;
@@ -24,6 +23,11 @@
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -33,7 +37,7 @@
 /**
  * Message handler for cluster view related messages.
  */
-public class ClusterViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class ClusterViewMessageHandler extends UiMessageHandler {
 
     /**
      * Creates a new message handler for the cluster messages.
@@ -44,18 +48,21 @@
 
     @Override
     public void process(ObjectNode message) {
+        String type = eventType(message);
+        if (type.equals("clusterDataRequest")) {
+            sendClusterList(message);
+        }
+    }
+
+    private void sendClusterList(ObjectNode message) {
         ObjectNode payload = payload(message);
-        String sortCol = string(payload, "sortCol", "id");
-        String sortDir = string(payload, "sortDir", "asc");
+        RowComparator rc = TableUtils.createRowComparator(payload);
 
         ClusterService service = get(ClusterService.class);
         TableRow[] rows = generateTableRows(service);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode clusterNodes = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("clusters", clusterNodes);
+        rootNode.set("clusters", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("clusterDataResponse", 0, rootNode);
     }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
index 9820f6d..5015cb5 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java
@@ -27,6 +27,11 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,7 +42,7 @@
 /**
  * Message handler for device view related messages.
  */
-public class DeviceViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class DeviceViewMessageHandler extends UiMessageHandler {
 
     private static final String ID = "id";
     private static final String TYPE = "type";
@@ -68,36 +73,31 @@
     }
 
     @Override
-    public void process(ObjectNode event) {
-        String type = string(event, "event", "unknown");
+    public void process(ObjectNode message) {
+        String type = eventType(message);
         if (type.equals("deviceDataRequest")) {
-            dataRequest(event);
+            dataRequest(message);
         } else if (type.equals("deviceDetailsRequest")) {
-            detailsRequest(event);
+            detailsRequest(message);
         }
     }
 
-    private void dataRequest(ObjectNode event) {
-        ObjectNode payload = payload(event);
-        String sortCol = string(payload, "sortCol", "id");
-        String sortDir = string(payload, "sortDir", "asc");
+    private void dataRequest(ObjectNode message) {
+        ObjectNode payload = payload(message);
+        RowComparator rc = TableUtils.createRowComparator(payload);
 
         DeviceService service = get(DeviceService.class);
         MastershipService mastershipService = get(MastershipService.class);
-
         TableRow[] rows = generateTableRows(service, mastershipService);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode devices = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("devices", devices);
+        rootNode.set("devices", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("deviceDataResponse", 0, rootNode);
     }
 
-    private void detailsRequest(ObjectNode event) {
-        ObjectNode payload = payload(event);
+    private void detailsRequest(ObjectNode message) {
+        ObjectNode payload = payload(message);
         String id = string(payload, "id", "of:0000000000000000");
 
         DeviceId deviceId = DeviceId.deviceId(id);
@@ -139,9 +139,7 @@
                                          MastershipService mastershipService) {
         List<TableRow> list = new ArrayList<>();
         for (Device dev : service.getDevices()) {
-            list.add(new DeviceTableRow(service,
-                                        mastershipService,
-                                        dev));
+            list.add(new DeviceTableRow(service, mastershipService, dev));
         }
         return list.toArray(new TableRow[list.size()]);
     }
@@ -159,13 +157,13 @@
 
         Set<Link> links = ls.getEgressLinks(new ConnectPoint(id, p.number()));
         if (!links.isEmpty()) {
-            String egressLinks = "";
+            StringBuilder egressLinks = new StringBuilder();
             for (Link l : links) {
                 ConnectPoint dest = l.dst();
-                egressLinks += dest.elementId().toString()
-                        + "/" + dest.port().toString() + " ";
+                egressLinks.append(dest.elementId()).append("/")
+                        .append(dest.port()).append(" ");
             }
-            port.put(LINK_DEST, egressLinks);
+            port.put(LINK_DEST, egressLinks.toString());
         }
 
         return port;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
index b14ba98..81965da 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/FlowViewMessageHandler.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.ui.impl;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.apache.commons.lang.WordUtils;
@@ -27,6 +26,11 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,7 +41,7 @@
 /**
  * Message handler for flow view related messages.
  */
-public class FlowViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class FlowViewMessageHandler extends UiMessageHandler {
 
     private static final String NO_DEV = "none";
 
@@ -50,10 +54,16 @@
 
     @Override
     public void process(ObjectNode message) {
+        String type = eventType(message);
+        if (type.equals("flowDataRequest")) {
+            sendFlowList(message);
+        }
+    }
+
+    private void sendFlowList(ObjectNode message) {
         ObjectNode payload = payload(message);
+        RowComparator rc = TableUtils.createRowComparator(payload);
         String uri = string(payload, "devId", NO_DEV);
-        String sortCol = string(payload, "sortCol", "id");
-        String sortDir = string(payload, "sortDir", "asc");
 
         ObjectNode rootNode;
         if (uri.equals(NO_DEV)) {
@@ -61,16 +71,11 @@
             rootNode.set("flows", mapper.createArrayNode());
         } else {
             DeviceId deviceId = DeviceId.deviceId(uri);
-
             FlowRuleService service = get(FlowRuleService.class);
             TableRow[] rows = generateTableRows(service, deviceId);
-            RowComparator rc =
-                    new RowComparator(sortCol, RowComparator.direction(sortDir));
             Arrays.sort(rows, rc);
-            ArrayNode flows = generateArrayNode(rows);
-
             rootNode = mapper.createObjectNode();
-            rootNode.set("flows", flows);
+            rootNode.set("flows", TableUtils.generateArrayNode(rows));
         }
 
         connection().sendMessage("flowDataResponse", 0, rootNode);
@@ -191,7 +196,6 @@
             return sb;
         }
 
-
         @Override
         protected String[] columnIds() {
             return COL_IDS;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
index 689a266..472ae16 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
@@ -15,13 +15,17 @@
  */
 package org.onosproject.ui.impl;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.host.HostService;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -32,7 +36,7 @@
 /**
  * Message handler for host view related messages.
  */
-public class HostViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class HostViewMessageHandler extends UiMessageHandler {
 
     /**
      * Creates a new message handler for the host messages.
@@ -43,18 +47,21 @@
 
     @Override
     public void process(ObjectNode message) {
+        String type = eventType(message);
+        if (type.equals("hostDataRequest")) {
+            sendHostList(message);
+        }
+    }
+
+    private void sendHostList(ObjectNode message) {
         ObjectNode payload = payload(message);
-        String sortCol = string(payload, "sortCol", "id");
-        String sortDir = string(payload, "sortDir", "asc");
+        RowComparator rc = TableUtils.createRowComparator(payload);
 
         HostService service = get(HostService.class);
         TableRow[] rows = generateTableRows(service);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode hosts = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("hosts", hosts);
+        rootNode.set("hosts", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("hostDataResponse", 0, rootNode);
     }
@@ -89,12 +96,11 @@
             HostLocation location = h.location();
 
             add(TYPE_IID, getTypeIconId(h));
-            add(ID, h.id().toString());
-            add(MAC, h.mac().toString());
-            add(VLAN, h.vlan().toString());
-            add(IPS, h.ipAddresses().toString());
-            add(LOCATION, (location.deviceId().toString() + '/' +
-                           location.port().toString()));
+            add(ID, h.id());
+            add(MAC, h.mac());
+            add(VLAN, h.vlan());
+            add(IPS, h.ipAddresses());
+            add(LOCATION, concat(location.deviceId(), "/", location.port()));
         }
 
         private String getTypeIconId(Host host) {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/IntentViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/IntentViewMessageHandler.java
index 8277465..4924d4b 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/IntentViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/IntentViewMessageHandler.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.ui.impl;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.onosproject.core.ApplicationId;
@@ -32,6 +31,11 @@
 import org.onosproject.net.intent.PathIntent;
 import org.onosproject.net.intent.PointToPointIntent;
 import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -41,7 +45,7 @@
 /**
  * Message handler for intent view related messages.
  */
-public class IntentViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class IntentViewMessageHandler extends UiMessageHandler {
 
     /**
      * Creates a new message handler for the intent messages.
@@ -52,18 +56,21 @@
 
     @Override
     public void process(ObjectNode message) {
+        String type = eventType(message);
+        if (type.equals("intentDataRequest")) {
+            sendIntentList(message);
+        }
+    }
+
+    private void sendIntentList(ObjectNode message) {
         ObjectNode payload = payload(message);
-        String sortCol = string(payload, "sortCol", "appId");
-        String sortDir = string(payload, "sortDir", "asc");
+        RowComparator rc = TableUtils.createRowComparator(payload);
 
         IntentService service = get(IntentService.class);
         TableRow[] rows = generateTableRows(service);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode intents = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("intents", intents);
+        rootNode.set("intents", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("intentDataResponse", 0, rootNode);
     }
@@ -222,10 +229,10 @@
         public IntentTableRow(Intent intent) {
             ApplicationId appid = intent.appId();
 
-            add(APP_ID, String.valueOf(appid.id()) + " : " + appid.name());
-            add(KEY, intent.key().toString());
+            add(APP_ID, concat(appid.id(), " : ", appid.name()));
+            add(KEY, intent.key());
             add(TYPE, intent.getClass().getSimpleName());
-            add(PRIORITY, Integer.toString(intent.priority()));
+            add(PRIORITY, intent.priority());
             add(RESOURCES, formatResources(intent));
             add(DETAILS, formatDetails(intent));
         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/LinkViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/LinkViewMessageHandler.java
index 7d496f5..6683188 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/LinkViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/LinkViewMessageHandler.java
@@ -16,7 +16,6 @@
 
 package org.onosproject.ui.impl;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
@@ -24,7 +23,12 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.LinkKey;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.impl.TopologyViewMessageHandlerBase.BiLink;
+import org.onosproject.ui.table.AbstractTableRow;
+import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRow;
+import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -36,7 +40,7 @@
 /**
  * Message handler for link view related messages.
  */
-public class LinkViewMessageHandler extends AbstractTabularViewMessageHandler {
+public class LinkViewMessageHandler extends UiMessageHandler {
 
     /**
      * Creates a new message handler for the link messages.
@@ -47,18 +51,21 @@
 
     @Override
     public void process(ObjectNode message) {
+        String type = eventType(message);
+        if (type.equals("linkDataRequest")) {
+            sendLinkList(message);
+        }
+    }
+
+    private void sendLinkList(ObjectNode message) {
         ObjectNode payload = payload(message);
-        String sortCol = string(payload, "sortCol", "one");
-        String sortDir = string(payload, "sortDir", "asc");
+        RowComparator rc = TableUtils.createRowComparator(payload, "one");
 
         LinkService service = get(LinkService.class);
         TableRow[] rows = generateTableRows(service);
-        RowComparator rc =
-                new RowComparator(sortCol, RowComparator.direction(sortDir));
         Arrays.sort(rows, rc);
-        ArrayNode links = generateArrayNode(rows);
         ObjectNode rootNode = mapper.createObjectNode();
-        rootNode.set("links", links);
+        rootNode.set("links", TableUtils.generateArrayNode(rows));
 
         connection().sendMessage("linkDataResponse", 0, rootNode);
     }
@@ -99,8 +106,8 @@
             ConnectPoint dst = link.one.dst();
             linkState(link);
 
-            add(ONE, src.elementId().toString() + "/" + src.port().toString());
-            add(TWO, dst.elementId().toString() + "/" + dst.port().toString());
+            add(ONE, concat(src.elementId(), "/", src.port()));
+            add(TWO, concat(dst.elementId(), "/", dst.port()));
             add(TYPE, linkType(link).toLowerCase());
             add(STATE, linkState(link));
             add(DIRECTION, link.two != null ? "A <--> B" : "A --> B");
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java b/web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java
deleted file mode 100644
index b588f48..0000000
--- a/web/gui/src/main/java/org/onosproject/ui/impl/RowComparator.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-package org.onosproject.ui.impl;
-
-import java.util.Comparator;
-
-/**
- * Comparator for {@link TableRow}.
- */
-public class RowComparator implements Comparator<TableRow> {
-    public static enum Direction { ASC, DESC }
-
-    public static final String DESC_STR = "desc";
-
-    private final String colId;
-    private final Direction dir;
-
-    /**
-     * Constructs a comparator for table rows that uses the given
-     * column ID and direction.
-     *
-     * @param colId the column to sort on
-     * @param dir the direction to sort in
-     */
-    public RowComparator(String colId, Direction dir) {
-        if (colId == null || dir == null) {
-            throw new NullPointerException("Null parameters not allowed");
-        }
-        this.colId = colId;
-        this.dir = dir;
-    }
-
-    @Override
-    public int compare(TableRow a, TableRow b) {
-        String cellA = a.get(colId);
-        String cellB = b.get(colId);
-
-        if (dir.equals(Direction.ASC)) {
-            return cellA.compareTo(cellB);
-        }
-        return cellB.compareTo(cellA);
-    }
-
-    /**
-     * Returns the sort direction constant for the given string.
-     * The expected strings are "asc" and "desc"; defaults to "asc".
-     *
-     * @param s the direction as a string
-     * @return the constant
-     */
-    public static Direction direction(String s) {
-        return DESC_STR.equals(s) ? Direction.DESC : Direction.ASC;
-    }
-}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java b/web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java
deleted file mode 100644
index 81a14ba..0000000
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TableRow.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2015 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.
- */
-
-package org.onosproject.ui.impl;
-
-
-import com.fasterxml.jackson.databind.node.ObjectNode;
-
-/**
- * Defines a table row abstraction to support sortable tables on the GUI.
- */
-public interface TableRow {
-    /**
-     * Returns the value of the cell for the given column ID.
-     *
-     * @param key the column ID
-     * @return the cell value
-     */
-    String get(String key);
-
-    /**
-     * Returns this table row in the form of a JSON object.
-     *
-     * @return the JSON node
-     */
-    ObjectNode toJsonNode();
-}
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 9beeb94..8ba8d3a 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
@@ -131,7 +131,8 @@
      * Creates a new web-socket for serving data to GUI topology view.
      */
     public TopologyViewMessageHandler() {
-        super(ImmutableSet.of("topoStart", "topoStop",
+        super(ImmutableSet.of("topoStart",
+                              "topoStop",
                               "requestDetails",
                               "updateMeta",
                               "addHostIntent",