You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by tu...@apache.org on 2012/07/14 02:31:28 UTC

svn commit: r1361453 - in /incubator/oozie/trunk: ./ core/src/main/java/org/apache/oozie/ core/src/main/java/org/apache/oozie/workflow/lite/ core/src/test/java/org/apache/oozie/workflow/lite/

Author: tucu
Date: Sat Jul 14 00:31:27 2012
New Revision: 1361453

URL: http://svn.apache.org/viewvc?rev=1361453&view=rev
Log:
OOZIE-865 ForkJoin validator checks total lengths of forks vs. joins instead of actual paths (rkanter via tucu)

Modified:
    incubator/oozie/trunk/core/src/main/java/org/apache/oozie/ErrorCode.java
    incubator/oozie/trunk/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
    incubator/oozie/trunk/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
    incubator/oozie/trunk/release-log.txt

Modified: incubator/oozie/trunk/core/src/main/java/org/apache/oozie/ErrorCode.java
URL: http://svn.apache.org/viewvc/incubator/oozie/trunk/core/src/main/java/org/apache/oozie/ErrorCode.java?rev=1361453&r1=1361452&r2=1361453&view=diff
==============================================================================
--- incubator/oozie/trunk/core/src/main/java/org/apache/oozie/ErrorCode.java (original)
+++ incubator/oozie/trunk/core/src/main/java/org/apache/oozie/ErrorCode.java Sat Jul 14 00:31:27 2012
@@ -142,6 +142,7 @@ public enum ErrorCode {
     E0734(XLog.STD, "Invalid transition from node [{0}] to node [{1}] while using fork/join"),
     E0735(XLog.STD, "There was an invalid \"error to\" transition to node [{1}] while using fork/join"),
     E0736(XLog.STD, "Workflow definition lenght [{0}] exceeded maximum allowed length [{1}]"),
+    E0737(XLog.STD, "Invalid transition from node [{0}] to node [{1}] while using Fork/Join because node [{1}] is of type [{2}]"),
 
     E0800(XLog.STD, "Action it is not running its in [{1}] state, action [{0}]"),
     E0801(XLog.STD, "Workflow already running, workflow [{0}]"),

Modified: incubator/oozie/trunk/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java
URL: http://svn.apache.org/viewvc/incubator/oozie/trunk/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java?rev=1361453&r1=1361452&r2=1361453&view=diff
==============================================================================
--- incubator/oozie/trunk/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java (original)
+++ incubator/oozie/trunk/core/src/main/java/org/apache/oozie/workflow/lite/LiteWorkflowAppParser.java Sat Jul 14 00:31:27 2012
@@ -170,19 +170,23 @@ public class LiteWorkflowAppParser {
         for (int i = 0; i < transitions.size(); i++) {
             NodeDef node = app.getNode(transitions.get(i));
             if (node instanceof DecisionNodeDef) {
-                Set<String> decisionSet = new HashSet<String>(node.getTransitions());
+                // Make sure the transition is valid
+                validateTransition(errorToTransitions, transitions, app, node);
+                // Add each transition to transitions (once) if they are not a kill node
+                HashSet<String> decisionSet = new HashSet<String>(node.getTransitions());
                 for (String ds : decisionSet) {
-                    if (transitions.contains(ds)) {
-                        throw new WorkflowException(ErrorCode.E0734, node.getName(), ds);
-                    } else {
+                    if (!(app.getNode(ds) instanceof KillNodeDef)) {
                         transitions.add(ds);
                     }
                 }
             } else if (node instanceof ActionNodeDef) {
                 // Make sure the transition is valid
                 validateTransition(errorToTransitions, transitions, app, node);
-                // Add the "ok-to" transition of node
-                transitions.add(node.getTransitions().get(0));
+                // Add the "ok-to" transition of node if its not a kill node
+                String okTo = node.getTransitions().get(0);
+                if (!(app.getNode(okTo) instanceof KillNodeDef)) {
+                    transitions.add(okTo);
+                }
                 String errorTo = node.getTransitions().get(1);
                 // Add the "error-to" transition if the transition is a Action Node
                 if (app.getNode(errorTo) instanceof ActionNodeDef) {
@@ -222,15 +226,20 @@ public class LiteWorkflowAppParser {
     private void validateTransition(List<String> errorToTransitions, List<String> transitions, LiteWorkflowApp app, NodeDef node)
             throws WorkflowException {
         for (String transition : node.getTransitions()) {
-            // Make sure the transition node is either a join node or is not already visited
-            if (transitions.contains(transition) && !(app.getNode(transition) instanceof JoinNodeDef)) {
-                throw new WorkflowException(ErrorCode.E0734, node.getName(), transition);
+            // Make sure the transition node is not an end node
+            NodeDef tNode = app.getNode(transition);
+            if (tNode instanceof EndNodeDef) {
+                throw new WorkflowException(ErrorCode.E0737, node.getName(), transition, "end");
             }
-            // Make sure the transition node is not the same as an already visited 'error-to' transition
-            if (errorToTransitions.contains(transition)) {
-                throw new WorkflowException(ErrorCode.E0735, node.getName(), transition);
+            // Make sure the transition node is either a join node or is not already visited
+            if (transitions.contains(transition) && !(tNode instanceof JoinNodeDef)) {
+                    throw new WorkflowException(ErrorCode.E0734, node.getName(), transition);
+                }
+                // Make sure the transition node is not the same as an already visited 'error-to' transition
+                if (errorToTransitions.contains(transition)) {
+                    throw new WorkflowException(ErrorCode.E0735, node.getName(), transition);
+                }
             }
-        }
 
     }
 

Modified: incubator/oozie/trunk/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java
URL: http://svn.apache.org/viewvc/incubator/oozie/trunk/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java?rev=1361453&r1=1361452&r2=1361453&view=diff
==============================================================================
--- incubator/oozie/trunk/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java (original)
+++ incubator/oozie/trunk/core/src/test/java/org/apache/oozie/workflow/lite/TestLiteWorkflowAppParser.java Sat Jul 14 00:31:27 2012
@@ -174,10 +174,11 @@ public class TestLiteWorkflowAppParser e
         LiteWorkflowApp def = new LiteWorkflowApp("wf", "<worklfow-app/>", new StartNodeDef("one"))
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f", "end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two", "three"})))
-        .addNode(new ActionNodeDef("two", dummyConf,  TestActionNodeHandler.class, "j", "end"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "end"))
+        .addNode(new ActionNodeDef("two", dummyConf,  TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
         .addNode(new JoinNodeDef("j", "four"))
         .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "end", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -205,15 +206,16 @@ public class TestLiteWorkflowAppParser e
         LiteWorkflowApp def = new LiteWorkflowApp("testWf", "<worklfow-app/>", new StartNodeDef("one"))
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two", "three"})))
-        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "f2","end"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j","end"))
+        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "f2","k"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j","k"))
         .addNode(new ForkNodeDef("f2", Arrays.asList(new String[]{"four", "five", "six"})))
-        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j2","end"))
-        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2","end"))
-        .addNode(new ActionNodeDef("six", dummyConf, TestActionNodeHandler.class,"j2", "end"))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j2","k"))
+        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2","k"))
+        .addNode(new ActionNodeDef("six", dummyConf, TestActionNodeHandler.class,"j2", "k"))
         .addNode(new JoinNodeDef("j2", "seven"))
-        .addNode(new ActionNodeDef("seven", dummyConf, TestActionNodeHandler.class, "j","end"))
+        .addNode(new ActionNodeDef("seven", dummyConf, TestActionNodeHandler.class, "j","k"))
         .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -237,9 +239,10 @@ public class TestLiteWorkflowAppParser e
         LiteWorkflowApp def = new LiteWorkflowApp("testWf", "<worklfow-app/>", new StartNodeDef("one"))
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two", "three"})))
-        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j","end"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "end","end"))
+        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j","k"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "end","k"))
         .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -247,7 +250,11 @@ public class TestLiteWorkflowAppParser e
             fail("Expected to catch an exception but did not encounter any");
         } catch (Exception ex) {
             WorkflowException we = (WorkflowException) ex.getCause();
-            assertEquals(ErrorCode.E0730, we.getErrorCode());
+            assertEquals(ErrorCode.E0737, we.getErrorCode());
+            // Make sure the message contains the nodes and type involved in the invalid transition to end
+            assertTrue(we.getMessage().contains("three"));
+            assertTrue(we.getMessage().contains("node [end]"));
+            assertTrue(we.getMessage().contains("type [end]"));
         }
     }
 
@@ -270,14 +277,15 @@ public class TestLiteWorkflowAppParser e
         LiteWorkflowApp def = new LiteWorkflowApp("testWf", "<worklfow-app/>", new StartNodeDef("one"))
             .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
             .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"four", "three", "two"})))
-            .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j","end"))
-            .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j","end"))
-            .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "f2","end"))
+            .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j","k"))
+            .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j","k"))
+            .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "f2","k"))
             .addNode(new ForkNodeDef("f2", Arrays.asList(new String[]{"five", "six"})))
-            .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2","end"))
-            .addNode(new ActionNodeDef("six", dummyConf, TestActionNodeHandler.class, "j2","end"))
+            .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2","k"))
+            .addNode(new ActionNodeDef("six", dummyConf, TestActionNodeHandler.class, "j2","k"))
             .addNode(new JoinNodeDef("j", "j2"))
-            .addNode(new JoinNodeDef("j2", "end"))
+            .addNode(new JoinNodeDef("j2", "k"))
+            .addNode(new KillNodeDef("k", "kill"))
             .addNode(new EndNodeDef("end"));
 
         try {
@@ -294,7 +302,7 @@ public class TestLiteWorkflowAppParser e
      2->ok->3
      2->fail->j
      3->ok->j
-     3->fail->end
+     3->fail->k
     */
     public void testTransitionFailure1() throws WorkflowException{
         LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
@@ -305,8 +313,9 @@ public class TestLiteWorkflowAppParser e
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
         .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "three", "j"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "end"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
         .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -327,7 +336,7 @@ public class TestLiteWorkflowAppParser e
     2->fail->3
     2->ok->j
     3->ok->j
-    3->fail->end
+    3->fail->k
    */
    public void testTransitionFailure2() throws WorkflowException{
        LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
@@ -338,8 +347,9 @@ public class TestLiteWorkflowAppParser e
        .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
        .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j", "three"))
-       .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "end"))
+       .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
        .addNode(new JoinNodeDef("j", "end"))
+       .addNode(new KillNodeDef("k", "kill"))
        .addNode(new EndNodeDef("end"));
 
        try {
@@ -371,9 +381,10 @@ public class TestLiteWorkflowAppParser e
        .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
        .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j", "four"))
-       .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "four", "end"))
-       .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "end"))
+       .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "four", "k"))
+       .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
        .addNode(new JoinNodeDef("j", "end"))
+       .addNode(new KillNodeDef("k", "kill"))
        .addNode(new EndNodeDef("end"));
 
        try {
@@ -409,11 +420,12 @@ public class TestLiteWorkflowAppParser e
        .addNode(new ActionNodeDef("two", dummyConf,  TestActionNodeHandler.class, "j", "f1"))
        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "f1"))
        .addNode(new ForkNodeDef("f1", Arrays.asList(new String[]{"four", "five"})))
-       .addNode(new ActionNodeDef("four", dummyConf,  TestActionNodeHandler.class, "j1", "end"))
-       .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j1", "end"))
+       .addNode(new ActionNodeDef("four", dummyConf,  TestActionNodeHandler.class, "j1", "k"))
+       .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j1", "k"))
        .addNode(new JoinNodeDef("j", "six"))
        .addNode(new JoinNodeDef("j1", "six"))
        .addNode(new ActionNodeDef("six", dummyConf, TestActionNodeHandler.class, "end", "end"))
+       .addNode(new KillNodeDef("k", "kill"))
        .addNode(new EndNodeDef("end"));
 
        try {
@@ -439,10 +451,71 @@ public class TestLiteWorkflowAppParser e
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
         .addNode(new DecisionNodeDef("two", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"four","five","four"})))
-        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "end"))
-        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j", "end"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "end"))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new KillNodeDef("k", "kill"))
+        .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new EndNodeDef("end"));
+
+        try {
+            invokeForkJoin(parser, def);
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail("Unexpected Exception");
+        }
+    }
+
+    /*
+    f->(2,3)
+    2->decision node->{4,j,4}
+    3->decision node->{j,5,j}
+    4->j
+    5->j
+    */
+    public void testDecisionsToJoinForkJoin() throws WorkflowException{
+        LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+                LiteWorkflowStoreService.LiteDecisionHandler.class,
+                LiteWorkflowStoreService.LiteActionHandler.class);
+        LiteWorkflowApp def = new LiteWorkflowApp("name", "def", new StartNodeDef("one"))
+        .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
+        .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
+        .addNode(new DecisionNodeDef("two", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"four","j","four"})))
+        .addNode(new DecisionNodeDef("three", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"j","five","j"})))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
+        .addNode(new EndNodeDef("end"));
+
+        try {
+            invokeForkJoin(parser, def);
+        } catch (Exception e) {
+            e.printStackTrace();
+            fail("Unexpected Exception");
+        }
+    }
+
+    /*
+    f->(2,3)
+    2->decision node->{4,k,4}
+    3->decision node->{k,5,k}
+    4->j
+    5->j
+    */
+    public void testDecisionsToKillForkJoin() throws WorkflowException{
+        LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+                LiteWorkflowStoreService.LiteDecisionHandler.class,
+                LiteWorkflowStoreService.LiteActionHandler.class);
+        LiteWorkflowApp def = new LiteWorkflowApp("name", "def", new StartNodeDef("one"))
+        .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
+        .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
+        .addNode(new DecisionNodeDef("two", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"four","k","four"})))
+        .addNode(new DecisionNodeDef("three", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"k","five","k"})))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j", "k"))
         .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -456,8 +529,8 @@ public class TestLiteWorkflowAppParser e
     /*
      *f->(2,3)
      *2->decision node->{3,4}
-     *3->end
-     *4->end
+     *3->j
+     *4->j
      */
     public void testDecisionForkJoinFailure() throws WorkflowException{
         LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
@@ -467,9 +540,10 @@ public class TestLiteWorkflowAppParser e
         .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
         .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
         .addNode(new DecisionNodeDef("two", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"four","three"})))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "end"))
-        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "end"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
         .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {
@@ -483,6 +557,39 @@ public class TestLiteWorkflowAppParser e
             assertTrue(we.getMessage().contains("three"));
         }
     }
+    
+    /*
+     *f->(2,3)
+     *2->decision node->{4,end}
+     *3->j
+     *4->j
+     */
+    public void testDecisionToEndForkJoinFailure() throws WorkflowException{
+        LiteWorkflowAppParser parser = new LiteWorkflowAppParser(null,
+                LiteWorkflowStoreService.LiteDecisionHandler.class,
+                LiteWorkflowStoreService.LiteActionHandler.class);
+        LiteWorkflowApp def = new LiteWorkflowApp("name", "def", new StartNodeDef("one"))
+        .addNode(new ActionNodeDef("one", dummyConf, TestActionNodeHandler.class, "f","end"))
+        .addNode(new ForkNodeDef("f", Arrays.asList(new String[]{"two","three"})))
+        .addNode(new DecisionNodeDef("two", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"four","end"})))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j", "k"))
+        .addNode(new JoinNodeDef("j", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
+        .addNode(new EndNodeDef("end"));
+
+        try {
+            invokeForkJoin(parser, def);
+            fail("Expected to catch an exception but did not encounter any");
+        } catch (Exception ex) {
+            WorkflowException we = (WorkflowException) ex.getCause();
+            assertEquals(ErrorCode.E0737, we.getErrorCode());
+            // Make sure the message contains the nodes and type involved in the invalid transition to end
+            assertTrue(we.getMessage().contains("two"));
+            assertTrue(we.getMessage().contains("node [end]"));
+            assertTrue(we.getMessage().contains("type [end]"));
+        }
+    }
 
     /*
      * 1->decision node->{f1, f2}
@@ -501,12 +608,13 @@ public class TestLiteWorkflowAppParser e
         .addNode(new DecisionNodeDef("one", dummyConf, TestDecisionNodeHandler.class, Arrays.asList(new String[]{"f1","f2"})))
         .addNode(new ForkNodeDef("f1", Arrays.asList(new String[]{"two","three"})))
         .addNode(new ForkNodeDef("f2", Arrays.asList(new String[]{"four","five"})))
-        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j1", "end"))
-        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j1", "end"))
-        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j2", "end"))
-        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2", "end"))
+        .addNode(new ActionNodeDef("two", dummyConf, TestActionNodeHandler.class, "j1", "k"))
+        .addNode(new ActionNodeDef("three", dummyConf, TestActionNodeHandler.class, "j1", "k"))
+        .addNode(new ActionNodeDef("four", dummyConf, TestActionNodeHandler.class, "j2", "k"))
+        .addNode(new ActionNodeDef("five", dummyConf, TestActionNodeHandler.class, "j2", "k"))
         .addNode(new JoinNodeDef("j1", "end"))
         .addNode(new JoinNodeDef("j2", "end"))
+        .addNode(new KillNodeDef("k", "kill"))
         .addNode(new EndNodeDef("end"));
 
         try {

Modified: incubator/oozie/trunk/release-log.txt
URL: http://svn.apache.org/viewvc/incubator/oozie/trunk/release-log.txt?rev=1361453&r1=1361452&r2=1361453&view=diff
==============================================================================
--- incubator/oozie/trunk/release-log.txt (original)
+++ incubator/oozie/trunk/release-log.txt Sat Jul 14 00:31:27 2012
@@ -1,5 +1,6 @@
 -- Oozie 3.3.0 release (trunk - unreleased)
 
+OOZIE-865 ForkJoin validator checks total lengths of forks vs. joins instead of actual paths (rkanter via tucu)
 OOZIE-905 Clarify and improve Oozie logging configuration and streaming (rkanter via tucu)
 OOZIE-890 Support for map-reduce in OozieCLI (britt via virag)
 OOZIE-887 Support for choosing timezone in Oozie UI (rkanter via tucu)