ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dbhowm...@apache.org
Subject [11/21] ambari git commit: AMBARI-16963: JDBC implementation of hive view. (dipayanb)
Date Tue, 31 May 2016 19:45:32 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index.js
new file mode 100644
index 0000000..9584508
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index.js
@@ -0,0 +1,731 @@
+/** * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.Controller.extend({
+  jobService: Ember.inject.service(constants.namingConventions.job),
+  jobProgressService: Ember.inject.service(constants.namingConventions.jobProgress),
+  databaseService: Ember.inject.service(constants.namingConventions.database),
+  notifyService: Ember.inject.service(constants.namingConventions.notify),
+  session: Ember.inject.service(constants.namingConventions.session),
+  settingsService: Ember.inject.service(constants.namingConventions.settings),
+
+  openQueries   : Ember.inject.controller(constants.namingConventions.openQueries),
+  udfs          : Ember.inject.controller(constants.namingConventions.udfs),
+  logs          : Ember.inject.controller(constants.namingConventions.jobLogs),
+  results       : Ember.inject.controller(constants.namingConventions.jobResults),
+  explain       : Ember.inject.controller(constants.namingConventions.jobExplain),
+  settings      : Ember.inject.controller(constants.namingConventions.settings),
+  visualExplain : Ember.inject.controller(constants.namingConventions.visualExplain),
+  tezUI         : Ember.inject.controller(constants.namingConventions.tezUI),
+
+  selectedDatabase: Ember.computed.alias('databaseService.selectedDatabase'),
+  isDatabaseExplorerVisible: true,
+  canKillSession: Ember.computed.and('model.sessionTag', 'model.sessionActive'),
+
+  queryProcessTabs: [
+    Ember.Object.create({
+      name: Ember.I18n.t('menus.logs'),
+      path: constants.namingConventions.subroutes.jobLogs
+    }),
+    Ember.Object.create({
+      name: Ember.I18n.t('menus.results'),
+      path: constants.namingConventions.subroutes.jobResults
+    }),
+    Ember.Object.create({
+      name: Ember.I18n.t('menus.explain'),
+      path: constants.namingConventions.subroutes.jobExplain
+    })
+  ],
+
+  queryPanelActions: [
+    Ember.Object.create({
+      icon: 'fa-expand',
+      action: 'toggleDatabaseExplorerVisibility',
+      tooltip: Ember.I18n.t('tooltips.expand')
+    })
+  ],
+
+  init: function () {
+    this._super();
+
+    // initialize queryParams with an empty array
+    this.set('queryParams', Ember.ArrayProxy.create({ content: Ember.A([]) }));
+
+    this.set('queryProcessTabs', Ember.ArrayProxy.create({ content: Ember.A([
+      Ember.Object.create({
+        name: Ember.I18n.t('menus.logs'),
+        path: constants.namingConventions.subroutes.jobLogs
+      }),
+      Ember.Object.create({
+        name: Ember.I18n.t('menus.results'),
+        path: constants.namingConventions.subroutes.jobResults
+      }),
+      Ember.Object.create({
+        name: Ember.I18n.t('menus.explain'),
+        path: constants.namingConventions.subroutes.jobExplain
+      })
+    ])}));
+
+    this.set('queryPanelActions', Ember.ArrayProxy.create({ content: Ember.A([
+      Ember.Object.create({
+        icon: 'fa-expand',
+        action: 'toggleDatabaseExplorerVisibility',
+        tooltip: Ember.I18n.t('tooltips.expand')
+      })
+    ])}));
+  },
+
+  canExecute: function () {
+    var isModelRunning = this.get('model.isRunning');
+    var hasParams = this.get('queryParams.length');
+
+    if (isModelRunning) {
+      return false;
+    }
+
+    if (hasParams) {
+      // all param have values?
+      return this.get('queryParams').every(function (param) { return param.value; });
+    }
+
+    return true;
+  }.property('model.isRunning', 'queryParams.@each.value'),
+
+  currentQueryObserver: function () {
+    var query = this.get('openQueries.currentQuery.fileContent'),
+        param,
+        updatedParams = [],
+        currentParams = this.get('queryParams'),
+        paramRegExp = /\$\w+/ig,
+        paramNames = query.match(paramRegExp) || [];
+
+    paramNames = paramNames.uniq();
+
+    paramNames.forEach(function (name) {
+      param = currentParams.findBy('name', name);
+      if (param) {
+        updatedParams.push(param);
+      } else {
+        updatedParams.push({ name: name, value: "" });
+      }
+    });
+
+    currentParams.setObjects(updatedParams);
+
+    this.set('visualExplain.shouldChangeGraph', true);
+  }.observes('openQueries.currentQuery.fileContent'),
+
+  _executeQuery: function (referrer, shouldExplain, shouldGetVisualExplain) {
+    var queryId,
+        query,
+        finalQuery,
+        job,
+        defer = Ember.RSVP.defer(),
+        originalModel = this.get('model');
+
+    job = this.store.createRecord(constants.namingConventions.job, {
+      title: originalModel.get('title'),
+      sessionTag: originalModel.get('sessionTag'),
+      dataBase: this.get('selectedDatabase.name'),
+      referrer: referrer
+    });
+
+    if (!shouldGetVisualExplain) {
+      originalModel.set('isRunning', true);
+    }
+
+     //if it's a saved query / history entry set the queryId
+    if (!originalModel.get('isNew')) {
+      queryId = originalModel.get('constructor.typeKey') === constants.namingConventions.job ?
+                originalModel.get('queryId') :
+                originalModel.get('id');
+
+      job.set('queryId', queryId);
+    }
+
+    query = this.get('openQueries').getQueryForModel(originalModel);
+
+    query = this.buildQuery(query, shouldExplain, shouldGetVisualExplain);
+
+
+    // Condition for no query.
+    if(query === ';') {
+      originalModel.set('isEmptyQuery', true);
+      originalModel.set('isRunning', false);
+      defer.reject({
+        message: 'No query to process.'
+      });
+      return defer.promise;
+    }
+
+    // for now we won't support multiple queries
+    // buildQuery will return false it multiple queries
+    // are selected
+    if (!query) {
+      originalModel.set('isRunning', false);
+      defer.reject({
+        message: 'Running multiple queries is not supported.'
+      });
+
+      return defer.promise;
+    }
+
+    finalQuery = query;
+    finalQuery = this.bindQueryParams(finalQuery);
+    finalQuery = this.prependGlobalSettings(finalQuery, job);
+
+    job.set('forcedContent', finalQuery);
+
+    if (shouldGetVisualExplain) {
+      return this.getVisualExplainJson(job, originalModel);
+    }
+
+    return this.createJob(job, originalModel);
+  },
+
+  getVisualExplainJson: function (job, originalModel) {
+    var self = this;
+    var defer = Ember.RSVP.defer();
+
+    job.save().then(function () {
+      self.get('results').getResultsJson(job).then(function (json) {
+        defer.resolve(json);
+      }, function (err) {
+        defer.reject(err);
+      });
+    }, function (err) {
+      defer.reject(err);
+    });
+
+    return defer.promise;
+  },
+
+  createJob: function (job, originalModel) {
+    var defer = Ember.RSVP.defer(),
+        self = this,
+        openQueries = this.get('openQueries');
+
+    var handleError = function (err) {
+      self.set('jobSaveSucceeded');
+      originalModel.set('isRunning', undefined);
+      defer.reject(err);
+
+      if(err.status == 401) {
+          self.send('passwordLDAP', job, originalModel);
+      }
+
+    };
+
+    job.save().then(function () {
+      //convert tab for current model since the execution will create a new job, and navigate to the new job route.
+      openQueries.convertTabToJob(originalModel, job).then(function () {
+        self.get('jobProgressService').setupProgress(job);
+        self.set('jobSaveSucceeded', true);
+
+        //reset flag on the original model
+        originalModel.set('isRunning', undefined);
+
+        defer.resolve(job);
+      }, function (err) {
+        handleError(err);
+      });
+    }, function (err) {
+      handleError(err);
+    });
+
+    return defer.promise;
+  },
+
+  prependGlobalSettings: function (query, job) {
+    var jobGlobalSettings = job.get('globalSettings');
+    var currentGlobalSettings = this.get('settingsService').getSettings();
+
+    // remove old globals
+    if (jobGlobalSettings) {
+      query.replace(jobGlobalSettings, '');
+    }
+
+    job.set('globalSettings', currentGlobalSettings);
+    query = currentGlobalSettings + query;
+
+    return query;
+  },
+
+  buildQuery: function (query, shouldExplain, shouldGetVisualExplain) {
+    var selections = this.get('openQueries.highlightedText'),
+        isQuerySelected = selections && selections[0] !== "",
+        queryContent = query ? query.get('fileContent') : '',
+        queryComponents = this.extractComponents(queryContent),
+        finalQuery = '',
+        queries = null;
+
+    if (isQuerySelected) {
+      queryComponents.queryString = selections.join('');
+    }
+
+    queries = queryComponents.queryString.split(';');
+    queries = queries.filter(Boolean);
+
+    var queriesLength = queries.length;
+
+    queries = queries.map(function (q, index) {
+      var newQuery = q.replace(/explain formatted|explain/gi, '');
+      return newQuery;
+    });
+
+    var lastQuery = queries[queriesLength - 1];
+
+    if(!Ember.isNone(lastQuery) && shouldExplain) {
+      if (shouldGetVisualExplain) {
+        lastQuery = constants.namingConventions.explainFormattedPrefix + lastQuery;
+      } else {
+        lastQuery = constants.namingConventions.explainPrefix + lastQuery;
+      }
+      queries[queriesLength - 1] = lastQuery;
+    }
+
+    if (queryComponents.files.length) {
+      finalQuery += queryComponents.files.join("\n") + "\n\n";
+    }
+
+    if (queryComponents.udfs.length) {
+      finalQuery += queryComponents.udfs.join("\n") + "\n\n";
+    }
+
+    finalQuery += queries.join(";") + ";";
+    return finalQuery.trim();
+  },
+
+  bindQueryParams: function (query) {
+    var params = this.get('queryParams');
+
+    if (!params.get('length')) {
+      return query;
+    }
+
+    params.forEach(function (param) {
+      query = query.split(param.name).join(param.value);
+    });
+
+    return query;
+  },
+
+  displayJobTabs: function () {
+    return this.get('content.constructor.typeKey') === constants.namingConventions.job &&
+           utils.isInteger(this.get('content.id')) &&
+           this.get('jobSaveSucceeded');
+  }.property('content', 'jobSaveSucceeded'),
+
+  databasesOrModelChanged: function () {
+    this.get('databaseService').setDatabaseByName(this.get('content.dataBase'));
+  }.observes('databaseService.databases', 'content'),
+
+  selectedDatabaseChanged: function () {
+    this.set('content.dataBase', this.get('selectedDatabase.name'));
+  }.observes('selectedDatabase'),
+
+  modelChanged: function () {
+    var self = this;
+    var content = this.get('content');
+    var openQueries = this.get('openQueries');
+
+    this.set('jobSaveSucceeded', true);
+
+    //update open queries list when current query model changes
+    openQueries.update(content).then(function (isExplainedQuery) {
+      var newId = content.get('id');
+      var tab = openQueries.getTabForModel(content);
+
+      //if not an ATS job
+      if (content.get('constructor.typeKey') === constants.namingConventions.job && utils.isInteger(newId)) {
+        self.get('queryProcessTabs').forEach(function (queryTab) {
+          queryTab.set('id', newId);
+        });
+
+        if (isExplainedQuery) {
+          self.set('explain.content', content);
+        } else {
+          self.set('logs.content', content);
+          self.set('results.content', content);
+        }
+
+        self.setExplainVisibility(isExplainedQuery);
+
+        self.transitionToRoute(tab.get('subroute'));
+      }
+    });
+  }.observes('content'),
+
+  csvUrl: function () {
+    if (this.get('content.constructor.typeKey') !== constants.namingConventions.job) {
+      return;
+    }
+
+    if (!utils.insensitiveCompare(this.get('content.status'), constants.statuses.succeeded)) {
+      return;
+    }
+
+    var url = this.container.lookup('adapter:application').buildURL();
+    url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id');
+    url += '/results/csv';
+
+    return url;
+  }.property('content'),
+
+  downloadMenu: function () {
+    var items = [];
+    var tabs = this.get('queryProcessTabs');
+    var isResultsTabVisible = tabs.findBy('path', constants.namingConventions.subroutes.jobResults).get('visible');
+
+    if (utils.insensitiveCompare(this.get('content.status'), constants.statuses.succeeded) && isResultsTabVisible) {
+      items.push({
+        title: Ember.I18n.t('buttons.saveHdfs'),
+        action: 'saveToHDFS'
+      });
+
+      if (this.get('csvUrl')) {
+        items.push(
+          Ember.Object.create({
+            title: Ember.I18n.t('buttons.saveCsv'),
+            action: 'downloadAsCSV'
+          })
+        );
+      }
+    }
+
+    return items.length ? items : null;
+  }.property('content.status', 'queryProcessTabs.@each.visible'),
+
+  extractComponents: function (queryString) {
+    var components = {};
+
+    var udfRegEx = new RegExp("(" + constants.namingConventions.udfInsertPrefix + ").+", "ig");
+    var fileRegEx = new RegExp("(" + constants.namingConventions.fileInsertPrefix + ").+", "ig");
+
+    components.udfs         = queryString.match(udfRegEx) || [];
+    components.files        = queryString.match(fileRegEx) || [];
+    components.queryString  = queryString.replace(udfRegEx, "").replace(fileRegEx, "").trim();
+
+    return components;
+  },
+
+  saveToHDFS: function (path) {
+    var job = this.get('content');
+
+    if (!utils.insensitiveCompare(job.get('status'), constants.statuses.succeeded)) {
+      return;
+    }
+
+    var self = this;
+
+    var file = path + ".csv";
+    var url = this.container.lookup('adapter:application').buildURL();
+    url +=  "/jobs/" + job.get('id') + "/results/csv/saveToHDFS";
+
+    Ember.$.getJSON(url, {
+        commence: true,
+        file: file
+    }).then(function (response) {
+      self.pollSaveToHDFS(response);
+    }, function (error) {
+      self.get('notifyService').error(error);
+    });
+  },
+
+  pollSaveToHDFS: function (data) {
+    var self = this;
+    var url = this.container.lookup('adapter:application').buildURL();
+    url += "/jobs/" + data.jobId + "/results/csv/saveToHDFS";
+
+    Ember.run.later(function () {
+      Ember.$.getJSON(url).then(function (response) {
+        if (!utils.insensitiveCompare(response.status, constants.results.statuses.terminated)) {
+          self.pollSaveToHDFS(response);
+        } else {
+          self.set('content.isRunning', false);
+        }
+      }, function (error) {
+        self.get('notifyService').error(error);
+      });
+    }, 2000);
+  },
+
+  setExplainVisibility: function (show) {
+    var tabs = this.get('queryProcessTabs');
+
+    tabs.findBy('path', constants.namingConventions.subroutes.jobExplain).set('visible', show);
+    tabs.findBy('path', constants.namingConventions.subroutes.jobLogs).set('visible', !show);
+    tabs.findBy('path', constants.namingConventions.subroutes.jobResults).set('visible', !show);
+  },
+
+  queryProcessTitle: function () {
+    return Ember.I18n.t('titles.query.process') + ' (' + Ember.I18n.t('titles.query.status') + this.get('content.status') + ')';
+  }.property('content.status'),
+
+  updateSessionStatus: function() {
+    this.get('session').updateSessionStatus(this.get('model'));
+  }.observes('model', 'model.status'),
+
+  actions: {
+    passwordLDAP: function(){
+      var job = arguments[0],
+            originalModel = arguments[1],
+            self = this,
+            defer = Ember.RSVP.defer();
+
+        this.send('openModal', 'modal-save', {
+          heading: "modals.authenticationLDAP.heading",
+          text:"",
+          type: "password",
+          defer: defer
+        });
+
+        defer.promise.then(function (text) {
+            // make a post call with the given ldap password.
+            var password = text;
+            var pathName = window.location.pathname;
+            var pathNameArray = pathName.split("/");
+            var hiveViewVersion = pathNameArray[3];
+            var hiveViewName = pathNameArray[4];
+            var ldapAuthURL = "/api/v1/views/HIVE/versions/"+ hiveViewVersion + "/instances/" + hiveViewName + "/jobs/auth";
+
+            $.ajax({
+                url: ldapAuthURL,
+                type: 'post',
+                headers: {'X-Requested-With': 'XMLHttpRequest', 'X-Requested-By': 'ambari'},
+                contentType: 'application/json',
+                data: JSON.stringify({ "password" : password}),
+                success: function( data, textStatus, jQxhr ){
+
+                  self.get('databaseService').getDatabases().then(function (databases) {
+                    var selectedDatabase = self.get('databaseService.selectedDatabase.name') || 'default';
+                    self.get('databaseService').setDatabaseByName( selectedDatabase);
+                    return self.send('executeQuery', 'job', self.get('openQueries.currentQuery.fileContent') );
+                  }).catch(function (error) {
+                    self.get('notifyService').error( "Error in accessing databases." );
+                  });
+
+                },
+                error: function( jqXhr, textStatus, errorThrown ){
+                    console.log( "LDAP fail: " + errorThrown );
+                    self.get('notifyService').error( "Wrong Credentials." );
+                }
+            });
+
+          });
+    },
+
+    stopCurrentJob: function () {
+      this.get('jobService').stopJob(this.get('model'));
+    },
+
+    saveToHDFS: function () {
+      var self = this,
+          defer = Ember.RSVP.defer();
+
+      this.send('openModal', 'modal-save', {
+        heading: "modals.download.hdfs",
+        text: this.get('content.title') + '_' + this.get('content.id'),
+        defer: defer
+      });
+
+      defer.promise.then(function (text) {
+        self.set('content.isRunning', true);
+        self.saveToHDFS(text);
+      });
+    },
+
+    downloadAsCSV: function () {
+      var self = this,
+          defer = Ember.RSVP.defer();
+
+      this.send('openModal', 'modal-save', {
+        heading: "modals.download.csv",
+        text: this.get('content.title'),
+        defer: defer
+      });
+
+      defer.promise.then(function (text) {
+        // download file ...
+        var urlString = "%@/?fileName=%@.csv";
+        var url = self.get('csvUrl');
+        url = urlString.fmt(url, text);
+        window.open(url);
+      });
+    },
+
+    insertUdf: function (item) {
+      var query = this.get('openQueries.currentQuery');
+
+      var queryString = query.get('fileContent');
+
+      var newUdf = constants.namingConventions.udfInsertPrefix + item.get('name') + " as '" + item.get('classname') + "';";
+      var newFileResource = item.get('fileResource.path');
+
+      if (item.get('fileResource.path')) {
+        newFileResource = constants.namingConventions.fileInsertPrefix + item.get('fileResource.path') + ";";
+      }
+
+      var components = this.extractComponents(queryString);
+
+      if (!components.files.contains(newFileResource) && newFileResource) {
+        components.files.push(newFileResource);
+      }
+
+      if (!components.udfs.contains(newUdf)) {
+        components.udfs.push(newUdf);
+      }
+
+      var updatedContent = components.files.join("\n") + "\n\n";
+      updatedContent += components.udfs.join("\n") + "\n\n";
+      updatedContent += components.queryString;
+
+      query.set('fileContent', updatedContent);
+    },
+
+    addQuery: (function () {
+      var idCounter = 0;
+
+      return function (workSheetName) {
+        var model = this.store.createRecord(constants.namingConventions.savedQuery, {
+          dataBase: this.get('selectedDatabase.name'),
+          title: workSheetName ? workSheetName : Ember.I18n.t('titles.query.tab'),
+          queryFile: '',
+          id: 'fixture_' + idCounter
+        });
+
+        if (idCounter && !workSheetName) {
+          model.set('title', model.get('title') + ' (' + idCounter + ')');
+        }
+
+        idCounter++;
+
+        this.transitionToRoute(constants.namingConventions.subroutes.savedQuery, model);
+      };
+    }()),
+
+    saveQuery: function () {
+      //case 1. Save a new query from a new query tab -> route changes to new id
+      //case 2. Save a new query from an existing query tab -> route changes to new id
+      //case 3. Save a new query from a job tab -> route doesn't change
+      //case 4. Update an existing query tab. -> route doesn't change
+
+      var self = this,
+          defer = Ember.RSVP.defer(),
+          currentQuery = this.get('openQueries.currentQuery');
+
+      this.set('model.dataBase', this.get('selectedDatabase.name'));
+
+      this.send('openModal', 'modal-save-query', {
+        heading: 'modals.save.heading',
+        message: 'modals.save.overwrite',
+        text: this.get('content.title'),
+        content: this.get('content'),
+        defer: defer
+      });
+
+      defer.promise.then(function (result) {
+        // we need to update the original model
+        // because when this is executed
+        // it sets the title from the original model
+        self.set('model.title', result.get('text'));
+
+        if (result.get('overwrite')) {
+          self.get('openQueries').save(self.get('content'), null, true, result.get('text')).then(function () {
+            self.get('notifyService').success(Ember.I18n.t('alerts.success.query.update'));
+          });
+        } else {
+          self.get('openQueries').save(self.get('content'), null, false, result.get('text')).then(function (newId) {
+            self.get('notifyService').success(Ember.I18n.t('alerts.success.query.save'));
+
+            if (self.get('model.constructor.typeKey') !== constants.namingConventions.job) {
+              self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, newId);
+            }
+          });
+        }
+      });
+    },
+
+    executeQuery: function (referrer, query) {
+      var self = this;
+
+      var isExplainQuery = (self.get('openQueries.currentQuery.fileContent').toUpperCase().trim().indexOf(constants.namingConventions.explainPrefix) === 0);
+
+      if(isExplainQuery){
+        self.send('explainQuery');
+        return;
+      }
+
+      var subroute;
+
+      if (query) {
+        this.set('openQueries.currentQuery.fileContent', query);
+      }
+
+      referrer = referrer || constants.jobReferrer.job;
+
+      this._executeQuery(referrer).then(function (job) {
+        if (job.get('status') !== constants.statuses.succeeded) {
+          subroute = constants.namingConventions.subroutes.jobLogs;
+        } else {
+          subroute = constants.namingConventions.subroutes.jobResults;
+        }
+
+        self.get('openQueries').updateTabSubroute(job, subroute);
+        self.get('notifyService').success(Ember.I18n.t('alerts.success.query.execution'));
+        self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+      }, function (error) {
+        self.get('notifyService').error(error);
+      });
+    },
+
+    explainQuery: function () {
+      var self = this;
+
+      this._executeQuery(constants.jobReferrer.explain, true).then(function (job) {
+        self.get('openQueries').updateTabSubroute(job, constants.namingConventions.subroutes.jobExplain);
+
+        self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+      }, function (error) {
+        self.get('notifyService').error(error);
+      });
+    },
+
+    toggleDatabaseExplorerVisibility: function () {
+      this.toggleProperty('isDatabaseExplorerVisible');
+    },
+
+    killSession: function() {
+      var self = this;
+      var model = this.get('model');
+
+      this.get('session').killSession(model)
+        .catch(function (response) {
+          if ([200, 404].contains(response.status)) {
+            model.set('sessionActive', false);
+            self.notify.success(Ember.I18n.t('alerts.success.sessions.deleted'));
+          } else {
+            self.notify.error(response);
+          }
+        });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
new file mode 100644
index 0000000..20092e2
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
@@ -0,0 +1,134 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.ObjectController.extend({
+  cachedExplains: [],
+
+  clearCachedExplainSet: function (jobId) {
+    var existingJob = this.get('cachedExplains').findBy('id', jobId);
+
+    if (existingJob) {
+      this.set('cachedExplains', this.get('cachedExplains').without(existingJob));
+    }
+  },
+
+  initExplain: function () {
+    var cachedExplain;
+
+    cachedExplain = this.get('cachedExplains').findBy('id', this.get('content.id'));
+
+    if (cachedExplain) {
+      this.formatExplainResults(cachedExplain);
+    } else {
+      this.getExplain(true);
+    }
+  }.observes('content'),
+
+  getExplain: function (firstPage, rows) {
+    var self = this;
+    var url = this.container.lookup('adapter:application').buildURL();
+    url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results';
+
+    if (firstPage) {
+      url += '?first=true';
+    }
+
+    this.get('content').reload().then(function () {
+      Ember.$.getJSON(url).then(function (data) {
+        var explainSet;
+
+        //if rows from a previous page read exist, prepend them
+        if (rows) {
+          data.rows.unshiftObjects(rows);
+        }
+
+        if (!data.hasNext) {
+          explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({
+            id: self.get('content.id'),
+            explain: data
+          }));
+
+          self.set('content.explain', explainSet);
+
+          self.formatExplainResults(explainSet);
+        } else {
+          self.getExplain(false, data.rows);
+        }
+      });
+    })
+  },
+
+  formatExplainResults: function (explainSet) {
+    var formatted = [],
+        currentNode,
+        currentNodeWhitespace,
+        previousNode,
+        getLeadingWhitespacesCount = function (str) {
+          return str.replace(utils.regexes.whitespaces, '$1').length;
+        };
+
+    explainSet = explainSet
+                 .get('explain.rows')
+                 .map(function (row) {
+                    return row[0];
+                  })
+                 .filter(Boolean)
+                 .map(function (str) {
+                    return {
+                      text: str,
+                      parentNode: null,
+                      contents: []
+                    };
+                  });
+
+    for (var i = 0; i < explainSet.length; i++) {
+      currentNode = explainSet[i];
+      previousNode = explainSet[i-1];
+
+      if (i > 0) {
+        currentNodeWhitespace = getLeadingWhitespacesCount(currentNode.text);
+
+        if (currentNodeWhitespace > getLeadingWhitespacesCount(previousNode.text)) {
+          currentNode.parentNode = previousNode;
+          previousNode.contents.pushObject(currentNode);
+        } else {
+          for (var j = i - 1; j >= 0; j--) {
+            if (currentNodeWhitespace === getLeadingWhitespacesCount(explainSet[j].text)) {
+              if (currentNodeWhitespace > 0) {
+                currentNode.parentNode = explainSet[j].parentNode;
+                currentNode.parentNode.contents.pushObject(currentNode);
+              } else {
+                formatted.pushObject(currentNode);
+              }
+
+              break;
+            }
+          }
+        }
+      } else {
+        formatted.pushObject(currentNode);
+      }
+    }
+
+    this.set('formattedExplain', formatted);
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
new file mode 100644
index 0000000..43c7a7e
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
@@ -0,0 +1,108 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.ObjectController.extend({
+  fileService: Ember.inject.service(constants.namingConventions.file),
+  notifyService: Ember.inject.service(constants.namingConventions.notify),
+
+  needs: [ constants.namingConventions.queryTabs,
+           constants.namingConventions.index,
+           constants.namingConventions.openQueries ],
+
+  queryTabs: Ember.computed.alias('controllers.' + constants.namingConventions.queryTabs),
+  index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
+  openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+
+  reloadJobLogs: function (job) {
+    var self = this;
+    var handleError = function (error) {
+      job.set('isRunning', false);
+
+      self.get('notifyService').error(error);
+    };
+
+    job.reload().then(function () {
+      if (utils.insensitiveCompare(job.get('status'), constants.statuses.error) ||
+          utils.insensitiveCompare(job.get('status'), constants.statuses.failed)) {
+        handleError(job.get('statusMessage'));
+      }
+
+      self.get('fileService').reloadFile(job.get('logFile')).then(function (file) {
+        var fileContent = file.get('fileContent');
+        var stillRunning = self.isJobRunning(job);
+        var currentIndexModelId = self.get('index.model.id');
+        var currentActiveTab = self.get('queryTabs.activeTab.name');
+
+        if (fileContent) {
+          job.set('log', fileContent);
+        }
+
+        //if the current model is the same with the one displayed, continue reloading job
+        if (stillRunning) {
+          Ember.run.later(self, function () {
+            this.reloadJobLogs(job);
+          }, 10000);
+        } else if (!stillRunning) {
+          job.set('isRunning', undefined);
+          job.set('retrievingLogs', false);
+
+          if (utils.insensitiveCompare(job.get('status'), constants.statuses.succeeded)) {
+            self.get('openQueries').updateTabSubroute(job, constants.namingConventions.subroutes.jobResults);
+
+            if (job.get('id') === currentIndexModelId && currentActiveTab === constants.namingConventions.index) {
+              self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+            }
+          }
+        }
+
+      },function (err) {
+        handleError(err);
+      });
+    }, function (err) {
+      handleError(err);
+    });
+  },
+
+  isJobRunning: function (job) {
+    return utils.insensitiveCompare(job.get('status'),
+                                    constants.statuses.unknown,
+                                    constants.statuses.initialized,
+                                    constants.statuses.running,
+                                    constants.statuses.pending);
+  },
+
+  getLogs: function () {
+    var job = this.get('content');
+
+    if (this.isJobRunning(job)) {
+      if (!job.get('retrievingLogs')) {
+        job.set('retrievingLogs', true);
+        job.set('isRunning', true);
+        this.reloadJobLogs(job);
+      }
+    } else if (utils.insensitiveCompare(job.get('status'), constants.statuses.succeeded) && !job.get('dagId')) {
+      //if a job that never polled for logs is succeeded, jump straight to results tab.
+      this.get('openQueries').updateTabSubroute(job, constants.namingConventions.subroutes.jobResults);
+      this.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+    }
+  }.observes('content')
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
new file mode 100644
index 0000000..9e94b17
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
@@ -0,0 +1,238 @@
+
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.ObjectController.extend({
+  cachedResults: [],
+  formattedResults: [],
+
+  processResults: function () {
+    var results = this.get('results');
+    var filterValue = this.get('filterValue');
+    var columns;
+    var rows;
+    var filteredColumns;
+    var filteredRows;
+
+    if (!results) {
+      return;
+    }
+
+    columns = results.schema;
+    rows = results.rows;
+
+    if (!columns || !rows) {
+      return;
+    }
+
+    columns = columns.map(function (column) {
+      return {
+        name: column[0],
+        type: column[1],
+        index: column[2] - 1 //normalize index to 0 based
+      };
+    });
+
+    if (filterValue) {
+      filteredColumns = columns.filter(function (column) {
+        return utils.insensitiveContains(column.name, filterValue);
+      });
+
+      if (filteredColumns.length < columns.length) {
+        filteredRows = rows.map(function (row) {
+          var updatedRow = [];
+
+          updatedRow.pushObjects(row.filter(function (item, index) {
+            return this.findBy('index', index);
+          }, this));
+
+          return updatedRow;
+        }, filteredColumns);
+      } else {
+        filteredRows = rows;
+      }
+    } else {
+      filteredColumns = columns;
+      filteredRows = rows;
+    }
+
+    this.set('formattedResults', { columns: filteredColumns, rows: filteredRows });
+  }.observes('results', 'filterValue'),
+
+  keepAlive: function (job) {
+    Ember.run.later(this, function () {
+      var self = this;
+      var url = this.container.lookup('adapter:application').buildURL();
+      url += '/' + constants.namingConventions.jobs + '/' + job.get('id') + '/results/keepAlive';
+
+      var existingJob = self.cachedResults.findBy('id', job.get('id'));
+
+      if (existingJob) {
+        Ember.$.getJSON(url).fail(function (data) {
+          //backend issue, this will be split in done and fail callbacks once its fixed.
+          if (data.status === 404) {
+            existingJob.set('results', []);
+            self.set('error', data.responseJSON.message);
+          } else if (data.status === 200) {
+            self.keepAlive(job);
+          }
+        });
+      }
+    }, 1000 * 300);
+  },
+
+  clearCachedResultsSet: function (jobId) {
+    this.set('cachedResults', this.get('cachedResults').without(this.get('cachedResults').findBy('id', jobId)));
+  },
+
+  initResults: function () {
+    var existingJob;
+
+    if (!utils.insensitiveCompare(this.get('content.status'), constants.statuses.succeeded)) {
+      return;
+    }
+
+    existingJob = this.cachedResults.findBy('id', this.get('content.id'));
+
+    if (existingJob) {
+      this.set('results', existingJob.results.findBy('offset', existingJob.get('offset')));
+    } else {
+      this.send('getNextPage', true);
+    }
+  }.observes('content.status'),
+
+  disableNext: function () {
+    return !this.get('results.hasNext');
+  }.property('results'),
+
+  disablePrevious: function () {
+    return this.cachedResults.findBy('id', this.get('content.id')).results.indexOf(this.get('results')) <= 0;
+  }.property('results'),
+
+  getResultsJson: function (job) {
+    var defer = Ember.RSVP.defer();
+    var url = this.container.lookup('adapter:application').buildURL();
+    url += '/' + constants.namingConventions.jobs + '/' + job.get('id') + '/results?first=true';
+
+    Ember.$.getJSON(url).then(function (results) {
+      defer.resolve(JSON.parse(results.rows[0][0]));
+    }, function (err) {
+      defer.reject(err);
+    });
+
+    return defer.promise;
+  },
+
+  getResult : function(url){
+    var promise = new Ember.RSVP.Promise(function(resolve,reject){
+      var getData =  function(){
+        //console.log("getData called.");
+        Ember.$.getJSON(url).done(function(data){
+          console.log('results.js : getResult : got success data');
+          resolve(data);
+        }).fail(function(err){
+          if(err.status == 503 && err.getResponseHeader('Retry-After')){
+            var time = Number(err.getResponseHeader('Retry-After'));
+            console.log("results.js : getResult : got error : " + err.status + " with retry.");
+            Ember.run.later(this,
+            function(){
+              getData();
+            },time*1000);
+          }else{
+            console.log("results.js : getResult : rejected. ");
+            reject(err);
+          }
+        });
+      };
+      getData();
+    });
+
+    return promise;
+  },
+
+  actions: {
+    getNextPage: function (firstPage, job) {
+      var self = this;
+      var id = job ? job.get('id') : this.get('content.id');
+      var existingJob = this.cachedResults.findBy('id', id);
+      var resultsIndex;
+      var url = this.container.lookup('adapter:application').buildURL();
+      url += '/' + constants.namingConventions.jobs + '/' + id + '/results';
+
+      if (firstPage) {
+        url += '?first=true';
+      }
+
+      if (existingJob) {
+        resultsIndex = existingJob.results.indexOf(this.get('results'));
+
+        if (~resultsIndex && resultsIndex < existingJob.get('results.length') - 1) {
+          this.set('results', existingJob.results.objectAt(resultsIndex + 1));
+          return;
+        }
+      }
+
+      this.getResult(url)
+      .then(function (results) {
+        //console.log("inside then : ", results);
+        if (existingJob) {
+          existingJob.results.pushObject(results);
+          existingJob.set('offset', results.offset);
+        } else {
+          self.cachedResults.pushObject(Ember.Object.create({
+            id: id,
+            results: [ results ],
+            offset: results.offset
+          }));
+        }
+
+        //only set results if the method was called for the current model, not after a keepAlive request.
+        if (!job) {
+          self.set('results', results);
+        }
+
+        if (firstPage) {
+          self.keepAlive(job || self.get('content'));
+        }
+
+      }, function (err) {
+        self.set('error', err.responseText);
+      });
+    },
+
+    getPreviousPage: function () {
+      var existingJob,
+          resultsIndex;
+
+      existingJob = this.cachedResults.findBy('id', this.get('content.id'));
+      resultsIndex = existingJob.results.indexOf(this.get('results'));
+
+      if (resultsIndex > 0) {
+        this.set('results', existingJob.results.objectAt(resultsIndex - 1));
+      }
+    },
+
+    filterResults: function (value) {
+      this.set('filterValue', value);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
new file mode 100644
index 0000000..09f17c3
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
@@ -0,0 +1,58 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+  needs: [ constants.namingConventions.udfs ],
+
+  model: Ember.A(),
+
+  udfs: Ember.computed.alias('controllers.' + constants.namingConventions.udfs + '.udfs'),
+
+  updateUdfs: function () {
+    var self = this,
+        udfs = this.get('udfs'),
+        udfsWithoutFiles;
+
+    this.clear();
+
+    if (udfs && udfs.get('length')) {
+      udfs.getEach('fileResource.id').uniq().forEach(function (fileResourceId) {
+        if (fileResourceId) {
+          self.pushObject(Ember.Object.create({
+            file: udfs.findBy('fileResource.id', fileResourceId).get('fileResource'),
+            udfs: udfs.filterBy('fileResource.id', fileResourceId)
+          }));
+        }
+      });
+
+      udfsWithoutFiles = udfs.filter(function (udf) {
+        return !udf.get('isNew') && !udf.get('fileResource.id');
+      });
+
+      if (udfsWithoutFiles.get('length')) {
+       self.pushObject(Ember.Object.create({
+          name: "placeholders.select.noFileResource",
+          udfs: udfsWithoutFiles
+        }));
+      }
+    }
+  }.on('init').observes('udfs.@each.isNew')
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/messages.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/messages.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/messages.js
new file mode 100644
index 0000000..11295d8
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/messages.js
@@ -0,0 +1,41 @@
+/**
+* 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.
+*/
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Controller.extend({
+  notifyService: Ember.inject.service(constants.namingConventions.notify),
+
+  messages: Ember.computed.alias('notifyService.messages'),
+  count: Ember.computed.alias('notifyService.unseenMessages.length'),
+
+  actions: {
+    removeMessage: function (message) {
+      this.get('notifyService').removeMessage(message);
+    },
+
+    removeAllMessages: function () {
+      this.get('notifyService').removeAllMessages();
+    },
+
+    markMessagesAsSeen: function () {
+      this.get('notifyService').markMessagesAsSeen();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-delete.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
new file mode 100644
index 0000000..0fbcd6b
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
@@ -0,0 +1,33 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  actions: {
+     delete: function () {
+      this.send('closeModal');
+      this.defer.resolve();
+    },
+
+    close: function () {
+      this.send('closeModal');
+      this.defer.reject();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
new file mode 100644
index 0000000..d878bc7
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save-query.js
@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import ModalSave from '../controllers/modal-save';
+import constants from '../utils/constants';
+
+export default ModalSave.extend({
+  showMessage: function () {
+    var content = this.get('content');
+
+    return !content.get('isNew') &&
+            content.get('title') === this.get('text') &&
+            content.get('constructor.typeKey') !== constants.namingConventions.job;
+  }.property('content.isNew', 'text'),
+
+  actions: {
+    save: function () {
+      this.send('closeModal');
+
+      this.defer.resolve(Ember.Object.create({
+        text: this.get('text'),
+        overwrite: this.get('showMessage')
+      }));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save.js
new file mode 100644
index 0000000..6c16291
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/modal-save.js
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  actions: {
+    save: function () {
+      this.send('closeModal');
+      this.defer.resolve(this.get('text'));
+      this.defer.resolve(this.get('type'));
+    },
+
+    close: function () {
+      this.send('closeModal');
+      this.defer.reject();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/open-queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/open-queries.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/open-queries.js
new file mode 100644
index 0000000..a4048be
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/open-queries.js
@@ -0,0 +1,397 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.ArrayController.extend({
+  fileService: Ember.inject.service(constants.namingConventions.file),
+  databaseService: Ember.inject.service(constants.namingConventions.database),
+
+  needs: [ constants.namingConventions.jobResults,
+           constants.namingConventions.jobExplain,
+           constants.namingConventions.index
+         ],
+
+  jobResults: Ember.computed.alias('controllers.' + constants.namingConventions.jobResults),
+  jobExplain: Ember.computed.alias('controllers.' + constants.namingConventions.jobExplain),
+  index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
+
+  selectedTables: Ember.computed.alias('databaseService.selectedTables'),
+  selectedDatabase: Ember.computed.alias('databaseService.selectedDatabase'),
+
+  init: function () {
+    this._super();
+
+    this.set('queryTabs', Ember.ArrayProxy.create({ content: Ember.A([])}));
+  },
+
+  pushObject: function (queryFile, model) {
+    return this._super(queryFile || Ember.Object.create({
+      id: model.get('id'),
+      fileContent: ""
+    }));
+  },
+
+  getTabForModel: function (model) {
+    return this.get('queryTabs').find(function (tab) {
+      return tab.id === model.get('id') && tab.type === model.get('constructor.typeKey');
+    });
+  },
+
+  updateTabSubroute: function (model, path) {
+    var tab = this.get('queryTabs').find(function (tab) {
+      return tab.id === model.get('id') && tab.type === model.get('constructor.typeKey');
+    });
+
+    if (tab) {
+      tab.set('subroute', path);
+    }
+  },
+
+  getQueryForModel: function (model) {
+    return this.find(function (openQuery) {
+      if (model.get('isNew')) {
+        return openQuery.get('id') === model.get('id');
+      }
+
+      return openQuery.get('id') === model.get('queryFile');
+    });
+  },
+
+  update: function (model) {
+    var path,
+        type,
+        currentQuery,
+        defer = Ember.RSVP.defer(),
+        existentTab,
+        self = this,
+        updateSubroute = function () {
+          var isExplainedQuery,
+              subroute;
+
+          //jobs that were run from hive ui (exclude ats jobs)
+          if (model.get('constructor.typeKey') === constants.namingConventions.job &&
+              utils.isInteger(model.get('id'))) {
+            isExplainedQuery = self.get('currentQuery.fileContent').indexOf(constants.namingConventions.explainPrefix) > -1;
+
+            if (isExplainedQuery) {
+              subroute = constants.namingConventions.subroutes.jobExplain;
+            } else {
+              subroute = constants.namingConventions.subroutes.jobLogs;
+            }
+
+            if (!existentTab.get('subroute')) {
+              self.updateTabSubroute(model, subroute);
+            }
+          }
+
+          defer.resolve(isExplainedQuery);
+        };
+
+    existentTab = this.getTabForModel(model);
+
+    if (!existentTab) {
+      type = model.get('constructor.typeKey');
+      path = type === constants.namingConventions.job ?
+             constants.namingConventions.subroutes.historyQuery :
+             constants.namingConventions.subroutes.savedQuery;
+
+      existentTab = this.get('queryTabs').pushObject(Ember.Object.create({
+        name: model.get('title'),
+        id: model.get('id'),
+        visible: true,
+        path: path,
+        type: type
+      }));
+
+      if (model.get('isNew')) {
+        this.set('currentQuery', this.pushObject(null, model));
+
+        defer.resolve();
+      } else {
+        this.get('fileService').loadFile(model.get('queryFile')).then(function (file) {
+          self.set('currentQuery', self.pushObject(file));
+
+          updateSubroute();
+        });
+
+        if (model.get('logFile') && !model.get('log')) {
+          this.get('fileService').loadFile(model.get('logFile')).then(function (file) {
+            model.set('log', file.get('fileContent'));
+          });
+        }
+      }
+    } else {
+      currentQuery = this.getQueryForModel(model);
+      this.set('currentQuery', currentQuery);
+
+      updateSubroute();
+    }
+
+    return defer.promise;
+  },
+
+  save: function (model, query, isUpdating, newTitle) {
+    var tab = this.getTabForModel(model),
+        self = this,
+        wasNew,
+        defer = Ember.RSVP.defer(),
+        jobModel = model;
+
+    if (!query) {
+      query = this.getQueryForModel(model);
+    }
+
+    if (model.get('isNew')) {
+      wasNew = true;
+      model.set('title', newTitle);
+      model.set('id', null);
+    }
+
+    //if current query it's a job, convert it to a savedQuery before saving
+    if (model.get('constructor.typeKey') === constants.namingConventions.job) {
+      model = this.store.createRecord(constants.namingConventions.savedQuery, {
+        dataBase: this.get('selectedDatabase.name'),
+        title: newTitle,
+        queryFile: model.get('queryFile'),
+        owner: model.get('owner')
+      });
+    }
+
+    tab.set('name', newTitle);
+
+    //if saving a new query from an existing one create a new record and save it
+    if (!isUpdating && !model.get('isNew') && model.get('constructor.typeKey') !== constants.namingConventions.job) {
+      model = this.store.createRecord(constants.namingConventions.savedQuery, {
+        dataBase: this.get('selectedDatabase.name'),
+        title: newTitle,
+        owner: model.get('owner')
+      });
+
+      wasNew = true;
+    }
+
+    model.save().then(function (updatedModel) {
+      jobModel.set('queryId', updatedModel.get('id'));
+
+      tab.set('isDirty', false);
+
+      var content = query.get('fileContent');
+      content = self.get('index').buildQuery(query);
+      content = self.get('index').bindQueryParams(content);
+
+      //update query tab path with saved model id if its a new record
+      if (wasNew) {
+        tab.set('id', updatedModel.get('id'));
+
+        self.get('fileService').loadFile(updatedModel.get('queryFile')).then(function (file) {
+          file.set('fileContent', content);
+          file.save().then(function (updatedFile) {
+            self.removeObject(query);
+            self.pushObject(updatedFile);
+            self.set('currentQuery', updatedFile);
+
+            defer.resolve(updatedModel.get('id'));
+          }, function (err) {
+            defer.reject(err);
+          });
+        }, function (err) {
+          defer.reject(err);
+        });
+      } else {
+        query.set('fileContent', content);
+        query.save().then(function () {
+          self.toggleProperty('tabUpdated');
+          defer.resolve(updatedModel.get('id'));
+
+        }, function (err) {
+          defer.reject(err);
+        });
+      }
+    }, function (err) {
+      defer.reject(err);
+    });
+
+    return defer.promise;
+  },
+
+  convertTabToJob: function (model, job) {
+    var defer = Ember.RSVP.defer(),
+        oldQuery = this.getQueryForModel(model),
+        tab = this.getTabForModel(model),
+        jobId = job.get('id'),
+        self = this;
+
+    tab.set('id', job.get('id'));
+    tab.set('type', constants.namingConventions.job);
+    tab.set('path', constants.namingConventions.subroutes.historyQuery);
+
+    this.get('fileService').loadFile(job.get('queryFile')).then(function (file) {
+      //replace old model representing file to reflect model update to job
+      if (self.keepOriginalQuery(jobId)) {
+        file.set('fileContent', oldQuery.get('fileContent'));
+      }
+
+      // Rollback the oldQuery if it is a DS model (type: 'savedQuery)
+      if (oldQuery.get('constructor.typeKey') !== undefined) {
+        oldQuery.rollback();
+      }
+
+      self.removeObject(oldQuery);
+      self.pushObject(file);
+
+      defer.resolve();
+    }, function (err) {
+      defer.reject(err);
+    });
+
+    return defer.promise;
+  },
+
+  keepOriginalQuery: function () {
+    var selected = this.get('highlightedText');
+    var hasQueryParams = this.get('index.queryParams.length');
+
+    return selected && selected[0] !== "" || hasQueryParams;
+  },
+
+  isDirty: function (model) {
+    var query = this.getQueryForModel(model);
+
+    if (model.get('isNew') && !query.get('fileContent')) {
+      return false;
+    }
+
+    if (query && query.get('isDirty')) {
+      return true;
+    }
+
+    return !!(!model.get('queryId') && model.get('isDirty'));
+  },
+
+  updatedDeletedQueryTab: function (model) {
+    var tab = this.getTabForModel(model);
+
+    if (tab) {
+      this.closeTab(tab);
+    }
+  },
+
+  dirtyObserver: function () {
+    var tab;
+    var model = this.get('index.model');
+
+    if (model) {
+      tab = this.getTabForModel(model);
+
+      if (tab) {
+        tab.set('isDirty', this.isDirty(model));
+      }
+    }
+  }.observes('currentQuery.isDirty', 'currentQuery.fileContent'),
+
+  closeTab: function (tab, goToNextTab) {
+    var remainingTabs = this.get('queryTabs').without(tab);
+
+    this.set('queryTabs', remainingTabs);
+
+    //remove cached results set
+    if (tab.type === constants.namingConventions.job) {
+      this.get('jobResults').clearCachedResultsSet(tab.id);
+      this.get('jobExplain').clearCachedExplainSet(tab.id);
+    }
+
+    if (goToNextTab) {
+      this.navigateToLastTab();
+    }
+  },
+
+  navigateToLastTab: function () {
+    var lastTab = this.get('queryTabs.lastObject');
+
+    if (lastTab) {
+      if (lastTab.type === constants.namingConventions.job) {
+        this.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id);
+      } else {
+        this.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id);
+      }
+    } else {
+      this.get('index').send('addQuery');
+    }
+  },
+
+  actions: {
+    removeQueryTab: function (tab) {
+      var self = this,
+          defer;
+
+      this.store.find(tab.type, tab.id).then(function (model) {
+        var query = self.getQueryForModel(model);
+
+        if (!self.isDirty(model)) {
+          self.closeTab(tab, true);
+        } else {
+          defer = Ember.RSVP.defer();
+          self.send('openModal',
+                    'modal-save',
+                     {
+                        heading: "modals.save.saveBeforeCloseHeading",
+                        text: model.get('title'),
+                        defer: defer
+                     });
+
+          defer.promise.then(function (text) {
+            model.set('title', text);
+            self.save(model, query, false, text).then(function () {
+              self.closeTab(tab, true);
+            });
+          }, function () {
+            model.rollback();
+            query.rollback();
+            self.closeTab(tab, true);
+          });
+        }
+      });
+    },
+
+    getColumnsForAutocomplete: function (tableName, callback) {
+      this.get('databaseService').getAllColumns(tableName).then(function () {
+        callback();
+      });
+    },
+
+    changeTabTitle: function(tab) {
+      var self = this,
+          defer = Ember.RSVP.defer(),
+          title = this.get('index.content.title');
+
+      this.send('openModal', 'modal-save', {
+        heading: 'modals.changeTitle.heading',
+        text: title,
+        defer: defer
+      });
+
+      defer.promise.then(function (result) {
+        self.set('index.model.title', result);
+        tab.set('name', result);
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/queries.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/queries.js
new file mode 100644
index 0000000..cbf6b42
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/queries.js
@@ -0,0 +1,125 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import FilterableMixin from 'hive/mixins/filterable';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend(FilterableMixin, {
+  needs: [ constants.namingConventions.routes.history,
+           constants.namingConventions.openQueries ],
+
+  history: Ember.computed.alias('controllers.' + constants.namingConventions.routes.history),
+  openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+
+  sortAscending: true,
+  sortProperties: [],
+
+  init: function () {
+    this._super();
+
+    this.set('columns', Ember.ArrayProxy.create({ content: Ember.A([
+       Ember.Object.create({
+        caption: "columns.shortQuery",
+        property: 'shortQuery',
+        link: constants.namingConventions.subroutes.savedQuery
+      }),
+      Ember.Object.create({
+        caption: "columns.title",
+        property: 'title',
+        link: constants.namingConventions.subroutes.savedQuery
+      }),
+      Ember.Object.create({
+        caption: "columns.database",
+        property: 'dataBase',
+        link: constants.namingConventions.subroutes.savedQuery
+      }),
+      Ember.Object.create({
+        caption: "columns.owner",
+        property: 'owner',
+        link: constants.namingConventions.subroutes.savedQuery
+      })
+    ])}));
+  },
+
+  //row buttons
+  links: [
+    "buttons.history",
+    "buttons.delete"
+  ],
+
+  model: function () {
+    return this.filter(this.get('queries'));
+  }.property('queries', 'filters.@each'),
+
+  actions: {
+    executeAction: function (action, savedQuery) {
+      var self = this;
+
+      switch (action) {
+        case "buttons.history":
+          this.get('history').filterBy('queryId', savedQuery.get('id'), true);
+          this.transitionToRoute(constants.namingConventions.routes.history);
+          break;
+        case "buttons.delete":
+          var defer = Ember.RSVP.defer();
+          this.send('openModal',
+                    'modal-delete',
+                     {
+                        heading: "modals.delete.heading",
+                        text: "modals.delete.message",
+                        defer: defer
+                     });
+
+          defer.promise.then(function () {
+            savedQuery.destroyRecord();
+            self.get('openQueries').updatedDeletedQueryTab(savedQuery);
+          });
+
+          break;
+      }
+    },
+
+    sort: function (property) {
+      //if same column has been selected, toggle flag, else default it to true
+      if (this.get('sortProperties').objectAt(0) === property) {
+        this.set('sortAscending', !this.get('sortAscending'));
+      } else {
+        this.set('sortAscending', true);
+        this.set('sortProperties', [ property ]);
+      }
+    },
+
+    clearFilters: function () {
+      var columns = this.get('columns');
+
+      if (columns) {
+        columns.forEach(function (column) {
+          var filterValue = column.get('filterValue');
+
+          if (filterValue && typeof filterValue === 'string') {
+            column.set('filterValue');
+          }
+        });
+      }
+
+      //call clear filters from Filterable mixin
+      this.clearFilters();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/query-tabs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/query-tabs.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/query-tabs.js
new file mode 100644
index 0000000..5f31c19
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/query-tabs.js
@@ -0,0 +1,176 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Controller.extend({
+  jobProgressService: Ember.inject.service(constants.namingConventions.jobProgress),
+  openQueries   : Ember.inject.controller(constants.namingConventions.openQueries),
+  notifyService: Ember.inject.service(constants.namingConventions.notify),
+  index: Ember.inject.controller(),
+
+  tabClassNames : "fa queries-icon query-context-tab",
+
+  tabs: [
+    Ember.Object.create({
+      iconClass: 'text-icon',
+      id: 'query-icon',
+      text: 'SQL',
+      action: 'setDefaultActive',
+      name: constants.namingConventions.index,
+      tooltip: Ember.I18n.t('tooltips.query')
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-gear',
+      id: 'settings-icon',
+      action: 'toggleOverlay',
+      template: 'settings',
+      outlet: 'overlay',
+      into: 'open-queries',
+      tooltip: Ember.I18n.t('tooltips.settings')
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-area-chart',
+      id: 'visualization-icon',
+      action: 'toggleOverlay',
+      tooltip: Ember.I18n.t('tooltips.visualization'),
+      into: 'index',
+      outlet: 'overlay',
+      template: 'visualization-ui',
+      onTabOpen: 'onTabOpen'
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-link',
+      id: 'visual-explain-icon',
+      action: 'toggleOverlay',
+      template: 'visual-explain',
+      outlet: 'overlay',
+      into: 'index',
+      onTabOpen: 'onTabOpen',
+      tooltip: Ember.I18n.t('tooltips.visualExplain')
+    }),
+    Ember.Object.create({
+      iconClass: 'text-icon',
+      id: 'tez-icon',
+      text: 'TEZ',
+      action: 'toggleOverlay',
+      template: 'tez-ui',
+      outlet: 'overlay',
+      into: 'index',
+      tooltip: Ember.I18n.t('tooltips.tez')
+    }),
+    Ember.Object.create({
+      iconClass: 'fa-envelope',
+      id: 'notifications-icon',
+      action: 'toggleOverlay',
+      template: 'messages',
+      outlet: 'overlay',
+      into: 'index',
+      badgeProperty: 'count',
+      onTabOpen: 'markMessagesAsSeen',
+      tooltip: Ember.I18n.t('tooltips.notifications')
+    })
+  ],
+
+  init: function() {
+    this.setupControllers();
+    this.setDefaultTab();
+    this.setupTabsBadges();
+  },
+
+  setupControllers: function() {
+    var tabs = this.get('tabs');
+    var self = this;
+
+    tabs.map(function (tab) {
+      var controller;
+
+      if (tab.get('template')) {
+        controller = self.container.lookup('controller:' + tab.get('template'));
+        tab.set('controller', controller);
+      }
+    });
+  },
+
+  setDefaultTab: function () {
+    var defaultTab = this.get('tabs.firstObject');
+
+    defaultTab.set('active', true);
+
+    this.set('default', defaultTab);
+    this.set('activeTab', defaultTab);
+  },
+
+  setupTabsBadges: function () {
+    var tabs = this.get('tabs').filterProperty('badgeProperty');
+
+    tabs.map(function (tab) {
+        Ember.oneWay(tab, 'badge', 'controller.' + tab.badgeProperty);
+    });
+  },
+
+  closeActiveOverlay: function () {
+    this.send('closeOverlay', this.get('activeTab'));
+  },
+
+  onTabOpen: function (tab) {
+    if (!tab.onTabOpen) {
+      return;
+    }
+
+    var controller = this.container.lookup('controller:' + tab.template);
+    controller.send(tab.onTabOpen, controller);
+  },
+
+  openOverlay: function (tab) {
+    this.closeActiveOverlay();
+    this.set('activeTab.active', false);
+    tab.set('active', true);
+    this.set('activeTab', tab);
+
+    this.onTabOpen(tab);
+    this.send('openOverlay', tab);
+  },
+
+  setDefaultActive: function () {
+    var activeTab = this.get('activeTab');
+    var defaultTab = this.get('default');
+
+    if (activeTab !== defaultTab) {
+      this.closeActiveOverlay();
+      defaultTab.set('active', true);
+      activeTab.set('active', false);
+      this.set('activeTab', defaultTab);
+    }
+  },
+
+  actions: {
+    toggleOverlay: function (tab) {
+      if (tab !== this.get('default') && tab.get('active')) {
+        this.setDefaultActive();
+      } else {
+        this.openOverlay(tab);
+      }
+    },
+
+    setDefaultActive: function () {
+      this.setDefaultActive();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/settings.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/settings.js
new file mode 100644
index 0000000..77250b4
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/settings.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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  openQueries: Ember.inject.controller(),
+  index: Ember.inject.controller(),
+
+  settingsService: Ember.inject.service('settings'),
+
+  predefinedSettings: Ember.computed.alias('settingsService.predefinedSettings'),
+  settings: Ember.computed.alias('settingsService.settings'),
+
+  init: function() {
+    this._super();
+
+    this.get('settingsService').loadDefaultSettings();
+  },
+
+  excluded: function() {
+    var settings = this.get('settings');
+
+    return this.get('predefinedSettings').filter(function(setting) {
+      return settings.findBy('key.name', setting.name);
+    });
+  }.property('settings.@each.key'),
+
+  parseGlobalSettings: function () {
+    this.get('settingsService').parseGlobalSettings(this.get('openQueries.currentQuery'), this.get('index.model'));
+  }.observes('openQueries.currentQuery', 'openQueries.currentQuery.fileContent', 'openQueries.tabUpdated').on('init'),
+
+  actions: {
+    add: function () {
+      this.get('settingsService').add();
+    },
+
+    remove: function (setting) {
+      this.get('settingsService').remove(setting);
+    },
+
+    addKey: function (name) {
+      this.get('settingsService').createKey(name);
+    },
+
+    removeAll: function () {
+      this.get('settingsService').removeAll();
+    },
+
+    saveDefaultSettings: function() {
+      this.get('settingsService').saveDefaultSettings();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/splash.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/splash.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/splash.js
new file mode 100644
index 0000000..5db93f7
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/splash.js
@@ -0,0 +1,126 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Controller.extend({
+
+  databaseService: Ember.inject.service(constants.namingConventions.database),
+  isExpanded: false,
+  errors: "",
+  stackTrace: "",
+  startTests: function() {
+
+    var model = this.get('model');
+    var url = this.container.lookup('adapter:application').buildURL() + '/resources/hive/'
+    var self = this;
+
+    var processResponse = function(name, data) {
+
+      if( data != undefined ){
+        if(data.databases){
+          data = Ember.Object.create( {trace: null, message: "OK", status: "200"});
+        } else {
+          data = data;
+        }
+      } else {
+        data = Ember.Object.create( {trace: null, message: "Server Error", status: "500"});
+      }
+
+      model.set(name + 'Test', data.status == 200);
+
+      if (data.status != 200) {
+        var checkFailedMessage = "Service '" + name + "' check failed";
+        var errors = self.get("errors");
+        errors += checkFailedMessage;
+        errors += (data.message)?(': <i>' + data.message + '</i><br>'):'<br>';
+        self.set("errors", errors);
+      }
+
+      if (data.trace != null) {
+        var stackTrace = self.get("stackTrace");
+        stackTrace += checkFailedMessage + ':\n' + data.trace;
+        self.set("stackTrace", stackTrace);
+      }
+
+      model.set(name + 'TestDone', true);
+
+      var percent = model.get('percent');
+      model.set('percent', percent + 33.33);
+    };
+
+    var promises = ['hdfs', 'hiveserver', 'ats'].map(function(name) {
+
+      var finalurl = ((name == 'hiveserver') ? self.get('databaseService.baseUrl') : (url + name + 'Status')) || '' ;
+
+      return Ember.$.getJSON( finalurl )
+        .then(
+          function(data) {
+            processResponse(name, data);
+          },
+          function(reason) {
+            processResponse(name, reason.responseJSON);
+          }
+        );
+    });
+
+    return Ember.RSVP.all(promises);
+  },
+
+  progressBarStyle: function() {
+    return 'width: ' + this.get("model").get("percent") +  '%;';
+  }.property("model.percent"),
+
+  allTestsCompleted: function(){
+    return this.get('modelhdfsTestDone') && this.get('modelhiveserverTestDone') && this.get('modelatsTestDone');
+  }.property('modelhdfsTestDone', 'modelhiveserverTestDone', 'modelatsTestDone'),
+
+  modelhdfsTestDone: function() {
+    return this.get('model.hdfsTestDone');
+  }.property('model.hdfsTestDone' ),
+
+  modelhiveserverTestDone: function() {
+    return this.get('model.hiveserverTestDone');
+  }.property('model.hiveserverTestDone' ),
+
+  modelatsTestDone: function() {
+    return this.get('model.atsTestDone');
+  }.property('model.atsTestDone' ),
+
+  modelhdfsTest: function() {
+    return this.get('model.hdfsTest');
+  }.property('model.hdfsTest' ),
+
+  modelhiveserverTest: function() {
+    return this.get('model.hiveserverTest');
+  }.property('model.hiveserverTest' ),
+
+  modelatsTest: function() {
+    return this.get('model.atsTest');
+  }.property('model.atsTest' ),
+
+  actions: {
+    toggleStackTrace:function () {
+      var value = this.get('isExpanded');
+      this.set('isExpanded', !value);
+    }
+  }
+});
+
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/f3df0252/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/tez-ui.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/tez-ui.js b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/tez-ui.js
new file mode 100644
index 0000000..43835e0
--- /dev/null
+++ b/contrib/views/hive-next/src/main/resources/ui/hive-web/app/controllers/tez-ui.js
@@ -0,0 +1,106 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Controller.extend({
+  needs: [ constants.namingConventions.index ],
+
+  index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
+
+  tezViewURL: null,
+  tezApiURL: '/api/v1/views/TEZ',
+  tezURLPrefix: '/views/TEZ',
+  tezDagPath: '?viewPath=/#/dag/',
+
+  isTezViewAvailable: Ember.computed.bool('tezViewURL'),
+
+  dagId: function () {
+    if (this.get('isTezViewAvailable')) {
+      return this.get('index.model.dagId');
+    }
+
+    return false;
+  }.property('index.model.dagId', 'isTezViewAvailable'),
+
+  dagURL: function () {
+    if (this.get('dagId')) {
+      return "%@%@%@".fmt(this.get('tezViewURL'), this.get('tezDagPath'), this.get('dagId'));
+    }
+
+    return false;
+  }.property('dagId'),
+
+  getTezView: function () {
+    if (this.get('isTezViewAvailable')) {
+      return;
+    }
+
+    var self = this;
+    Ember.$.getJSON(this.get('tezApiURL'))
+      .then(function (response) {
+        self.getTezViewInstance(response);
+      })
+      .fail(function (response) {
+        self.setTezViewError(response);
+      });
+  }.on('init'),
+
+  getTezViewInstance: function (data) {
+    var self = this;
+    var url = this.get('tezApiURL') + '/versions/' + data.versions[0].ViewVersionInfo.version;
+
+    Ember.$.getJSON(url)
+      .then(function (response) {
+        if (!response.instances.length) {
+          self.setTezViewError(response);
+          return;
+        }
+
+        self.set('isTezViewAvailable', true);
+
+        var instance = response.instances[0].ViewInstanceInfo;
+        self.setTezViewURL(instance);
+      });
+  },
+
+  setTezViewURL: function (instance) {
+    var url = "%@/%@/%@/".fmt(
+      this.get('tezURLPrefix'),
+      instance.version,
+      instance.instance_name
+    );
+
+    this.set('tezViewURL', url);
+  },
+
+  setTezViewError: function (data) {
+    // status: 404 => Tev View isn't deployed
+    if (data.status && data.status === 404) {
+      this.set('error', 'tez.errors.not.deployed');
+      return;
+    }
+
+    // no instance created
+    if (data.instances && !data.instances.length) {
+      this.set('error', 'tez.errors.no.instance');
+      return;
+    }
+  }
+});


Mime
View raw message