azkaban-uncached

Details

diff --git a/src/java/azkaban/executor/ExecutorManager.java b/src/java/azkaban/executor/ExecutorManager.java
index f83b49c..db1eaff 100644
--- a/src/java/azkaban/executor/ExecutorManager.java
+++ b/src/java/azkaban/executor/ExecutorManager.java
@@ -107,7 +107,7 @@ public class ExecutorManager {
 	
 	public boolean isFlowRunning(int projectId, String flowId) {
 		for (Pair<ExecutionReference, ExecutableFlow> ref : runningFlows.values()) {
-			if (ref.getSecond().getFlowId().equals(flowId)) {
+			if (ref.getSecond().getProjectId() == projectId && ref.getSecond().getFlowId().equals(flowId)) {
 				return true;
 			}
 		}
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
index 9319c46..c29cab7 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
@@ -1,5 +1,6 @@
 <script type="text/javascript" src="${context}/js/azkaban.layout.js"></script>
 <script type="text/javascript" src="${context}/js/svgNavigate.js"></script>
+<script type="text/javascript" src="${context}/js/azkaban.context.menu.js"></script>
 <script type="text/javascript" src="${context}/js/azkaban.svg.graph.view.js"></script>
 <script type="text/javascript" src="${context}/js/azkaban.flow.execute.view.js"></script>
 
@@ -13,36 +14,107 @@
 	<div class="panel">
 		<div id="executionGraphOptions">
 			<div id="graphOptions" class="sideMenu">
-				<h3>Jobs</h3>
+				<h3 id="flowOption" viewpanel="svgDivCustom">Flow View</h3>
 				<div>
-					<p>Jobs Panel</p>
+					<p>Right click on the jobs to disable and enable jobs in the flow.</p>
 				</div>
-				<h3>Notification</h3>
+				<h3 viewpanel="notificationPanel">Notification</h3>
 				<div>
-					<p>Jobs Panel</p>
+					<p>Change the addresses where success and failure emails will be sent.</p>
 				</div>
-				<h3>Concurrent Runs</h3>
+				<h3 viewpanel="failureOptions">Failure Options</h3>
 				<div>
-					<p>Jobs Panel</p>
+					<p>Select flow behavior when a failure is detected.</p>
 				</div>
-				<h3>SLA</h3>
+				<h3 viewpanel="concurrentPanel">Concurrent</h3>
 				<div>
-					<p>Jobs Panel</p>
+					<p>Change the behavior of the flow if it is already running.</p>
 				</div>
-				<h3>Flow Parameters</h3>
+				<h3 viewpanel="slaPanel">SLA</h3>
 				<div>
-					<p>Jobs Panel</p>
+					<p>Add service level agreements to notify or kill the flow if it exceeds certain runtime duration.</p>
+				</div>
+				<h3 viewpanel="flowParametersPanel">Flow Parameters</h3>
+				<div>
+					<p>Add temporary flow parameters that are used to override global properties for each job.</p>
 				</div>
 			</div>
 		</div>
-		<div id="svgDivCustom" class="svgDiv" >
-			<svg class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
-			</svg>
+		<div id="executionGraphOptionsPanel" class="rightPanel">
+			<div id="svgDivCustom" class="svgDiv sidePanel" >
+				<svg class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
+				</svg>
+			</div>
+			<div id="notificationPanel" class="sidePanel">
+				<div>
+					<h4>Notify on Failure</h4>
+					<p>On a job failure, notify on either the first failure, and/or when the failed flow finishes.</p>
+					<input id="notifyFailureFirst" class="checkbox" type="checkbox" name="notify" value="first" checked /> <label for="notify">First Failure</label>
+					<input id="notifyFailureLast" class="checkbox" type="checkbox" name="notify" value="last"></input> <label for="notify">Flow Finished</label>
+					
+					
+					<h4>Failure Emails</h4>
+					<p>Notify these addresses on failure. Comma, space or semi-colon delimited list.</p>
+					<textarea id="failureEmails"></textarea>
+				</div>
+			
+				<div>
+					<h4>Success Emails</h4>
+					<p>Notify when the flow finishes successfully. Comma, space or semi-colon delimited list.</p>
+					<textarea id="successEmails"></textarea>
+				</div>
+			</div> 
+			<div id="failureOptions" class="failureOptions sidePanel">
+				<h4>Failure Options</h4>
+				<p>When a failure first occurs in the flow, select the execution behavior.</p>
+				<ul>
+					<li><span class="bold">Finish Current Running</span> finishes only the currently running jobs. It will not start any new jobs.</p></li>
+					<li><span class="bold">Cancel All</span> immediately kills all jobs and fails the flow.</p></li>
+					<li><span class="bold">Finish All Possible</span> will keep executing jobs as long as its dependencies are met.</p></li>
+				</ul>
+
+				<select id="failureAction" name="failureAction">
+					<option value="finishCurrent">Finish Current Running</option>
+					<option value="cancelImmediately">Cancel All</option>
+					<option value="finishPossible">Finish All Possible</option>
+				</select>
+			</div>
+			<div id="concurrentPanel" class="sidePanel">
+				<h4>Concurrent Execution Options</h4>
+				<p>If the flow is currently running, these are the options that can be set.</p>
+
+				<input id="ignore" class="radio" type="radio" name="concurrent" value="skip" checked /><label for="skip">Skip Execution</label>
+				<p>Do not run flow if it is already running.</p>
+				
+				<input id="ignore" class="radio" type="radio" name="concurrent" value="ignore" checked /><label for="ignore">Run Concurrently</label>
+				<p>Run the flow anyways. Previous execution is unaffected.</p>
+
+				<input id="pipeline" class="radio" type="radio" name="concurrent" value="pipeline" /><label for="pipeline">Pipeline</label>
+				<select id="pipelineLevel" name="pipelineLevel">
+					<option value="1">Level 1</option>
+					<option value="2">Level 2</option>
+				</select>
+				<p>Pipeline the flow, so the current execution will not be overrun.</p>
+				<ul>
+					<li>Level 1: block job A until the previous flow job A has completed.</li>
+					<li>Level 2: block job A until the previous flow job A's children have completed.</li>
+				</ul>
+				<input id="queue" class="radio" type="radio" name="concurrent" value="queue" /><label for="queue">Queue Job</label>
+				<select id="queueLevel" name="queueLevel">
+					<option value="1">1</option>
+					<option value="2">2</option>
+				</select>
+				<p>Queue up to 2. Wait until the previous execution has completed before running.</p>
+			</div>
+			<div id="slaPanel" class="sidePanel">
+			</div>
+			<div id="flowParametersPanel" class="sidePanel">
+			</div>
 		</div>
 	</div>
 	
 	<div class="actions">
-		<a class="btn2" id="advanced-btn">Advanced Settings</a>
+		<a class="btn2" id="schedule-btn">Schedule</a>
 		<a class="yes btn1" id="execute-btn">Execute</a>
 		<a class="no simplemodal-close btn3">Cancel</a>
 	</div>
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index 845017d..e9b543e 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -1336,21 +1336,82 @@ tr:hover td {
 	width: 100%;
 	height: 100%;
 	background: #fff;
+	left: 0px
 }
 
-
-.svgDiv {
+.rightPanel {
 	position: absolute;
 	top: 0px;
 	right: 0px;
-	left: 260px;
+	left: 270px;
 	bottom: 0px;
 }
+.rightPanel .sidePanel {
+	width: 100%;
+	height: 100%;
+	background: #fff;
+	left: 0px;
+	overflow:auto;
+}
+.rightPanel h4 {
+	font-size: 11pt;
+	padding: 0px;
+	margin: 15px 10px 5px 2px;
+}
+
+.rightPanel p {
+	font-size: 10pt;
+	margin-left: 25px;
+	color: #555;
+}
+
+.rightPanel .sidePanel.svgDiv {
+	overflow:hidden;
+}
+
+.rightPanel .checkbox {
+	height: 10px;
+	margin-left: 20px;
+}
+
+.rightPanel label {
+	font-size: 11pt;
+}
+
+.rightPanel textarea {
+	font-size: 10pt;
+	width: 500px;
+}
+
+.rightPanel .radio {
+	margin-left: 15px;
+	margin-bottom: 5px;
+	margin-top: 15px;
+}
 
 .radioLabel.disabled {
 	opacity: 0.3;
 }
 
+.sideMenu p {
+	color: #555;
+}
+
+.rightPanel ul {
+	margin-left: 20px;
+}
+
+.rightPanel li {
+	list-style-type: circle;
+	margin-left: 20px;
+	font-size: 9pt;
+	color: #555;
+}
+
+.rightPanel select {
+	margin: 15px 20px 5px 20px;
+}
+
 /* executing options panel*/
 #executing-options {
 	left: 100px;
@@ -2671,7 +2732,7 @@ tr.row td.tb-name {
 	float: left;
 }
 
-#advanced-btn {
+#schedule-btn {
 	float: left;
 	margin-left: 20px;
 }
@@ -2688,7 +2749,7 @@ tr.row td.tb-name {
 #execute-flow-panel .svgDiv {
 	position: absolute;
 	padding: 1px;
-	left: 270px;
+	left: 0px;
 	right: 0px;
 	top: 0px;
 	bottom: 0px;
@@ -2703,6 +2764,29 @@ tr.row td.tb-name {
 	border: none;
 }
 
+h3.menuHeader {
+	font-size: 12pt;
+	margin: 0px 20px;
+	padding: 5px 3px 5px 15px;
+	color: #888;
+	border: none;
+}
+
+h3.menuHeader:hover {
+	color: #000;
+	background-color: #CCC;
+}
+
+h3.menuHeader.selected {
+	color: #000;
+}
+
+div.menuContent {
+	font-size: 10pt;
+	padding-left: 20px;
+	padding-bottom: 10px;
+}
+
 .contextMenu {
 	position: absolute;
 	background-color: #FFF;
diff --git a/src/web/js/azkaban.context.menu.js b/src/web/js/azkaban.context.menu.js
index cdb2c9c..e17b61f 100644
--- a/src/web/js/azkaban.context.menu.js
+++ b/src/web/js/azkaban.context.menu.js
@@ -1,19 +1,90 @@
 $.namespace('azkaban');
 
-var flowExecuteDialogView;
-azkaban.FlowExecuteDialogView= Backbone.View.extend({
-  events : {
-  },
-  initialize : function(settings) {
-  },
-  render: function() {
-  },
-  showContextMenu: function(menuData) {
-  	//$('#execute-flow-panel').show();
-  	$(this.el).show();
-  },
-  hideContextMenu: function(menuData) {
-  	//$('#execute-flow-panel').hide();
-  	$(this.el).hide();
-  }
+azkaban.ContextMenuView = Backbone.View.extend({
+	events :  {
+	},
+	initialize : function(settings) {
+		var div = this.el;
+		$('body').click(function(e) {
+			$(".contextMenu").remove();
+		});
+		$('body').bind("contextmenu", function(e) {$(".contextMenu").remove()});
+	},
+	show : function(evt, menu) {
+		console.log("Show context menu");
+		$(".contextMenu").remove();
+		var x = evt.pageX;
+		var y = evt.pageY;
+
+		var contextMenu = this.setupMenu(menu);
+		$(contextMenu).css({top: y, left: x});
+		$(this.el).after(contextMenu);
+	},
+	hide : function(evt) {
+		console.log("Hide context menu");
+		$(".contextMenu").remove();
+	},
+	handleClick: function(evt) {
+		console.log("handling click");
+	},
+	setupMenu: function(menu) {
+		var contextMenu = document.createElement("div");
+		$(contextMenu).addClass("contextMenu");
+		var ul = document.createElement("ul");
+		$(contextMenu).append(ul);
+
+		for (var i = 0; i < menu.length; ++i) {
+			var menuItem = document.createElement("li");
+			if (menu[i].break) {
+				$(menuItem).addClass("break");
+			}
+			else {
+				var title = menu[i].title;
+				var callback = menu[i].callback;
+				$(menuItem).addClass("menuitem");
+				$(menuItem).text(title);
+				menuItem.callback = callback;
+				$(menuItem).click(function() { 
+					$(contextMenu).hide(); 
+					this.callback.call();});
+					
+				if (menu[i].submenu) {
+					var expandSymbol = document.createElement("div");
+					$(expandSymbol).addClass("expandSymbol");
+					$(menuItem).append(expandSymbol);
+					
+					var subMenu = this.setupMenu(menu[i].submenu);
+					$(subMenu).addClass("subMenu");
+					subMenu.parent = contextMenu;
+					menuItem.subMenu = subMenu;
+					$(subMenu).hide();
+					$(this.el).after(subMenu);
+					
+					$(menuItem).mouseenter(function() {
+						$(".subMenu").hide();
+						var menuItem = this;
+						menuItem.selected = true;
+						setTimeout(function() {
+							if (menuItem.selected) {
+								var offset = $(menuItem).offset();
+								var left = offset.left;
+								var top = offset.top;
+								var width = $(menuItem).width();
+								var subMenu = menuItem.subMenu;
+								
+								var newLeft = left + width - 5;
+								$(subMenu).css({left: newLeft, top: top});
+								$(subMenu).show();
+							}
+						}, 500);
+					});
+					$(menuItem).mouseleave(function() {this.selected = false;});
+				}
+			}
+
+			$(ul).append(menuItem);
+		}
+
+		return contextMenu;
+	}
 });
\ No newline at end of file
diff --git a/src/web/js/azkaban.flow.execute.view.js b/src/web/js/azkaban.flow.execute.view.js
index ff2931d..fa867eb 100644
--- a/src/web/js/azkaban.flow.execute.view.js
+++ b/src/web/js/azkaban.flow.execute.view.js
@@ -95,119 +95,61 @@ azkaban.FlowExecuteDialogView= Backbone.View.extend({
 var sideMenuDialogView;
 azkaban.SideMenuDialogView= Backbone.View.extend({
 	events : {
-		"click .menuHeader" : "expandItem"
+		"click .menuHeader" : "menuClick"
   	},
   	initialize : function(settings) {
   		var children = $(this.el).children();
+  		var currentParent;
+  		var parents = [];
+  		var realChildren = [];
   		for (var i = 0; i < children.length; ++i ) {
   			var child = children[i];
   			if ((i % 2) == 0) {
+  				currentParent = child;
   				$(child).addClass("menuHeader");
+  				parents.push(child);
   			}
   			else {
   				$(child).addClass("menuContent");
   				$(child).hide();
-  				
-  			
+  				currentParent.child = child;
+  				realChildren.push(child);
   			}
   		}
+  		
+  		this.menuSelect($("#flowOption"));
+  		
+  		this.parents = parents;
+  		this.children = realChildren;
   	},
-  	expandItem : function(self) {
+  	menuClick : function(evt) {
+  		this.menuSelect(evt.currentTarget);
+  	},
+  	menuSelect : function(target) {
+  		if ($(target).hasClass("selected")) {
+  			return;
+  		}
+  		
+  		$(".sidePanel").each(function() {
+  			$(this).hide();
+  		});
+  		
+  		$(".menuHeader").each(function() {
+  			$(this.child).slideUp("fast");
+  			$(this).removeClass("selected");
+  		});
+  		
+  		$(".sidePanel").each(function() {
+  			$(this).hide();
+  		});
   		
+  		$(target).addClass("selected");
+  		$(target.child).slideDown("fast");
+  		var panelName = $(target).attr("viewpanel");
+  		$("#" + panelName).show();
   	}
 });
 
-var contextMenuView;
-azkaban.ContextMenuView = Backbone.View.extend({
-	events :  {
-	},
-	initialize : function(settings) {
-		var div = this.el;
-		$('body').click(function(e) {
-			$(".contextMenu").remove();
-		});
-		$('body').bind("contextmenu", function(e) {$(".contextMenu").remove()});
-		this.svgGraph = settings.graph;
-	},
-	show : function(evt, menu) {
-		console.log("Show context menu");
-		$(".contextMenu").remove();
-		var x = evt.pageX;
-		var y = evt.pageY;
-
-		var contextMenu = this.setupMenu(menu);
-		$(contextMenu).css({top: y, left: x});
-		$(this.el).after(contextMenu);
-	},
-	hide : function(evt) {
-		console.log("Hide context menu");
-		$(".contextMenu").remove();
-	},
-	handleClick: function(evt) {
-		console.log("handling click");
-	},
-	setupMenu: function(menu) {
-		var contextMenu = document.createElement("div");
-		$(contextMenu).addClass("contextMenu");
-		var ul = document.createElement("ul");
-		$(contextMenu).append(ul);
-
-		for (var i = 0; i < menu.length; ++i) {
-			var menuItem = document.createElement("li");
-			if (menu[i].break) {
-				$(menuItem).addClass("break");
-			}
-			else {
-				var title = menu[i].title;
-				var callback = menu[i].callback;
-				$(menuItem).addClass("menuitem");
-				$(menuItem).text(title);
-				menuItem.callback = callback;
-				$(menuItem).click(function() { 
-					$(contextMenu).hide(); 
-					this.callback.call();});
-					
-				if (menu[i].submenu) {
-					var expandSymbol = document.createElement("div");
-					$(expandSymbol).addClass("expandSymbol");
-					$(menuItem).append(expandSymbol);
-					
-					var subMenu = this.setupMenu(menu[i].submenu);
-					$(subMenu).addClass("subMenu");
-					subMenu.parent = contextMenu;
-					menuItem.subMenu = subMenu;
-					$(subMenu).hide();
-					$(this.el).after(subMenu);
-					
-					$(menuItem).mouseenter(function() {
-						$(".subMenu").hide();
-						var menuItem = this;
-						menuItem.selected = true;
-						setTimeout(function() {
-							if (menuItem.selected) {
-								var offset = $(menuItem).offset();
-								var left = offset.left;
-								var top = offset.top;
-								var width = $(menuItem).width();
-								var subMenu = menuItem.subMenu;
-								
-								var newLeft = left + width - 5;
-								$(subMenu).css({left: newLeft, top: top});
-								$(subMenu).show();
-							}
-						}, 500);
-					});
-					$(menuItem).mouseleave(function() {this.selected = false;});
-				}
-			}
-
-			$(ul).append(menuItem);
-		}
-
-		return contextMenu;
-	}
-});
-
 var handleJobMenuClick = function(action, el, pos) {
 	var jobid = el[0].jobid;
 	
@@ -305,7 +247,7 @@ var nodeClickCallback = function(event) {
 	var flowId = executableGraphModel.get("flowId");
 	var requestURL = contextURL + "/manager?project=" + projectId + "&flow=" + flowId + "&job=" + jobId;
 
-	var menu = [	{title: "Open in New Window...", callback: function() {window.location.href=requestURL;}},
+	var menu = [	{title: "Open Job in New Window...", callback: function() {window.location.href=requestURL;}},
 			{break: 1},
 			{title: "Enable", callback: function() {touchNode(jobId, false);}, submenu: [
 									{title: "Parents", callback: function(){touchParents(jobId, false);}},
@@ -334,14 +276,28 @@ var edgeClickCallback = function(event) {
 
 var graphClickCallback = function(event) {
 	console.log("Graph clicked callback");
+	var flowId = executableGraphModel.get("flowId");
+	var requestURL = contextURL + "/manager?project=" + projectId + "&flow=" + flowId;
+	
+	var menu = [	{title: "Open Flow in New Window...", callback: function() {window.location.href=requestURL;}},
+		{break: 1},
+		{title: "Enable All", callback: function() {enableAll();}},
+		{title: "Disable All", callback: function() {disableAll();}},
+		{break: 1},
+		{title: "Center Graph", callback: function() {executableGraphModel.trigger("resetPanZoom");}}
+	];
+	
+	contextMenuView.show(event, menu);
 }
 
+var contextMenuView;
 $(function() {
 	flowExecuteDialogView = new azkaban.FlowExecuteDialogView({el:$('#execute-flow-panel')});
 	executableGraphModel = new azkaban.GraphModel();
 	svgGraphView = new azkaban.SvgGraphView({el:$('#svgDivCustom'), model: executableGraphModel, topGId:"topG", graphMargin: 10, rightClick: { "node": nodeClickCallback, "edge": edgeClickCallback, "graph": graphClickCallback }});
 	
 	sideMenuDialogView = new azkaban.SideMenuDialogView({el:$('#graphOptions')});
+	
 	var svgGraph = document.getElementById('svgGraph');
 	contextMenuView = new azkaban.ContextMenuView({el:$('#contextMenu'), graph: svgGraph});
 });