azkaban-memoizeit
Changes
src/java/azkaban/flow/Node.java 18(+17 -1)
src/java/azkaban/user/User.java 6(+6 -0)
src/web/css/azkaban.css 140(+6 -134)
src/web/css/azkaban-graph.css 198(+198 -0)
src/web/images/graph-icon.png 0(+0 -0)
src/web/js/azkaban.exflow.view.js 8(+6 -2)
src/web/js/azkaban.flow.job.view.js 4(+4 -0)
src/web/js/azkaban.flow.view.js 8(+6 -2)
src/web/js/azkaban.layout.js 35(+20 -15)
src/web/js/azkaban.svg.graph.view.js 301(+250 -51)
src/web/js/svgNavigate.js 15(+13 -2)
unit/build.xml 11(+11 -0)
unit/executions/embedded/joba.job 4(+4 -0)
unit/executions/embedded/jobb.job 3(+3 -0)
unit/executions/embedded/jobc.job 3(+3 -0)
unit/executions/embedded/jobd.job 3(+3 -0)
unit/executions/embedded/jobe.job 5(+5 -0)
Details
src/java/azkaban/flow/Node.java 18(+17 -1)
diff --git a/src/java/azkaban/flow/Node.java b/src/java/azkaban/flow/Node.java
index fbe5a39..03ca850 100644
--- a/src/java/azkaban/flow/Node.java
+++ b/src/java/azkaban/flow/Node.java
@@ -32,6 +32,8 @@ public class Node {
private int expectedRunTimeSec = 1;
private String type;
+ private String embeddedFlowId;
+
public Node(String id) {
this.id = id;
}
@@ -102,6 +104,14 @@ public class Node {
return expectedRunTimeSec;
}
+ public void setEmbeddedFlowId(String flowId) {
+ embeddedFlowId = flowId;
+ }
+
+ public String getEmbeddedFlowId() {
+ return embeddedFlowId;
+ }
+
@SuppressWarnings("unchecked")
public static Node fromObject(Object obj) {
Map<String,Object> mapObj = (Map<String,Object>)obj;
@@ -111,10 +121,13 @@ public class Node {
String jobSource = (String)mapObj.get("jobSource");
String propSource = (String)mapObj.get("propSource");
String jobType = (String)mapObj.get("jobType");
-
+
+ String embeddedFlowId = (String)mapObj.get("embeddedFlowId");
+
node.setJobSource(jobSource);
node.setPropsSource(propSource);
node.setType(jobType);
+ node.setEmbeddedFlowId(embeddedFlowId);
Integer expectedRuntime = (Integer)mapObj.get("expectedRuntime");
if (expectedRuntime != null) {
@@ -153,6 +166,9 @@ public class Node {
objMap.put("jobSource", jobSource);
objMap.put("propSource", propsSource);
objMap.put("jobType", type);
+ if (embeddedFlowId != null) {
+ objMap.put("embeddedFlowId", embeddedFlowId);
+ }
objMap.put("expectedRuntime", expectedRunTimeSec);
HashMap<String, Object> layoutInfo = new HashMap<String, Object>();
diff --git a/src/java/azkaban/jobExecutor/AbstractJob.java b/src/java/azkaban/jobExecutor/AbstractJob.java
index 4e5f573..74f5a07 100644
--- a/src/java/azkaban/jobExecutor/AbstractJob.java
+++ b/src/java/azkaban/jobExecutor/AbstractJob.java
@@ -28,7 +28,6 @@ public abstract class AbstractJob implements Job {
public static final String JOB_FULLPATH = "job.fullpath";
public static final String JOB_ID = "job.id";
-
private final String _id;
private final Logger _log;
private volatile double _progress;
src/java/azkaban/user/User.java 6(+6 -0)
diff --git a/src/java/azkaban/user/User.java b/src/java/azkaban/user/User.java
index d538e72..ee5abbf 100644
--- a/src/java/azkaban/user/User.java
+++ b/src/java/azkaban/user/User.java
@@ -17,6 +17,7 @@
package azkaban.user;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -25,6 +26,7 @@ public class User {
private final String userid;
private Set<String> roles = new HashSet<String>();
private Set<String> groups = new HashSet<String>();
+ private HashMap<String,String> properties = new HashMap<String,String>();
public User(String userid) {
this.userid = userid;
@@ -62,6 +64,10 @@ public class User {
return roles.contains(role);
}
+ public String getProperty(String name) {
+ return properties.get(name);
+ }
+
public String toString() {
String groupStr = "[";
for (String group: groups) {
diff --git a/src/java/azkaban/utils/DirectoryFlowLoader.java b/src/java/azkaban/utils/DirectoryFlowLoader.java
index bad673c..0343033 100644
--- a/src/java/azkaban/utils/DirectoryFlowLoader.java
+++ b/src/java/azkaban/utils/DirectoryFlowLoader.java
@@ -71,7 +71,8 @@ public class DirectoryFlowLoader {
duplicateJobs = new HashSet<String>();
nodeDependencies = new HashMap<String, Map<String, Edge>>();
rootNodes = new HashSet<String>();
-
+ flowDependencies = new HashMap<String, Set<String>>();
+
// Load all the props files and create the Node objects
loadProjectFromDir(baseDirectory.getPath(), baseDirectory, null);
@@ -163,9 +164,12 @@ public class DirectoryFlowLoader {
}
private void resolveEmbeddedFlow(String flowId, Set<String> visited) {
- visited.add(flowId);
-
Set<String> embeddedFlow = flowDependencies.get(flowId);
+ if (embeddedFlow == null) {
+ return;
+ }
+
+ visited.add(flowId);
for (String embeddedFlowId: embeddedFlow) {
if (visited.contains(embeddedFlowId)) {
errors.add("Embedded flow cycle found in " + flowId + "->" + embeddedFlowId);
@@ -305,6 +309,7 @@ public class DirectoryFlowLoader {
flowDependencies.put(flow.getId(), embeddedFlows);
}
+ node.setEmbeddedFlowId(embeddedFlow);
embeddedFlows.add(embeddedFlow);
}
Map<String, Edge> dependencies = nodeDependencies.get(node.getId());
diff --git a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
index 0b2d413..ea5b901 100644
--- a/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
+++ b/src/java/azkaban/webapp/servlet/ProjectManagerServlet.java
@@ -513,7 +513,11 @@ public class ProjectManagerServlet extends LoginAbstractAzkabanServlet {
HashMap<String, Object> nodeObj = new HashMap<String,Object>();
nodeObj.put("id", node.getId());
nodeObj.put("level", node.getLevel());
-
+ nodeObj.put("type", node.getType());
+ if (node.getEmbeddedFlowId() != null) {
+ nodeObj.put("flowId", node.getEmbeddedFlowId());
+ }
+
nodeList.add(nodeObj);
}
diff --git a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
index de86051..7a03448 100644
--- a/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/executingflowpage.vm
@@ -47,6 +47,7 @@
var execId = "${execid}";
</script>
<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
+ <link rel="stylesheet" type="text/css" href="${context}/css/azkaban-graph.css" />
</head>
<body>
#set($current_page="all")
@@ -98,7 +99,12 @@
</div>
<div id="list" class="list">
</div>
- <div id="resetPanZoomBtn" class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
+ <div id="flowGraphOptions">
+ <div id="autoPanZoom">
+ <input type="checkbox" id="autoPanZoomCheckbox" class="autoPanZoom" value="autoPanZoom" /><label for="autoPanZoom">Auto Pan Zoom</label>
+ </div>
+ <div id="resetPanZoomBtn" class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
+ </div>
</div>
<div id="svgDiv" class="svgDiv">
<svg id="svgGraph" class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
index 6d4ddcb..aab0e61 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowexecutionpanel.vm
@@ -3,6 +3,7 @@
<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>
+<link rel="stylesheet" type="text/css" href="${context}/css/azkaban-graph.css" />
<div id="modalBackground" class="modalBackground2">
<div id="execute-flow-panel" class="modal modalContainer2">
diff --git a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
index d1ab610..1bec383 100644
--- a/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
+++ b/src/java/azkaban/webapp/servlet/velocity/flowpage.vm
@@ -31,7 +31,6 @@
<script type="text/javascript" src="${context}/js/azkaban.nav.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.layout.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.flow.job.view.js"></script>
- <script type="text/javascript" src="${context}/js/azkaban.flow.graph.view.js"></script>
<script type="text/javascript" src="${context}/js/azkaban.flow.view.js"></script>
<script type="text/javascript" src="${context}/js/svgNavigate.js"></script>
<script type="text/javascript">
@@ -47,6 +46,7 @@
var execId = null;
</script>
<link rel="stylesheet" type="text/css" href="${context}/css/jquery-ui-1.10.1.custom.css" />
+ <link rel="stylesheet" type="text/css" href="${context}/css/azkaban-graph.css" />
</head>
<body>
#set($current_page="all")
@@ -87,7 +87,12 @@
</div>
<div id="list" class="list">
</div>
- <div id="resetPanZoomBtn" class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
+ <div id="flowGraphOptions">
+ <div id="autoPanZoom">
+ <input type="checkbox" id="autoPanZoomCheckbox" class="autoPanZoom" value="autoPanZoom" /><label for="autoPanZoom">Auto Pan Zoom</label>
+ </div>
+ <div id="resetPanZoomBtn" class="btn5 resetPanZoomBtn" >Reset Pan Zoom</div>
+ </div>
</div>
<div id="svgDiv" class="svgDiv">
<svg id="svgGraph" class="svgGraph" xmlns="http://www.w3.org/2000/svg" version="1.1" shape-rendering="optimize-speed" text-rendering="optimize-speed" >
diff --git a/src/java/azkaban/webapp/session/SessionCache.java b/src/java/azkaban/webapp/session/SessionCache.java
index be76c55..09d0b36 100644
--- a/src/java/azkaban/webapp/session/SessionCache.java
+++ b/src/java/azkaban/webapp/session/SessionCache.java
@@ -33,7 +33,7 @@ import azkaban.utils.cache.Element;
*/
public class SessionCache {
private static final int MAX_NUM_SESSIONS = 10000;
- private static final int SESSION_TIME_TO_LIVE = 10000;
+ private static final int SESSION_TIME_TO_LIVE = 86400000;
// private CacheManager manager = CacheManager.create();
private Cache cache;
diff --git a/src/package/webserver/bin/azkaban-web-start.sh b/src/package/webserver/bin/azkaban-web-start.sh
index bdd19e0..dba3bb9 100755
--- a/src/package/webserver/bin/azkaban-web-start.sh
+++ b/src/package/webserver/bin/azkaban-web-start.sh
@@ -28,7 +28,7 @@ serverpath=`pwd`
if [ -z $AZKABAN_OPTS ]; then
AZKABAN_OPTS=-Xmx3G
fi
-AZKABAN_OPTS=$AZKABAN_OPTS -server -Dcom.sun.management.jmxremote -Djava.io.tmpdir=$tmpdir -Dexecutorport=$executorport -Dserverpath=$serverpath
+AZKABAN_OPTS="$AZKABAN_OPTS -server -Dcom.sun.management.jmxremote -Djava.io.tmpdir=$tmpdir -Dexecutorport=$executorport -Dserverpath=$serverpath"
java $AZKABAN_OPTS -cp $CLASSPATH azkaban.webapp.AzkabanWebServer -conf $azkaban_dir/conf $@ &
src/web/css/azkaban.css 140(+6 -134)
diff --git a/src/web/css/azkaban.css b/src/web/css/azkaban.css
index aadf527..f7f3fa8 100644
--- a/src/web/css/azkaban.css
+++ b/src/web/css/azkaban.css
@@ -1833,9 +1833,13 @@ tr:hover td {
width: 250px;
}
-.resetPanZoomBtn {
+#flowGraphOptions {
position: absolute;
- bottom: 90px;
+ bottom: 70px;
+}
+
+#flowGraphOptions label{
+ font-size: 9pt;
}
#nodeOptions {
@@ -2135,138 +2139,6 @@ table.parameters tr td {
text-align:center;
}
-svg .edge {
- stroke: #777;
- stroke-width: 2;
-}
-
-svg .edge:hover {
- stroke: #009FC9;
- stroke-width: 4;
-}
-
-svg .node.disabled {
- opacity: 0.3;
-}
-
-svg .node .backboard {
- fill: #FFF;
- opacity: 0.05;
-}
-
-svg .node:hover {
- cursor: pointer;
-}
-
-svg .node:hover .backboard {
- opacity: 0.7;
-}
-
-svg .selected .backboard {
- opacity: 0.4;
-}
-
-svg .node circle {
- fill: #888;
- stroke: #777;
- stroke-width: 2;
-}
-
-svg .node:hover circle {
- stroke: #009FC9;
-}
-
-svg .node:hover text {
- fill: #009FC9;
-}
-
-svg .selected text {
- fill: #338AB0;
-}
-
-svg .selected circle {
- stroke: #009FC9;
- stroke-width: 4;
-}
-
-svg .READY circle {
- fill: #CCC;
-}
-
-svg .RUNNING circle {
- fill: #009FC9;
-}
-
-svg .QUEUED circle {
- opacity: 0.5;
- fill: #009FC9;
-}
-
-svg .FAILED circle {
- fill: #CC0000;
-}
-
-svg .KILLED circle {
- fill: #CC0000;
-}
-
-svg .SUCCEEDED circle {
- fill: #00CC33;
-}
-
-svg .DISABLED circle {
- opacity: 0.3;
-}
-
-svg .SKIPPED circle {
- opacity: 0.3;
-}
-
-#Used for charts
-svg circle.READY {
- stroke: #CCC;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.RUNNING {
- stroke: #009FC9;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.FAILED {
- stroke: #CC0000;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.KILLED {
- stroke: #CC0000;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.SUCCEEDED {
- stroke: #00CC33;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.DISABLED {
- stroke: #CCC;
- opacity: 0.3;
- stroke-width: 2px;
- fill: #FFF;
-}
-
-svg circle.SKIPPED {
- stroke: #CCC;
- opacity: 0.3;
- stroke-width: 2px;
- fill: #FFF;
-}
-
span.sublabel {
font-size: 8pt;
margin-left: 12px;
src/web/css/azkaban-graph.css 198(+198 -0)
diff --git a/src/web/css/azkaban-graph.css b/src/web/css/azkaban-graph.css
new file mode 100644
index 0000000..c20e6d9
--- /dev/null
+++ b/src/web/css/azkaban-graph.css
@@ -0,0 +1,198 @@
+
+svg .edge {
+ stroke: #BBB;
+ stroke-width: 2;
+}
+
+svg .edge:hover {
+ stroke: #009FC9;
+ stroke-width: 4;
+}
+
+svg .node.disabled {
+ opacity: 0.3;
+}
+
+svg .node .backboard {
+ fill: #FFF;
+ opacity: 0.05;
+}
+
+svg .node:hover {
+ cursor: pointer;
+}
+
+svg .node:hover .backboard {
+ opacity: 0.7;
+}
+
+svg .selected .backboard {
+ opacity: 0.4;
+}
+
+svg .node circle {
+ fill: #888;
+ stroke: #777;
+ stroke-width: 2;
+}
+
+svg .node:hover circle {
+ stroke: #009FC9;
+}
+
+svg .node:hover text {
+ fill: #009FC9;
+}
+
+svg .selected text {
+ fill: #338AB0;
+}
+
+svg .selected circle {
+ stroke: #009FC9;
+ stroke-width: 4;
+}
+
+svg .READY circle {
+ fill: #CCC;
+}
+
+svg .RUNNING circle {
+ fill: #009FC9;
+}
+
+svg .QUEUED circle {
+ opacity: 0.5;
+ fill: #009FC9;
+}
+
+svg .FAILED circle {
+ fill: #CC0000;
+}
+
+svg .KILLED circle {
+ fill: #CC0000;
+}
+
+svg .SUCCEEDED circle {
+ fill: #00CC33;
+}
+
+svg .DISABLED circle {
+ opacity: 0.3;
+}
+
+svg .SKIPPED circle {
+ opacity: 0.3;
+}
+
+svg .selected circle {
+ stroke: #009FC9;
+ stroke-width: 4;
+}
+
+svg .selected .nodebox rect {
+ stroke: #009FC9;
+ stroke-width: 3;
+}
+
+svg .node rect {
+ fill: #F8F8F8;
+ stroke: #CCC;
+ stroke-width: 2;
+}
+
+svg .node .nodebox text {
+ fill: #FFF;
+}
+
+svg .READY .nodebox text {
+ fill: #000;
+}
+
+svg .node:hover rect {
+ stroke: #009FC9;
+}
+
+svg .READY rect {
+ fill: #EEE;
+}
+
+svg .RUNNING rect {
+ fill: #009FC9;
+}
+
+svg .QUEUED rect {
+ opacity: 0.5;
+ fill: #009FC9;
+}
+
+svg .FAILED rect {
+ fill: #CC0000;
+}
+
+svg .KILLED rect {
+ fill: #CC0000;
+}
+
+svg .SUCCEEDED rect {
+ fill: #30ad23;
+}
+
+svg .DISABLED rect {
+ opacity: 0.3;
+}
+
+svg .SKIPPED rect {
+ opacity: 0.3;
+}
+
+svg .nodebox text {
+ fill: #fff;
+}
+
+
+#Used for charts
+svg circle.READY {
+ stroke: #CCC;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.RUNNING {
+ stroke: #009FC9;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.FAILED {
+ stroke: #CC0000;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.KILLED {
+ stroke: #CC0000;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.SUCCEEDED {
+ stroke: #00CC33;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.DISABLED {
+ stroke: #CCC;
+ opacity: 0.3;
+ stroke-width: 2px;
+ fill: #FFF;
+}
+
+svg circle.SKIPPED {
+ stroke: #CCC;
+ opacity: 0.3;
+ stroke-width: 2px;
+ fill: #FFF;
+}
src/web/images/graph-icon.png 0(+0 -0)
diff --git a/src/web/images/graph-icon.png b/src/web/images/graph-icon.png
new file mode 100644
index 0000000..d315d97
Binary files /dev/null and b/src/web/images/graph-icon.png differ
src/web/js/azkaban.exflow.view.js 8(+6 -2)
diff --git a/src/web/js/azkaban.exflow.view.js b/src/web/js/azkaban.exflow.view.js
index bde19da..388c42e 100644
--- a/src/web/js/azkaban.exflow.view.js
+++ b/src/web/js/azkaban.exflow.view.js
@@ -634,7 +634,9 @@ var exNodeClickCallback = function(event) {
var menu = [
{title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+ {break: 1},
+ {title: "Center Job", callback: function() {graphModel.trigger("centerNode", jobId)}}
];
contextMenuView.show(event, menu);
@@ -647,7 +649,9 @@ var exJobClickCallback = function(event) {
var menu = [
{title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+ {break: 1},
+ {title: "Center Job", callback: function() {graphModel.trigger("centerNode", jobId)}}
];
contextMenuView.show(event, menu);
diff --git a/src/web/js/azkaban.flow.execute.view.js b/src/web/js/azkaban.flow.execute.view.js
index c8aed58..90319c6 100644
--- a/src/web/js/azkaban.flow.execute.view.js
+++ b/src/web/js/azkaban.flow.execute.view.js
@@ -548,7 +548,9 @@ var nodeClickCallback = function(event) {
{title: "Descendents", callback: function(){touchDescendents(jobId, true);}},
{title: "Disable All", callback: function(){disableAll();}}
]
- }
+ },
+ {break: 1},
+ {title: "Center Job", callback: function() {executableGraphModel.trigger("centerNode", jobId)}}
];
contextMenuView.show(event, menu);
src/web/js/azkaban.flow.job.view.js 4(+4 -0)
diff --git a/src/web/js/azkaban.flow.job.view.js b/src/web/js/azkaban.flow.job.view.js
index fa14b58..a85f9a2 100644
--- a/src/web/js/azkaban.flow.job.view.js
+++ b/src/web/js/azkaban.flow.job.view.js
@@ -3,6 +3,7 @@ azkaban.JobListView = Backbone.View.extend({
"keyup input": "filterJobs",
"click li": "handleJobClick",
"click .resetPanZoomBtn" : "handleResetPanZoom",
+ "change .autoPanZoom" : "handleAutoPanZoom",
"contextmenu li" : "handleContextMenuClick"
},
initialize: function(settings) {
@@ -192,5 +193,8 @@ azkaban.JobListView = Backbone.View.extend({
},
handleResetPanZoom: function(evt) {
this.model.trigger("resetPanZoom");
+ },
+ handleAutoPanZoom: function(evt) {
+ this.model.set({"autoPanZoom": $(evt.currentTarget).is(':checked')});
}
});
src/web/js/azkaban.flow.view.js 8(+6 -2)
diff --git a/src/web/js/azkaban.flow.view.js b/src/web/js/azkaban.flow.view.js
index 9f3536b..46a336f 100644
--- a/src/web/js/azkaban.flow.view.js
+++ b/src/web/js/azkaban.flow.view.js
@@ -282,7 +282,9 @@ var exNodeClickCallback = function(event) {
var menu = [
{title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+ {break: 1},
+ {title: "Center Job", callback: function() {graphModel.trigger("centerNode", jobId)}}
];
contextMenuView.show(event, menu);
@@ -295,7 +297,9 @@ var exJobClickCallback = function(event) {
var menu = [
{title: "Open Job...", callback: function() {window.location.href=requestURL;}},
- {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}}
+ {title: "Open Job in New Window...", callback: function() {window.open(requestURL);}},
+ {break: 1},
+ {title: "Center Job", callback: function() {graphModel.trigger("centerNode", jobId)}}
];
contextMenuView.show(event, menu);
src/web/js/azkaban.layout.js 35(+20 -15)
diff --git a/src/web/js/azkaban.layout.js b/src/web/js/azkaban.layout.js
index 53e7692..209a52b 100644
--- a/src/web/js/azkaban.layout.js
+++ b/src/web/js/azkaban.layout.js
@@ -4,7 +4,7 @@ var degreeRatio = 1/8;
var maxHeight = 200;
var cornerGap = 10;
-function layoutGraph(nodes, edges) {
+function layoutGraph(nodes, edges, hmargin) {
var startLayer = [];
var numLayer = 0;
var nodeMap = {};
@@ -12,20 +12,17 @@ function layoutGraph(nodes, edges) {
var maxLayer = 0;
var layers = {};
+ if (!hmargin) {
+ hmargin = 8;
+ }
+
// Assign to layers
for (var i = 0; i < nodes.length; ++i) {
numLayer = Math.max(numLayer, nodes[i].level);
- /*
- if (nodes[i].id.length > maxTextSize) {
- var label = nodes[i].id.substr(0, reductionSize) + "...";
- nodes[i].label = label;
- }
- else {*/
- nodes[i].label = nodes[i].id;
- //}
-
- var width = nodes[i].label.length * 10;
- var node = { id: nodes[i].id, node: nodes[i], level: nodes[i].level, in:[], out:[], width: width, x:0 };
+
+ var width = nodes[i].width ? nodes[i].width : nodes[i].label.length * 11.5 + 4;
+ var height = nodes[i].height ? nodes[i].height : 1;
+ var node = { id: nodes[i].id, node: nodes[i], level: nodes[i].level, in:[], out:[], width: width + hmargin, x:0, height:height };
nodeMap[nodes[i].id] = node;
maxLayer = Math.max(node.level, maxLayer);
@@ -244,15 +241,21 @@ function spreadLayerSmart(layer) {
function spaceVertically(layers, maxLayer) {
var startY = 0;
var startLayer = layers[0];
+ var startMaxHeight = 1;
for (var i=0; i < startLayer.length; ++i) {
startLayer[i].y = startY;
+ startMaxHeight = Math.max(startMaxHeight, startLayer[i].height);
}
- var minHeight = 50;
+ var minHeight = 40;
for (var a=1; a <= maxLayer; ++a) {
var maxDelta = 0;
var layer = layers[a];
+
+ var layerMaxHeight = 1;
for (var i=0; i < layer.length; ++i) {
+ layerMaxHeight = Math.max(layerMaxHeight, layer[i].height);
+
for (var j=0; j < layer[i].in.length; ++j) {
var upper = layer[i].in[j];
var delta = Math.abs(upper.x - layer[i].x);
@@ -264,8 +267,10 @@ function spaceVertically(layers, maxLayer) {
console.log("Max " + maxDelta);
var calcHeight = maxDelta*degreeRatio;
- calcHeight = Math.min(calcHeight, maxHeight);
- startY += Math.max(calcHeight, minHeight);
+ var newMinHeight = minHeight + startMaxHeight/2 + layerMaxHeight / 2;
+ startMaxHeight = layerMaxHeight;
+
+ startY += Math.max(calcHeight, newMinHeight);
for (var i=0; i < layer.length; ++i) {
layer[i].y=startY;
}
src/web/js/azkaban.svg.graph.view.js 301(+250 -51)
diff --git a/src/web/js/azkaban.svg.graph.view.js b/src/web/js/azkaban.svg.graph.view.js
index 009b4e4..3703e27 100644
--- a/src/web/js/azkaban.svg.graph.view.js
+++ b/src/web/js/azkaban.svg.graph.view.js
@@ -34,13 +34,14 @@ azkaban.SvgGraphView = Backbone.View.extend({
},
initialize: function(settings) {
this.model.bind('change:selected', this.changeSelected, this);
+ this.model.bind('centerNode', this.centerNode, this);
this.model.bind('change:graph', this.render, this);
this.model.bind('resetPanZoom', this.resetPanZoom, this);
this.model.bind('change:update', this.handleStatusUpdate, this);
this.model.bind('change:disabled', this.handleDisabledChange, this);
this.model.bind('change:updateAll', this.handleUpdateAllStatus, this);
- this.graphMargin = settings.graphMargin ? settings.graphMargin : 200;
+ this.graphMargin = settings.graphMargin ? settings.graphMargin : 25;
this.svgns = "http://www.w3.org/2000/svg";
this.xlinksn = "http://www.w3.org/1999/xlink";
@@ -59,7 +60,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
},
initializeDefs: function(self) {
var def = document.createElementNS(svgns, 'defs');
- def.setAttributeNS(null, "id", "buttonDefs");
+ def.setAttribute("id", "buttonDefs");
// ArrowHead
var arrowHeadMarker = document.createElementNS(svgns, 'marker');
@@ -97,15 +98,23 @@ azkaban.SvgGraphView = Backbone.View.extend({
nodes.sort();
edges.sort();
- // layout
- layoutGraph(nodes, edges);
var bounds = {};
this.nodes = {};
for (var i = 0; i < nodes.length; ++i) {
this.nodes[nodes[i].id] = nodes[i];
+ nodes[i].label = nodes[i].id;
+ }
+
+ this.gNodes = {};
+ for (var i = 0; i < nodes.length; ++i) {
+ this.drawNode(this, nodes[i]);
}
+ // layout
+ layoutGraph(nodes, edges, 10);
+ this.moveNodes(bounds);
+
for (var i = 0; i < edges.length; ++i) {
var inNodes = this.nodes[edges[i].target].inNodes;
if (!inNodes) {
@@ -124,11 +133,6 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.drawEdge(this, edges[i]);
}
- this.gNodes = {};
- for (var i = 0; i < nodes.length; ++i) {
- this.drawNode(this, nodes[i], bounds);
- }
-
this.model.set({"nodes": this.nodes, "edges": edges});
var margin = this.graphMargin;
@@ -162,7 +166,12 @@ azkaban.SvgGraphView = Backbone.View.extend({
for (var i = 0; i < data.nodes.length; ++i) {
var updateNode = data.nodes[i];
var g = this.gNodes[updateNode.id];
- addClass(g, updateNode.status);
+ if (updateNode.status) {
+ addClass(g, updateNode.status);
+ }
+ else {
+ addClass(g, "READY");
+ }
}
},
changeSelected: function(self) {
@@ -182,12 +191,15 @@ azkaban.SvgGraphView = Backbone.View.extend({
addClass(g, "selected");
- var offset = 200;
- var widthHeight = offset*2;
- var x = node.x - offset;
- var y = node.y - offset;
-
- $(this.svgGraph).svgNavigate("transformToBox", {x: x, y: y, width: widthHeight, height: widthHeight});
+ console.log(this.model.get("autoPanZoom"));
+ if (this.model.get("autoPanZoom")) {
+ var offset = 150;
+ var widthHeight = offset*2;
+ var x = node.x - offset;
+ var y = node.y - offset;
+
+ $(this.svgGraph).svgNavigate("transformToBox", {x: x, y: y, width: widthHeight, height: widthHeight});
+ }
}
},
handleStatusUpdate: function(evt) {
@@ -240,33 +252,206 @@ azkaban.SvgGraphView = Backbone.View.extend({
var startNode = this.nodes[edge.from];
var endNode = this.nodes[edge.target];
+ var startPointY = startNode.y + startNode.height/2 - 3;
+ var endPointY = endNode.y - endNode.height/2 + 3;
if (edge.guides) {
- var pointString = "" + startNode.x + "," + startNode.y + " ";
+ var pointString = "" + startNode.x + "," + startPointY + " ";
for (var i = 0; i < edge.guides.length; ++i ) {
edgeGuidePoint = edge.guides[i];
pointString += edgeGuidePoint.x + "," + edgeGuidePoint.y + " ";
}
- pointString += endNode.x + "," + endNode.y;
+ pointString += endNode.x + "," + endPointY;
var polyLine = document.createElementNS(svgns, "polyline");
- polyLine.setAttributeNS(null, "class", "edge");
- polyLine.setAttributeNS(null, "points", pointString);
- polyLine.setAttributeNS(null, "style", "fill:none;");
- self.mainG.appendChild(polyLine);
+ polyLine.setAttribute("class", "edge");
+ polyLine.setAttribute("points", pointString);
+ polyLine.setAttribute("style", "fill:none;");
+ $(self.mainG).prepend(polyLine);
}
else {
var line = document.createElementNS(svgns, 'line');
- line.setAttributeNS(null, "class", "edge");
- line.setAttributeNS(null, "x1", startNode.x);
- line.setAttributeNS(null, "y1", startNode.y);
- line.setAttributeNS(null, "x2", endNode.x);
- line.setAttributeNS(null, "y2", endNode.y);
+ line.setAttribute("class", "edge");
+ line.setAttribute("x1", startNode.x);
+ line.setAttribute("y1", startPointY);
+ line.setAttribute("x2", endNode.x);
+ line.setAttribute("y2", endPointY);
- self.mainG.appendChild(line);
+ $(self.mainG).prepend(line);
}
},
- drawNode: function(self, node, bounds) {
+ drawNode: function(self, node) {
+ if (node.type == 'flow') {
+ this.drawFlowNode(self, node);
+ }
+ else {
+ this.drawBoxNode(self,node);
+ //this.drawCircleNode(self,node,bounds);
+ }
+ },
+ moveNodes: function(bounds) {
+ var nodes = this.nodes;
+ var gNodes = this.gNodes;
+
+ for (var key in nodes) {
+ var node = nodes[key];
+ var gNode = gNodes[node.id];
+ var centerX = node.centerX;
+ var centerY = node.centerY;
+
+ gNode.setAttribute("transform", "translate(" + node.x + "," + node.y + ")");
+ this.addBounds(bounds, {minX:node.x - centerX, minY: node.y - centerY, maxX: node.x + centerX, maxY: node.y + centerY});
+ }
+ },
+ drawFlowNode: function(self, node) {
+ var svg = self.svgGraph;
+ var svgns = self.svgns;
+
+ var height = 26;
+ var nodeG = document.createElementNS(svgns, "g");
+ nodeG.setAttribute("class", "jobnode");
+ nodeG.setAttribute("font-family", "helvetica");
+ nodeG.setAttribute("transform", "translate(" + node.x + "," + node.y + ")");
+ this.gNodes[node.id] = nodeG;
+
+ var innerG = document.createElementNS(svgns, "g");
+ innerG.setAttribute("class", "nodebox");
+
+ var rect = document.createElementNS(svgns, 'rect');
+ rect.setAttribute("rx", 3);
+ rect.setAttribute("ry", 5);
+ rect.setAttribute("style", "width:inherit;stroke-opacity:1");
+ //rect.setAttribute("class", "nodecontainer");
+
+ var text = document.createElementNS(svgns, 'text');
+ var textLabel = document.createTextNode(node.label);
+ text.appendChild(textLabel);
+ text.setAttribute("y", 1);
+ text.setAttribute("height", 10);
+
+ var flowIdText = document.createElementNS(svgns, 'text');
+ var flowIdLabel = document.createTextNode(node.flowId);
+ flowIdText.appendChild(flowIdLabel);
+ flowIdText.setAttribute("y", 11);
+ flowIdText.setAttribute("font-size", 8);
+
+ var iconHeight = 20;
+ var iconWidth = 21;
+ var iconMargin = 4;
+ var iconNode = document.createElementNS(svgns, 'image');
+ iconNode.setAttribute("width", iconHeight);
+ iconNode.setAttribute("height", iconWidth);
+ iconNode.setAttribute("x", 0);
+ iconNode.setAttribute("y", -10);
+ iconNode.setAttributeNS('http://www.w3.org/1999/xlink', "xlink:href", contextURL + "/images/graph-icon.png");
+
+ innerG.appendChild(rect);
+ innerG.appendChild(text);
+ innerG.appendChild(flowIdText);
+ innerG.appendChild(iconNode);
+ innerG.jobid = node.id;
+
+ nodeG.appendChild(innerG);
+ self.mainG.appendChild(nodeG);
+
+ var horizontalMargin = 8;
+ var verticalMargin = 2;
+
+ // Need to get text width after attaching to SVG.
+ var subLabelTextLength = flowIdText.getComputedTextLength();
+
+ var computeTextLength = text.getComputedTextLength();
+ var computeTextHeight = 22;
+
+ var width = computeTextLength > subLabelTextLength ? computeTextLength : subLabelTextLength;
+ width += iconWidth + iconMargin;
+ var halfWidth = width/2;
+ var halfHeight = height/2;
+
+ // Margin for surrounding box.
+ var boxWidth = width + horizontalMargin * 2;
+ var boxHeight = height + verticalMargin * 2;
+
+ node.width = boxWidth;
+ node.height = boxHeight;
+ node.centerX = boxWidth/2;
+ node.centerY = boxHeight/2;
+
+ var textXOffset = -halfWidth + iconWidth + iconMargin;
+ iconNode.setAttribute("x", -halfWidth);
+ text.setAttribute("x", textXOffset);
+ flowIdText.setAttribute("x",textXOffset);
+ rect.setAttribute("x", -node.centerX);
+ rect.setAttribute("y", -node.centerY);
+ rect.setAttribute("width", node.width);
+ rect.setAttribute("height", node.height);
+
+ nodeG.setAttribute("class", "node");
+ nodeG.jobid=node.id;
+ },
+ drawBoxNode: function(self, node) {
+ var svg = self.svgGraph;
+ var svgns = self.svgns;
+
+ var height = 18;
+ var nodeG = document.createElementNS(svgns, "g");
+ nodeG.setAttribute("class", "jobnode");
+ nodeG.setAttribute("font-family", "helvetica");
+ nodeG.setAttribute("transform", "translate(" + node.x + "," + node.y + ")");
+ this.gNodes[node.id] = nodeG;
+
+ var innerG = document.createElementNS(svgns, "g");
+ innerG.setAttribute("class", "nodebox");
+
+ var rect = document.createElementNS(svgns, 'rect');
+ rect.setAttribute("rx", 3);
+ rect.setAttribute("ry", 5);
+ rect.setAttribute("style", "width:inherit;stroke-opacity:1");
+ //rect.setAttribute("class", "nodecontainer");
+
+ var text = document.createElementNS(svgns, 'text');
+ var textLabel = document.createTextNode(node.label);
+ text.appendChild(textLabel);
+ text.setAttribute("y", 6);
+ text.setAttribute("height", 10);
+
+ //this.addBounds(bounds, {minX:node.x - xOffset, minY: node.y - yOffset, maxX: node.x + xOffset, maxY: node.y + yOffset});
+
+ innerG.appendChild(rect);
+ innerG.appendChild(text);
+ innerG.jobid = node.id;
+
+ nodeG.appendChild(innerG);
+ self.mainG.appendChild(nodeG);
+
+ var horizontalMargin = 8;
+ var verticalMargin = 2;
+
+ // Need to get text width after attaching to SVG.
+ var computeText = text.getComputedTextLength();
+ var computeTextHeight = 22;
+ var halfWidth = computeText/2;
+ var halfHeight = height/2;
+
+ // Margin for surrounding box.
+ var boxWidth = computeText + horizontalMargin * 2;
+ var boxHeight = height + verticalMargin * 2;
+
+ node.width = boxWidth;
+ node.height = boxHeight;
+ node.centerX = boxWidth/2;
+ node.centerY = boxHeight/2;
+
+ text.setAttribute("x", -halfWidth);
+ rect.setAttribute("x", -node.centerX);
+ rect.setAttribute("y", -node.centerY);
+ rect.setAttribute("width", node.width);
+ rect.setAttribute("height", node.height);
+
+ nodeG.setAttribute("class", "node");
+ nodeG.jobid=node.id;
+ },
+ drawCircleNode: function(self, node, bounds) {
var svg = self.svgGraph;
var svgns = self.svgns;
@@ -274,36 +459,36 @@ azkaban.SvgGraphView = Backbone.View.extend({
var yOffset = 10;
var nodeG = document.createElementNS(svgns, "g");
- nodeG.setAttributeNS(null, "class", "jobnode");
- nodeG.setAttributeNS(null, "font-family", "helvetica");
- nodeG.setAttributeNS(null, "transform", "translate(" + node.x + "," + node.y + ")");
+ nodeG.setAttribute("class", "jobnode");
+ nodeG.setAttribute("font-family", "helvetica");
+ nodeG.setAttribute("transform", "translate(" + node.x + "," + node.y + ")");
this.gNodes[node.id] = nodeG;
var innerG = document.createElementNS(svgns, "g");
- innerG.setAttributeNS(null, "transform", "translate(-10,-10)");
+ innerG.setAttribute("transform", "translate(-10,-10)");
var circle = document.createElementNS(svgns, 'circle');
- circle.setAttributeNS(null, "cy", 10);
- circle.setAttributeNS(null, "cx", 10);
- circle.setAttributeNS(null, "r", 12);
- circle.setAttributeNS(null, "style", "width:inherit;stroke-opacity:1");
-
+ circle.setAttribute("cy", 10);
+ circle.setAttribute("cx", 10);
+ circle.setAttribute("r", 12);
+ circle.setAttribute("style", "width:inherit;stroke-opacity:1");
+ //circle.setAttribute("class", "border");
+ //circle.setAttribute("class", "nodecontainer");
var text = document.createElementNS(svgns, 'text');
var textLabel = document.createTextNode(node.label);
text.appendChild(textLabel);
- text.setAttributeNS(null, "x", 4);
- text.setAttributeNS(null, "y", 15);
- text.setAttributeNS(null, "height", 10);
+ text.setAttribute("x", 0);
+ text.setAttribute("y", 0);
this.addBounds(bounds, {minX:node.x - xOffset, minY: node.y - yOffset, maxX: node.x + xOffset, maxY: node.y + yOffset});
var backRect = document.createElementNS(svgns, 'rect');
- backRect.setAttributeNS(null, "x", 0);
- backRect.setAttributeNS(null, "y", 2);
- backRect.setAttributeNS(null, "class", "backboard");
- backRect.setAttributeNS(null, "width", 10);
- backRect.setAttributeNS(null, "height", 15);
+ backRect.setAttribute("x", 0);
+ backRect.setAttribute("y", 2);
+ backRect.setAttribute("class", "backboard");
+ backRect.setAttribute("width", 10);
+ backRect.setAttribute("height", 15);
innerG.appendChild(circle);
innerG.appendChild(backRect);
@@ -316,11 +501,11 @@ azkaban.SvgGraphView = Backbone.View.extend({
// Need to get text width after attaching to SVG.
var computeText = text.getComputedTextLength();
var halfWidth = computeText/2;
- text.setAttributeNS(null, "x", -halfWidth + 10);
- backRect.setAttributeNS(null, "x", -halfWidth);
- backRect.setAttributeNS(null, "width", computeText + 20);
+ text.setAttribute("x", -halfWidth + 10);
+ backRect.setAttribute("x", -halfWidth);
+ backRect.setAttribute("width", computeText + 20);
- nodeG.setAttributeNS(null, "class", "node");
+ nodeG.setAttribute("class", "node");
nodeG.jobid=node.id;
},
addBounds: function(toBounds, addBounds) {
@@ -333,6 +518,20 @@ azkaban.SvgGraphView = Backbone.View.extend({
var bounds = this.graphBounds;
var param = {x: bounds.minX, y: bounds.minY, width: (bounds.maxX - bounds.minX), height: (bounds.maxY - bounds.minY), duration: duration };
- $(this.svgGraph).svgNavigate("transformToBox", param);
+ this.panZoom(param);
+ },
+ centerNode: function(jobId) {
+ var node = this.nodes[jobId];
+
+ var offset = 150;
+ var widthHeight = offset*2;
+ var x = node.x - offset;
+ var y = node.y - offset;
+
+ this.panZoom({x: x, y: y, width: widthHeight, height: widthHeight});
+ },
+ panZoom: function(params) {
+ params.maxScale = 2;
+ $(this.svgGraph).svgNavigate("transformToBox", params);
}
});
\ No newline at end of file
src/web/js/svgNavigate.js 15(+13 -2)
diff --git a/src/web/js/svgNavigate.js b/src/web/js/svgNavigate.js
index 68121f9..d1f7892 100644
--- a/src/web/js/svgNavigate.js
+++ b/src/web/js/svgNavigate.js
@@ -316,8 +316,19 @@
var aspectRatioDiv = divHeight/divWidth;
var scale = aspectRatioGraph > aspectRatioDiv ? (divHeight/height)*factor : (divWidth/width)*factor;
- console.log("(" + x + "," + y + "," + width.toPrecision(4) + "," + height.toPrecision(4) + ")");
- console.log("(rg:" + aspectRatioGraph.toPrecision(3) + ",rd:" + aspectRatioDiv.toPrecision(3) + "," + scale.toPrecision(3) + ")");
+ //console.log("(" + x + "," + y + "," + width.toPrecision(4) + "," + height.toPrecision(4) + ")");
+ //console.log("(rg:" + aspectRatioGraph.toPrecision(3) + ",rd:" + aspectRatioDiv.toPrecision(3) + "," + scale.toPrecision(3) + ")");
+
+ if (arguments.maxScale) {
+ if (scale > arguments.maxScale) {
+ scale = arguments.maxScale;
+ }
+ }
+ if (arguments.minScale) {
+ if (scale < arguments.minScale) {
+ scale = arguments.minScale;
+ }
+ }
// Center
var scaledWidth = width*scale;
unit/build.xml 11(+11 -0)
diff --git a/unit/build.xml b/unit/build.xml
index cb07807..64d1314 100644
--- a/unit/build.xml
+++ b/unit/build.xml
@@ -87,4 +87,15 @@
</zip>
</target>
+ <target name="package-embedded" depends="jars" description="Creates a test zip">
+ <delete dir="${dist.packages.dir}" />
+ <mkdir dir="${dist.packages.dir}" />
+
+ <!-- Tarball it -->
+ <zip destfile="${dist.packages.dir}/embedded.zip">
+ <zipfileset dir="${dist.jar.dir}" />
+ <zipfileset dir="${base.dir}/unit/executions/embedded" />
+ </zip>
+ </target>
+
</project>
diff --git a/unit/executions/embedded/innerFlow.job b/unit/executions/embedded/innerFlow.job
new file mode 100644
index 0000000..da71d64
--- /dev/null
+++ b/unit/executions/embedded/innerFlow.job
@@ -0,0 +1,5 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
+dependencies=innerJobB,innerJobC
\ No newline at end of file
diff --git a/unit/executions/embedded/innerJobA.job b/unit/executions/embedded/innerJobA.job
new file mode 100644
index 0000000..665b38d
--- /dev/null
+++ b/unit/executions/embedded/innerJobA.job
@@ -0,0 +1,4 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
diff --git a/unit/executions/embedded/innerJobB.job b/unit/executions/embedded/innerJobB.job
new file mode 100644
index 0000000..178bbef
--- /dev/null
+++ b/unit/executions/embedded/innerJobB.job
@@ -0,0 +1,5 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
+dependencies=innerJobA
diff --git a/unit/executions/embedded/innerJobC.job b/unit/executions/embedded/innerJobC.job
new file mode 100644
index 0000000..178bbef
--- /dev/null
+++ b/unit/executions/embedded/innerJobC.job
@@ -0,0 +1,5 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
+dependencies=innerJobA
unit/executions/embedded/joba.job 4(+4 -0)
diff --git a/unit/executions/embedded/joba.job b/unit/executions/embedded/joba.job
new file mode 100644
index 0000000..665b38d
--- /dev/null
+++ b/unit/executions/embedded/joba.job
@@ -0,0 +1,4 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
unit/executions/embedded/jobb.job 3(+3 -0)
diff --git a/unit/executions/embedded/jobb.job b/unit/executions/embedded/jobb.job
new file mode 100644
index 0000000..8844d8c
--- /dev/null
+++ b/unit/executions/embedded/jobb.job
@@ -0,0 +1,3 @@
+type=flow
+flow.name=innerFlow
+dependencies=joba
unit/executions/embedded/jobc.job 3(+3 -0)
diff --git a/unit/executions/embedded/jobc.job b/unit/executions/embedded/jobc.job
new file mode 100644
index 0000000..8844d8c
--- /dev/null
+++ b/unit/executions/embedded/jobc.job
@@ -0,0 +1,3 @@
+type=flow
+flow.name=innerFlow
+dependencies=joba
unit/executions/embedded/jobd.job 3(+3 -0)
diff --git a/unit/executions/embedded/jobd.job b/unit/executions/embedded/jobd.job
new file mode 100644
index 0000000..8844d8c
--- /dev/null
+++ b/unit/executions/embedded/jobd.job
@@ -0,0 +1,3 @@
+type=flow
+flow.name=innerFlow
+dependencies=joba
unit/executions/embedded/jobe.job 5(+5 -0)
diff --git a/unit/executions/embedded/jobe.job b/unit/executions/embedded/jobe.job
new file mode 100644
index 0000000..fe986d5
--- /dev/null
+++ b/unit/executions/embedded/jobe.job
@@ -0,0 +1,5 @@
+type=javaprocess
+java.class=azkaban.test.executor.SleepJavaJob
+seconds=1
+fail=false
+dependencies=jobb,jobc,jobd
diff --git a/unit/java/azkaban/test/executor/SleepJavaJob.java b/unit/java/azkaban/test/executor/SleepJavaJob.java
index ac831fc..535151a 100644
--- a/unit/java/azkaban/test/executor/SleepJavaJob.java
+++ b/unit/java/azkaban/test/executor/SleepJavaJob.java
@@ -1,6 +1,9 @@
package azkaban.test.executor;
+import java.io.BufferedReader;
+import java.io.FileReader;
import java.util.Map;
+import java.util.Properties;
public class SleepJavaJob {
private boolean fail;
@@ -8,8 +11,19 @@ public class SleepJavaJob {
private int attempts;
private int currentAttempt;
+ public SleepJavaJob(String id, Properties props) {
+ setup(props);
+ }
+
public SleepJavaJob(String id, Map<String, String> parameters) {
- String failStr = parameters.get("fail");
+ Properties properties = new Properties();
+ properties.putAll(parameters);
+
+ setup(properties);
+ }
+
+ private void setup(Properties props) {
+ String failStr = (String)props.get("fail");
if (failStr == null || failStr.equals("false")) {
fail = false;
@@ -18,15 +32,15 @@ public class SleepJavaJob {
fail = true;
}
- currentAttempt = parameters.containsKey("azkaban.job.attempt") ? Integer.parseInt(parameters.get("azkaban.job.attempt")) : 0;
- String attemptString = parameters.get("passRetry");
+ currentAttempt = props.containsKey("azkaban.job.attempt") ? Integer.parseInt((String)props.get("azkaban.job.attempt")) : 0;
+ String attemptString = (String)props.get("passRetry");
if (attemptString == null) {
attempts = -1;
}
else {
attempts = Integer.valueOf(attemptString);
}
- seconds = parameters.get("seconds");
+ seconds = (String)props.get("seconds");
if (fail) {
System.out.println("Planning to fail after " + seconds + " seconds. Attempts left " + currentAttempt + " of " + attempts);
@@ -36,6 +50,17 @@ public class SleepJavaJob {
}
}
+ public static void main(String[] args) throws Exception {
+ String propsFile = System.getenv("JOB_PROP_FILE");
+ Properties prop = new Properties();
+ prop.load(new BufferedReader(new FileReader(propsFile)));
+
+ String jobName = System.getenv("JOB_NAME");
+ SleepJavaJob job = new SleepJavaJob(jobName, prop);
+
+ job.run();
+ }
+
public void run() throws Exception {
if (seconds == null) {
throw new RuntimeException("Seconds not set");