001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.workflow.lite; 020 021import org.apache.oozie.ErrorCode; 022import org.apache.oozie.workflow.WorkflowException; 023 024import java.util.ArrayList; 025import java.util.List; 026 027 028/** 029 * Node handler that provides the necessary workflow logic for control nodes: START/END/KILL/FORK/JOIN. 030 */ 031public abstract class ControlNodeHandler extends NodeHandler { 032 033 public static final String FORK_COUNT_PREFIX = "workflow.fork."; 034 035 /** 036 * Called by {@link #enter(Context)} when returning TRUE. 037 * 038 * @param context workflow context 039 * @throws WorkflowException thrown if an error occurred. 040 */ 041 public abstract void touch(Context context) throws WorkflowException; 042 043 @Override 044 public boolean enter(Context context) throws WorkflowException { 045 boolean doTouch; 046 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 047 if (nodeClass.equals(StartNodeDef.class)) { 048 if (!context.getSignalValue().equals(StartNodeDef.START)) { 049 throw new WorkflowException(ErrorCode.E0715, context.getSignalValue()); 050 } 051 doTouch = true; 052 } 053 else if (nodeClass.equals(EndNodeDef.class)) { 054 doTouch = true; 055 } 056 else if (nodeClass.equals(KillNodeDef.class)) { 057 doTouch = true; 058 } 059 else if (nodeClass.equals(ForkNodeDef.class)) { 060 doTouch = true; 061 } 062 else if (nodeClass.equals(JoinNodeDef.class)) { 063 String parentExecutionPath = context.getParentExecutionPath(context.getExecutionPath()); 064 String forkCount = context.getVar(FORK_COUNT_PREFIX + parentExecutionPath); 065 if (forkCount == null) { 066 throw new WorkflowException(ErrorCode.E0720, context.getNodeDef().getName()); 067 } 068 int count = Integer.parseInt(forkCount) - 1; 069 if (count > 0) { 070 context.setVar(FORK_COUNT_PREFIX + parentExecutionPath, "" + count); 071 context.deleteExecutionPath(); 072 } 073 else { 074 context.setVar(FORK_COUNT_PREFIX + parentExecutionPath, null); 075 } 076 doTouch = (count == 0); 077 } 078 else { 079 throw new IllegalStateException("Invalid node type: " + nodeClass); 080 } 081 if (doTouch) { 082 touch(context); 083 } 084 return false; 085 } 086 087 @Override 088 public String exit(Context context) throws WorkflowException { 089 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 090 if (nodeClass.equals(StartNodeDef.class)) { 091 return context.getNodeDef().getTransitions().get(0); 092 } 093 else if (nodeClass.equals(EndNodeDef.class)) { 094 context.completeJob(); 095 return null; 096 } 097 else if (nodeClass.equals(KillNodeDef.class)) { 098 context.killJob(); 099 return null; 100 } 101 else if (nodeClass.equals(ForkNodeDef.class)) { 102 throw new UnsupportedOperationException(); 103 } 104 else if (nodeClass.equals(JoinNodeDef.class)) { 105 throw new UnsupportedOperationException(); 106 } 107 else { 108 throw new IllegalStateException("Invalid node type: " + nodeClass); 109 } 110 } 111 112 @Override 113 public void loopDetection(Context context) 114 throws WorkflowException { 115 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 116 if (nodeClass.equals(StartNodeDef.class)) { 117 } 118 else if (nodeClass.equals(EndNodeDef.class)) { 119 } 120 else if (nodeClass.equals(KillNodeDef.class)) { 121 } 122 else if (nodeClass.equals(ForkNodeDef.class)) { 123 } 124 else if (nodeClass.equals(JoinNodeDef.class)) { 125 String flag = getLoopFlag(context.getNodeDef().getName()); 126 if (context.getVar(flag) != null) { 127 throw new WorkflowException(ErrorCode.E0709, context.getNodeDef().getName()); 128 } 129 String parentExecutionPath = context.getParentExecutionPath(context.getExecutionPath()); 130 String forkCount = context.getVar(FORK_COUNT_PREFIX + parentExecutionPath); 131 if (forkCount == null) { 132 throw new WorkflowException(ErrorCode.E0720, context.getNodeDef().getName()); 133 } 134 int count = Integer.parseInt(forkCount) - 1; 135 if (count == 0) { 136 context.setVar(flag, "true"); 137 } 138 139 } 140 else { 141 throw new IllegalStateException("Invalid node type: " + nodeClass); 142 } 143 } 144 145 @Override 146 public List<String> multiExit(Context context) 147 throws WorkflowException { 148 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 149 if (nodeClass.equals(StartNodeDef.class)) { 150 return super.multiExit(context); 151 } 152 else if (nodeClass.equals(EndNodeDef.class)) { 153 return super.multiExit(context); 154 } 155 else if (nodeClass.equals(KillNodeDef.class)) { 156 return super.multiExit(context); 157 } 158 else if (nodeClass.equals(ForkNodeDef.class)) { 159 List<String> transitions = context.getNodeDef().getTransitions(); 160 context.setVar(FORK_COUNT_PREFIX + context.getExecutionPath(), "" + transitions.size()); 161 162 List<String> fullTransitions = new ArrayList<String>(transitions.size()); 163 164 for (String transition : transitions) { 165 String childExecutionPath = context.createExecutionPath(transition); 166 String fullTransition = context.createFullTransition(childExecutionPath, transition); 167 fullTransitions.add(fullTransition); 168 } 169 return fullTransitions; 170 } 171 else if (nodeClass.equals(JoinNodeDef.class)) { 172 String parentExecutionPath = context.getParentExecutionPath(context.getExecutionPath()); 173 // NOW we delete.. 174 context.deleteExecutionPath(); 175 176 String transition = context.getNodeDef().getTransitions().get(0); 177 String fullTransition = context.createFullTransition(parentExecutionPath, transition); 178 List<String> transitions = new ArrayList<String>(1); 179 transitions.add(fullTransition); 180 return transitions; 181 } 182 else { 183 throw new IllegalStateException("Invalid node type: " + nodeClass); 184 } 185 } 186 187 @Override 188 public void kill(Context context) { 189 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 190 if (nodeClass.equals(StartNodeDef.class)) { 191 //NOP 192 } 193 else if (nodeClass.equals(EndNodeDef.class)) { 194 //NOP 195 } 196 else if (nodeClass.equals(KillNodeDef.class)) { 197 //NOP 198 } 199 else if (nodeClass.equals(ForkNodeDef.class)) { 200 //NOP 201 } 202 else if (nodeClass.equals(JoinNodeDef.class)) { 203 //NOP 204 } 205 else { 206 throw new IllegalStateException("Invalid node type: " + nodeClass); 207 } 208 } 209 210 @Override 211 public void fail(Context context) { 212 Class<? extends NodeDef> nodeClass = context.getNodeDef().getClass(); 213 if (nodeClass.equals(StartNodeDef.class)) { 214 //NOP 215 } 216 else if (nodeClass.equals(EndNodeDef.class)) { 217 //NOP 218 } 219 else if (nodeClass.equals(KillNodeDef.class)) { 220 //NOP 221 } 222 else if (nodeClass.equals(ForkNodeDef.class)) { 223 //NOP 224 } 225 else if (nodeClass.equals(JoinNodeDef.class)) { 226 //NOP 227 } 228 else { 229 throw new IllegalStateException("Invalid node type: " + nodeClass); 230 } 231 } 232}