GUI -- Introduce TableRequestHandler and refactor all table-based requests to use this class.

Change-Id: Ia26a78e9c4abead17de5e7f6babd54202c6772d9
diff --git a/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java b/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java
new file mode 100644
index 0000000..f90c187
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/ui/table/TableRequestHandler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.table;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.ui.RequestHandler;
+
+import java.util.Arrays;
+
+/**
+ * Message handler specifically for table views.
+ */
+public abstract class TableRequestHandler extends RequestHandler {
+
+    private final String respType;
+    private final String nodeName;
+
+    /**
+     * Constructs a table request handler for a specific table view. When
+     * table requests come in, the handler will generate the appropriate
+     * table rows, sort them according the the request sort parameters, and
+     * send back the response to the client.
+     *
+     * @param reqType   type of the request event
+     * @param respType  type of the response event
+     * @param nodeName  name of JSON node holding row data
+     */
+    public TableRequestHandler(String reqType, String respType, String nodeName) {
+        super(reqType);
+        this.respType = respType;
+        this.nodeName = nodeName;
+    }
+
+    @Override
+    public void process(long sid, ObjectNode payload) {
+        RowComparator rc = TableUtils.createRowComparator(payload, defaultColId());
+        TableRow[] rows = generateTableRows(payload);
+        Arrays.sort(rows, rc);
+        ObjectNode rootNode = MAPPER.createObjectNode();
+        rootNode.set(nodeName, TableUtils.generateArrayNode(rows));
+        sendMessage(respType, 0, rootNode);
+    }
+
+    /**
+     * Returns the default column ID, when one is not supplied in the payload
+     * defining the column on which to sort. This implementation returns "id".
+     *
+     * @return default sort column id
+     */
+    protected String defaultColId() {
+        return "id";
+    }
+
+    /**
+     * Subclasses should generate table rows for their specific table instance.
+     *
+     * @param payload provided in case custom parameters are present
+     * @return generated table rows
+     */
+    protected abstract TableRow[] generateTableRows(ObjectNode payload);
+}
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 f7124e1..6a00f13 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
@@ -25,11 +25,9 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -42,8 +40,21 @@
 public class ApplicationViewMessageHandler extends UiMessageHandler {
 
     private static final String APP_DATA_REQ = "appDataRequest";
+    private static final String APP_DATA_RESP = "appDataResponse";
+    private static final String APPS = "apps";
+
     private static final String APP_MGMT_REQ = "appManagementRequest";
 
+    private static final String STATE = "state";
+    private static final String STATE_IID = "_iconid_state";
+    private static final String ID = "id";
+    private static final String VERSION = "version";
+    private static final String ORIGIN = "origin";
+    private static final String DESC = "desc";
+
+    private static final String ICON_ID_ACTIVE = "active";
+    private static final String ICON_ID_INACTIVE = "appInactive";
+
     @Override
     protected Collection<RequestHandler> getHandlers() {
         return ImmutableSet.of(
@@ -52,38 +63,25 @@
         );
     }
 
-    // ======================================================================
-
-    private final class AppDataRequest extends RequestHandler {
-
+    // handler for application table requests
+    private final class AppDataRequest extends TableRequestHandler {
         private AppDataRequest() {
-            super(APP_DATA_REQ);
+            super(APP_DATA_REQ, APP_DATA_RESP, APPS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload);
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             ApplicationService service = get(ApplicationService.class);
-            TableRow[] rows = generateTableRows(service);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("apps", TableUtils.generateArrayNode(rows));
-
-            sendMessage("appDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(ApplicationService service) {
             List<TableRow> list = service.getApplications().stream()
                     .map(application -> new ApplicationTableRow(service, application))
                     .collect(Collectors.toList());
             return list.toArray(new TableRow[list.size()]);
         }
+
     }
-    // ======================================================================
 
+    // handler for application management control button actions
     private final class AppMgmtRequest extends RequestHandler {
-
         private AppMgmtRequest() {
             super(APP_MGMT_REQ);
         }
@@ -106,7 +104,6 @@
             }
         }
     }
-    // ======================================================================
 
     /**
      * TableRow implementation for
@@ -114,21 +111,10 @@
      */
     private static class ApplicationTableRow extends AbstractTableRow {
 
-        private static final String STATE = "state";
-        private static final String STATE_IID = "_iconid_state";
-        private static final String ID = "id";
-        private static final String VERSION = "version";
-        private static final String ORIGIN = "origin";
-        private static final String DESC = "desc";
-
         private static final String[] COL_IDS = {
                 STATE, STATE_IID, ID, VERSION, ORIGIN, DESC
         };
 
-        private static final String ICON_ID_ACTIVE = "active";
-        private static final String ICON_ID_INACTIVE = "appInactive";
-
-
         public ApplicationTableRow(ApplicationService service, Application app) {
             ApplicationState state = service.getState(app.id());
             String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE;
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 209b98e..1199b13 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
@@ -26,11 +26,9 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -42,34 +40,29 @@
 public class ClusterViewMessageHandler extends UiMessageHandler {
 
     private static final String CLUSTER_DATA_REQ = "clusterDataRequest";
+    private static final String CLUSTER_DATA_RESP = "clusterDataResponse";
+    private static final String CLUSTERS = "clusters";
+
+    private static final String ID = "id";
+    private static final String IP = "ip";
+    private static final String TCP_PORT = "tcp";
+    private static final String STATE_IID = "_iconid_state";
+    private static final String UPDATED = "updated";
 
     @Override
     protected Collection<RequestHandler> getHandlers() {
         return ImmutableSet.of(new ClusterDataRequest());
     }
 
-    // ======================================================================
-
-    private final class ClusterDataRequest extends RequestHandler {
-
+    // handler for cluster table requests
+    private final class ClusterDataRequest extends TableRequestHandler {
         private ClusterDataRequest() {
-            super(CLUSTER_DATA_REQ);
+            super(CLUSTER_DATA_REQ, CLUSTER_DATA_RESP, CLUSTERS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload);
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             ClusterService service = get(ClusterService.class);
-            TableRow[] rows = generateTableRows(service);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("clusters", TableUtils.generateArrayNode(rows));
-
-            sendMessage("clusterDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(ClusterService service) {
             List<TableRow> list = service.getNodes().stream()
                     .map(node -> new ControllerNodeTableRow(service, node))
                     .collect(Collectors.toList());
@@ -77,19 +70,11 @@
         }
     }
 
-    // ======================================================================
-
     /**
      * TableRow implementation for {@link ControllerNode controller nodes}.
      */
     private static class ControllerNodeTableRow extends AbstractTableRow {
 
-        private static final String ID = "id";
-        private static final String IP = "ip";
-        private static final String TCP_PORT = "tcp";
-        private static final String STATE_IID = "_iconid_state";
-        private static final String UPDATED = "updated";
-
         private static final String[] COL_IDS = {
                 ID, IP, TCP_PORT, STATE_IID, UPDATED
         };
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 2e8336c..806e8c6 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
@@ -30,12 +30,10 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -47,7 +45,12 @@
 public class DeviceViewMessageHandler extends UiMessageHandler {
 
     private static final String DEV_DATA_REQ = "deviceDataRequest";
-    private static final String DEV_DETAIL_REQ = "deviceDetailRequest";
+    private static final String DEV_DATA_RESP = "deviceDataResponse";
+    private static final String DEVICES = "devices";
+
+    private static final String DEV_DETAILS_REQ = "deviceDetailsRequest";
+    private static final String DEV_DETAILS_RESP = "deviceDetailsResponse";
+    private static final String DETAILS = "details";
 
     private static final String ID = "id";
     private static final String TYPE = "type";
@@ -78,30 +81,16 @@
         );
     }
 
-    // ======================================================================
-
-    private final class DataRequestHandler extends RequestHandler {
-
+    // handler for device table requests
+    private final class DataRequestHandler extends TableRequestHandler {
         private DataRequestHandler() {
-            super(DEV_DATA_REQ);
+            super(DEV_DATA_REQ, DEV_DATA_RESP, DEVICES);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload);
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             DeviceService service = get(DeviceService.class);
             MastershipService mastershipService = get(MastershipService.class);
-            TableRow[] rows = generateTableRows(service, mastershipService);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("devices", TableUtils.generateArrayNode(rows));
-
-            sendMessage("deviceDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(DeviceService service,
-                                             MastershipService mastershipService) {
             List<TableRow> list = new ArrayList<>();
             for (Device dev : service.getDevices()) {
                 list.add(new DeviceTableRow(service, mastershipService, dev));
@@ -110,11 +99,10 @@
         }
     }
 
-    // ======================================================================
-
+    // handler for selected device detail requests
     private final class DetailRequestHandler extends RequestHandler {
         private DetailRequestHandler() {
-            super(DEV_DETAIL_REQ);
+            super(DEV_DETAILS_REQ);
         }
 
         @Override
@@ -152,8 +140,8 @@
             data.set(PORTS, ports);
 
             ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("details", data);
-            sendMessage("deviceDetailsResponse", 0, rootNode);
+            rootNode.set(DETAILS, data);
+            sendMessage(DEV_DETAILS_RESP, 0, rootNode);
         }
 
         private ObjectNode portData(Port p, DeviceId id) {
@@ -183,7 +171,6 @@
 
     }
 
-
     private static String getTypeIconId(Device d) {
         return DEV_ICON_PREFIX + d.type().toString();
     }
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 dc9e898..18419708 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
@@ -17,8 +17,8 @@
 package org.onosproject.ui.impl;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import org.apache.commons.lang.WordUtils;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowRuleService;
@@ -29,16 +29,16 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
+import static org.apache.commons.lang.WordUtils.capitalizeFully;
+
 
 /**
  * Message handler for flow view related messages.
@@ -46,45 +46,42 @@
 public class FlowViewMessageHandler extends UiMessageHandler {
 
     private static final String FLOW_DATA_REQ = "flowDataRequest";
+    private static final String FLOW_DATA_RESP = "flowDataResponse";
+    private static final String FLOWS = "flows";
 
-    private static final String NO_DEV = "none";
+    private static final String ID = "id";
+    private static final String APP_ID = "appId";
+    private static final String GROUP_ID = "groupId";
+    private static final String TABLE_ID = "tableId";
+    private static final String PRIORITY = "priority";
+    private static final String SELECTOR = "selector";
+    private static final String TREATMENT = "treatment";
+    private static final String TIMEOUT = "timeout";
+    private static final String PERMANENT = "permanent";
+    private static final String STATE = "state";
+
+    private static final String COMMA = ", ";
 
     @Override
     protected Collection<RequestHandler> getHandlers() {
         return ImmutableSet.of(new FlowDataRequest());
     }
 
-    // ======================================================================
-
-    private final class FlowDataRequest extends RequestHandler {
+    // handler for flow table requests
+    private final class FlowDataRequest extends TableRequestHandler {
 
         private FlowDataRequest() {
-            super(FLOW_DATA_REQ);
+            super(FLOW_DATA_REQ, FLOW_DATA_RESP, FLOWS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload);
-            String uri = string(payload, "devId", NO_DEV);
-
-            ObjectNode rootNode;
-            if (uri.equals(NO_DEV)) {
-                rootNode = MAPPER.createObjectNode();
-                rootNode.set("flows", MAPPER.createArrayNode());
-            } else {
-                DeviceId deviceId = DeviceId.deviceId(uri);
-                FlowRuleService service = get(FlowRuleService.class);
-                TableRow[] rows = generateTableRows(service, deviceId);
-                Arrays.sort(rows, rc);
-                rootNode = MAPPER.createObjectNode();
-                rootNode.set("flows", TableUtils.generateArrayNode(rows));
+        protected TableRow[] generateTableRows(ObjectNode payload) {
+            String uri = string(payload, "devId");
+            if (Strings.isNullOrEmpty(uri)) {
+                return new TableRow[0];
             }
-
-            sendMessage("flowDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(FlowRuleService service,
-                                             DeviceId deviceId) {
+            DeviceId deviceId = DeviceId.deviceId(uri);
+            FlowRuleService service = get(FlowRuleService.class);
             List<TableRow> list = new ArrayList<>();
             for (FlowEntry flow : service.getFlowEntries(deviceId)) {
                 list.add(new FlowTableRow(flow));
@@ -93,42 +90,28 @@
         }
     }
 
-    // ======================================================================
-
     /**
-     * TableRow implementation for {@link org.onosproject.net.flow.FlowRule flows}.
+     * TableRow implementation for
+     * {@link org.onosproject.net.flow.FlowRule flows}.
      */
     private static class FlowTableRow extends AbstractTableRow {
 
-        private static final String ID = "id";
-        private static final String APP_ID = "appId";
-        private static final String GROUP_ID = "groupId";
-        private static final String TABLE_ID = "tableId";
-        private static final String PRIORITY = "priority";
-        private static final String SELECTOR = "selector";
-        private static final String TREATMENT = "treatment";
-        private static final String TIMEOUT = "timeout";
-        private static final String PERMANENT = "permanent";
-        private static final String STATE = "state";
-
-        private static final String COMMA = ", ";
-
         private static final String[] COL_IDS = {
-            ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, SELECTOR,
+                ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, SELECTOR,
                 TREATMENT, TIMEOUT, PERMANENT, STATE
         };
 
         public FlowTableRow(FlowEntry f) {
-            add(ID, Long.toString(f.id().value()));
-            add(APP_ID, Short.toString(f.appId()));
-            add(GROUP_ID, Integer.toString(f.groupId().id()));
-            add(TABLE_ID, Integer.toString(f.tableId()));
-            add(PRIORITY, Integer.toString(f.priority()));
+            add(ID, f.id().value());
+            add(APP_ID, f.appId());
+            add(GROUP_ID, f.groupId().id());
+            add(TABLE_ID, f.tableId());
+            add(PRIORITY, f.priority());
             add(SELECTOR, getSelectorString(f));
             add(TREATMENT, getTreatmentString(f));
-            add(TIMEOUT, Integer.toString(f.timeout()));
-            add(PERMANENT, Boolean.toString(f.isPermanent()));
-            add(STATE, WordUtils.capitalizeFully(f.state().toString()));
+            add(TIMEOUT, f.timeout());
+            add(PERMANENT, f.isPermanent());
+            add(STATE, capitalizeFully(f.state().toString()));
         }
 
         private String getSelectorString(FlowEntry f) {
@@ -141,8 +124,7 @@
             } else {
                 StringBuilder sb = new StringBuilder("Criteria = ");
                 for (Criterion c : criteria) {
-                    sb.append(WordUtils.capitalizeFully(c.type().toString()))
-                            .append(COMMA);
+                    sb.append(capitalizeFully(c.type().toString())).append(COMMA);
                 }
                 result = removeTrailingComma(sb).toString();
             }
@@ -177,8 +159,7 @@
             if (!deferred.isEmpty()) {
                 sb.append("Deferred instructions = ");
                 for (Instruction i : deferred) {
-                    sb.append(WordUtils.capitalizeFully(i.type().toString()))
-                            .append(COMMA);
+                    sb.append(capitalizeFully(i.type().toString())).append(COMMA);
                 }
                 removeTrailingComma(sb);
             }
@@ -188,8 +169,7 @@
             if (!immediate.isEmpty()) {
                 sb.append("Immediate instructions = ");
                 for (Instruction i : immediate) {
-                    sb.append(WordUtils.capitalizeFully(i.type().toString()))
-                            .append(COMMA);
+                    sb.append(capitalizeFully(i.type().toString())).append(COMMA);
                 }
                 removeTrailingComma(sb);
             }
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 42d7a1e..e20b3e1 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
@@ -24,12 +24,10 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
@@ -41,6 +39,17 @@
 public class HostViewMessageHandler extends UiMessageHandler {
 
     private static final String HOST_DATA_REQ = "hostDataRequest";
+    private static final String HOST_DATA_RESP = "hostDataResponse";
+    private static final String HOSTS = "hosts";
+
+    private static final String TYPE_IID = "_iconid_type";
+    private static final String ID = "id";
+    private static final String MAC = "mac";
+    private static final String VLAN = "vlan";
+    private static final String IPS = "ips";
+    private static final String LOCATION = "location";
+
+    private static final String HOST_ICON_PREFIX = "hostIcon_";
 
 
     @Override
@@ -48,28 +57,15 @@
         return ImmutableSet.of(new HostDataRequest());
     }
 
-    // ======================================================================
-
-    private final class HostDataRequest extends RequestHandler {
-
+    // handler for host table requests
+    private final class HostDataRequest extends TableRequestHandler {
         private HostDataRequest() {
-            super(HOST_DATA_REQ);
+            super(HOST_DATA_REQ, HOST_DATA_RESP, HOSTS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload);
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             HostService service = get(HostService.class);
-            TableRow[] rows = generateTableRows(service);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("hosts", TableUtils.generateArrayNode(rows));
-
-            sendMessage("hostDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(HostService service) {
             List<TableRow> list = new ArrayList<>();
             for (Host host : service.getHosts()) {
                 list.add(new HostTableRow(host));
@@ -78,22 +74,11 @@
         }
     }
 
-    // ======================================================================
-
     /**
      * TableRow implementation for {@link Host hosts}.
      */
     private static class HostTableRow extends AbstractTableRow {
 
-        private static final String TYPE_IID = "_iconid_type";
-        private static final String ID = "id";
-        private static final String MAC = "mac";
-        private static final String VLAN = "vlan";
-        private static final String IPS = "ips";
-        private static final String LOCATION = "location";
-
-        private static final String HOST_ICON_PREFIX = "hostIcon_";
-
         private static final String[] COL_IDS = {
                 TYPE_IID, ID, MAC, VLAN, IPS, LOCATION
         };
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 eb565cf..6d4ef6d 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
@@ -34,12 +34,10 @@
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.AbstractTableRow;
-import org.onosproject.ui.table.RowComparator;
+import org.onosproject.ui.table.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -50,6 +48,8 @@
 public class IntentViewMessageHandler extends UiMessageHandler {
 
     private static final String INTENT_DATA_REQ = "intentDataRequest";
+    private static final String INTENT_DATA_RESP = "intentDataResponse";
+    private static final String INTENTS = "intents";
 
     private static final String APP_ID = "appId";
     private static final String KEY = "key";
@@ -63,37 +63,27 @@
         return ImmutableSet.of(new IntentDataRequest());
     }
 
-    // ======================================================================
-
-    private final class IntentDataRequest extends RequestHandler {
-
+    // handler for intent table requests
+    private final class IntentDataRequest extends TableRequestHandler {
         private IntentDataRequest() {
-            super(INTENT_DATA_REQ);
+            super(INTENT_DATA_REQ, INTENT_DATA_RESP, INTENTS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload, APP_ID);
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             IntentService service = get(IntentService.class);
-            TableRow[] rows = generateTableRows(service);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("intents", TableUtils.generateArrayNode(rows));
-
-            sendMessage("intentDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(IntentService service) {
             List<TableRow> list = new ArrayList<>();
             for (Intent intent : service.getIntents()) {
                 list.add(new IntentTableRow(intent));
             }
             return list.toArray(new TableRow[list.size()]);
         }
-    }
 
-    // ======================================================================
+        @Override
+        protected String defaultColId() {
+            return APP_ID;
+        }
+    }
 
     /**
      * TableRow implementation for {@link Intent intents}.
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 06349b4..ae7f489 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
@@ -27,12 +27,10 @@
 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.TableRequestHandler;
 import org.onosproject.ui.table.TableRow;
-import org.onosproject.ui.table.TableUtils;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -45,35 +43,30 @@
 public class LinkViewMessageHandler extends UiMessageHandler {
 
     private static final String LINK_DATA_REQ = "linkDataRequest";
+    private static final String LINK_DATA_RESP = "linkDataResponse";
+    private static final String LINKS = "links";
 
+    private static final String ONE = "one";
+    private static final String TWO = "two";
+    private static final String TYPE = "type";
+    private static final String STATE = "_iconid_state";
+    private static final String DIRECTION = "direction";
+    private static final String DURABLE = "durable";
 
     @Override
     protected Collection<RequestHandler> getHandlers() {
         return ImmutableSet.of(new LinkDataRequest());
     }
 
-    // ======================================================================
-
-    private final class LinkDataRequest extends RequestHandler {
-
+    // handler for link table requests
+    private final class LinkDataRequest extends TableRequestHandler {
         private LinkDataRequest() {
-            super(LINK_DATA_REQ);
+            super(LINK_DATA_REQ, LINK_DATA_RESP, LINKS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            RowComparator rc = TableUtils.createRowComparator(payload, "one");
-
+        protected TableRow[] generateTableRows(ObjectNode payload) {
             LinkService service = get(LinkService.class);
-            TableRow[] rows = generateTableRows(service);
-            Arrays.sort(rows, rc);
-            ObjectNode rootNode = MAPPER.createObjectNode();
-            rootNode.set("links", TableUtils.generateArrayNode(rows));
-
-            sendMessage("linkDataResponse", 0, rootNode);
-        }
-
-        private TableRow[] generateTableRows(LinkService service) {
             List<TableRow> list = new ArrayList<>();
 
             // First consolidate all uni-directional links into two-directional ones.
@@ -84,22 +77,18 @@
             biLinks.values().forEach(biLink -> list.add(new LinkTableRow(biLink)));
             return list.toArray(new TableRow[list.size()]);
         }
-    }
 
-    // ======================================================================
+        @Override
+        protected String defaultColId() {
+            return ONE;
+        }
+    }
 
     /**
      * TableRow implementation for {@link org.onosproject.net.Link links}.
      */
     private static class LinkTableRow extends AbstractTableRow {
 
-        private static final String ONE = "one";
-        private static final String TWO = "two";
-        private static final String TYPE = "type";
-        private static final String STATE = "_iconid_state";
-        private static final String DIRECTION = "direction";
-        private static final String DURABLE = "durable";
-
         private static final String[] COL_IDS = {
                 ONE, TWO, TYPE, STATE, DIRECTION, DURABLE
         };