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}