tez-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jeag...@apache.org
Subject [39/53] tez git commit: TEZ-1757. Column selector for tables (Sreenath Somarajapuram via Rajesh Balamohan)
Date Wed, 10 Dec 2014 03:34:02 GMT
TEZ-1757. Column selector for tables (Sreenath Somarajapuram via Rajesh Balamohan)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/3a0b70c1
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/3a0b70c1
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/3a0b70c1

Branch: refs/heads/master
Commit: 3a0b70c181c898914eeb39a4966342287f21edb8
Parents: 8af5ea3
Author: Rajesh Balamohan <rbalamohan@hortonworks.com>
Authored: Fri Nov 21 04:14:01 2014 +0530
Committer: Rajesh Balamohan <rbalamohan@hortonworks.com>
Committed: Fri Nov 21 04:14:01 2014 +0530

----------------------------------------------------------------------
 tez-ui/src/main/webapp/app/index.html           |   1 +
 tez-ui/src/main/webapp/app/scripts/app.js       |  15 +-
 .../app/scripts/components/extended-table.js    |  33 ++-
 tez-ui/src/main/webapp/app/scripts/configs.js   |  94 ++++++++
 .../webapp/app/scripts/controllers/dag_tasks.js | 203 ++++++++--------
 .../app/scripts/controllers/dag_vertices.js     | 190 ++++++++-------
 .../app/scripts/controllers/dags_controller.js  | 209 ++++++++--------
 .../task_task_attempts_controller.js            | 178 +++++++-------
 .../app/scripts/controllers/tasks_controller.js | 118 +++++----
 .../controllers/tez-app-dags-controller.js      | 153 ++++++------
 .../controllers/vertex-inputs-controller.js     |  79 ++++---
 .../vertex_task_attempts_controller.js          | 237 ++++++++++---------
 .../controllers/vertex_tasks_controller.js      | 187 ++++++++-------
 .../main/webapp/app/scripts/helpers/dialogs.js  |  77 ++++++
 .../src/main/webapp/app/scripts/helpers/misc.js |  54 +++++
 .../app/scripts/mixins/column-selector-mixin.js |  69 ++++++
 .../app/scripts/models/TimelineRestAdapter.js   |  23 +-
 tez-ui/src/main/webapp/app/styles/main.less     |  16 +-
 .../src/main/webapp/app/templates/dag/tasks.hbs |  67 ++++--
 .../main/webapp/app/templates/dag/vertices.hbs  |  67 ++++--
 tez-ui/src/main/webapp/app/templates/dags.hbs   |   3 +
 .../main/webapp/app/templates/task/attempts.hbs |   3 +
 tez-ui/src/main/webapp/app/templates/tasks.hbs  |  61 +++--
 .../src/main/webapp/app/templates/tez-app.hbs   |   2 +-
 .../main/webapp/app/templates/tez-app/dags.hbs  |   3 +
 .../main/webapp/app/templates/vertex/index.hbs  |   4 +
 .../main/webapp/app/templates/vertex/inputs.hbs |   3 +
 .../app/templates/vertex/task_attempts.hbs      |   3 +
 .../main/webapp/app/templates/vertex/tasks.hbs  |   3 +
 29 files changed, 1317 insertions(+), 838 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/index.html b/tez-ui/src/main/webapp/app/index.html
index 1653e94..ac6a0ec 100644
--- a/tez-ui/src/main/webapp/app/index.html
+++ b/tez-ui/src/main/webapp/app/index.html
@@ -27,6 +27,7 @@
     <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
     <link rel="stylesheet" href="bower_components/ember-table/dist/ember-table.css">
     <link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.css">
+    <link rel="stylesheet" href="bower_components/jquery-ui/themes/smoothness/jquery-ui.css">
     <!-- endbuild -->
 
   </head>

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/app.js b/tez-ui/src/main/webapp/app/scripts/app.js
index 91f0e7a..4af2b5e 100644
--- a/tez-ui/src/main/webapp/app/scripts/app.js
+++ b/tez-ui/src/main/webapp/app/scripts/app.js
@@ -27,14 +27,16 @@ var App = window.App = Em.Application.createWithMixins(Bootstrap, {
   LOG_TRANSITIONS_INTERNAL: true,
 
   env: {
-    standalone: window.parent === window,
-    timelineBaseUrl: "http://localhost:8188",
-    RMWebUrl: "http://localhost:8088"
+    isStandalone: true // Can ne set false in the wrapper initializer
   }
 });
 
 App.Helpers = Em.Namespace.create();
 App.Mappers = Em.Namespace.create();
+App.Configs = Em.Namespace.create();
+
+require('scripts/configs');
+$.extend(App.env, App.Configs.envDefaults);
 
 Ember.Application.initializer({
   name: "initApp",
@@ -46,7 +48,7 @@ Ember.Application.initializer({
     application.ApplicationSerializer = App.TimelineSerializer.extend();
 
     application.AppDetailAdapter = DS.RESTAdapter.extend({
-      namespace: 'ws/v1/applicationhistory',
+      namespace: App.Configs.restNamespace.applicationHistory,
       host: App.env.timelineBaseUrl,
       pathForType: function() {
         return "apps";
@@ -100,12 +102,17 @@ Ember.Application.initializer({
 require('scripts/translations');
 require('scripts/mixins/*');
 require('scripts/helpers/*');
+
 require('scripts/router');
 require('scripts/store');
 require('scripts/views/**/*');
 require('scripts/models/**/*');
+
 require('scripts/mappers/server_data_mapper.js');
 require('scripts/mappers/**/*');
+
+require('scripts/controllers/table-page-controller.js');
 require('scripts/controllers/**/*');
+
 require('scripts/components/*');
 require('scripts/adapters/*');

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/components/extended-table.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/components/extended-table.js b/tez-ui/src/main/webapp/app/scripts/components/extended-table.js
index fc8e092..de2e483 100644
--- a/tez-ui/src/main/webapp/app/scripts/components/extended-table.js
+++ b/tez-ui/src/main/webapp/app/scripts/components/extended-table.js
@@ -160,10 +160,12 @@ App.ExTable.ColumnDefinition = Ember.Table.ColumnDefinition.extend({
 App.ExTable.TableComponent = Ember.Table.EmberTableComponent.extend({
 	layoutName: 'components/extended-table/extable',
 	filters: {},
-
 	hasFilter: true,
 	minFilterHeight: 30, //TODO: less changes
 
+  enableContentSelection: false,
+  selectionMode: 'none',
+
   actions: {
     filterUpdated: function(columnDef, value) {
       var filterID = columnDef.get('filterID');
@@ -171,12 +173,41 @@ App.ExTable.TableComponent = Ember.Table.EmberTableComponent.extend({
       if (this.get('onFilterUpdated')) {
       	this.sendAction('onFilterUpdated', filterID, value);
       }
+    },
+  },
+
+  doForceFillColumns: function() {
+    var additionWidthPerColumn, availableContentWidth, columnsToResize, contentWidth, fixedColumnsWidth, remainingWidth, tableColumns, totalWidth;
+    totalWidth = this.get('_width');
+
+    fixedColumnsWidth = this.get('_fixedColumnsWidth');
+    tableColumns = this.get('tableColumns');
+    contentWidth = this._getTotalWidth(tableColumns);
+
+    availableContentWidth = totalWidth - fixedColumnsWidth;
+    remainingWidth = availableContentWidth - contentWidth;
+    columnsToResize = tableColumns.filterProperty('canAutoResize');
+
+    additionWidthPerColumn = Math.floor(remainingWidth / columnsToResize.length);
+    if(availableContentWidth <= this._getTotalWidth(tableColumns, 'minWidth')) {
+      return columnsToResize;
     }
+    return columnsToResize.forEach(function(column) {
+      var columnWidth = column.get('columnWidth') + additionWidthPerColumn;
+      return column.set('columnWidth', columnWidth);
+    });
   },
 
 	// private variables
 	// Dynamic filter height that adjusts according to the filter content height
 	_contentFilterHeight: null,
+
+  _onColumnsChange: Ember.observer(function() {
+    return Ember.run.next(this, function() {
+      return Ember.run.once(this, this.updateLayout);
+    });
+  }, 'columns.length'),
+
   _filterHeight: function() {
     var minHeight = this.get('minFilterHeight');
     var contentFilterHeight = this.get('_contentFilterHeight');

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/configs.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/configs.js b/tez-ui/src/main/webapp/app/scripts/configs.js
new file mode 100644
index 0000000..14a79d2
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/configs.js
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+App.Configs.setProperties({
+
+  // Default environment configurations
+  envDefaults: {
+    // Host URLs: Change the following URLs for pointing tez-ui to the respective servers.
+    timelineBaseUrl: 'http://localhost:8188', // ip:po at which time;line server is running
+    RMWebUrl: 'http://localhost:8088', // Location of RM web url
+  },
+
+  restNamespace: {
+    timeline: 'ws/v1/timeline',
+    applicationHistory: 'ws/v1/applicationhistory'
+  },
+
+  table: {
+    commonColumns: {
+      /*
+       * More counters can be added into the tables by adding an entry into the following array.
+       * Believe counterId and groupId are self descriptive, value in headerCellName would be
+       * displayed as the column header.
+       */
+      counters: [
+        {
+          counterId: 'FILE_BYTES_READ',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'File Bytes Read'
+        },
+        {
+          counterId: 'FILE_BYTES_WRITTEN',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'File Bytes Written'
+        },
+        {
+          counterId: 'FILE_READ_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'File Read Ops'
+        },
+        {
+          counterId: 'FILE_LARGE_READ_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'File Large Read Ops'
+        },
+        {
+          counterId: 'FILE_WRITE_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'File Write Ops'
+        },
+        {
+          counterId: 'HDFS_BYTES_READ',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'HDFS Bytes Read'
+        },
+        {
+          counterId: 'HDFS_BYTES_WRITTEN',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'HDFS Bytes Written'
+        },
+        {
+          counterId: 'HDFS_READ_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'HDFS Read Ops'
+        },
+        {
+          counterId: 'HDFS_LARGE_READ_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'HDFS Large Read Ops'
+        },
+        {
+          counterId: 'HDFS_WRITE_OPS',
+          groupId: 'org.apache.tez.common.counters.FileSystemCounter',
+          headerCellName: 'HDFS Write Ops'
+        }
+      ]
+    }
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js
index e925414..3241702 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_tasks.js
@@ -16,9 +16,11 @@
  * limitations under the License.
  */
 
-App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   needs: "dag",
 
+  controllerName: 'DagTasksController',
+
   // required by the PaginatedContentMixin
   childEntityType: 'task',
 
@@ -53,7 +55,7 @@ App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
       that.set('entities', entities);
       var pivotLoaders = [];
       entities.forEach(function (task) {
-        var taskAttemptId = task.get('successfulAttemptId') || task.get('attempts').lastObject;
+        var taskAttemptId = task.get('successfulAttemptId') || task.get('attempts.lastObject');
         if (!!taskAttemptId) {
           // Pivot attempt selection logic
           fetcher = store.find('taskAttempt', taskAttemptId);
@@ -84,102 +86,111 @@ App.DagTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
     }
   },
 
-	columns: function() {
-		var idCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Task ID',
-      tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile(
-      		"{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      contentPath: 'id',
-    });
-
-    var vertexCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Vertex ID',
-      filterID: 'vertex_id_filter',
-      contentPath: 'vertexID'
-    });
-
-    var startTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-      	return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'id',
+        headerCellName: 'Task ID',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        }),
+        contentPath: 'id',
+      },
+      {
+        id: 'vertexID',
+        headerCellName: 'Vertex ID',
+        filterID: 'vertex_id_filter',
+        contentPath: 'vertexID'
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'Duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
+        }
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.taskStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'actions',
+        headerCellName: 'Actions',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+            {{#link-to "task.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
+            {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}}\
+            </span>'
+            )
+        }),
+        contentPath: 'id'
+      },
+      {
+        id: 'logs',
+        textAlign: 'text-align-left',
+        headerCellName: 'Logs',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+              {{#unless view.cellContent}}\
+                Not Available\
+              {{else}}\
+                <a href="//{{unbound view.cellContent}}">View</a>\
+                &nbsp;\
+                <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
+              {{/unless}}\
+            </span>')
+        }),
+        getCellContent: function(row) {
+          var attempt = row.get('pivotAttempt');
+          var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
+          if(logFile) logFile += "/syslog_" + attempt.get('id');
+          return logFile;
         }
       }
-    });
-
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.taskStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-      	return { 
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
+    ];
+  }.property(),
 
-    var actionsCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Actions',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-          {{#link-to "task.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
-          {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}}\
-          </span>'
-          )
-      }),
-      contentPath: 'id'
-    });
-
-    var logs = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Logs',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-            {{#unless view.cellContent}}\
-              Not Available\
-            {{else}}\
-              <a href="//{{unbound view.cellContent}}">View</a>\
-              &nbsp;\
-              <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
-            {{/unless}}\
-          </span>')
-      }),
-      getCellContent: function(row) {
-        var attempt = row.get('pivotAttempt');
-        var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
-        if(logFile) logFile += "/syslog_" + attempt.get('id');
-        return logFile;
-      }
-    });
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.task') || []
+    );
+  }.property()
 
-		return [idCol, vertexCol, startTimeCol, endTimeCol, durationCol, statusCol, actionsCol, logs];
-	}.property(),
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js b/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
index 947d717..924ca14 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dag_vertices.js
@@ -16,9 +16,11 @@
  * limitations under the License.
  */
 
-App.DagVerticesController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.DagVerticesController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   needs: "dag",
 
+  controllerName: 'DagVerticesController',
+
   // required by the PaginatedContentMixin
   childEntityType: 'vertex',
 
@@ -52,94 +54,108 @@ App.DagVerticesController = Em.ObjectController.extend(App.PaginatedContentMixin
     }
   },
 
-  columns: function() {
-    var idCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Vertex Name',
-      tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile(
-      		"{{#link-to 'vertex' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-        return {
-          id: row.get('id'),
-          name: row.get('name')
-        };
-      }
-    });
-
-    var nameCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Vertex ID',
-      contentPath: 'id',
-    });
-
-    var startTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-      	return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var firstTaskStartTime = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'First Task Start Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('firstTaskStartTime'));
-      }
-    });
-
-    var numTasksCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Tasks',
-      contentPath: 'numTasks'
-    });
-
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.vertexStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-        return {
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-
-    var configsCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Input Configurations',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          " {{#if view.cellContent.inputId}}\
-              {{#if view.cellContent.showConfigs}}\
-               {{#link-to 'vertexInput.configs' view.cellContent.vertexId view.cellContent.inputId class='ember-table-content'}}View configurations{{/link-to}}\
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'vertexName',
+        headerCellName: 'Vertex Name',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'vertex' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
+        }),
+        getCellContent: function(row) {
+          return {
+            id: row.get('id'),
+            name: row.get('name')
+          };
+        }
+      },
+      {
+        id: 'id',
+        headerCellName: 'Vertex ID',
+        contentPath: 'id',
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'firstTaskStartTime',
+        headerCellName: 'First Task Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('firstTaskStartTime'));
+        }
+      },
+      {
+        id: 'tasks',
+        headerCellName: 'Tasks',
+        contentPath: 'numTasks'
+      },
+      {
+        id: 'processorClass',
+        headerCellName: 'Processor Class',
+        contentPath: 'processorClassName'
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.vertexStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'configurations',
+        headerCellName: 'Input Configurations',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            " {{#if view.cellContent.inputId}}\
+                {{#if view.cellContent.showConfigs}}\
+                 {{#link-to 'vertexInput.configs' view.cellContent.vertexId view.cellContent.inputId class='ember-table-content'}}View configurations{{/link-to}}\
+                {{else}}\
+                 {{#link-to 'vertex.inputs' view.cellContent.vertexId class='ember-table-content'}}View Inputs{{/link-to}}\
+                {{/if}}\
               {{else}}\
-               {{#link-to 'vertex.inputs' view.cellContent.vertexId class='ember-table-content'}}View Inputs{{/link-to}}\
-              {{/if}}\
-            {{else}}\
-              <span class='ember-table-content'>No Inputs</span>\
-            {{/if}}")
-      }),
-      getCellContent: function(row) {
-        return {
-          showConfigs: row.get('inputs.content.length') == 1 && row.get('inputs.content.0.configs.length') > 0,
-          inputId: row.get('inputs.content.0.id'),
-          vertexId: row.get('id')
-        };
+                <span class='ember-table-content'>No Inputs</span>\
+              {{/if}}")
+        }),
+        getCellContent: function(row) {
+          return {
+            showConfigs: row.get('inputs.content.length') == 1 && row.get('inputs.content.0.configs.length') > 0,
+            inputId: row.get('inputs.content.0.id'),
+            vertexId: row.get('id')
+          };
+        }
       }
-    });
+    ];
+  }.property(),
 
-    return [idCol, nameCol, startTimeCol, endTimeCol, firstTaskStartTime, statusCol, numTasksCol, configsCol];
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.vertex') || []
+    );
   }.property(),
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
index ee92a33..9349357 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   childEntityType: 'dag',
 
 	controllerName: 'DagsController',
@@ -121,111 +121,116 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
     },
   },
 
-	/* table view for dags */
-  columns: function() {
-    var store = this.get('store');
-    var columnHelper = function(columnName, valName) {
-      return App.ExTable.ColumnDefinition.create({
-        textAlign: 'text-align-left',
-        headerCellName: columnName,
-        contentPath: valName
-      });
-    }
-
-    var nameCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
-      textAlign: 'text-align-left',
-      headerCellName: 'Dag Name',
-      filterID: 'dagName_filter',
-      tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile(
-          "{{#link-to 'dag.vertices' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-      	return {
-          id: row.get('id'),
-          name: row.get('name')
-        };
-      }
-    });
-    var idCol = columnHelper('Dag ID', 'id');
-    var userCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
-      textAlign: 'text-align-left',
-      headerCellName: 'Submitter',
-      filterID: 'user_filter',
-      contentPath: 'user'
-    }); 
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      textAlign: 'text-align-left',
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.dagStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-      	return { 
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-    var startTime = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-    var endTime = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  /*
+   * Columns that would be displayed by default
+   * @return Array of column configs
+   */
+  defaultColumnConfigs: function () {
+    return [
+      {
+        id: 'dagName',
+        headerCellName: 'Dag Name',
+        filterID: 'dagName_filter',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'dag.vertices' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
+        }),
+        getCellContent: function(row) {
+          return {
+            id: row.get('id'),
+            name: row.get('name')
+          };
         }
-      }
-    });
-    var appIdCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
-      textAlign: 'text-align-left',
-      headerCellName: 'Application ID',
-      filterID: 'appId_filter',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#if view.cellContent.enableLink}}\
-             {{#link-to 'tez-app' view.cellContent.appId class='ember-table-content'}}{{view.cellContent.appId}}{{/link-to}}\
-           {{else}}\
-             <span class='ember-table-content'>{{view.cellContent.appId}}</span>\
-           {{/if}}")
-      }),
-      getCellContent: function(row) {
-        return  {
-          enableLink: row.get('tezApp'),
-          appId: row.get('applicationId')
+      },
+      {
+        id: 'id',
+        headerCellName: 'Id',
+        contentPath: 'id'
+      },
+      {
+        id: 'user',
+        headerCellName: 'Submitter',
+        filterID: 'user_filter',
+        contentPath: 'user'
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.dagStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'Duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
+        }
+      },
+      {
+        id: 'appId',
+        headerCellName: 'Application ID',
+        filterID: 'appId_filter',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#if view.cellContent.enableLink}}\
+               {{#link-to 'tez-app' view.cellContent.appId class='ember-table-content'}}{{view.cellContent.appId}}{{/link-to}}\
+             {{else}}\
+               <span class='ember-table-content'>{{view.cellContent.appId}}</span>\
+             {{/if}}")
+        }),
+        getCellContent: function(row) {
+          return  {
+            enableLink: row.get('tezApp'),
+            appId: row.get('applicationId')
+          }
+        }
+      },
+      {
+        id: 'queue',
+        headerCellName: 'Queue',
+        getCellContent: function(row) {
+          return row.get('appDetail.queue') || 'Not Available';
         }
       }
-    });
-    var queue = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Queue',
-      getCellContent: function(row) {
-        return row.get('appDetail.queue') || 'Not Available';
-      }
-    });
-    return [nameCol, idCol, userCol, statusCol, startTime, endTime, durationCol, appIdCol, queue];
+    ];
   }.property(),
 
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.dag') || []
+    );
+  }.property(),
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/task_task_attempts_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/task_task_attempts_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/task_task_attempts_controller.js
index 1099d04..fe62d01 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/task_task_attempts_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/task_task_attempts_controller.js
@@ -16,10 +16,12 @@
  * limitations under the License.
  */
 
-App.TaskAttemptsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.TaskAttemptsController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   // required by the PaginatedContentMixin
   childEntityType: 'task_attempt',
 
+  controllerName: 'TaskAttemptsController',
+
   needs: 'task',
 
   queryParams: {
@@ -51,93 +53,103 @@ App.TaskAttemptsController = Em.ObjectController.extend(App.PaginatedContentMixi
     }
   },
 
-	columns: function() {
-		var idCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Task ID',
-      tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile(
-      		"{{#link-to 'taskAttempt' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      contentPath: 'id',
-    });
-
-    var startTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-      	return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'taskId',
+        headerCellName: 'Task ID',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'taskAttempt' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        }),
+        contentPath: 'id',
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'Duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
+        }
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.taskStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'nodeId',
+        headerCellName: 'Node ID',
+        contentPath: 'nodeId'
+      },
+      {
+        id: 'containerId',
+        headerCellName: 'Container ID',
+        contentPath: 'containerId'
+      },
+      {
+        id: 'logs',
+        headerCellName: 'Logs',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+              {{#unless view.cellContent}}\
+                Not Available\
+              {{else}}\
+                <a href="//{{unbound view.cellContent}}">View</a>\
+                &nbsp;\
+                <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
+              {{/unless}}\
+            </span>')
+        }),
+        getCellContent: function(row) {
+          var logFile = row.get('inProgressLog') || row.get('completedLog');
+          if(logFile) logFile += "/syslog_" + row.get('id');
+          return logFile;
         }
       }
-    });
-
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.taskStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-      	return { 
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-
-    var nodeIdCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Node ID',
-      contentPath: 'nodeId'
-    });
+    ];
 
-    var containerCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Container ID',
-      contentPath: 'containerId'
-    });
+  }.property(),
 
-    var logs = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Logs',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-            {{#unless view.cellContent}}\
-              Not Available\
-            {{else}}\
-              <a href="//{{unbound view.cellContent}}">View</a>\
-              &nbsp;\
-              <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
-            {{/unless}}\
-          </span>')
-      }),
-      getCellContent: function(row) {
-        var logFile = row.get('inProgressLog') || row.get('completedLog');
-        if(logFile) logFile += "/syslog_" + row.get('id');
-        return logFile;
-      }
-    });
-    return [idCol, startTimeCol, endTimeCol, durationCol, statusCol, nodeIdCol, containerCol, logs];
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.taskAttempt') || []
+    );
   }.property(),
+
 });
 
 

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/tasks_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/tasks_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/tasks_controller.js
index 3573759..96d84f5 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/tasks_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/tasks_controller.js
@@ -16,15 +16,15 @@
  * limitations under the License.
  */
 
-App.TasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.TasksController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   // Required by the PaginatedContentMixin
   childEntityType: 'task',
 
-	controllerName: 'TasksController',
+  controllerName: 'TasksController',
 
-	pageTitle: 'Tasks',
+  pageTitle: 'Tasks',
 
-	pageSubTitle: 'All Tasks',
+  pageSubTitle: 'All Tasks',
 
   queryParams: {
     parentType: true,
@@ -47,66 +47,62 @@ App.TasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
     this.setFiltersAndLoadEntities(filters);
   },
 
-	/* table view for tasks */
-  columns: function() {
-    var store = this.get('store');
-    var columnHelper = function(columnName, valName) {
-      return Em.Table.ColumnDefinition.create({
-        textAlign: 'text-align-left',
-        headerCellName: columnName,
+  defaultColumnConfigs: function() {
+
+    return [
+      {
+        id: 'taskId',
+        headerCellName: 'Task Id',
+        contentPath: 'id',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        })
+      },
+      {
+        id: 'vertexId',
+        headerCellName: 'Vertex ID',
+        contentPath: 'vertexID'
+      },
+      {
+        id: 'submissionTime',
+        headerCellName: 'Submission Time',
         getCellContent: function(row) {
-          return row.get(valName);
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
         }
-      });
-    }
-
-    var idColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Task Id',
-      tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile(
-          "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-      	return row.get('id');
-      }
-    });
-
-    var vertexColumn = columnHelper('Vertex ID', 'vertexID');
-
-    var startTimeColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Submission Time',
-      getCellContent: function(row) {
-      	return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var statusColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Status',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-      	return { 
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
       }
-    });
+    ];
     
-    return [idColumn, vertexColumn, startTimeColumn, endTimeColumn, statusColumn];
   }.property(),
+
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.task') || []
+    );
+  }.property(),
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
index e2855e1..772b53c 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/tez-app-dags-controller.js
@@ -16,9 +16,11 @@
  * limitations under the License.
  */
 
-App.TezAppDagsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.TezAppDagsController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   needs: "tezApp",
 
+  controllerName: 'TezAppDagsController',
+
   // required by the PaginatedContentMixin
   childEntityType: 'dag',
 
@@ -54,83 +56,82 @@ App.TezAppDagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
     }
   },
 
-  /* table view for dags */
-  columns: function() {
-    var columnHelper = function(columnName, valName) {
-      return App.ExTable.ColumnDefinition.create({
-        textAlign: 'text-align-left',
-        headerCellName: columnName,
-        contentPath: valName
-      });
-    }
-
-    var nameCol = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Dag Name',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#link-to 'dag' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-        return {
-          id: row.get('id'),
-          name: row.get('name')
-        };
-      }
-    });
-    var idCol = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Dag ID',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#link-to 'dag' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-        return row.get('id')
-      }
-    });
-    var userCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, {
-      textAlign: 'text-align-left',
-      headerCellName: 'Submitter',
-      filterID: 'user_filter',
-      contentPath: 'user'
-    }); 
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      textAlign: 'text-align-left',
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-        return { 
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-    var submittedTimeCol = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Submitted Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('submittedTime'));
-      }
-    });
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'dagName',
+        headerCellName: 'Dag Name',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'dag' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
+        }),
+        getCellContent: function(row) {
+          return {
+            id: row.get('id'),
+            name: row.get('name')
+          };
+        }
+      },
+      {
+        id: 'dagId',
+        headerCellName: 'Dag ID',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'dag' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        }),
+        getCellContent: function(row) {
+          return row.get('id')
+        }
+      },
+      {
+        id: 'submitter',
+        headerCellName: 'Submitter',
+        filterID: 'user_filter',
+        contentPath: 'user'
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'submittedTime',
+        headerCellName: 'Submitted Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('submittedTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'Duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
         }
       }
-    });
-    return [nameCol, idCol, userCol, statusCol, submittedTimeCol, durationCol];
+    ];
+  }.property(),
+
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.dag') || []
+    );
   }.property(),
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/vertex-inputs-controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/vertex-inputs-controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/vertex-inputs-controller.js
index d08949e..118dc05 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/vertex-inputs-controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/vertex-inputs-controller.js
@@ -16,9 +16,11 @@
  * limitations under the License.
  */
 
-App.VertexInputsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.VertexInputsController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   needs: 'vertex',
 
+  controllerName: 'VertexInputsController',
+
   loadEntities: function() {
     var content = this.get('inputs').content;
     this.set('entities', content);
@@ -38,40 +40,51 @@ App.VertexInputsController = Em.ObjectController.extend(App.PaginatedContentMixi
     }
   },
 
-  columns: function() {
-    var nameCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Name',
-      contentPath: 'inputName',
-    });
-
-    var classCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Class',
-      contentPath: 'inputClass',
-    });
-
-    var initializerCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Initializer',
-      contentPath: 'inputInitializer',
-    });
-
-    var configCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Configurations',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#if view.cellContent.count}}\
-            {{#link-to 'vertexInput.configs' view.cellContent.id class='ember-table-content'}}View Configurations{{/link-to}}\
-          {{else}}\
-            <span class='ember-table-content'>Not Available</span>\
-          {{/if}}")
-      }),
-      getCellContent: function(row) {
-        return {
-          count: row.get('configs.content.length'),
-          id: row.get('id')
-        };
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'inputId',
+        headerCellName: 'Name',
+        contentPath: 'inputName',
+      },
+      {
+        id: 'inputClass',
+        headerCellName: 'Class',
+        contentPath: 'inputClass',
+      },
+      {
+        id: 'inputInitializer',
+        headerCellName: 'Initializer',
+        contentPath: 'inputInitializer',
+      },
+      {
+        id: 'configurations',
+        headerCellName: 'Configurations',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#if view.cellContent.count}}\
+              {{#link-to 'vertexInput.configs' view.cellContent.id class='ember-table-content'}}View Configurations{{/link-to}}\
+            {{else}}\
+              <span class='ember-table-content'>Not Available</span>\
+            {{/if}}")
+        }),
+        getCellContent: function(row) {
+          return {
+            count: row.get('configs.content.length'),
+            id: row.get('id')
+          };
+        }
       }
-    });
+    ];
 
     return [nameCol, classCol, initializerCol, configCol];
   }.property(),
+
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.input') || []
+    );
+  }.property(),
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/vertex_task_attempts_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/vertex_task_attempts_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/vertex_task_attempts_controller.js
index 70ceb76..d5d9ebf 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/vertex_task_attempts_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/vertex_task_attempts_controller.js
@@ -16,10 +16,12 @@
  * limitations under the License.
  */
 
-App.VertexTaskAttemptsController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.VertexTaskAttemptsController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   // Required by the PaginatedContentMixin
   childEntityType: 'taskAttempt',
 
+  controllerName: 'VertexTaskAttemptsController',
+
   needs: 'vertex',
 
   queryParams: {
@@ -51,121 +53,130 @@ App.VertexTaskAttemptsController = Em.ObjectController.extend(App.PaginatedConte
     }
   },
 
-  columns: function() {
-    var idCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Task ID',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      contentPath: 'taskID',
-    });
-
-    var attemptNoCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Attempt No',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#link-to 'taskAttempt' view.cellContent.attemptID class='ember-table-content'}}{{view.cellContent.attemptNo}}{{/link-to}}")
-      }),
-      getCellContent: function(row) {
-        var attemptID = row.get('id') || '',
-            attemptNo = attemptID.split(/[_]+/).pop();
-        return {
-          attemptNo: attemptNo,
-          attemptID: attemptID
-        };
-      }
-    });
-
-    var startTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'taskId',
+        headerCellName: 'Task ID',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        }),
+        contentPath: 'taskID',
+      },
+      {
+        id: 'attemptNo',
+        headerCellName: 'Attempt No',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'taskAttempt' view.cellContent.attemptID class='ember-table-content'}}{{view.cellContent.attemptNo}}{{/link-to}}")
+        }),
+        getCellContent: function(row) {
+          var attemptID = row.get('id') || '',
+              attemptNo = attemptID.split(/[_]+/).pop();
+          return {
+            attemptNo: attemptNo,
+            attemptID: attemptID
+          };
+        }
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'Duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
+        }
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.taskStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'containerId',
+        headerCellName: 'Container',
+        contentPath: 'containerId'
+      },
+      {
+        id: 'nodeId',
+        headerCellName: 'Node',
+        contentPath: 'nodeId'
+      },
+      {
+        id: 'actions',
+        headerCellName: 'Actions',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+            {{#link-to "taskAttempt.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
+            </span>'
+            )
+        }),
+        contentPath: 'id'
+      },
+      {
+        id: 'logs',
+        headerCellName: 'Logs',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+              {{#unless view.cellContent}}\
+                Not Available\
+              {{else}}\
+                <a href="//{{unbound view.cellContent}}">View</a>\
+                &nbsp;\
+                <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
+              {{/unless}}\
+            </span>')
+        }),
+        getCellContent: function(row) {
+          var attempt = row.get('pivotAttempt');
+          var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
+          if(logFile) logFile += "/syslog_" + attempt.get('id');
+          return logFile;
         }
       }
-    });
-
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.taskStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-        return {
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-
-    var containerCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Container',
-      contentPath: 'containerId'
-    });
-
-    var nodeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Node',
-      contentPath: 'nodeId'
-    });
-
-    var actionsCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Actions',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-          {{#link-to "taskAttempt.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
-          </span>'
-          )
-      }),
-      contentPath: 'id'
-    });
+    ];
 
-    var logs = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Logs',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-            {{#unless view.cellContent}}\
-              Not Available\
-            {{else}}\
-              <a href="//{{unbound view.cellContent}}">View</a>\
-              &nbsp;\
-              <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
-            {{/unless}}\
-          </span>')
-      }),
-      getCellContent: function(row) {
-        var attempt = row.get('pivotAttempt');
-        var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
-        if(logFile) logFile += "/syslog_" + attempt.get('id');
-        return logFile;
-      }
-    });
+  }.property(),
 
-    return [idCol, attemptNoCol, startTimeCol, endTimeCol, durationCol, statusCol, containerCol, nodeCol, actionsCol, logs];
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.taskAttempt') || []
+    );
   }.property(),
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/controllers/vertex_tasks_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/vertex_tasks_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/vertex_tasks_controller.js
index e963225..4077449 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/vertex_tasks_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/vertex_tasks_controller.js
@@ -16,10 +16,12 @@
  * limitations under the License.
  */
 
-App.VertexTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, {
+App.VertexTasksController = Em.ObjectController.extend(App.PaginatedContentMixin, App.ColumnSelectorMixin, {
   // Required by the PaginatedContentMixin
   childEntityType: 'task',
 
+  controllerName: 'VertexTasksController',
+
   needs: 'vertex',
 
   queryParams: {
@@ -81,96 +83,105 @@ App.VertexTasksController = Em.ObjectController.extend(App.PaginatedContentMixin
     }
   },
 
-  columns: function() {
-    var idCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Task ID',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
-      }),
-      contentPath: 'id',
-    });
-
-    var startTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Start Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('startTime'));
-      }
-    });
-
-    var endTimeCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'End Time',
-      getCellContent: function(row) {
-        return App.Helpers.date.dateFormat(row.get('endTime'));
-      }
-    });
-
-    var durationCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'duration',
-      getCellContent: function(row) {
-        var st = row.get('startTime');
-        var et = row.get('endTime');
-        if (st && et) {
-          return App.Helpers.date.durationSummary(st, et);
+  defaultColumnConfigs: function() {
+    return [
+      {
+        id: 'taskId',
+        headerCellName: 'Task ID',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+        }),
+        contentPath: 'id',
+      },
+      {
+        id: 'startTime',
+        headerCellName: 'Start Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('startTime'));
+        }
+      },
+      {
+        id: 'endTime',
+        headerCellName: 'End Time',
+        getCellContent: function(row) {
+          return App.Helpers.date.dateFormat(row.get('endTime'));
+        }
+      },
+      {
+        id: 'duration',
+        headerCellName: 'duration',
+        getCellContent: function(row) {
+          var st = row.get('startTime');
+          var et = row.get('endTime');
+          if (st && et) {
+            return App.Helpers.date.durationSummary(st, et);
+          }
+        }
+      },
+      {
+        id: 'status',
+        headerCellName: 'Status',
+        filterID: 'status_filter',
+        filterType: 'dropdown',
+        dropdownValues: App.Helpers.misc.taskStatusUIOptions,
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">&nbsp;\
+            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
+            &nbsp;&nbsp;{{view.cellContent.status}}</span>')
+        }),
+        getCellContent: function(row) {
+          return {
+            status: row.get('status'),
+            statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+          };
+        }
+      },
+      {
+        id: 'actions',
+        headerCellName: 'Actions',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+            {{#link-to "task.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
+            {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}}\
+            </span>'
+            )
+        }),
+        contentPath: 'id'
+      },
+      {
+        id: 'logs',
+        headerCellName: 'Logs',
+        tableCellViewClass: Em.Table.TableCell.extend({
+          template: Em.Handlebars.compile(
+            '<span class="ember-table-content">\
+              {{#unless view.cellContent}}\
+                Not Available\
+              {{else}}\
+                <a href="//{{unbound view.cellContent}}">View</a>\
+                &nbsp;\
+                <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
+              {{/unless}}\
+            </span>')
+        }),
+        getCellContent: function(row) {
+          var attempt = row.get('pivotAttempt');
+          var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
+          if(logFile) logFile += "/syslog_" + attempt.get('id');
+          return logFile;
         }
       }
-    });
-
-    var statusCol = App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin,{
-      headerCellName: 'Status',
-      filterID: 'status_filter',
-      filterType: 'dropdown',
-      dropdownValues: App.Helpers.misc.taskStatusUIOptions,
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">&nbsp;\
-          <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-          &nbsp;&nbsp;{{view.cellContent.status}}</span>')
-      }),
-      getCellContent: function(row) {
-        return {
-          status: row.get('status'),
-          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
-        };
-      }
-    });
-
-    var actionsCol = App.ExTable.ColumnDefinition.create({
-      headerCellName: 'Actions',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-          {{#link-to "task.counters" view.cellContent}}counters{{/link-to}}&nbsp;\
-          {{#link-to "task.attempts" view.cellContent}}attempts{{/link-to}}\
-          </span>'
-          )
-      }),
-      contentPath: 'id'
-    });
+    ];
 
-    var logs = App.ExTable.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Logs',
-      tableCellViewClass: Em.Table.TableCell.extend({
-        template: Em.Handlebars.compile(
-          '<span class="ember-table-content">\
-            {{#unless view.cellContent}}\
-              Not Available\
-            {{else}}\
-              <a href="//{{unbound view.cellContent}}">View</a>\
-              &nbsp;\
-              <a href="//{{unbound view.cellContent}}?start=0" download target="_blank" type="application/octet-stream">Download</a>\
-            {{/unless}}\
-          </span>')
-      }),
-      getCellContent: function(row) {
-        var attempt = row.get('pivotAttempt');
-        var logFile = attempt && (attempt.get('inProgressLog') || attempt.get('completedLog'));
-        if(logFile) logFile += "/syslog_" + attempt.get('id');
-        return logFile;
-      }
-    });
+  }.property(),
 
-    return [idCol, startTimeCol, endTimeCol, durationCol, statusCol, actionsCol, logs];
+  columnConfigs: function() {
+    return this.get('defaultColumnConfigs').concat(
+      App.Helpers.misc.normalizeCounterConfigs(App.get('Configs.table.commonColumns.counters')),
+      App.get('Configs.table.entitieSpecificColumns.task') || []
+    );
   }.property(),
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/helpers/dialogs.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/dialogs.js b/tez-ui/src/main/webapp/app/scripts/helpers/dialogs.js
new file mode 100644
index 0000000..2e713a1
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/helpers/dialogs.js
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+App.Dialogs = Em.Namespace.create({
+  /*
+   * Displays a dialog with a multiselector based on the provided data.
+   * - Helper looks for id & displayText in listItems.
+   * @param listItems Array of all items
+   * @param selectedItems
+   * @param keyHash Defines the key that helper must use to get value from item.
+   * @return Returns a promoise that would be fulfilled when Ok is pressed
+   */
+  displayMultiSelect: function (listItems, selectedItems, keyHash) {
+    /*
+     * Looks in an object for properties.
+     */
+    function getProperty(object, propertyName) {
+      var propertyName = (keyHash && keyHash[propertyName]) || propertyName;
+      return object[propertyName] || (object.get && object.get(propertyName));
+    }
+
+    var container = $( "<div/>" ),
+        listHTML = "";
+
+    listItems.forEach(function (item) {
+      var id = getProperty(item, 'id'),
+          displayText = getProperty(item, 'displayText');
+
+      listHTML += '<li><input id=%@ type="checkbox" %@ /> %@</li>'.fmt(
+        id,
+        selectedItems[id] ? 'checked' : '',
+        displayText
+      );
+    });
+
+    container.append('<ol class="selectable"> %@ </ol>'.fmt(listHTML));
+    $('#dialogContainer').append(container);
+
+    return new Em.RSVP.Promise(function (resolve, reject) {
+      container.dialog({
+        modal: true,
+        title: "Select Items",
+        width: 350,
+        height: 500,
+        resizable: false,
+        buttons: {
+          Ok: function() {
+            var visibleColumnIds = {};
+
+            container.find('input:checked').each(function(index, checkbox){
+              visibleColumnIds[checkbox.id] = true;
+            });
+            resolve(visibleColumnIds);
+
+            $( this ).dialog("close");
+            container.remove();
+          }
+        }
+      });
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/helpers/misc.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js
index aebde79..976dec3 100644
--- a/tez-ui/src/main/webapp/app/scripts/helpers/misc.js
+++ b/tez-ui/src/main/webapp/app/scripts/helpers/misc.js
@@ -74,6 +74,60 @@ App.Helpers.misc = {
     return $.inArray(status, ['RUNNING', 'SUCCEEDED', 'FAILED', 'KILLED']) != -1;
   },
 
+  /*
+   * Normalizes counter style configurations
+   * @param counterConfigs Array
+   * @return Normalized configurations
+   */
+  normalizeCounterConfigs: function (counterConfigs) {
+    return counterConfigs.map(function (configuration) {
+      configuration.id = '%@/%@'.fmt(configuration.groupId, configuration.counterId),
+      configuration.getCellContent = App.Helpers.misc.getCounterCellContent;
+      return configuration;
+    });
+  },
+
+  /*
+   * Creates column definitions form configuration object array
+   * @param columnConfigs Array
+   * @return columnDefinitions Array
+   */
+  createColumnsFromConfigs: function (columnConfigs) {
+    return columnConfigs.map(function (columnConfig) {
+      if(columnConfig.getCellContentHelper) {
+        columnConfig.getCellContent = App.Helpers.get(columnConfig.getCellContentHelper);
+      }
+      columnConfig.minWidth = columnConfig.minWidth || 150;
+
+      return columnConfig.filterID ?
+          App.ExTable.ColumnDefinition.createWithMixins(App.ExTable.FilterColumnMixin, columnConfig) :
+          App.ExTable.ColumnDefinition.create(columnConfig);
+    });
+  },
+
+  /*
+   * Returns a counter value from for a row
+   * @param row
+   * @return value
+   */
+  getCounterCellContent: function (row) {
+    var contentPath = this.id.split('/'),
+        group = contentPath[0],
+        counter = contentPath[1],
+        id = row.get('id'),
+        value = 'Not Available';
+
+    try{
+      value = row.get('counterGroups').
+          findBy('id', '%@/%@'.fmt(id, group)).
+          get('counters').
+          findBy('id', '%@/%@/%@'.fmt(id, group, counter)).
+          get('value');
+    }catch(e){}
+
+    return value;
+  },
+
   dagStatusUIOptions: [
     { label: 'All', id: null },
     { label: 'Submitted', id: 'SUBMITTED' },

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/mixins/column-selector-mixin.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/mixins/column-selector-mixin.js b/tez-ui/src/main/webapp/app/scripts/mixins/column-selector-mixin.js
new file mode 100644
index 0000000..5a190ab
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/mixins/column-selector-mixin.js
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+App.ColumnSelectorMixin = Em.Mixin.create({
+
+  name: 'PaginatedContentMixin',
+
+  _storeKey: '',
+  visibleColumnIds: {},
+
+  init: function(){
+    var visibleColumnIds;
+
+    this._storeKey = this.controllerName + ':visibleColumnIds';
+    try {
+      visibleColumnIds = JSON.parse(localStorage.getItem(this._storeKey));
+    }catch(e){}
+
+    if(!visibleColumnIds) {
+      visibleColumnIds = {};
+
+      this.get('defaultColumnConfigs').forEach(function (config) {
+        visibleColumnIds[config.id] = true;
+      });
+    }
+
+    this._super();
+    this.set('visibleColumnIds', visibleColumnIds);
+  },
+
+  columns: function() {
+    var visibleColumnConfigs = this.get('columnConfigs').filter(function (column) {
+      return this.visibleColumnIds[column.id];
+    }, this);
+
+    return App.Helpers.misc.createColumnsFromConfigs(visibleColumnConfigs);
+  }.property('visibleColumnIds'),
+
+  actions: {
+    selectColumns: function () {
+      var that = this;
+
+      App.Dialogs.displayMultiSelect(this.get('columnConfigs'), this.visibleColumnIds, {
+        displayText: 'headerCellName'
+      }).then(function (data) {
+        try {
+          localStorage.setItem(that._storeKey , JSON.stringify(data));
+        }catch(e){}
+        that.set('visibleColumnIds', data);
+      });
+    }
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
index 6463bac..841e188 100644
--- a/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
+++ b/tez-ui/src/main/webapp/app/scripts/models/TimelineRestAdapter.js
@@ -24,12 +24,10 @@ var typeToPathMap = {
 };
 
 App.TimelineRESTAdapter = DS.RESTAdapter.extend({
-	namespace: 'ws/v1/timeline',
-
+	namespace: App.Configs.restNamespace.timeline,
 	pathForType: function(type) {
 		return typeToPathMap[type];
-	},
-
+	}
 });
 
 App.TimelineSerializer = DS.RESTSerializer.extend({
@@ -224,7 +222,7 @@ var timelineJsonToTaskMap = {
   successfulAttemptId: 'otherinfo.successfulAttemptId',
   attempts: 'relatedentities.TEZ_TASK_ATTEMPT_ID',
   dagID: 'primaryfilters.TEZ_DAG_ID.0',
-  numAttempts: 'relatedentities'
+  numAttempts: 'relatedentities.TEZ_TASK_ATTEMPT_ID.length'
 };
 
 App.TaskSerializer = App.TimelineSerializer.extend({
@@ -274,7 +272,7 @@ var timelineJsonToVertexMap = {
   id: 'entity',
   name: 'otherinfo.vertexName',
   dagID: 'primaryfilters.TEZ_DAG_ID.0',
-  processorClassName: 'otherinfo.stats.processorClassName',
+  processorClassName: 'processorClassName',
   counterGroups: 'counterGroups',
   inputs: 'inputs',
 
@@ -305,11 +303,14 @@ var timelineJsonToVertexMap = {
 
 App.VertexSerializer = App.TimelineSerializer.extend({
   _normalizeSingleVertexPayload: function(vertex) {
-    var normalizedCounterGroupData = this.normalizeCounterGroupsHelper('vertex', vertex.entity, 
-        vertex);
-        vertex.counterGroups = normalizedCounterGroupData.counterGroupsIDs,
-        vertexInputs = [],
-        inputIds = [];
+    var normalizedCounterGroupData = this.normalizeCounterGroupsHelper('vertex', vertex.entity,
+        vertex),
+    processorClassName = Ember.get(vertex, 'otherinfo.processorClassName'),
+    vertexInputs = [],
+    inputIds = [];
+
+    vertex.processorClassName = processorClassName.substr(processorClassName.lastIndexOf('.') + 1),
+    vertex.counterGroups = normalizedCounterGroupData.counterGroupsIDs;
 
     delete vertex.otherinfo.counters;
 

http://git-wip-us.apache.org/repos/asf/tez/blob/3a0b70c1/tez-ui/src/main/webapp/app/styles/main.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/main.less b/tez-ui/src/main/webapp/app/styles/main.less
index 3823d47..00222e3 100644
--- a/tez-ui/src/main/webapp/app/styles/main.less
+++ b/tez-ui/src/main/webapp/app/styles/main.less
@@ -69,8 +69,7 @@
 /* Navigation */
 .page-nav-link {
 	.fa;
-	//TODO: change this and remove below .fa-3x;
-	font-size: 2.5em;
+	.fa-2x;
 	cursor: pointer;
 	color: black;
 
@@ -143,6 +142,19 @@
 	}
 }
 
+.fa-action {
+	.fa;
+	.fa-2x;
+	cursor: pointer;
+	color: black;
+}
+
+.left-divider{
+	padding-left: 5px;
+	border-left: 1px solid lightgrey;
+	margin-left: 5px;
+}
+
 div.indent {
 	margin: 0px 0px 0px 20px;
 	i {


Mime
View raw message