You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/10/28 17:45:48 UTC

[09/38] ambari git commit: AMBARI-18691. Improve and Update Workflow designer to support coordinators and bundles. (Belliraj HB via dipayanb)

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator-xml-importer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator-xml-importer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator-xml-importer.js
new file mode 100644
index 0000000..fca90b3
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator-xml-importer.js
@@ -0,0 +1,272 @@
+/*
+*    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 { Coordinator } from '../coordinator/coordinator';
+
+var CoordinatorXmlImporter= Ember.Object.extend({
+  x2js : new X2JS(),
+  importCoordinator (xml){
+    var coordinatorJson = this.get("x2js").xml_str2json(xml);
+    return this.processCoordinatorXML(coordinatorJson);
+  },
+  createNewCoordinator(){
+    return Coordinator.create({
+      workflow : {
+        appPath : '',
+        configuration :{
+          property : Ember.A([])
+        }
+      },
+      frequency : {
+        type : '',
+        value : ''
+      },
+      start : {
+        value : '',
+        displayValue : '',
+        type : 'date'
+      },
+      end : {
+        value : '',
+        displayValue : '',
+        type : 'date'
+      },
+      timezone : '',
+      datasets : Ember.A([]),
+      dataInputs : Ember.A([]),
+      dataOutputs : Ember.A([]),
+      dataInputType : 'simple',
+      parameters : {
+        configuration :{
+          property : Ember.A([])
+        }
+      },
+      controls : Ember.A([])
+    });
+  },
+  processCoordinatorXML(coordinatorJson){
+    var coordinatorApp = coordinatorJson["coordinator-app"];
+    var coordinator = this.createNewCoordinator();
+    coordinator.name = coordinatorApp._name;
+    var frequency = coordinatorApp._frequency;
+    if(frequency.startsWith('${coord:')){
+      coordinator.frequency.type = frequency.substring(frequency.indexOf(':')+1, frequency.indexOf('('));
+      coordinator.frequency.value = frequency.substring(frequency.indexOf('(')+1, frequency.indexOf(')'));
+    }else{
+      coordinator.frequency.type = 'cron';
+      coordinator.frequency.value = frequency;
+    }
+    coordinator.start = this.extractDateField(coordinatorApp._start);
+    coordinator.end = this.extractDateField(coordinatorApp._end);
+    coordinator.timezone = coordinatorApp._timezone;
+    this.extractDataSets(coordinatorApp, coordinator);
+    if(coordinatorApp['input-events'] && coordinatorApp['input-events']['data-in']){
+      coordinator.dataInputType = 'simple';
+      this.extractInputEvents(coordinatorApp, coordinator);
+    }else{
+      coordinator.dataInputType = 'logical';
+      coordinator.supportsConditionalDataInput = true;
+      this.extractLogicalInputEvents(coordinatorApp, coordinator);
+    }
+    this.extractOutputEvents(coordinatorApp, coordinator);
+    this.extractAction(coordinatorApp, coordinator);
+    this.extractParameters(coordinatorApp, coordinator);
+    this.extractControls(coordinatorApp, coordinator);
+    return coordinator;
+  },
+  extractDateField(value){
+    var dateField = {};
+    var date = new Date(value);
+    dateField.value = value;
+    if(isNaN(date.getTime())){
+      dateField.displayValue = value;
+      dateField.type = 'expr';
+    }else{
+      dateField.type = 'date';
+      var utcDate = new Date(date.getTime() + date.getTimezoneOffset()*60*1000);
+      dateField.displayValue = moment(utcDate).format("MM/DD/YYYY hh:mm A");
+    }
+    return dateField;
+  },
+  extractDataSet(dataset){
+    var dataSetJson = {
+      name : dataset._name,
+      frequency : {},
+      initialInstance :this.extractDateField( dataset['_initial-instance']),
+      timezone : dataset._timezone
+    };
+    var frequency = dataset._frequency;
+    if(frequency.startsWith('${coord:')){
+      dataSetJson.frequency.type = frequency.substring(frequency.indexOf(':')+1, frequency.indexOf('('));
+      dataSetJson.frequency.value = frequency.substring(frequency.indexOf('(')+1, frequency.indexOf(')'));
+    }else{
+      dataSetJson.frequency.type = 'cron';
+      dataSetJson.frequency.value = frequency;
+    }
+    dataSetJson["uriTemplate"] = dataset['uri-template'];
+    if (dataset['done-flag']){
+      dataSetJson.doneFlag = dataset['done-flag'];
+    }
+    return dataSetJson;
+  },
+  extractDataSets(coordinatorApp, coordinator){
+    if (coordinatorApp.datasets && coordinatorApp.datasets.dataset){
+      if(Array.isArray(coordinatorApp.datasets.dataset)) {
+        coordinatorApp.datasets.dataset.forEach(function(dataset){
+          coordinator.datasets.push(this.extractDataSet(dataset));
+        }, this);
+      }else{
+        coordinator.datasets.push(this.extractDataSet(coordinatorApp.datasets.dataset));
+      }
+    }
+  },
+  extractDataInput(datain){
+    var datainJson = {
+      name : datain._name,
+      dataset : datain._dataset
+    };
+    if (datain.instance && datain.instance.length>0){
+      datainJson.instances = Ember.A([]);
+      if(Array.isArray(datain.instance)) {
+        datain.instance.forEach(function(instance){
+          datainJson.instances.pushObject(this.extractDateField(instance));
+        }, this);
+      }else{
+        datainJson.instances.pushObject(this.extractDateField(datain.instance));
+      }
+      datainJson.isList = true;
+    }else if (datain["start-instance"] && ["end-instance"]){
+      datainJson.start = this.extractDateField(datain["start-instance"]);
+      datainJson.end = this.extractDateField(datain["end-instance"]);
+      datainJson.isList = false;
+    }
+    return datainJson;
+  },
+  extractInputEvents(coordinatorApp, coordinator){
+    if(Array.isArray(coordinatorApp['input-events']['data-in'])){
+      coordinatorApp['input-events']['data-in'].forEach(function(datain){
+        coordinator.dataInputs.push(this.extractDataInput(datain));
+      }, this);
+    }else{
+      coordinator.dataInputs.push(this.extractDataInput(coordinatorApp['input-events']['data-in']));
+    }
+  },
+  extractLogicalInputEvents(coordinatorApp, coordinator){
+    var conditionJson = coordinatorApp['input-events'];
+    var condition = {};
+    coordinator.conditionalDataInput = condition;
+    Object.keys(conditionJson).forEach((key)=>{
+      condition.operator = key;
+      this.parseConditionTree(conditionJson[key], condition);
+    }, this);
+  },
+  extractDataInputOperand(operandJson){
+    var operand = {};
+    operand.name = operandJson._name;
+    operand.type = 'dataInput';
+    operand.dataset = operandJson._dataset;
+    if(operandJson._min) {
+      operand.min = operandJson._min;
+    }
+    if(operandJson._wait) {
+      operand.wait = operandJson._wait;
+    }
+    return operand;
+  },
+  parseConditionTree(conditionJson, condition) {
+    condition.name = conditionJson._name;
+    condition.operands = Ember.A([]);
+    Object.keys(conditionJson).forEach( (key) => {
+      var operandsJson = conditionJson[key];
+      if(key === 'data-in') {
+        if(Array.isArray(operandsJson) ) {
+          operandsJson.forEach((json) => {
+            condition.operands.pushObject(this.extractDataInputOperand(json));
+          }, this);
+        }else{
+          condition.operands.pushObject(this.extractDataInputOperand(operandsJson));
+        }
+      }else if(key !== '_name') {
+        var operand = {};
+        operand.operator = key;
+        operand.type = 'condition';
+        condition.operands.pushObject(operand);
+        this.parseConditionTree(operandsJson, operand);
+      }
+    }, this);
+  },
+  extractDataOutput(dataOutJson){
+    return {
+      dataset:dataOutJson._dataset,
+      name:dataOutJson._name,
+      instance:this.extractDateField(dataOutJson.instance)
+    };
+  },
+  extractOutputEvents(coordinatorApp, coordinator){
+    if (coordinatorApp['output-events'] && coordinatorApp['output-events']['data-out']){
+      var dataOutputsJson = coordinatorApp["output-events"]["data-out"];
+      if(Array.isArray(dataOutputsJson)){
+        dataOutputsJson.forEach(function(dataOutJson){
+          coordinator.dataOutputs.pushObject(this.extractDataOutput(dataOutJson));
+        }, this);
+      }else{
+        coordinator.dataOutputs.pushObject(this.extractDataOutput(dataOutputsJson));
+      }
+    }
+  },
+  extractAction(coordinatorApp, coordinator){
+    var actionJson = coordinatorApp['action']['workflow'];
+    coordinator.workflow.appPath = actionJson['app-path'];
+    if(actionJson.configuration && actionJson.configuration.property){
+      if(Array.isArray(actionJson.configuration.property)){
+        actionJson.configuration.property.forEach(function(prop){
+          coordinator.workflow.configuration.property.push(this.extractConfigProperty(prop));
+        }, this);
+      }else{
+        coordinator.workflow.configuration.property.push(this.extractConfigProperty(actionJson.configuration.property));
+      }
+
+    }
+  },
+  extractConfigProperty(propJson){
+    return {"name" : propJson.name, "value" : propJson.value};
+  },
+  extractParameters(coordinatorApp, coordinator){
+    var paramJson = coordinatorApp['parameters'];
+    if(!paramJson) {
+      return;
+    }
+    if(paramJson.configuration && paramJson.configuration.property){
+      if(Array.isArray(paramJson.configuration.property)){
+        paramJson.configuration.property.forEach(function(prop){
+          coordinator.parameters.configuration.property.push(this.extractConfigProperty(prop));
+        }, this);
+      }else{
+        coordinator.parameters.configuration.property.push(this.extractConfigProperty(paramJson.configuration.property));
+      }
+    }
+  },
+  extractControls(coordinatorApp, coordinator) {
+    var controls = coordinatorApp["controls"];
+    if(controls && Object.keys(controls).length > 0){
+      Object.keys(controls).forEach((controlName)=>{
+        coordinator.controls.pushObject({'name':controlName, 'value':controls[controlName]});
+      }, this);
+    }
+  }
+});
+export { CoordinatorXmlImporter };

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator.js
new file mode 100644
index 0000000..58396d7
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/coordinator/coordinator.js
@@ -0,0 +1,22 @@
+/*
+*    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';
+var Coordinator = Ember.Object.extend({
+
+});
+export {Coordinator};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-flow-renderer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-flow-renderer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-flow-renderer.js
new file mode 100644
index 0000000..086916f
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-flow-renderer.js
@@ -0,0 +1,348 @@
+/*
+*    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 CytoscapeStyles from '../domain/cytoscape-style';
+var CytoscapeRenderer= Ember.Object.extend({
+  currentCyNode: null,
+  staticNodes: ['start', 'end', 'join', 'placeholder'],
+  dataNodes: [],
+  cyOverflow: {},
+  cy: null,
+  layoutConfigs: { name: 'dagre', fit: false, edgeSep: 100 },
+  _initCY(settings){
+    this.get("context").$('#'+this.id).height(settings.canvasHeight);
+    this.cy = cytoscape({
+      container: this.get("context").$('#'+this.id),
+      elements: [],
+      style: CytoscapeStyles.style,
+      layout: this.get("layoutConfigs")
+    });
+
+    // the default values of each option are outlined below:
+    var defaults = {
+      zoomFactor: 2.0, // zoom factor per zoom tick
+      minZoom: 0.1, // min zoom level
+      maxZoom: 10, // max zoom level
+
+      // icon class names
+      sliderHandleIcon: 'fa fa-minus',
+      zoomInIcon: 'fa fa-plus',
+      zoomOutIcon: 'fa fa-minus',
+      resetIcon: 'fa fa-expand'
+    };
+
+    this.cy.panzoom( defaults );
+    //this.cy.center();
+    this.cy.pan({x:200,y:50});
+    this._addEvents(this.cy);
+    var self = this;
+    this.get("context").$('.overlay-transition-content').popover({
+      html : true,
+      title : "Add Node <button type='button' class='close'>&times;</button>",
+      placement: 'right',
+      trigger : 'focus',
+      content : function(){
+        return self.get("context").$('#workflow-actions').html();
+      }
+    });
+
+    this.get("context").$("#cyRenderer").on('click','.popover .close',function(){
+      this.get("context").$('.popover').popover('hide');
+    }.bind(this));
+  },
+  _setCyOverflow() {
+    Ember.set(this.get("cyOverflow"), "overflown", this.cy.elements().renderedBoundingBox().y2 > this.cy.height());
+  },
+  _getShape(nodeType) {
+    switch(nodeType) {
+      case 'start' :
+      case 'end' :
+      case 'kill' :
+      case 'placeholder' :
+        return 'ellipse';
+      case 'action' :
+        return 'roundrectangle';
+      case 'fork' :
+      case 'join' :
+        return 'rectangle';
+      case 'decision' :
+        return 'diamond';
+      default :
+        return 'star';
+    }
+  },
+
+  _getCyDataNodes(workflow){
+    this.get('dataNodes').clear();
+    var self=this;
+    workflow.nodeVisitor.process(workflow.startNode, function(node) {
+      if (node.type === 'kill') {
+        return;
+      }
+      self.get('dataNodes').pushObject({
+        data: {
+          id: node.id, name: node.name, type: node.type,
+          shape: self._getShape(node.type),
+          node: node
+        },
+        dataNodeName: Ember.computed.alias('data.node.name')
+      });
+      if (node.transitions.length > 0) {
+        node.transitions.forEach(function(transition){
+          if (transition.isOnError()){
+            return;
+          }
+          self.get('dataNodes').pushObject(
+            {
+              data: {
+                id: transition.sourceNodeId + '_to_' + transition.targetNode.id,
+                source:transition.sourceNodeId,
+                target: transition.targetNode.id,
+                transition: transition,
+                transitionCount: node.getOkTransitionCount()
+              }
+            }
+          );
+        });
+      }
+    });
+  },
+
+  _showNodeEditor(node, nodeObj){
+    if (nodeObj && this.get("currentCyNode") && (nodeObj.data().id === this.get("currentCyNode").data().id)) {
+      if (this.staticNodes.contains(node.data().type)) {
+        return;
+      }
+      this.get("context").$("#"+ node.data().id  + " :input").show();
+      this.get("context").$("#"+ node.data().id  + " :input").css({
+        top: nodeObj.renderedPosition().y  - (nodeObj.outerHeight()/2),
+        left: nodeObj.renderedPosition().x  - (nodeObj.outerWidth()/2)
+      });
+    }
+    this.set("currentCyNode", nodeObj);
+  },
+
+  _showNodeTooltip(event){
+    var node = event.cyTarget;
+    var nodeObj = this.cy.$('#' + node.id());
+    if (this.staticNodes.contains(node.data().type)) {
+      return;
+    }
+    this.get("context").$(".overlay-node-label").css({
+      top: nodeObj.renderedPosition().y - (nodeObj.outerHeight() - ((node.data().type === 'decision')?20:0)),
+      left: nodeObj.renderedPosition().x + (nodeObj.outerWidth()/2 - 30)
+    });
+    this.get("context").$(".overlay-node-label").text(node.data().node.name);
+    this.get("context").$(".overlay-node-label").show();
+  },
+
+  _addEvents(cy){
+    cy.on('pan', function() {
+      this.get("context").$(".overlay_node_editor, .overlay-node-actions, .overlay-fork-icon, .overlay-trash-icon, .overlay-settings-icon").hide();
+      this.get("context").$(".overlay-transition-content").hide();
+      this._setCyOverflow();
+    }.bind(this));
+
+    cy.on('click', function(event) {
+      if (event.cyTarget === cy) {
+        this.get("context").$(".overlay_node_editor, .overlay-node-actions, .overlay-fork-icon, .overlay-trash-icon, .overlay-settings-icon").hide();
+        this.get("context").$(".overlay-transition-content").hide();
+      }
+    }.bind(this));
+
+    cy.on('mousemove', 'node', function(event) {
+      event.cyTarget.css({'border-color': '#5bb75b'});
+      this.get("context").actionInfo(event.cyTarget.data().node);
+      this._showNodeTooltip(event);
+    }.bind(this));
+
+    cy.on('mouseout', 'node',function(event) {
+      event.cyTarget.css({'border-color': '#ABABAB'});
+      this.get("context").$(".overlay-node-label").hide();
+    }.bind(this));
+
+    cy.on('mousemove', 'edge', function(event) {
+      event.cyTarget.css({'line-color': '#5bb75b', 'target-arrow-color': '#5bb75b'});
+    }.bind(this));
+
+    cy.on('mouseout', 'edge',function(event) {
+      event.cyTarget.css({'line-color': '#ABABAB', 'target-arrow-color': '#ABABAB'});
+    }.bind(this));
+
+    cy.on('click', 'node', function(event) {
+      this.get("context").$(".overlay-node-actions span").hide();
+      this.get("context").$(".overlay-transition-content").hide();
+      var node = event.cyTarget;
+      var nodeObj = cy.$('#' + node.id());
+      this._showNodeEditor(node, nodeObj);
+      if (!(node.data().type === 'start' || node.data().type === 'end' || node.data().type === 'placeholder')) {
+        this.get("context").$(".overlay-node-actions, .overlay-trash-icon").show();
+      }
+      if (node.data().type === 'action' || node.data().type === 'decision') {
+        this.get("context").$(".overlay-settings-icon").show();
+      }
+      if (node.data().type === 'action') {
+        this.get("context").$(".overlay-copy-icon").show();
+        this.get("context").$(".overlay-cut-icon").show();
+        if(this.get('context').get('clipboard')){
+          this.get("context").$(".overlay-paste-icon").show();
+        }
+      }
+      if (node.data().type === 'fork' || node.data().type === 'decision') {
+        this.get("context").$(".overlay-fork-icon").show();
+      }
+      this.get("context").$(".overlay-node-actions").css({
+        top: nodeObj.renderedPosition().y - (nodeObj.outerHeight()) + 20,
+        left: nodeObj.renderedPosition().x + (nodeObj.outerWidth()/3) + 50
+      });
+      this.get("context").$(".overlay-trash-icon, .overlay-fork-icon, .overlay-settings-icon, .overlay-copy-icon, .overlay-paste-icon, .overlay-cut-icon").data("node", node.data().node);
+    }.bind(this));
+
+    cy.on('click', 'edge', function(event) {
+      this.get("context").$(".decision-condition-label").hide();
+      this.get("context").$(".overlay-transition-content").hide();
+      this.get("context").$(".overlay_node_editor, .overlay-node-actions, .overlay-fork-icon, .overlay-trash-icon, .overlay-settings-icon").hide();
+      this.get("context").$(".overlay-transition-content").show();
+      this.get("context").$(".overlay-transition-content").css({
+        top: event.originalEvent.offsetY + 10,
+        left: event.originalEvent.offsetX + 15
+      });
+      if (event.cyTarget.data().transitionCount>1){
+            this.get("context").$(".overlay-trash-transition-icon").show();
+      }else{
+          this.get("context").$(".overlay-trash-transition-icon").hide();
+      }
+      this.get("context").$(".overlay-transition-content").data("transition",event.cyTarget.data().transition);
+
+      if (event.cyTarget.data().transition && event.cyTarget.data().transition.condition) {
+        this.get("context").$(".decision-condition-body").html(event.cyTarget.data().transition.condition);
+        this.get("context").$(".decision-condition-label").css({
+          top: event.originalEvent.offsetY,
+          left: event.originalEvent.offsetX + 10
+        });
+        this.get("context").$(".decision-condition-label").show();
+      }
+    }.bind(this));
+
+    this.get("context").$('.overlay-plus-icon').off('click');
+    this.get("context").$('.overlay-plus-icon').on('click',function(){
+      this.get("context").$(".overlay-transition-content").popover("show");
+      this.get("context").set('popOverElement', this.get("context").$('.overlay-transition-content'));
+      this.get("context").setCurrentTransition(this.get("context").$(".overlay-transition-content").data("transition"));
+      Ember.run.later(this, function() {
+        this.get("context").$('.overlay-transition-content').hide();
+      }, 1000);
+    }.bind(this));
+
+    this.get("context").$('.overlay-trash-transition-icon').off('click');
+    this.get("context").$('.overlay-trash-transition-icon').on('click',function(){
+      this.get("context").deleteTransition(this.get("context").$(".overlay-transition-content").data("transition"));
+      this.get("context").$('.overlay-transition-content').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-trash-icon i').off('click');
+    this.get("context").$('.overlay-trash-icon i').on('click',function(){
+      this.get("context").deleteWorkflowNode(this.get("context").$(".overlay-trash-icon").data("node"));
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-copy-icon i').off('click');
+    this.get("context").$('.overlay-copy-icon i').on('click',function(){
+      this.get("context").copyNode(this.get("context").$(".overlay-copy-icon").data("node"));
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-paste-icon i').off('click');
+    this.get("context").$('.overlay-paste-icon i').on('click',function(){
+      this.get("context").replaceNode(this.get("context").$(".overlay-paste-icon").data("node"));
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-cut-icon i').off('click');
+    this.get("context").$('.overlay-cut-icon i').on('click',function(){
+      this.get("context").cutNode(this.get("context").$(".overlay-cut-icon").data("node"));
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-fork-icon i').off('click');
+    this.get("context").$('.overlay-fork-icon i').on('click',function(){
+      var node = this.get("context").$(".overlay-fork-icon").data("node");
+
+      if (node.isDecisionNode()) {
+        this.get("context").openDecisionEditor(this.get("context").$(".overlay-fork-icon").data("node"));
+        this.get("context").$("#selector-content").css({
+          top: this.get("currentCyNode").renderedPosition().y - (this.get("currentCyNode").outerHeight()),
+          left: this.get("currentCyNode").renderedPosition().x + (this.get("currentCyNode").outerWidth()/2)
+        });
+      } else if (node.isForkNode()) {
+        this.get("context").addWorkflowBranch(this.get("context").$(".overlay-fork-icon").data("node"));
+      }
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+
+    this.get("context").$('.overlay-settings-icon i').off('click');
+    this.get("context").$('.overlay-settings-icon i').on('click',function(){
+      this.get("context").openWorkflowEditor(this.get("context").$(".overlay-settings-icon").data("node"));
+      this.get("context").$('.overlay-node-actions').hide();
+    }.bind(this));
+  },
+
+  renderWorkflow(workflow){
+    this._getCyDataNodes(workflow);
+    this.cy.$('node').remove();
+    this.cy.add(this.get('dataNodes'));
+    this.cy.layout(this.get("layoutConfigs"));
+    this._setCyOverflow();
+  },
+
+  initRenderer(callback, settings){
+    this.context=settings.context;
+    this.dataNodes=settings.dataNodes;
+    this.cyOverflow=settings.cyOverflow;
+    this._initCY(settings);
+    callback();
+  },
+  reset(){
+
+  },
+  resetLayout() {
+    this.cy.layout();
+  },
+  refresh(){
+
+  },
+  onDidUpdate(){
+    return true;
+  },
+  cleanup(){
+  },
+  resize(){
+    if (this.cy){
+      Ember.run.later(this, function() {
+        this.cy.resize();
+      },50);
+    }
+  },
+  getBottomPosition() {
+    return {
+      top: this.get("context").$('#'+this.id).offset().top + this.get("context").$('#'+this.id).height,
+      left: this.get("context").$('#'+this.id).offset().left + 100
+    };
+  }
+});
+export {CytoscapeRenderer};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-style.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-style.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-style.js
new file mode 100644
index 0000000..92820f9
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/cytoscape-style.js
@@ -0,0 +1,123 @@
+/*
+ *    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';
+var defaultNodeColor = '#fff';
+var actionNodeColor = '#f5f5f5';
+export default Ember.Object.create({
+  style: [
+    {
+      selector: 'node',
+      style: {
+        shape: 'data(shape)',
+        'background-color': defaultNodeColor,
+        'border-width': 1,
+        'border-color': '#ABABAB',
+        //'text-margin-x': 10,
+        label: function(target) {
+          if (!target.data().node.name) {
+            return "";
+          } else if (target.data().node.name.length>12){
+            return target.data().node.name.slice(0, 12)+"...";
+          } else{
+            return target.data().node.name;
+          }
+        },
+        'text-valign': 'center',
+        'font-size': 14,
+        height: 40,
+        width: 40
+      }
+    },
+    {
+      selector: 'node[type = "fork"]',
+      style: {
+        'background-image': 'assets/sitemap.png',
+        'background-position-x': 10,
+        width: 150
+      }
+    },
+    {
+      selector: 'node[type = "join"]',
+      style: {
+        'background-image': 'assets/join.png',
+        label: '',
+        width: 80
+      }
+    },
+    {
+      selector: 'node[type = "decision"]',
+      style: {
+        height: 60,
+        width: 120
+      }
+    },
+    {
+      selector: 'node[type = "start"]',
+      style: {
+        'background-image': 'assets/play.png',
+        label: ''
+      }
+    },
+    {
+      selector: 'node[type = "end"]',
+      style: {
+        'background-image': 'assets/stop.png',
+        label: ''
+      }
+    },
+    {
+      selector: 'node[type = "placeholder"]',
+      style: {
+        width: 1,
+        height: 1,
+        label: ''
+      }
+    },
+    {
+      selector: 'node[type = "action"]',
+      style: {
+        'background-color': actionNodeColor,
+        width: 150
+      }
+    },
+    {
+      selector: 'edge',
+      style: {
+        'curve-style': 'bezier',
+				'target-arrow-shape': function(target){
+          if (target.data().transition && target.data().transition.getTargetNode(false) && !target.data().transition.getTargetNode(false).isPlaceholder()) {
+            return "triangle";
+          }else{
+            return "none";
+          }
+        },
+        width: 1,
+        'font-size': 12,
+        label: function(target) {
+          if (!target.data().transition || !target.data().transition.condition) {
+            return "";
+          }else if (target.data().transition.condition.length>5){
+            return target.data().transition.condition.slice(0, 5)+"...";
+          }else{
+            return target.data().transition.condition;
+          }
+        }
+      }
+    }
+  ]
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
index e208f83..b7121f4 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/default-layout-manager.js
@@ -17,13 +17,13 @@
 
 import Ember from 'ember';
 var DefaultLayoutManager= Ember.Object.extend({
-  doDagreLayout(nodes,edges){
+  doDagreLayout(component, nodes,edges){
     var g = new dagre.graphlib.Graph();
     g.setGraph({rankdir:"TB", nodesep:100,edgesep:200,marginx:40,ranksep:130});
     g.setDefaultEdgeLabel(function() { return {}; });
 
     for (var i = 0; i < nodes.length; i++) {
-      var n = Ember.$(nodes[i]);
+      var n = component.$(nodes[i]);
       g.setNode(n.attr("id"), {width: n.width(), height: n.height()});
     }
 
@@ -35,13 +35,13 @@ var DefaultLayoutManager= Ember.Object.extend({
     return g;
   },
   doLayout(component,nodes,edges){
-    var g=this.doDagreLayout(nodes,edges);
+    var g=this.doDagreLayout(component, nodes,edges);
     g.nodes().forEach(function(v) {
       try{
         var nodeWidth=component.$("#" + v).width();
         var displacement=150-Math.floor(nodeWidth/2);
-        Ember.$("#" + v).css("left", g.node(v).x+displacement + "px");
-        Ember.$("#" + v).css("top",g.node(v).y+ "px");
+        component.$("#" + v).css("left", g.node(v).x+displacement + "px");
+        component.$("#" + v).css("top",g.node(v).y+ "px");
       }catch(err){
       }
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
index 0566e06..c770fb0 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/findnode-mixin.js
@@ -18,22 +18,65 @@
 import Ember from 'ember';
 var FindNodeMixin= Ember.Mixin.create({
   findNodeById(startNode,id){
-
-    return this.findNodeByIdInternal(startNode,id);
+    return this._findNodeById(startNode,id);
+  },
+  findTransition(startNode,sourceId,targetId){
+    return this._findTransition(startNode,sourceId,targetId);
+  },
+  _findTransition(node,sourceId,targetId){
+    if (!node.transitions){
+      return null;
+    }
+    var res;
+    for (var i = 0; i < node.transitions.length; i++) {
+      var tran= node.transitions[i];
+      if (node.id===sourceId && tran.getTargetNode(false).id===targetId){
+        res=tran;
+      }else{
+        res=this._findTransition(tran.getTargetNode(false),sourceId,targetId);
+      }
+      if (res){
+        break;
+      }
+    }
+    return res;
+  },
+  findTransitionTo(startNode,nodeid){
+    return this._findTransitionTo(startNode,nodeid);
+  },
+  _findTransitionTo(node,nodeid){
+    if (!node.transitions){
+      return null;
+    }
+    var res;
+    for (var i = 0; i < node.transitions.length; i++) {
+      var tran= node.transitions[i];
+      if (tran.getTargetNode(false).id===nodeid){
+        res=tran;
+      }else{
+        res=this._findTransitionTo(tran.getTargetNode(false),nodeid);
+      }
+      if (res){
+        break;
+      }
+    }
+    return res;
   },
-  findNodeByIdInternal(node,id){
+  _findNodeById(node,id){
     var self=this;
     if (node.get("id")===id){
       return node;
     }else{
       if (node.transitions){
+        var res;
         for (var i = 0; i < node.transitions.length; i++) {
           var transition=node.transitions[i];
-          var result=self.findNodeByIdInternal(transition.getTargetNode(true),id);
-          if (result){
-            return result;
+          res= self._findNodeById(transition.getTargetNode(false),id);
+          if (res){
+            break;
           }
         }
+        return res;
       }else{
         return null;
       }
@@ -63,7 +106,7 @@ var FindNodeMixin= Ember.Mixin.create({
     for(var i =0; i< nxtPath.length; i++){
       currNode = nxtPath[i];
       do {
-        if(this.insertUniqueNodes(currNode, nodes) && currNode){
+        if(this._insertUniqueNodes(currNode, nodes) && currNode){
           nodes.push(currNode);
         }
         var nodesList = currNode.getTargets();
@@ -74,7 +117,7 @@ var FindNodeMixin= Ember.Mixin.create({
               if(tmp.length){
                 nodes = nodes.concat(tmp);
               }
-            } else if(this.insertUniqueNodes(nodesList[j], nodes) && nodesList[j]){
+            } else if(this._insertUniqueNodes(nodesList[j], nodes) && nodesList[j]){
               nodes.push(nodesList[j]);
               currNode = nodesList[j];
             } else {
@@ -91,7 +134,7 @@ var FindNodeMixin= Ember.Mixin.create({
     }
     return nodes;
   },
-  insertUniqueNodes(currNode, nodes){
+  _insertUniqueNodes(currNode, nodes){
     if(nodes.indexOf(currNode) > -1){
     } else {
       if (!( currNode.isKillNode() || currNode.isPlaceholder() || currNode.isJoinNode() || currNode.isDecisionEnd())){
@@ -99,5 +142,57 @@ var FindNodeMixin= Ember.Mixin.create({
       }
     }
   },
+  _findCommonTargetNodeId(node){
+    var nodeIds = {}, targ, decPath = node.getTargets(), tempId = 0;
+    for(var i =0; i< decPath.length; i++){
+      var currNode = decPath[i];
+      do {
+        if(nodeIds.hasOwnProperty(currNode.get("id"))){
+          nodeIds[currNode.get("id")] = nodeIds[currNode.get("id")] + 1;
+        } else {
+          nodeIds[currNode.get("id")] = 1;
+        }
+        if(currNode.get("id") === "node-end"){
+          break;
+        }
+        currNode = currNode.getTargets()[0];
+      } while(currNode && currNode.get("id"));
+    }
+    for(var j in nodeIds){
+      if(tempId < nodeIds[j]){
+        targ = j;
+        tempId = nodeIds[j];
+      }
+    }
+    return targ;
+  },
+  _findCommonTargetNode(node){
+    var nodeIds = {}, targ, decPath = node.getTargets(), tempId = 0;
+    for(var i =0; i< decPath.length; i++){
+      var currNode = decPath[i];
+      do {
+        if(nodeIds.hasOwnProperty(currNode.get("id"))){
+          nodeIds[currNode.get("id")] = nodeIds[currNode.get("id")] + 1;
+        } else {
+          nodeIds[currNode.get("id")] = 1;
+        }
+        if(currNode.get("id") === "node-end"){
+          break;
+        }
+        currNode = currNode.getTargets()[0];
+      } while(currNode && currNode.get("id"));
+    }
+    for(var j in nodeIds){
+      if(tempId < nodeIds[j]){
+        targ = j;
+        tempId = nodeIds[j];
+      }
+    }
+    return targ;
+  },
+  findCommonTargetNode(startNode,node){
+    var commonTargetId=this._findCommonTargetNodeId(node);
+    return this.findNodeById(startNode,commonTargetId);
+  }
 });
 export{FindNodeMixin};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
index 0ee985e..ccbfcc3 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/id-gen.js
@@ -29,6 +29,10 @@ var IdGen = Ember.Object.extend({
   reset(){
     this.nameCount=0;
     this.idCount=0;
+  },
+  resetTo(val){
+    this.nameCount=val;
+    this.idCount=val;
   }
 });
 var idGen=IdGen.create({});

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/jsplumb-flow-renderer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/jsplumb-flow-renderer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/jsplumb-flow-renderer.js
new file mode 100644
index 0000000..c3e3133
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/jsplumb-flow-renderer.js
@@ -0,0 +1,194 @@
+/*
+*    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 '../utils/constants';
+import {DefaultLayoutManager as LayoutManager} from '../domain/default-layout-manager';
+var JSPlumbRenderer= Ember.Object.extend({
+  designerPlumb:null,
+  flattenedNodes:null,
+  _createConnection(sourceNode,target,transition){
+    var connectionColor="#777";
+    var lineWidth=1;
+    if (transition.condition){
+      if(transition.condition==="default"){
+        lineWidth=2;
+      }else if (transition.condition==="error"|| transition.errorPath){
+        connectionColor=Constants.globalSetting.errorTransitionColor;
+      }
+    }
+    var connectionObj={
+      source:sourceNode.id,
+      target:target.id,
+      connector:["Straight"],
+      paintStyle:{lineWidth:lineWidth,strokeStyle:connectionColor},
+      endpointStyle:{fillStyle:'rgb(243,229,0)'},
+      endpoint: ["Dot", {
+        radius: 1
+      }],
+      alwaysRespectStubs:true,
+      anchors: [["Bottom"],["Top"]],
+      overlays:[]
+    };
+    return connectionObj;
+  },
+  _getAddNodeOverlay(context,sourceNode,target,transition){
+    var location=target.type==="placeholder"?1:0.5;
+    var transitionCount=sourceNode.transitions.length;
+    return {
+      id: sourceNode.id+"_"+target.id+"_"+"connector",
+      location:location,
+      /* jshint unused:vars */
+      create:function(component) {
+        var container=Ember.$('<div />');
+        var plus= Ember.$('<div class="fa fa-plus connector_overlay_new"></div>');
+        if ((sourceNode.isDecisionNode() && transitionCount>1 ||sourceNode.isForkNode() && transitionCount>2 ) &&
+        target.isPlaceholder() &&
+        !transition.isDefaultCasePath()){
+          var trash=Ember.$('<div class="node_actions node_left"><i class="fa fa-trash-o"></i></div>');
+          trash.on("click",function(){
+            context.deleteTransition(transition);
+          });
+          plus.append(trash);
+        }
+        container.append(plus);
+        return container;
+      },
+      events:{
+        click:function(labelOverlay, originalEvent) {
+          var element = originalEvent.target;
+          context.set('popOverElement', element);
+          context.setCurrentTransition(transition);
+          context.showWorkflowActionSelect(element);
+        }
+      }
+    };
+  },
+
+  _renderNodes(node,visitedNodes){
+    if (!node || node.isKillNode()){
+      return;
+    }
+    if (visitedNodes.contains(node)){
+      return;
+    }
+    visitedNodes.push(node);
+    if(!this.get("flattenedNodes").contains(node)){
+      this.get("flattenedNodes").pushObject(node);
+    }
+    if (node.transitions.length > 0){
+      node.transitions.forEach(function(transition) {
+        var target = transition.targetNode;
+        this._renderNodes(target,visitedNodes);
+      }.bind(this));
+    }
+  },
+  _connectNodes(context,sourceNode){
+    var connections=[];
+    var visitedNodes=[];
+    this._renderTransitions(sourceNode,connections,visitedNodes,context);
+    this._layout(connections);
+    this.designerPlumb.setSuspendDrawing(true);
+    this.designerPlumb.batch(function(){
+      connections.forEach(function(conn){
+        this.designerPlumb.connect(conn);
+      }.bind(this));
+    }.bind(this));
+    this.designerPlumb.setSuspendDrawing(false,true);
+
+  },
+  _renderTransitions(sourceNode,connections,visitedNodes,context){
+    var self=this;
+    if(!sourceNode){
+      return;
+    }
+    if (visitedNodes.contains(sourceNode)){
+      return;
+    }
+    if (sourceNode.hasTransition() ){
+      sourceNode.transitions.forEach(function(transition) {
+        var target = transition.targetNode;
+        if (target.isKillNode() || !Constants.showErrorTransitions && transition.isOnError()){
+          return;
+        }
+        var connectionObj=self._createConnection(sourceNode,target,transition);
+
+        if (transition.condition){
+          var conditionHTML = "<div class='decision-condition' title='"+transition.condition+"'>"+ transition.condition+"</div>";
+          connectionObj.overlays.push([ "Label", {label:conditionHTML, location:0.75, id:"myLabel" } ]);
+        }
+        if (!target.isPlaceholder()){
+          connectionObj.overlays.push(["PlainArrow",{location:-0.1,width: 7,length: 7}]);
+        }
+        if (!(sourceNode.isPlaceholder() || target.isKillNode())){
+          var addNodeoverlay=["Custom" , self._getAddNodeOverlay(context,sourceNode,target,transition)];
+          connectionObj.overlays.push(addNodeoverlay);
+        }
+        connections.push(connectionObj);
+        self._renderTransitions(target,connections,visitedNodes,context);
+      });
+    }
+  },
+  _layout(edges){
+    var nodes = Ember.$(".nodecontainer");
+    this.layoutManager.doLayout(this.get("context"),nodes,edges,this.get("workflow"));
+  },
+  initRenderer(callback,settings){
+    this.designerPlumb=jsPlumb.getInstance({});
+    this.layoutManager=LayoutManager.create({});
+    this.context=settings.context;
+    this.flattenedNodes=settings.flattenedNodes;
+    this.designerPlumb.ready(function() {
+      callback();
+    }.bind(this));
+    return this.designerPlumb;
+  },
+  refresh(){
+    this.designerPlumb.repaintEverything();
+  },
+  reset(){
+    if(!this.get('flattenedNodes')){
+      return;
+    }
+    this.get("flattenedNodes").clear();
+    this.designerPlumb.reset();
+  },
+  cleanup(){
+    if(!this.get('flattenedNodes')){
+      return;
+    }
+    this.get('flattenedNodes').clear();
+    this.designerPlumb.detachEveryConnection();
+  },
+  onDidUpdate(){
+    this._connectNodes(this.get("context"),this.get("workflow").startNode,this.get("workflow"));
+  },
+  renderWorkflow(workflow){
+    var visitedNodes=[];
+    this.set("workflow",workflow);
+    this._renderNodes(this.get("workflow").startNode,visitedNodes);
+  },
+
+  getBottomPosition(){
+    return {
+      top : this.get("context").$(".nodeEnd").offset().top,
+      left : this.get("context").$(".nodeEnd").offset().left
+    };
+  }
+
+});
+export {JSPlumbRenderer};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
index 7cb82e1..2962e71 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/mapping-utils.js
@@ -22,7 +22,6 @@ var MappingMixin= Ember.Mixin.create({
   handleMapping(nodeDomain,nodeObj,mappings,nodeName){
     var self=this;
     mappings.forEach(function(mapping){
-      var nodeVals=[];
       if (mapping.mandatory){
         if (!(nodeDomain[mapping.domain] || mapping.customHandler)){
           var msgForVal=mapping.domain;
@@ -113,6 +112,7 @@ var MappingMixin= Ember.Mixin.create({
   }
 });
 var ConfigurationMapper= Ember.Object.extend({
+  /* jshint unused:vars */
   hanldeGeneration(node,nodeObj){
     if (!node || !node.configuration || !node.configuration.property){
       return;
@@ -205,24 +205,25 @@ var SLAMapper= Ember.Object.extend({
   hanldeGeneration(sla,nodeObj){
     if (sla){
       var slaInfo=nodeObj["info"]={};
-      slaInfo["__prefix"]="sla";
+      var slaPrefix="sla";
+      slaInfo["__prefix"]=slaPrefix;
       if (sla.nominalTime){
-        slaInfo["nominal-time"]=sla.nominalTime;
+        slaInfo[slaPrefix+":"+"nominal-time"]=sla.nominalTime;
       }
       if (sla.shouldStart){
-        slaInfo["should-start"]="${"+sla.shouldStart.time+ "*"+sla.shouldStart.unit+"}";
+        slaInfo[slaPrefix+":"+"should-start"]="${"+sla.shouldStart.time+ "*"+sla.shouldStart.unit+"}";
       }
       if (sla.shouldEnd){
-        slaInfo["should-end"]="${"+sla.shouldEnd.time+ "*"+sla.shouldEnd.unit+"}";
+        slaInfo[slaPrefix+":"+"should-end"]="${"+sla.shouldEnd.time+ "*"+sla.shouldEnd.unit+"}";
       }
       if (sla.maxDuration){
-        slaInfo["max-duration"]="${"+sla.maxDuration.time+ "*"+sla.maxDuration.unit+"}";
+        slaInfo[slaPrefix+":"+"max-duration"]="${"+sla.maxDuration.time+ "*"+sla.maxDuration.unit+"}";
       }
       if (sla.alertEvents){
-        slaInfo["alert-events"]=sla.alertEvents;
+        slaInfo[slaPrefix+":"+"alert-events"]=sla.alertEvents;
       }
       if (sla.alertContact){
-        slaInfo["alert-contact"]=sla.alertContact;
+        slaInfo[slaPrefix+":"+"alert-contact"]=sla.alertContact;
       }
 
     }
@@ -230,18 +231,22 @@ var SLAMapper= Ember.Object.extend({
   },
   handleImport(domain,infoJson,key){
     var sla=domain[key]=SlaInfo.create({});
-    if (infoJson["nominal-time"]){
-      sla.nominalTime=infoJson["nominal-time"];
+    if (infoJson["nominal-time"] && infoJson["nominal-time"].__text){
+      sla.nominalTime=infoJson["nominal-time"].__text;
+    }
+    if (infoJson["alert-contact"]&& infoJson["alert-contact"].__text){
+      sla.alertContact=infoJson["alert-contact"].__text;
+    }
+    if (infoJson["alert-events"] && infoJson["alert-events"].__text){
+      sla.alertEvents=infoJson["alert-events"].__text;
     }
-    sla.alertContact=infoJson["alert-contact"];
-    sla.alertEvents=infoJson["alert-events"];
     this.processTimePeriods(sla,infoJson,"should-start","shouldStart");
     this.processTimePeriods(sla,infoJson,"should-end","shouldEnd");
     this.processTimePeriods(sla,infoJson,"max-duration","maxDuration");
   },
   processTimePeriods(sla,infoJson,key,domainKey){
     if (infoJson[key]){
-      var timeParts=this.parseSlaTime(infoJson[key],key);
+      var timeParts=this.parseSlaTime(infoJson[key].__text,key);
       sla[domainKey].time=timeParts[0];
       sla[domainKey].unit=timeParts[1];
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
index c440b8c..b6e2c73 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-factory.js
@@ -21,10 +21,10 @@ import {Node} from '../domain/node';
 import {idGen} from '../domain/id-gen';
 var NodeFactory= Ember.Object.extend({
   createStartNode(){
-    return this.createNode({id:'node-start', type:'start',name:"Start"});
+    return this.createNode({id:this.generateNodeId(), type:'start',name:"Start"});
   },
   createEndNode(name){
-    return this.createNode({id:'node-end', type:'end', name:name});
+    return this.createNode({id:this.generateNodeId(), type:'end', name:name});
   },
   createKillNode(name,message){
     return this.createNode({id:this.generateNodeId(), type:"kill", name:name,killMessage:message});
@@ -108,6 +108,9 @@ var NodeFactory= Ember.Object.extend({
   },
   createNode(settings){
     settings.factory=this;
+	if (!settings.id){
+      settings.id=this.generateNodeId();
+    }
     return Node.create(settings);
   },
   generateNodeId(){
@@ -115,6 +118,9 @@ var NodeFactory= Ember.Object.extend({
   },
   generateName(){
     return idGen.generateNodeName();
+  },
+  resetNodeIdTo(id){
+    return idGen.resetTo(id);
   }
 });
 export{NodeFactory};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
index 49347d8..6bc305a 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node-handler.js
@@ -17,49 +17,8 @@
 
 import Ember from 'ember';
 import {NodeFactory} from '../domain/node-factory';
-import * as actionJobHandler from '../domain/actionjob_hanlder';
-import {SlaInfo} from '../domain/sla-info';
 import {SLAMapper} from "../domain/mapping-utils";
-var ActionTypeResolver=Ember.Object.extend({
-  actionJobHandlerMap:null,
-  validStandardActionProps:["ok","error","info"],
-  init(){
-    var settings={schemaVersions:this.schemaVersions};
-    this.actionJobHandlerMap=new Map();
-    this.actionJobHandlerMap.set("java",actionJobHandler.JavaActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("pig",actionJobHandler.PigActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("hive",actionJobHandler.HiveActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("hive2",actionJobHandler.Hive2ActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("sqoop",actionJobHandler.SqoopActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("shell",actionJobHandler.ShellActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("spark",actionJobHandler.SparkActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("map-reduce",actionJobHandler.MapRedActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("sub-workflow",actionJobHandler.SubWFActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("distcp",actionJobHandler.DistCpJobHandler.create(settings));
-    this.actionJobHandlerMap.set("ssh",actionJobHandler.SshActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("email",actionJobHandler.EmailActionJobHandler.create(settings));
-    this.actionJobHandlerMap.set("fs",actionJobHandler.FSActionJobHandler.create(settings));
-  },
-  getActionType(json){
-    var self=this;
-    var resolvedType=null;
-    var problaleActionsTypes=[];
-    Object.keys(json).forEach(function functionName(key) {
-      if (!self.validStandardActionProps.contains(key) && !key.startsWith("_")){
-        problaleActionsTypes.push(key);
-      }
-    });
-    if (problaleActionsTypes.length===1){
-      return problaleActionsTypes[0];
-    }else{
-      console.error("Invalid Action spec..",json);
-    }
-    return resolvedType;
-  },
-  getActionJobHandler(jobType){
-    return this.actionJobHandlerMap.get(jobType);
-  }
-});
+
 var NodeHandler=Ember.Object.extend({
   nodeFactory:NodeFactory.create({}),
   context : {},
@@ -75,12 +34,14 @@ var NodeHandler=Ember.Object.extend({
   handleNode(node){
     return {"_name":node.get("name")};
   },
-
+  /* jshint unused:vars */
   handleTransitions(transitions,nodeObj){
 
   },
+  /* jshint unused:vars */
   handleImportNode(type,node){
   },
+    /* jshint unused:vars */
   handleImportTransitions(node,json,nodeMap){
   }
 });
@@ -248,4 +209,4 @@ var JoinNodeHandler= NodeHandler.extend({
     node.addTransitionTo(nodeMap.get(json._to).node);
   }
 });
-export{ActionTypeResolver,NodeHandler,StartNodeHandler,EndNodeHandler,KillNodeHandler,ActionNodeHandler,DecisionNodeHandler,ForkNodeHandler,JoinNodeHandler};
+export{NodeHandler,StartNodeHandler,EndNodeHandler,KillNodeHandler,ActionNodeHandler,DecisionNodeHandler,ForkNodeHandler,JoinNodeHandler};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
index c7ce003..cda7609 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/node.js
@@ -100,7 +100,7 @@ var Node = Ember.Object.extend(FindNodeMixin,{
     }
   },
   addTransitionTo(target,condition){
-    var transition = Transition.create({targetNode:target,sourceNode:this,condition:condition});
+    var transition = Transition.create({targetNode:target,sourceNodeId:this.id,condition:condition});
     this.addTransition(transition);
     return transition;
   },
@@ -120,7 +120,6 @@ var Node = Ember.Object.extend(FindNodeMixin,{
   },
 
   removeTransition(transition){
-    var transitions=this.get("transitions");
     if (transition && this.transitions.indexOf(transition) > -1) {
       this.transitions.splice(this.transitions.indexOf(transition), 1);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
index 76dffbd..9d90280 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/sla-info.js
@@ -17,43 +17,43 @@
 import Ember from 'ember';
 var SlaInfo = Ember.Object.extend(Ember.Copyable,{
   copy (){
-    var slaInfo = {}
+    var slaInfo = {};
     for (let key in this) {
       slaInfo[key] = Ember.copy(this[key]) ;
     }
     return slaInfo;
   },
   init (){
-    this.nominalTime='';
+    this.nominalTime=undefined;
     this.shouldStart = {
-      time : '',
-      unit : ''
+      time : undefined,
+      unit : undefined
     };
     this.shouldEnd = {
-      time : '',
-      unit : ''
+      time : undefined,
+      unit : undefined
     };
     this.maxDuration = {
-      time : '',
-      unit : ''
+      time : undefined,
+      unit : undefined
     };
-    this.alertEvents = '';
-    this.alertContacts = '';
+    this.alertEvents = undefined;
+    this.alertContacts = undefined;
   },
-  nominalTime:'',
+  nominalTime:undefined,
   shouldStart : {
-    time : '',
-    unit : ''
+    time : undefined,
+    unit : undefined
   },
   shouldEnd : {
-    time : '',
-    unit : ''
+    time : undefined,
+    unit : undefined
   },
   maxDuration : {
-    time : '',
-    unit : ''
+    time : undefined,
+    unit : undefined
   },
-  alertEvents : '',
-  alertContacts : ''
+  alertEvents : undefined,
+  alertContacts : undefined
 });
 export {SlaInfo};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
index 70d7a81..b14484e 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/transition.js
@@ -18,7 +18,6 @@
 import Ember from 'ember';
 var Transition = Ember.Object.extend({
   id:null,
-  sourceNode:null,
   targetNode:null,
   type:null,
   condition:null,
@@ -35,9 +34,7 @@ var Transition = Ember.Object.extend({
   isDefaultCasePath(){
     return this.condition==="default";
   },
-  getSourceNode(){
-    return this.get("sourceNode");
-  },
+
   getTargetNode(skipPlaceholder){
     var currNode=this.targetNode;
     if (skipPlaceholder===false){

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
index f29adb6..7415544 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-importer.js
@@ -95,7 +95,7 @@ var WorkflowImporter= Ember.Object.extend({
   },
   getNodeIds(nodeMap){
     var ids=[];
-    nodeMap.forEach(function(entry,key){
+    nodeMap.forEach(function(entry){
       var node=entry.node;
       ids.push(node.id);
     });
@@ -103,7 +103,7 @@ var WorkflowImporter= Ember.Object.extend({
   },
   getNodeNames(nodeMap){
     var names=[];
-    nodeMap.forEach(function(entry,key){
+    nodeMap.forEach(function(entry){
       var node=entry.node;
       names.push(node.id);
     });
@@ -113,7 +113,7 @@ var WorkflowImporter= Ember.Object.extend({
     if (this.containsKillNode(nodeMap)){
       workflow.resetKillNodes();
     }
-    nodeMap.forEach(function(entry,key){
+    nodeMap.forEach(function(entry){
       var node=entry.node;
       if (node.isKillNode()){
         workflow.get("killNodes").pushObject(node);
@@ -122,7 +122,7 @@ var WorkflowImporter= Ember.Object.extend({
   },
   containsKillNode(nodeMap){
     var containsKillNode=false;
-    nodeMap.forEach(function(entry,key){
+    nodeMap.forEach(function(entry){
       var node=entry.node;
       if (node.isKillNode()){
         containsKillNode=true;

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-json-importer.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-json-importer.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-json-importer.js
new file mode 100644
index 0000000..fa428bb
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-json-importer.js
@@ -0,0 +1,92 @@
+/*
+ *    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 {Workflow} from '../domain/workflow';
+import {NodeFactory} from '../domain/node-factory';
+var WorkflowJsonImporter= Ember.Object.extend({
+    nodeFactory:NodeFactory.create({}),
+    importWorkflow(workflowJsonStr){
+      if (!workflowJsonStr){
+        return null;
+      }
+      try{
+        var workflowJson=JSON.parse(workflowJsonStr);
+        var workflow=Workflow.create({});
+        workflow.initialize();
+        workflow.set("name",workflowJson.name);
+        this.restoreKillNodes(workflowJson.killNodes,workflow);
+        var nodeMap= new Map();
+        var startNode=this.visitNode(workflowJson.startNode,nodeMap);
+        workflow.set("startNode",startNode);
+        var maxId=0;
+        for(let value of nodeMap.keys()){
+            console.log("Value in it=",value);
+            var id=Number.parseInt(value.substr(5));
+            if (id>maxId){
+              maxId=id;
+            }
+        }
+        this.nodeFactory.resetNodeIdTo(maxId+1);
+        console.log("imported workflow==",workflow);
+        return workflow;
+      }catch(e){
+        console.error(e);
+        return null;
+      }
+    },
+    visitNode(nodeJson,nodeMap){
+      var self=this;
+      if (!nodeJson){
+        return;
+      }
+      var node;
+      if (!nodeMap.has(nodeJson.id)){
+        node=this.nodeFactory.createNode({id:nodeJson.id, type:nodeJson.type,name:nodeJson.name,actionType:nodeJson.actionType,killMessage:nodeJson.killMessage});
+        node.set("domain",nodeJson.domain);
+        node.set("errorMsgs",nodeJson.errorMsgs);
+        node.set("errors",nodeJson.errors);
+        nodeMap.set(node.id,node);
+        if (nodeJson.transitions){
+          nodeJson.transitions.forEach(function(nodeTran){
+            var transitions=nodeTran;
+            if (!Ember.isArray(nodeTran)){
+              transitions=[nodeTran];
+            }
+            transitions.forEach(function(tran){
+              var targetNodeJson=tran.targetNode;
+              var targetNode=self.visitNode(targetNodeJson,nodeMap);
+              node.addTransitionTo(targetNode,tran.condition);
+            });
+          });
+        }
+      }else{
+        node=nodeMap.get(nodeJson.id);
+      }
+      return node;
+    },
+    restoreKillNodes(killnodesJson,workflow){
+      if (!killnodesJson){
+        return;
+      }
+      workflow.resetKillNodes();
+      killnodesJson.forEach(function(killNodeJson){
+        workflow.createKillNode(killNodeJson.name,killNodeJson.killMessage);
+      });
+      console.log("killnodes json=",killnodesJson);
+    }
+});
+export {WorkflowJsonImporter};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-path-util.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-path-util.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-path-util.js
new file mode 100644
index 0000000..c921455
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-path-util.js
@@ -0,0 +1,73 @@
+/*
+*    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.Object.create({
+  findPath(source, target){
+    var visitedNodes = [];
+    var currentPath = [];
+    var allPaths = [];
+    this._findPath(source, target, visitedNodes, currentPath, 0, allPaths);
+    return allPaths;
+  },
+  _findPath(source, target, visitedNodes, currentPath, pathIndex, allPaths){
+    visitedNodes.pushObject(source);
+    currentPath[pathIndex++] = source;
+    if(source.id === target.id){
+      if(!allPaths[allPaths.length]){
+        var index = currentPath.indexOf(target);
+        allPaths[allPaths.length] = currentPath.slice(0, index+1);
+      }
+    }
+    if(source.hasTransition()){
+      source.transitions.forEach((transition)=>{
+        var node = transition.targetNode;
+        if(node.hasTransition() && !visitedNodes.findBy('id', node.id)){
+          this._findPath(node, target, visitedNodes, currentPath, pathIndex, allPaths);
+        }
+      }, this);
+    }
+    pathIndex--;
+    visitedNodes.removeObject(source);
+  },
+  _getAllNodes(workflow){
+    var workflowNodes = [];
+    workflow.nodeVisitor.process(workflow.startNode, (node) =>{
+      workflowNodes.pushObject(node);
+    });
+    return workflowNodes;
+  },
+  findValidTransitionsTo(workflow, node){
+    var validTransitionsTo = [];
+    if(!node.hasTransition()){
+      return validTransitionsTo;
+    }
+    var paths = this.findPath(workflow.get('startNode'), node);
+    var workflowNodes = this._getAllNodes(workflow);
+    validTransitionsTo = workflowNodes.slice();
+    workflowNodes.forEach((node)=>{
+      paths.forEach((path)=>{
+        if(path.contains(node)){
+          validTransitionsTo.removeObject(node);
+        }
+      }, this);
+    }, this);
+    validTransitionsTo = validTransitionsTo.reject((node)=>{
+      return node.get('type') === 'placeholder';
+    }, this);
+    return validTransitionsTo;
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
index 9fc791c..7049fde 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow-xml-generator.js
@@ -18,7 +18,6 @@
 import Ember from 'ember';
 import {WorkflowXmlMapper} from '../domain/workflow_xml_mapper';
 import {NodeVisitor} from '../domain/node-visitor';
-import Constants from '../utils/constants';
 var WorkflowGenerator= Ember.Object.extend({
   workflowMapper:null,
   x2js : new X2JS({useDoubleQuotes:true}),

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
index 5908de5..f4fc20d 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow.js
@@ -21,8 +21,7 @@ import {FindNodeMixin} from '../domain/findnode-mixin';
 import {NodeFactory} from '../domain/node-factory';
 import SchemaVersions from '../domain/schema-versions';
 import {NodeVisitor} from '../domain/node-visitor';
-import {idGen} from '../domain/id-gen';
-import {SlaInfo} from '../domain/sla-info'
+import {SlaInfo} from '../domain/sla-info';
 var Workflow= Ember.Object.extend(FindNodeMixin,{
   name:"",
   startNode:null,
@@ -60,52 +59,13 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
     //TODO idGen.reset();
     this.initialize();
   },
-  findCommonTargetNodeId(node){
-    var nodeIds = {}, targ, decPath = node.getTargets(), tempId = 0;
-    for(var i =0; i< decPath.length; i++){
-      var currNode = decPath[i];
-      do {
-        if(nodeIds.hasOwnProperty(currNode.get("id"))){
-          nodeIds[currNode.get("id")] = nodeIds[currNode.get("id")] + 1;
-        } else {
-          nodeIds[currNode.get("id")] = 1;
-        }
-        if(currNode.get("id") === "node-end"){
-          break;
-        }
-        currNode = currNode.getTargets()[0];
-      } while(currNode && currNode.get("id"));
-    }
-    for(var j in nodeIds){
-      if(tempId < nodeIds[j]){
-        targ = j;
-        tempId = nodeIds[j];
-      }
-    }
-    return targ;
-  },
+
   findJoinNode(node){
-    var commonTargetId=null;
-    var commonTarget=null;
-    if (node.isDecisionNode()){
-      if (Constants.globalSetting.useJoinNodeForDecision){
-        var target=this.findNodeById(node,"decision_end_"+node.get("id"));
-        if (!target){
-          commonTargetId=this.findCommonTargetNodeId(node);
-          commonTarget=this.findNodeById(this.startNode,commonTargetId);
-          return commonTarget;
-        }else{
-          return target;
-        }
-      }else{
-        commonTargetId=this.findCommonTargetNodeId(node);
-        commonTarget=this.findNodeById(this.startNode,commonTargetId);
-        return commonTarget;
-      }
+    if (node.isDecisionNode() || node.isForkNode()){
+      return this.findCommonTargetNode(this.startNode,node);
     }else if (node.isForkNode()) {
-      commonTargetId=this.findCommonTargetNodeId(node);
-      commonTarget=this.findNodeById(this.startNode,commonTargetId);
-      return commonTarget;
+      //TODO find join node by id if it is efficient later..
+      return this.findCommonTargetNode(this.startNode,node);
     }else{
       return null;
     }
@@ -136,7 +96,7 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
     }else{
     }
   },
-  generatedNode(target,type){
+  generatedNode(target,type,settings){
     var generatedNode=null;
     if ("decision" === type){
       generatedNode=this.nodeFactory.generateDecisionNode(target);
@@ -144,32 +104,38 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
       generatedNode=this.nodeFactory.generateForkNode(target);
     }else  if ("kill" === type){
       generatedNode = this.nodeFactory.createKillNode(settings.name);
-      source.deleteCurrentKillNode();
+      //source.deleteCurrentKillNode();//TODO how to get source...
     }else{
       generatedNode = this.nodeFactory.createActionNode(type);
       generatedNode.addTransitionTo(target);
     }
     return generatedNode;
   },
-  addKillNode(node,settings){
-    var generatedNode=this.generatedNode(null,"kill");
+
+  addKillNode(source,settings){
+    var generatedNode=this.generatedNode(null,"kill",settings);
     return source.addTransitionTo(generatedNode,"error");
   },
   addNode(transition,type,settings) {
-    var source=transition.sourceNode;
     var target=transition.targetNode;
     var computedTarget=target;
     if (target && target.isPlaceholder()){
       computedTarget=target.getTargets()[0];
     }
-    var generatedNode=this.generatedNode(computedTarget,type);
-    transition.targetNode=generatedNode;
+    var generatedNode=this.generatedNode(computedTarget,type,settings);
+    var sourceNode=this.findNodeById(this.startNode,transition.sourceNodeId);
+    if (sourceNode.isPlaceholder()){
+      var orignalTransition=this.findTransitionTo(this.startNode,sourceNode.id);
+      orignalTransition.targetNode=generatedNode;
+    }else{
+      transition.targetNode=generatedNode;
+    }
     return generatedNode;
   },
   deleteKillNode(node){
     let killNodes = this.get("killNodes");
     var killNodeReferenced=false;
-    this.nodeVisitor.process(this.startNode,function(n,ctx){
+    this.nodeVisitor.process(this.startNode,function(n){
       if (n.errorNode && n.errorNode.name===node.name){
         killNodeReferenced=true;
       }
@@ -195,35 +161,37 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
     var target=node.getDefaultTransitionTarget();
     if (node.isForkNode()|| node.isDecisionNode()){
       target=this.findJoinNode(node);
+      if (!target){//A bug will give target as null if the decision has single path.
+        target=node.getDefaultTransitionTarget();
+      }
       if (target.isJoinNode()){
         target=target.getDefaultTransitionTarget();
       }
     }
     var transitionslist=this.findTransistionsToNode(node);
     transitionslist.forEach(function(tran){
-      if (tran.getSourceNode().isDecisionNode()){
-        var joinNode=self.findJoinNode(tran.getSourceNode());
+      var sourceNode=self.findNodeById(self.startNode,tran.sourceNodeId);
+      var joinNode;
+      if (sourceNode.isDecisionNode()){
+        joinNode=self.findJoinNode(sourceNode);
         if (joinNode===target){
           if (tran.isDefaultCasePath()){
-            var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
-            tran.targetNode=placeholderNode;
-          }else   if (tran.getSourceNode().getOkTransitionCount()>2){
-            tran.getSourceNode().removeTransition(tran);
+            tran.targetNode=self.nodeFactory.createPlaceholderNode(target);
+          }else   if (sourceNode.getOkTransitionCount()>2){
+            sourceNode.removeTransition(tran);
           }else{
-            var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
-            tran.targetNode=placeholderNode;
+            tran.targetNode=self.nodeFactory.createPlaceholderNode(target);
           }
         }else{
           tran.targetNode=target;
         }
-      }else if (tran.getSourceNode().isForkNode()){
-        var joinNode=self.findJoinNode(tran.getSourceNode());
+      }else if (sourceNode.isForkNode()){
+        joinNode=self.findJoinNode(sourceNode);
         if (joinNode===target){
-          if (tran.getSourceNode().getOkTransitionCount()>2){
-            tran.getSourceNode().removeTransition(tran);
+          if (sourceNode.getOkTransitionCount()>2){
+            sourceNode.removeTransition(tran);
           }else{
-            var placeholderNode=self.nodeFactory.createPlaceholderNode(target);
-            tran.targetNode=placeholderNode;
+            tran.targetNode=self.nodeFactory.createPlaceholderNode(target);
           }
         }else{
           tran.targetNode=target;
@@ -234,13 +202,15 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
     });
   },
   deleteTransition(transition){
-    var src=transition.getSourceNode();
+    var src=this.findNodeById(this.startNode,transition.sourceNodeId);
     src.removeTransition(transition);
   },
   deleteEmptyTransitions(transitionslist){
+    var self=this;
     transitionslist.forEach(function(tran){
-      if (tran.getSourceNode().isForkNode()&& tran.getTargetNode().isJoinNode()){
-        tran.getSourceNode().removeTransition(tran);
+      var sourceNode=this.findNodeById(self.startNode,tran.sourceNodeId);
+      if (sourceNode.isForkNode()&& tran.getTargetNode().isJoinNode()){
+        sourceNode.removeTransition(tran);
       }
     });
   },
@@ -261,6 +231,4 @@ var Workflow= Ember.Object.extend(FindNodeMixin,{
     }
   }
 });
-
-
 export {Workflow};

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
index d5dc4da..70581bf 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/domain/workflow_xml_mapper.js
@@ -18,6 +18,7 @@
 import Ember from 'ember';
 import * as nodeHandler from '../domain/node-handler';
 import {SLAMapper} from "../domain/mapping-utils";
+import {ActionTypeResolver} from "../domain/action-type-resolver";
 
 import {MappingMixin,ConfigurationMapper} from "../domain/mapping-utils";
 var WorkflowXmlMapper= Ember.Object.extend({
@@ -27,7 +28,7 @@ var WorkflowXmlMapper= Ember.Object.extend({
   slaMapper: SLAMapper.create({}),
   schemaVersions:null,
   init: function() {
-    this.actionTypeResolver=nodeHandler.ActionTypeResolver.create({schemaVersions:this.schemaVersions});
+    this.actionTypeResolver=ActionTypeResolver.create({schemaVersions:this.schemaVersions});
     this.set("globalConfigHandler",GlobalConfigHandler.create({}));
     this.set("slaMapper",SLAMapper.create({}));
     this.nodeHandlerMap=new Map();
@@ -106,7 +107,7 @@ var WorkflowXmlMapper= Ember.Object.extend({
     if (!parameters|| !parameters.property){
       return;
     }
-    workflow.parameters={"configuration":{property:[]}}
+    workflow.parameters={"configuration":{property:[]}};
     parameters.property.forEach(function(prop){
       workflow.parameters.configuration.property.push({"name":prop.name,"value":prop.value});
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/helpers/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/helpers/.gitkeep b/contrib/views/wfmanager/src/main/resources/ui/app/helpers/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/index.html b/contrib/views/wfmanager/src/main/resources/ui/app/index.html
index a317a48..df243ad 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/index.html
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/index.html
@@ -33,6 +33,15 @@
 </head>
 
 <body>
+
+<div style="display:none;">
+<!-- preloading images for designer-->
+  <img src="assets/play.png"/>
+  <img src="assets/stop.png"/>
+  <img src="assets/join.png"/>
+  <img src="assets/sitemap.png"/>
+</div>
+
     {{content-for "body"}}
     <script src="assets/vendor.js"></script>
     <script src="assets/oozie-designer.js"></script>

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/routes/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/.gitkeep b/contrib/views/wfmanager/src/main/resources/ui/app/routes/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
index d0ef5e0..949d39a 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/dashboard.js
@@ -62,11 +62,19 @@ export default Ember.Route.extend({
     });
     return deferred.promise;
   },
+  setPageResultLen(){
+    /* 
+      setting the no of jobs to be displayed to multiple of 5
+    */
+    var relHeight = parseInt(Ember.$(window).width()/100); 
+    return relHeight - relHeight%5;
+  },
   search(params){
     params = params || {};
     var type = params.type || "wf",
     start = Number(params.start || 1),
-    len = Number(params.len || Ember.ENV.PAGE_SIZE),
+    //len = Number(params.len || Ember.ENV.PAGE_SIZE),
+    len = this.setPageResultLen(),
     index = 0,
     filter = params.filter || "",
     API_URL = Ember.ENV.API_URL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js b/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
index cec0e9e..5ed2619 100644
--- a/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/routes/design.js
@@ -1,25 +1,27 @@
 /*
- *    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.
- */
+*    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.Route.extend({
-xmlAppPath : null,
-beforeModel: function(transition){
-      this.set("xmlAppPath", transition.queryParams.appPath);
+
+  beforeModel: function(transition){
+    this.set("xmlAppPath", transition.queryParams.appPath);
+    this.controllerFor('design').set("xmlAppPath", transition.queryParams.appPath);
   }
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/d1b0bb9e/contrib/views/wfmanager/src/main/resources/ui/app/services/workflow-clipboard.js
----------------------------------------------------------------------
diff --git a/contrib/views/wfmanager/src/main/resources/ui/app/services/workflow-clipboard.js b/contrib/views/wfmanager/src/main/resources/ui/app/services/workflow-clipboard.js
new file mode 100644
index 0000000..8784cda
--- /dev/null
+++ b/contrib/views/wfmanager/src/main/resources/ui/app/services/workflow-clipboard.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.Service.extend({
+  clipboard : null,
+  setContent(node, operation){
+    var clipboardContent = {
+      name : node.name,
+      domain : Ember.copy(node.domain),
+      type : node.type,
+      actionType : node.actionType,
+      operation : operation
+    };
+    this.set('clipboard', clipboardContent);
+  },
+  getContent (){
+    return this.get('clipboard');
+  }
+});