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 }