You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by rk...@apache.org on 2015/07/15 01:33:01 UTC
oozie git commit: OOZIE-2187 Add a way to specify a default JT/RM and
NN (rkanter)
Repository: oozie
Updated Branches:
refs/heads/master 6a731f992 -> a762991ab
OOZIE-2187 Add a way to specify a default JT/RM and NN (rkanter)
Project: http://git-wip-us.apache.org/repos/asf/oozie/repo
Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/a762991a
Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/a762991a
Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/a762991a
Branch: refs/heads/master
Commit: a762991ab00df5e60d5e8b7f430b89861abe0671
Parents: 6a731f9
Author: Robert Kanter <rk...@cloudera.com>
Authored: Tue Jul 14 16:31:43 2015 -0700
Committer: Robert Kanter <rk...@cloudera.com>
Committed: Tue Jul 14 16:31:43 2015 -0700
----------------------------------------------------------------------
.../org/apache/oozie/action/ActionExecutor.java | 23 +-
.../oozie/action/hadoop/JavaActionExecutor.java | 9 +-
.../workflow/lite/LiteWorkflowAppParser.java | 357 ++++++++++---------
core/src/main/resources/oozie-default.xml | 21 ++
.../lite/TestLiteWorkflowAppParser.java | 270 ++++++++++++--
.../wf-schema-no-jobtracker-global.xml | 61 ++++
.../test/resources/wf-schema-no-jobtracker.xml | 57 +++
.../resources/wf-schema-no-namenode-global.xml | 61 ++++
.../test/resources/wf-schema-no-namenode.xml | 57 +++
.../resources/wf-schema-valid-global-ext.xml | 16 +-
release-log.txt | 1 +
11 files changed, 738 insertions(+), 195 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java b/core/src/main/java/org/apache/oozie/action/ActionExecutor.java
index 07c6d26..2be4549 100644
--- a/core/src/main/java/org/apache/oozie/action/ActionExecutor.java
+++ b/core/src/main/java/org/apache/oozie/action/ActionExecutor.java
@@ -60,8 +60,6 @@ public abstract class ActionExecutor {
* Error code used by {@link #convertException} when there is not register error information for an exception.
*/
public static final String ERROR_OTHER = "OTHER";
-
- public boolean requiresNNJT = false;
public static enum RETRYPOLICY {
EXPONENTIAL, PERIODIC
@@ -562,4 +560,25 @@ public abstract class ActionExecutor {
*/
public abstract boolean isCompleted(String externalStatus);
+ /**
+ * Returns true if this action type requires a NameNode and JobTracker. These can either be specified directly in the action
+ * via <name-node> and <job-tracker>, from the fields in the global section, or from their default values. If
+ * false, Oozie won't ensure (i.e. won't throw an Exception if non-existant) that this action type has these values.
+ *
+ * @return true if a NameNode and JobTracker are required; false if not
+ */
+ public boolean requiresNameNodeJobTracker() {
+ return false;
+ }
+
+ /**
+ * Returns true if this action type supports a Configuration and JobXML. In this case, Oozie will include the
+ * <configuration> and <job-xml> elements from the global section (if provided) with the action. If false, Oozie
+ * won't add these.
+ *
+ * @return true if the global section's Configuration and JobXML should be given; false if not
+ */
+ public boolean supportsConfigurationJobXML() {
+ return false;
+ }
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java b/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java
index 492ceaf..6e959df 100644
--- a/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java
+++ b/core/src/main/java/org/apache/oozie/action/hadoop/JavaActionExecutor.java
@@ -135,7 +135,6 @@ public class JavaActionExecutor extends ActionExecutor {
protected JavaActionExecutor(String type) {
super(type);
- requiresNNJT = true;
}
public static List<Class> getCommonLauncherClasses() {
@@ -1628,4 +1627,12 @@ public class JavaActionExecutor extends ActionExecutor {
}
}
}
+
+ public boolean requiresNameNodeJobTracker() {
+ return true;
+ }
+
+ public boolean supportsConfigurationJobXML() {
+ return true;
+ }
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
index c857011..d3a6523 100644
--- a/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
+++ b/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
@@ -93,6 +93,14 @@ public class LiteWorkflowAppParser {
public static final String VALIDATE_FORK_JOIN = "oozie.validate.ForkJoin";
public static final String WF_VALIDATE_FORK_JOIN = "oozie.wf.validate.ForkJoin";
+ public static final String DEFAULT_NAME_NODE = "oozie.actions.default.name-node";
+ public static final String DEFAULT_JOB_TRACKER = "oozie.actions.default.job-tracker";
+
+ private static final String JOB_TRACKER = "job-tracker";
+ private static final String NAME_NODE = "name-node";
+ private static final String JOB_XML = "job-xml";
+ private static final String CONFIGURATION = "configuration";
+
private Schema schema;
private Class<? extends ControlNodeHandler> controlNodeHandler;
private Class<? extends DecisionNodeHandler> decisionHandlerClass;
@@ -121,6 +129,9 @@ public class LiteWorkflowAppParser {
private List<NodeAndTopDecisionParent> visitedOkNodes = new ArrayList<NodeAndTopDecisionParent>();
private List<String> visitedJoinNodes = new ArrayList<String>();
+ private String defaultNameNode;
+ private String defaultJobTracker;
+
public LiteWorkflowAppParser(Schema schema,
Class<? extends ControlNodeHandler> controlNodeHandler,
Class<? extends DecisionNodeHandler> decisionHandlerClass,
@@ -129,6 +140,21 @@ public class LiteWorkflowAppParser {
this.controlNodeHandler = controlNodeHandler;
this.decisionHandlerClass = decisionHandlerClass;
this.actionHandlerClass = actionHandlerClass;
+
+ defaultNameNode = ConfigurationService.get(DEFAULT_NAME_NODE);
+ if (defaultNameNode != null) {
+ defaultNameNode = defaultNameNode.trim();
+ if (defaultNameNode.isEmpty()) {
+ defaultNameNode = null;
+ }
+ }
+ defaultJobTracker = ConfigurationService.get(DEFAULT_JOB_TRACKER);
+ if (defaultJobTracker != null) {
+ defaultJobTracker = defaultJobTracker.trim();
+ if (defaultJobTracker.isEmpty()) {
+ defaultJobTracker = null;
+ }
+ }
}
public LiteWorkflowApp validateAndParse(Reader reader, Configuration jobConf) throws WorkflowException {
@@ -391,115 +417,78 @@ public class LiteWorkflowAppParser {
throws WorkflowException {
Namespace ns = root.getNamespace();
LiteWorkflowApp def = null;
- Element global = null;
+ GlobalSectionData gData = null;
for (Element eNode : (List<Element>) root.getChildren()) {
if (eNode.getName().equals(START_E)) {
def = new LiteWorkflowApp(root.getAttributeValue(NAME_A), strDef,
new StartNodeDef(controlNodeHandler, eNode.getAttributeValue(TO_A)));
- }
- else {
- if (eNode.getName().equals(END_E)) {
- def.addNode(new EndNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler));
+ } else if (eNode.getName().equals(END_E)) {
+ def.addNode(new EndNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler));
+ } else if (eNode.getName().equals(KILL_E)) {
+ def.addNode(new KillNodeDef(eNode.getAttributeValue(NAME_A),
+ eNode.getChildText(KILL_MESSAGE_E, ns), controlNodeHandler));
+ } else if (eNode.getName().equals(FORK_E)) {
+ List<String> paths = new ArrayList<String>();
+ for (Element tran : (List<Element>) eNode.getChildren(FORK_PATH_E, ns)) {
+ paths.add(tran.getAttributeValue(FORK_START_A));
}
- else {
- if (eNode.getName().equals(KILL_E)) {
- def.addNode(new KillNodeDef(eNode.getAttributeValue(NAME_A),
- eNode.getChildText(KILL_MESSAGE_E, ns), controlNodeHandler));
+ def.addNode(new ForkNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, paths));
+ } else if (eNode.getName().equals(JOIN_E)) {
+ def.addNode(new JoinNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, eNode.getAttributeValue(TO_A)));
+ } else if (eNode.getName().equals(DECISION_E)) {
+ Element eSwitch = eNode.getChild(DECISION_SWITCH_E, ns);
+ List<String> transitions = new ArrayList<String>();
+ for (Element e : (List<Element>) eSwitch.getChildren(DECISION_CASE_E, ns)) {
+ transitions.add(e.getAttributeValue(TO_A));
+ }
+ transitions.add(eSwitch.getChild(DECISION_DEFAULT_E, ns).getAttributeValue(TO_A));
+
+ String switchStatement = XmlUtils.prettyPrint(eSwitch).toString();
+ def.addNode(new DecisionNodeDef(eNode.getAttributeValue(NAME_A), switchStatement, decisionHandlerClass,
+ transitions));
+ } else if (ACTION_E.equals(eNode.getName())) {
+ String[] transitions = new String[2];
+ Element eActionConf = null;
+ for (Element elem : (List<Element>) eNode.getChildren()) {
+ if (ACTION_OK_E.equals(elem.getName())) {
+ transitions[0] = elem.getAttributeValue(TO_A);
+ } else if (ACTION_ERROR_E.equals(elem.getName())) {
+ transitions[1] = elem.getAttributeValue(TO_A);
+ } else if (SLA_INFO.equals(elem.getName()) || CREDENTIALS.equals(elem.getName())) {
+ continue;
+ } else {
+ eActionConf = elem;
+ handleDefaultsAndGlobal(gData, configDefault, elem);
}
- else {
- if (eNode.getName().equals(FORK_E)) {
- List<String> paths = new ArrayList<String>();
- for (Element tran : (List<Element>) eNode.getChildren(FORK_PATH_E, ns)) {
- paths.add(tran.getAttributeValue(FORK_START_A));
- }
- def.addNode(new ForkNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler, paths));
- }
- else {
- if (eNode.getName().equals(JOIN_E)) {
- def.addNode(new JoinNodeDef(eNode.getAttributeValue(NAME_A), controlNodeHandler,
- eNode.getAttributeValue(TO_A)));
- }
- else {
- if (eNode.getName().equals(DECISION_E)) {
- Element eSwitch = eNode.getChild(DECISION_SWITCH_E, ns);
- List<String> transitions = new ArrayList<String>();
- for (Element e : (List<Element>) eSwitch.getChildren(DECISION_CASE_E, ns)) {
- transitions.add(e.getAttributeValue(TO_A));
- }
- transitions.add(eSwitch.getChild(DECISION_DEFAULT_E, ns).getAttributeValue(TO_A));
-
- String switchStatement = XmlUtils.prettyPrint(eSwitch).toString();
- def.addNode(new DecisionNodeDef(eNode.getAttributeValue(NAME_A), switchStatement, decisionHandlerClass,
- transitions));
- }
- else {
- if (ACTION_E.equals(eNode.getName())) {
- String[] transitions = new String[2];
- Element eActionConf = null;
- for (Element elem : (List<Element>) eNode.getChildren()) {
- if (ACTION_OK_E.equals(elem.getName())) {
- transitions[0] = elem.getAttributeValue(TO_A);
- }
- else {
- if (ACTION_ERROR_E.equals(elem.getName())) {
- transitions[1] = elem.getAttributeValue(TO_A);
- }
- else {
- if (SLA_INFO.equals(elem.getName()) || CREDENTIALS.equals(elem.getName())) {
- continue;
- }
- else {
- eActionConf = elem;
- handleGlobal(ns, global, configDefault, elem);
- }
- }
- }
- }
-
- String credStr = eNode.getAttributeValue(CRED_A);
- String userRetryMaxStr = eNode.getAttributeValue(USER_RETRY_MAX_A);
- String userRetryIntervalStr = eNode.getAttributeValue(USER_RETRY_INTERVAL_A);
- try {
- if (!StringUtils.isEmpty(userRetryMaxStr)) {
- userRetryMaxStr = ELUtils.resolveAppName(userRetryMaxStr, jobConf);
- }
- if (!StringUtils.isEmpty(userRetryIntervalStr)) {
- userRetryIntervalStr = ELUtils.resolveAppName(userRetryIntervalStr,
- jobConf);
- }
- }
- catch (Exception e) {
- throw new WorkflowException(ErrorCode.E0703, e.getMessage());
- }
-
- String actionConf = XmlUtils.prettyPrint(eActionConf).toString();
- def.addNode(new ActionNodeDef(eNode.getAttributeValue(NAME_A), actionConf, actionHandlerClass,
- transitions[0], transitions[1], credStr,
- userRetryMaxStr, userRetryIntervalStr));
- }
- else {
- if (SLA_INFO.equals(eNode.getName()) || CREDENTIALS.equals(eNode.getName())) {
- // No operation is required
- }
- else {
- if (eNode.getName().equals(GLOBAL)) {
- global = eNode;
- }
- else {
- if (eNode.getName().equals(PARAMETERS)) {
- // No operation is required
- }
- else {
- throw new WorkflowException(ErrorCode.E0703, eNode.getName());
- }
- }
- }
- }
- }
- }
- }
+ }
+
+ String credStr = eNode.getAttributeValue(CRED_A);
+ String userRetryMaxStr = eNode.getAttributeValue(USER_RETRY_MAX_A);
+ String userRetryIntervalStr = eNode.getAttributeValue(USER_RETRY_INTERVAL_A);
+ try {
+ if (!StringUtils.isEmpty(userRetryMaxStr)) {
+ userRetryMaxStr = ELUtils.resolveAppName(userRetryMaxStr, jobConf);
}
+ if (!StringUtils.isEmpty(userRetryIntervalStr)) {
+ userRetryIntervalStr = ELUtils.resolveAppName(userRetryIntervalStr, jobConf);
+ }
+ }
+ catch (Exception e) {
+ throw new WorkflowException(ErrorCode.E0703, e.getMessage());
}
+
+ String actionConf = XmlUtils.prettyPrint(eActionConf).toString();
+ def.addNode(new ActionNodeDef(eNode.getAttributeValue(NAME_A), actionConf, actionHandlerClass,
+ transitions[0], transitions[1], credStr,
+ userRetryMaxStr, userRetryIntervalStr));
+ } else if (SLA_INFO.equals(eNode.getName()) || CREDENTIALS.equals(eNode.getName())) {
+ // No operation is required
+ } else if (eNode.getName().equals(GLOBAL)) {
+ gData = parseGlobalSection(ns, eNode);
+ } else if (eNode.getName().equals(PARAMETERS)) {
+ // No operation is required
+ } else {
+ throw new WorkflowException(ErrorCode.E0703, eNode.getName());
}
}
return def;
@@ -575,85 +564,138 @@ public class LiteWorkflowAppParser {
traversed.put(node.getName(), VisitStatus.VISITED);
}
- /**
- * Handle the global section
- *
- * @param ns
- * @param global
- * @param eActionConf
- * @throws WorkflowException
- */
-
- @SuppressWarnings("unchecked")
- private void handleGlobal(Namespace ns, Element global, Configuration configDefault, Element eActionConf)
- throws WorkflowException {
+ private void addChildElement(Element parent, Namespace ns, String childName, String childValue) {
+ Element child = new Element(childName, ns);
+ child.setText(childValue);
+ parent.addContent(child);
+ }
- // Use the action's namespace when getting children of the action (will
- // be different than ns for extension actions)
- Namespace actionNs = eActionConf.getNamespace();
+ private class GlobalSectionData {
+ final String jobTracker;
+ final String nameNode;
+ final List<String> jobXmls;
+ final Configuration conf;
+
+ public GlobalSectionData(String jobTracker, String nameNode, List<String> jobXmls, Configuration conf) {
+ this.jobTracker = jobTracker;
+ this.nameNode = nameNode;
+ this.jobXmls = jobXmls;
+ this.conf = conf;
+ }
+ }
+ private GlobalSectionData parseGlobalSection(Namespace ns, Element global) throws WorkflowException {
+ GlobalSectionData gData = null;
if (global != null) {
- Element globalJobTracker = global.getChild("job-tracker", ns);
- Element globalNameNode = global.getChild("name-node", ns);
- List<Element> globalJobXml = global.getChildren("job-xml", ns);
- Element globalConfiguration = global.getChild("configuration", ns);
+ String globalJobTracker = null;
+ Element globalJobTrackerElement = global.getChild(JOB_TRACKER, ns);
+ if (globalJobTrackerElement != null) {
+ globalJobTracker = globalJobTrackerElement.getValue();
+ }
- if (globalJobTracker != null && eActionConf.getChild("job-tracker", actionNs) == null) {
- Element jobTracker = new Element("job-tracker", actionNs);
- jobTracker.setText(globalJobTracker.getText());
- eActionConf.addContent(jobTracker);
+ String globalNameNode = null;
+ Element globalNameNodeElement = global.getChild(NAME_NODE, ns);
+ if (globalNameNodeElement != null) {
+ globalNameNode = globalNameNodeElement.getValue();
}
- if (globalNameNode != null && eActionConf.getChild("name-node", actionNs) == null) {
- Element nameNode = new Element("name-node", actionNs);
- nameNode.setText(globalNameNode.getText());
- eActionConf.addContent(nameNode);
+ List<String> globalJobXmls = null;
+ @SuppressWarnings("unchecked")
+ List<Element> globalJobXmlElements = global.getChildren(JOB_XML, ns);
+ if (!globalJobXmlElements.isEmpty()) {
+ globalJobXmls = new ArrayList<String>(globalJobXmlElements.size());
+ for(Element jobXmlElement: globalJobXmlElements) {
+ globalJobXmls.add(jobXmlElement.getText());
+ }
}
- if (!globalJobXml.isEmpty()) {
- List<Element> actionJobXml = eActionConf.getChildren("job-xml", actionNs);
- for(Element jobXml: globalJobXml){
+ Configuration globalConf = null;
+ Element globalConfigurationElement = global.getChild(CONFIGURATION, ns);
+ if (globalConfigurationElement != null) {
+ try {
+ globalConf = new XConfiguration(new StringReader(XmlUtils.prettyPrint(globalConfigurationElement).toString()));
+ } catch (IOException ioe) {
+ throw new WorkflowException(ErrorCode.E0700, "Error while processing global section conf");
+ }
+ }
+ gData = new GlobalSectionData(globalJobTracker, globalNameNode, globalJobXmls, globalConf);
+ }
+ return gData;
+ }
+
+ private void handleDefaultsAndGlobal(GlobalSectionData gData, Configuration configDefault, Element actionElement)
+ throws WorkflowException {
+ ActionExecutor ae = Services.get().get(ActionService.class).getExecutor(actionElement.getName());
+ if (ae == null) {
+ throw new WorkflowException(ErrorCode.E0723, actionElement.getName(), ActionService.class.getName());
+ }
+
+ Namespace actionNs = actionElement.getNamespace();
+
+ if (ae.requiresNameNodeJobTracker()) {
+ if (actionElement.getChild(NAME_NODE, actionNs) == null) {
+ if (gData != null && gData.nameNode != null) {
+ addChildElement(actionElement, actionNs, NAME_NODE, gData.nameNode);
+ } else if (defaultNameNode != null) {
+ addChildElement(actionElement, actionNs, NAME_NODE, defaultNameNode);
+ } else {
+ throw new WorkflowException(ErrorCode.E0701, "No " + NAME_NODE + " defined");
+ }
+ }
+ if (actionElement.getChild(JOB_TRACKER, actionNs) == null) {
+ if (gData != null && gData.jobTracker != null) {
+ addChildElement(actionElement, actionNs, JOB_TRACKER, gData.jobTracker);
+ } else if (defaultJobTracker != null) {
+ addChildElement(actionElement, actionNs, JOB_TRACKER, defaultJobTracker);
+ } else {
+ throw new WorkflowException(ErrorCode.E0701, "No " + JOB_TRACKER + " defined");
+ }
+ }
+ }
+
+ if (ae.supportsConfigurationJobXML()) {
+ @SuppressWarnings("unchecked")
+ List<Element> actionJobXmls = actionElement.getChildren(JOB_XML, actionNs);
+ if (gData != null && gData.jobXmls != null) {
+ for(String gJobXml : gData.jobXmls) {
boolean alreadyExists = false;
- for(Element actionXml: actionJobXml){
- if(jobXml.getText().equals(actionXml.getText())){
+ for (Element actionXml : actionJobXmls) {
+ if (gJobXml.equals(actionXml.getText())) {
alreadyExists = true;
break;
}
}
-
- if (!alreadyExists){
- Element ejobXml = new Element("job-xml", actionNs);
- ejobXml.setText(jobXml.getText());
- eActionConf.addContent(ejobXml);
+ if (!alreadyExists) {
+ Element ejobXml = new Element(JOB_XML, actionNs);
+ ejobXml.setText(gJobXml);
+ actionElement.addContent(ejobXml);
}
-
}
}
+
try {
XConfiguration actionConf = new XConfiguration();
if (configDefault != null)
XConfiguration.copy(configDefault, actionConf);
- if (globalConfiguration != null) {
- Configuration globalConf = new XConfiguration(new StringReader(XmlUtils.prettyPrint(
- globalConfiguration).toString()));
- XConfiguration.copy(globalConf, actionConf);
+ if (gData != null && gData.conf != null) {
+ XConfiguration.copy(gData.conf, actionConf);
}
- Element actionConfiguration = eActionConf.getChild("configuration", actionNs);
+ Element actionConfiguration = actionElement.getChild(CONFIGURATION, actionNs);
if (actionConfiguration != null) {
//copy and override
- XConfiguration.copy(new XConfiguration(new StringReader(XmlUtils.prettyPrint(
- actionConfiguration).toString())), actionConf);
+ XConfiguration.copy(new XConfiguration(new StringReader(XmlUtils.prettyPrint(actionConfiguration).toString())),
+ actionConf);
}
- int position = eActionConf.indexOf(actionConfiguration);
- eActionConf.removeContent(actionConfiguration); //replace with enhanced one
+ int position = actionElement.indexOf(actionConfiguration);
+ actionElement.removeContent(actionConfiguration); //replace with enhanced one
Element eConfXml = XmlUtils.parseXml(actionConf.toXmlString(false));
eConfXml.detach();
eConfXml.setNamespace(actionNs);
if (position > 0) {
- eActionConf.addContent(position, eConfXml);
+ actionElement.addContent(position, eConfXml);
}
else {
- eActionConf.addContent(eConfXml);
+ actionElement.addContent(eConfXml);
}
}
catch (IOException e) {
@@ -663,21 +705,6 @@ public class LiteWorkflowAppParser {
throw new WorkflowException(ErrorCode.E0700, "Error while processing action conf");
}
}
- else {
- ActionExecutor ae = Services.get().get(ActionService.class).getExecutor(eActionConf.getName());
- if (ae == null) {
- throw new WorkflowException(ErrorCode.E0723, eActionConf.getName(), ActionService.class.getName());
- }
- if (ae.requiresNNJT) {
-
- if (eActionConf.getChild("name-node", actionNs) == null) {
- throw new WorkflowException(ErrorCode.E0701, "No name-node defined");
- }
- if (eActionConf.getChild("job-tracker", actionNs) == null) {
- throw new WorkflowException(ErrorCode.E0701, "No job-tracker defined");
- }
- }
- }
}
}
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/main/resources/oozie-default.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/oozie-default.xml b/core/src/main/resources/oozie-default.xml
index 4dc127e..2f44827 100644
--- a/core/src/main/resources/oozie-default.xml
+++ b/core/src/main/resources/oozie-default.xml
@@ -2543,4 +2543,25 @@
Set it false if there is any security concern.
</description>
</property>
+
+ <property>
+ <name>oozie.actions.default.name-node</name>
+ <value> </value>
+ <description>
+ The default value to use for the <name-node> element in applicable action types. This value will be used when
+ neither the action itself nor the global section specifies a <name-node>. As expected, it should be of the form
+ "hdfs://HOST:PORT".
+ </description>
+ </property>
+
+ <property>
+ <name>oozie.actions.default.job-tracker</name>
+ <value> </value>
+ <description>
+ The default value to use for the <job-tracker> element in applicable action types. This value will be used when
+ neither the action itself nor the global section specifies a <job-tracker>. As expected, it should be of the form
+ "HOST:PORT".
+ </description>
+ </property>
+
</configuration>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
index 0eb1ee0..1fc1736 100644
--- a/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
+++ b/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.oozie.service.ActionService;
+import org.apache.oozie.service.ConfigurationService;
import org.apache.oozie.service.LiteWorkflowStoreService;
import org.apache.oozie.service.SchemaService;
import org.apache.oozie.service.Services;
@@ -47,7 +48,7 @@ public class TestLiteWorkflowAppParser extends XTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- setSystemProperty("oozie.service.SchemaService.wf.ext.schemas", "hive-action-0.2.xsd");
+ setSystemProperty("oozie.service.SchemaService.wf.ext.schemas", "hive-action-0.2.xsd,email-action-0.2.xsd");
new Services().init();
}
@@ -57,6 +58,13 @@ public class TestLiteWorkflowAppParser extends XTestCase {
super.tearDown();
}
+ private String cleanupXml(String xml) {
+ xml = xml.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
+ xml = xml.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
+ xml = xml.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
+ return xml;
+ }
+
public void testParserGlobal() throws Exception {
LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
LiteWorkflowStoreService.LiteControlNodeHandler.class,
@@ -79,8 +87,8 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" </streaming>\r\n" +
" <file>/tmp</file>\r\n" +
" <archive>/tmp</archive>\r\n" +
- " <job-tracker>${foo}</job-tracker>\r\n" +
" <name-node>bar</name-node>\r\n" +
+ " <job-tracker>${foo}</job-tracker>\r\n" +
" <configuration>\r\n" +
" <property>\r\n" +
" <name>b</name>\r\n" +
@@ -92,11 +100,8 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" </property>\r\n" +
" </configuration>\r\n" +
"</map-reduce>";
- d = d.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
- d = d.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
- d = d.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
- System.out.println("\n" + d +"\n");
- assertEquals(expectedD.replaceAll(" ",""), d.replaceAll(" ", ""));
+ d = cleanupXml(d);
+ assertEquals(expectedD.replaceAll(" ", ""), d.replaceAll(" ", ""));
}
@@ -123,8 +128,8 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" <job-xml>/tmp</job-xml>\r\n" +
" <file>/tmp</file>\r\n" +
" <archive>/tmp</archive>\r\n" +
- " <job-tracker>foo</job-tracker>\r\n" +
" <name-node>bar</name-node>\r\n" +
+ " <job-tracker>foo</job-tracker>\r\n" +
" <job-xml>/spam1</job-xml>\r\n" +
" <job-xml>/spam2</job-xml>\r\n" +
" <configuration>\r\n" +
@@ -138,10 +143,8 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" </property>\r\n" +
" </configuration>\r\n" +
"</map-reduce>";
- d = d.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
- d = d.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
- d = d.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
- assertEquals(expectedD.replaceAll(" ",""), d.replaceAll(" ", ""));
+ d = cleanupXml(d);
+ assertEquals(expectedD.replaceAll(" ", ""), d.replaceAll(" ", ""));
}
@@ -175,12 +178,10 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" <param>x</param>\r\n" +
" <file>/tmp</file>\r\n" +
" <file>/tmp</file>\r\n" +
- " <job-tracker>${foo}</job-tracker>\r\n" +
" <name-node>bar</name-node>\r\n" +
+ " <job-tracker>${foo}</job-tracker>\r\n" +
"</pig>";
- e = e.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
- e = e.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
- e = e.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
+ e = cleanupXml(e);
assertEquals(expectedE.replaceAll(" ", ""), e.replaceAll(" ", ""));
}
@@ -218,12 +219,10 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" <script>script.q</script>\r\n" +
" <param>INPUT=/tmp/table</param>\r\n" +
" <param>OUTPUT=/tmp/hive</param>\r\n" +
- " <job-tracker>foo</job-tracker>\r\n" +
" <name-node>bar</name-node>\r\n" +
+ " <job-tracker>foo</job-tracker>\r\n" +
"</hive>";
- a = a.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
- a = a.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
- a = a.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
+ a = cleanupXml(a);
assertEquals(expectedA.replaceAll(" ",""), a.replaceAll(" ", ""));
}
@@ -258,10 +257,29 @@ public class TestLiteWorkflowAppParser extends XTestCase {
" <arg>/tmp/data.txt</arg>\r\n" +
" <arg>/tmp2/data.txt</arg>\r\n" +
"</distcp>";
- b = b.replaceAll(" xmlns=?(\"|\')(\"|\')", "");
- b = b.replaceAll("\\s*<source>.*</source>", ""); // remove the <source> added by Hadoop 2
- b = b.replaceAll("\\s*<!--Loaded from Unknown-->", ""); // remove the <!--LoadedfromUnknown--> added by Hadoop 1.2.1
- assertEquals(expectedB.replaceAll(" ",""), b.replaceAll(" ", ""));
+ b = cleanupXml(b);
+ assertEquals(expectedB.replaceAll(" ", ""), b.replaceAll(" ", ""));
+ }
+
+ public void testParserGlobalExtensionActionsNotApplicable() throws Exception {
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ // Not all actions want a JT, NN, conf, or jobxml (e.g. email action)
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-valid-global-ext.xml", -1),
+ new Configuration());
+
+ String c1 = app.getNode("c1").getConf();
+ String expectedC1 =
+ "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" +
+ " <to>foo@bar.com</to>\r\n" +
+ " <subject>foo</subject>\r\n" +
+ " <body>bar</body>\r\n" +
+ "</email>";
+ c1 = cleanupXml(c1);
+ assertEquals(expectedC1.replaceAll(" ", ""), c1.replaceAll(" ", ""));
}
public void testParserGlobalExtensionActionsNoGlobal() throws Exception {
@@ -288,6 +306,210 @@ public class TestLiteWorkflowAppParser extends XTestCase {
}
}
+ public void testParserDefaultNameNode() throws Exception {
+ ConfigurationService.set("oozie.actions.default.name-node", "default-nn");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1),
+ new Configuration());
+ String a = app.getNode("a").getConf();
+ String expectedA =
+ "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" +
+ " <prepare>\r\n" +
+ " <delete path=\"/tmp\" />\r\n" +
+ " <mkdir path=\"/tmp\" />\r\n" +
+ " </prepare>\r\n" +
+ " <job-tracker>foo</job-tracker>\r\n" +
+ " <configuration>\r\n" +
+ " <property>\r\n" +
+ " <name>c</name>\r\n" +
+ " <value>C</value>\r\n" +
+ " </property>\r\n" +
+ " </configuration>\r\n" +
+ " <script>script.q</script>\r\n" +
+ " <param>INPUT=/tmp/table</param>\r\n" +
+ " <param>OUTPUT=/tmp/hive</param>\r\n" +
+ " <name-node>default-nn</name-node>\r\n" +
+ "</hive>";
+ a = cleanupXml(a);
+ assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultNameNodeWithGlobal() throws Exception {
+ ConfigurationService.set("oozie.actions.default.name-node", "default-nn");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode-global.xml", -1),
+ new Configuration());
+ String a = app.getNode("a").getConf();
+ String expectedA =
+ "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" +
+ " <prepare>\r\n" +
+ " <delete path=\"/tmp\" />\r\n" +
+ " <mkdir path=\"/tmp\" />\r\n" +
+ " </prepare>\r\n" +
+ " <job-tracker>foo</job-tracker>\r\n" +
+ " <configuration>\r\n" +
+ " <property>\r\n" +
+ " <name>c</name>\r\n" +
+ " <value>C</value>\r\n" +
+ " </property>\r\n" +
+ " </configuration>\r\n" +
+ " <script>script.q</script>\r\n" +
+ " <param>INPUT=/tmp/table</param>\r\n" +
+ " <param>OUTPUT=/tmp/hive</param>\r\n" +
+ " <name-node>global-nn</name-node>\r\n" +
+ "</hive>";
+ a = cleanupXml(a);
+ assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultNameNodeNotApplicable() throws Exception {
+ ConfigurationService.set("oozie.actions.default.name-node", "default-nn");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ // Not all actions want a NN (e.g. email action)
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1),
+ new Configuration());
+ String b1 = app.getNode("b1").getConf();
+ String expectedB1 =
+ "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" +
+ " <to>foo@bar.com</to>\r\n" +
+ " <subject>foo</subject>\r\n" +
+ " <body>bar</body>\r\n" +
+ "</email>";
+ b1 = cleanupXml(b1);
+ assertEquals(expectedB1.replaceAll(" ", ""), b1.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultNameNodeFail() throws Exception {
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ // No default NN is set
+ try {
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-namenode.xml", -1),
+ new Configuration());
+ fail();
+ } catch (WorkflowException e) {
+ assertEquals(ErrorCode.E0701, e.getErrorCode());
+ assertTrue(e.getMessage().contains("No name-node defined"));
+ }
+ }
+
+ public void testParserDefaultJobTracker() throws Exception {
+ ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1),
+ new Configuration());
+ String a = app.getNode("a").getConf();
+ String expectedA =
+ "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" +
+ " <prepare>\r\n" +
+ " <delete path=\"/tmp\" />\r\n" +
+ " <mkdir path=\"/tmp\" />\r\n" +
+ " </prepare>\r\n" +
+ " <name-node>bar</name-node>\r\n" +
+ " <configuration>\r\n" +
+ " <property>\r\n" +
+ " <name>c</name>\r\n" +
+ " <value>C</value>\r\n" +
+ " </property>\r\n" +
+ " </configuration>\r\n" +
+ " <script>script.q</script>\r\n" +
+ " <param>INPUT=/tmp/table</param>\r\n" +
+ " <param>OUTPUT=/tmp/hive</param>\r\n" +
+ " <job-tracker>default-jt</job-tracker>\r\n" +
+ "</hive>";
+ a = cleanupXml(a);
+ assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultJobTrackerWithGlobal() throws Exception {
+ ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker-global.xml", -1),
+ new Configuration());
+ String a = app.getNode("a").getConf();
+ String expectedA =
+ "<hive xmlns=\"uri:oozie:hive-action:0.2\">\r\n" +
+ " <prepare>\r\n" +
+ " <delete path=\"/tmp\" />\r\n" +
+ " <mkdir path=\"/tmp\" />\r\n" +
+ " </prepare>\r\n" +
+ " <name-node>bar</name-node>\r\n" +
+ " <configuration>\r\n" +
+ " <property>\r\n" +
+ " <name>c</name>\r\n" +
+ " <value>C</value>\r\n" +
+ " </property>\r\n" +
+ " </configuration>\r\n" +
+ " <script>script.q</script>\r\n" +
+ " <param>INPUT=/tmp/table</param>\r\n" +
+ " <param>OUTPUT=/tmp/hive</param>\r\n" +
+ " <job-tracker>global-jt</job-tracker>\r\n" +
+ "</hive>";
+ a = cleanupXml(a);
+ assertEquals(expectedA.replaceAll(" ", ""), a.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultJobTrackerNotApplicable() throws Exception {
+ ConfigurationService.set("oozie.actions.default.job-tracker", "default-jt");
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ // Not all actions want a NN (e.g. email action)
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1),
+ new Configuration());
+ String b1 = app.getNode("b1").getConf();
+ String expectedB1 =
+ "<email xmlns=\"uri:oozie:email-action:0.2\">\r\n" +
+ " <to>foo@bar.com</to>\r\n" +
+ " <subject>foo</subject>\r\n" +
+ " <body>bar</body>\r\n" +
+ "</email>";
+ b1 = cleanupXml(b1);
+ assertEquals(expectedB1.replaceAll(" ", ""), b1.replaceAll(" ", ""));
+ }
+
+ public void testParserDefaultJobTrackerFail() throws Exception {
+ LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+ LiteWorkflowStoreService.LiteControlNodeHandler.class,
+ LiteWorkflowStoreService.LiteDecisionHandler.class,
+ LiteWorkflowStoreService.LiteActionHandler.class);
+
+ // No default NN is set
+ try {
+ LiteWorkflowApp app = parser.validateAndParse(IOUtils.getResourceAsReader("wf-schema-no-jobtracker.xml", -1),
+ new Configuration());
+ fail();
+ } catch (WorkflowException e) {
+ assertEquals(ErrorCode.E0701, e.getErrorCode());
+ assertTrue(e.getMessage().contains("No job-tracker defined"));
+ }
+ }
+
public void testParser() throws Exception {
LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
LiteWorkflowStoreService.LiteControlNodeHandler.class,
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-jobtracker-global.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/wf-schema-no-jobtracker-global.xml b/core/src/test/resources/wf-schema-no-jobtracker-global.xml
new file mode 100644
index 0000000..433b6b8
--- /dev/null
+++ b/core/src/test/resources/wf-schema-no-jobtracker-global.xml
@@ -0,0 +1,61 @@
+<!--
+ 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.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf">
+ <global>
+ <job-tracker>global-jt</job-tracker>
+ </global>
+
+ <start to="a"/>
+
+ <action name="a">
+ <hive xmlns="uri:oozie:hive-action:0.2">
+ <prepare>
+ <delete path="/tmp"/>
+ <mkdir path="/tmp"/>
+ </prepare>
+ <name-node>bar</name-node>
+ <configuration>
+ <property>
+ <name>c</name>
+ <value>C</value>
+ </property>
+ </configuration>
+ <script>script.q</script>
+ <param>INPUT=/tmp/table</param>
+ <param>OUTPUT=/tmp/hive</param>
+ </hive>
+ <ok to="b2"/>
+ <error to="b1"/>
+ </action>
+
+ <action name="b1">
+ <email xmlns="uri:oozie:email-action:0.2">
+ <to>foo@bar.com</to>
+ <subject>foo</subject>
+ <body>bar</body>
+ </email>
+ <ok to="b2"/>
+ <error to="b2"/>
+ </action>
+
+ <kill name="b2">
+ <message>fail</message>
+ </kill>
+
+ <end name="c"/>
+</workflow-app>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-jobtracker.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/wf-schema-no-jobtracker.xml b/core/src/test/resources/wf-schema-no-jobtracker.xml
new file mode 100644
index 0000000..2eea734
--- /dev/null
+++ b/core/src/test/resources/wf-schema-no-jobtracker.xml
@@ -0,0 +1,57 @@
+<!--
+ 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.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf">
+ <start to="a"/>
+
+ <action name="a">
+ <hive xmlns="uri:oozie:hive-action:0.2">
+ <prepare>
+ <delete path="/tmp"/>
+ <mkdir path="/tmp"/>
+ </prepare>
+ <name-node>bar</name-node>
+ <configuration>
+ <property>
+ <name>c</name>
+ <value>C</value>
+ </property>
+ </configuration>
+ <script>script.q</script>
+ <param>INPUT=/tmp/table</param>
+ <param>OUTPUT=/tmp/hive</param>
+ </hive>
+ <ok to="b2"/>
+ <error to="b1"/>
+ </action>
+
+ <action name="b1">
+ <email xmlns="uri:oozie:email-action:0.2">
+ <to>foo@bar.com</to>
+ <subject>foo</subject>
+ <body>bar</body>
+ </email>
+ <ok to="b2"/>
+ <error to="b2"/>
+ </action>
+
+ <kill name="b2">
+ <message>fail</message>
+ </kill>
+
+ <end name="c"/>
+</workflow-app>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-namenode-global.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/wf-schema-no-namenode-global.xml b/core/src/test/resources/wf-schema-no-namenode-global.xml
new file mode 100644
index 0000000..874ec52
--- /dev/null
+++ b/core/src/test/resources/wf-schema-no-namenode-global.xml
@@ -0,0 +1,61 @@
+<!--
+ 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.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf">
+ <global>
+ <name-node>global-nn</name-node>
+ </global>
+
+ <start to="a"/>
+
+ <action name="a">
+ <hive xmlns="uri:oozie:hive-action:0.2">
+ <prepare>
+ <delete path="/tmp"/>
+ <mkdir path="/tmp"/>
+ </prepare>
+ <job-tracker>foo</job-tracker>
+ <configuration>
+ <property>
+ <name>c</name>
+ <value>C</value>
+ </property>
+ </configuration>
+ <script>script.q</script>
+ <param>INPUT=/tmp/table</param>
+ <param>OUTPUT=/tmp/hive</param>
+ </hive>
+ <ok to="b2"/>
+ <error to="b1"/>
+ </action>
+
+ <action name="b1">
+ <email xmlns="uri:oozie:email-action:0.2">
+ <to>foo@bar.com</to>
+ <subject>foo</subject>
+ <body>bar</body>
+ </email>
+ <ok to="b2"/>
+ <error to="b2"/>
+ </action>
+
+ <kill name="b2">
+ <message>fail</message>
+ </kill>
+
+ <end name="c"/>
+</workflow-app>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-no-namenode.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/wf-schema-no-namenode.xml b/core/src/test/resources/wf-schema-no-namenode.xml
new file mode 100644
index 0000000..14ff8d4
--- /dev/null
+++ b/core/src/test/resources/wf-schema-no-namenode.xml
@@ -0,0 +1,57 @@
+<!--
+ 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.
+-->
+<workflow-app xmlns="uri:oozie:workflow:0.4" name="test-wf">
+ <start to="a"/>
+
+ <action name="a">
+ <hive xmlns="uri:oozie:hive-action:0.2">
+ <prepare>
+ <delete path="/tmp"/>
+ <mkdir path="/tmp"/>
+ </prepare>
+ <job-tracker>foo</job-tracker>
+ <configuration>
+ <property>
+ <name>c</name>
+ <value>C</value>
+ </property>
+ </configuration>
+ <script>script.q</script>
+ <param>INPUT=/tmp/table</param>
+ <param>OUTPUT=/tmp/hive</param>
+ </hive>
+ <ok to="b2"/>
+ <error to="b1"/>
+ </action>
+
+ <action name="b1">
+ <email xmlns="uri:oozie:email-action:0.2">
+ <to>foo@bar.com</to>
+ <subject>foo</subject>
+ <body>bar</body>
+ </email>
+ <ok to="b2"/>
+ <error to="b2"/>
+ </action>
+
+ <kill name="b2">
+ <message>fail</message>
+ </kill>
+
+ <end name="c"/>
+</workflow-app>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/core/src/test/resources/wf-schema-valid-global-ext.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/wf-schema-valid-global-ext.xml b/core/src/test/resources/wf-schema-valid-global-ext.xml
index 86d274d..88bf6c7 100644
--- a/core/src/test/resources/wf-schema-valid-global-ext.xml
+++ b/core/src/test/resources/wf-schema-valid-global-ext.xml
@@ -50,7 +50,7 @@
<param>OUTPUT=/tmp/hive</param>
</hive>
<ok to="b"/>
- <error to="c"/>
+ <error to="c1"/>
</action>
<action name="b">
@@ -75,10 +75,20 @@
<arg>/tmp2/data.txt</arg>
</distcp>
<ok to="d"/>
- <error to="c"/>
+ <error to="c1"/>
</action>
- <kill name="c">
+ <action name="c1">
+ <email xmlns="uri:oozie:email-action:0.2">
+ <to>foo@bar.com</to>
+ <subject>foo</subject>
+ <body>bar</body>
+ </email>
+ <ok to="c2"/>
+ <error to="c2"/>
+ </action>
+
+ <kill name="c2">
<message>fail</message>
</kill>
http://git-wip-us.apache.org/repos/asf/oozie/blob/a762991a/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index b19a913..4ffc302 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 4.3.0 release (trunk - unreleased)
+OOZIE-2187 Add a way to specify a default JT/RM and NN (rkanter)
OOZIE-2272 Use Hadoop's CredentialProvider for passwords in oozie-site (rkanter)
OOZIE-2287 Add support for deleting hcat partitions in fs action delete (kailongs via rohini)
OOZIE-2285 Change in concurrency should trigger coord action ready command (kailongs via rohini)