001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.command.wf;
016    
017    import java.sql.Timestamp;
018    import java.util.Date;
019    
020    import org.apache.oozie.WorkflowActionBean;
021    import org.apache.oozie.WorkflowJobBean;
022    import org.apache.oozie.action.ActionExecutor;
023    import org.apache.oozie.action.ActionExecutorException;
024    import org.apache.oozie.client.WorkflowJob;
025    import org.apache.oozie.client.WorkflowAction.Status;
026    import org.apache.oozie.command.CommandException;
027    import org.apache.oozie.service.ActionService;
028    import org.apache.oozie.service.Services;
029    import org.apache.oozie.service.UUIDService;
030    import org.apache.oozie.store.StoreException;
031    import org.apache.oozie.store.WorkflowStore;
032    import org.apache.oozie.util.Instrumentation;
033    import org.apache.oozie.util.XLog;
034    
035    /**
036     * Executes the check command for ActionHandlers. </p> Ensures the action is in RUNNING state before executing {@link
037     * ActionExecutor#check(org.apache.oozie.action.ActionExecutor.Context, org.apache.oozie.client.WorkflowAction)}
038     */
039    public class ActionCheckCommand extends ActionCommand<Void> {
040        public static final String EXEC_DATA_MISSING = "EXEC_DATA_MISSING";
041        private String id;
042        private String jobId;
043        private int actionCheckDelay;
044    
045        public ActionCheckCommand(String id) {
046            this(id, -1);
047        }
048    
049        public ActionCheckCommand(String id, int priority, int checkDelay) {
050            super("action.check", "action.check", priority);
051            this.id = id;
052            this.actionCheckDelay = checkDelay;
053        }
054    
055        public ActionCheckCommand(String id, int checkDelay) {
056            this(id, 0, checkDelay);
057        }
058    
059        @Override
060        protected Void call(WorkflowStore store) throws StoreException, CommandException {
061    
062            // String jobId = Services.get().get(UUIDService.class).getId(id);
063            WorkflowJobBean workflow = store.getWorkflow(jobId, false);
064            setLogInfo(workflow);
065            WorkflowActionBean action = store.getAction(id, false);
066            setLogInfo(action);
067            if (action.isPending() && action.getStatus() == WorkflowActionBean.Status.RUNNING) {
068                setLogInfo(workflow);
069                // if the action has been updated, quit this command
070                if (actionCheckDelay > 0) {
071                    Timestamp actionCheckTs = new Timestamp(System.currentTimeMillis() - actionCheckDelay * 1000);
072                    Timestamp actionLmt = action.getLastCheckTimestamp();
073                    if (actionLmt.after(actionCheckTs)) {
074                        XLog.getLog(getClass()).debug(
075                                "The wf action :" + id + " has been udated recently. Ignoring ActionCheckCommand!");
076                        return null;
077                    }
078                }
079                if (workflow.getStatus() == WorkflowJob.Status.RUNNING) {
080                    ActionExecutor executor = Services.get().get(ActionService.class).getExecutor(action.getType());
081                    if (executor != null) {
082                        ActionExecutorContext context = null;
083                        try {
084                            boolean isRetry = false;
085                            context = new ActionCommand.ActionExecutorContext(workflow, action, isRetry);
086                            incrActionCounter(action.getType(), 1);
087    
088                            Instrumentation.Cron cron = new Instrumentation.Cron();
089                            cron.start();
090                            executor.check(context, action);
091                            cron.stop();
092                            addActionCron(action.getType(), cron);
093    
094                            if (action.isExecutionComplete()) {
095                                if (!context.isExecuted()) {
096                                    XLog.getLog(getClass()).warn(XLog.OPS,
097                                                                 "Action Completed, ActionExecutor [{0}] must call setExecutionData()",
098                                                                 executor.getType());
099                                    action.setErrorInfo(EXEC_DATA_MISSING,
100                                                        "Execution Complete, but Execution Data Missing from Action");
101                                    failJob(context);
102                                    action.setLastCheckTime(new Date());
103                                    store.updateAction(action);
104                                    store.updateWorkflow(workflow);
105                                    return null;
106                                }
107                                action.setPending();
108                                queueCallable(new ActionEndCommand(action.getId(), action.getType()));
109                            }
110                            action.setLastCheckTime(new Date());
111                            store.updateAction(action);
112                            store.updateWorkflow(workflow);
113                        }
114                        catch (ActionExecutorException ex) {
115                            XLog.getLog(getClass()).warn(
116                                    "Exception while executing check(). Error Code [{0}], Message[{1}]", ex.getErrorCode(),
117                                    ex.getMessage(), ex);
118    
119                            switch (ex.getErrorType()) {
120                                case FAILED:
121                                    failAction(workflow, action);
122                                    break;
123                            }
124                            action.setLastCheckTime(new Date());
125                            store.updateAction(action);
126                            store.updateWorkflow(workflow);
127                            return null;
128                        }
129                    }
130                }
131                else {
132                    action.setLastCheckTime(new Date());
133                    store.updateAction(action);
134                    XLog.getLog(getClass()).warn(
135                            "Action [{0}] status is running but WF Job [{1}] status is [{2}]. Expected status is RUNNING.",
136                            action.getId(), workflow.getId(), workflow.getStatus());
137                }
138            }
139            return null;
140        }
141    
142        private void failAction(WorkflowJobBean workflow, WorkflowActionBean action) throws CommandException {
143            XLog.getLog(getClass()).warn("Failing Job [{0}] due to failed action [{1}]", workflow.getId(), action.getId());
144            action.resetPending();
145            action.setStatus(Status.FAILED);
146            workflow.setStatus(WorkflowJob.Status.FAILED);
147            incrJobCounter(INSTR_FAILED_JOBS_COUNTER, 1);
148        }
149    
150        /**
151         * @param args
152         * @throws Exception
153         */
154        public static void main(String[] args) throws Exception {
155            new Services().init();
156    
157            try {
158                new ActionCheckCommand("0000001-100122154231282-oozie-dani-W@pig1").call();
159                Thread.sleep(100000);
160            }
161            finally {
162                new Services().destroy();
163            }
164        }
165    
166        @Override
167        protected Void execute(WorkflowStore store) throws CommandException, StoreException {
168            try {
169                XLog.getLog(getClass()).debug("STARTED ActionCheckCommand for wf actionId=" + id + " priority =" + getPriority());
170                jobId = Services.get().get(UUIDService.class).getId(id);
171                if (lock(jobId)) {
172                    call(store);
173                }
174                else {
175                    queueCallable(new ActionCheckCommand(id, actionCheckDelay), LOCK_FAILURE_REQUEUE_INTERVAL);
176                    XLog.getLog(getClass()).warn("ActionCheckCommand lock was not acquired - failed {0}", id);
177                }
178            }
179            catch (InterruptedException e) {
180                queueCallable(new ActionCheckCommand(id, actionCheckDelay), LOCK_FAILURE_REQUEUE_INTERVAL);
181                XLog.getLog(getClass()).warn("ActionCheckCommand lock was not acquired - interrupted exception failed {0}",
182                                             id);
183            }
184            XLog.getLog(getClass()).debug("ENDED ActionCheckCommand for wf actionId=" + id + ", jobId=" + jobId);
185            return null;
186        }
187    }