/**
* Copyright (c) 2010 Yahoo! Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. See accompanying LICENSE file.
*/
package org.apache.oozie.command.wf;
import java.sql.Timestamp;
import java.util.Date;
import org.apache.oozie.WorkflowActionBean;
import org.apache.oozie.WorkflowJobBean;
import org.apache.oozie.action.ActionExecutor;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.client.WorkflowJob;
import org.apache.oozie.client.WorkflowAction.Status;
import org.apache.oozie.command.CommandException;
import org.apache.oozie.service.ActionService;
import org.apache.oozie.service.Services;
import org.apache.oozie.service.UUIDService;
import org.apache.oozie.store.StoreException;
import org.apache.oozie.store.WorkflowStore;
import org.apache.oozie.util.Instrumentation;
import org.apache.oozie.util.XLog;
/**
* Executes the check command for ActionHandlers. </p> Ensures the action is in RUNNING state before executing {@link
* ActionExecutor#check(org.apache.oozie.action.ActionExecutor.Context, org.apache.oozie.client.WorkflowAction)}
*/
public class ActionCheckCommand extends ActionCommand<Void> {
public static final String EXEC_DATA_MISSING = "EXEC_DATA_MISSING";
private String id;
private String jobId;
private int actionCheckDelay;
public ActionCheckCommand(String id) {
this(id, -1);
}
public ActionCheckCommand(String id, int priority, int checkDelay) {
super("action.check", "action.check", priority);
this.id = id;
this.actionCheckDelay = checkDelay;
}
public ActionCheckCommand(String id, int checkDelay) {
this(id, 0, checkDelay);
}
@Override
protected Void call(WorkflowStore store) throws StoreException, CommandException {
// String jobId = Services.get().get(UUIDService.class).getId(id);
WorkflowJobBean workflow = store.getWorkflow(jobId, false);
setLogInfo(workflow);
WorkflowActionBean action = store.getAction(id, false);
setLogInfo(action);
if (action.isPending() && action.getStatus() == WorkflowActionBean.Status.RUNNING) {
setLogInfo(workflow);
// if the action has been updated, quit this command
if (actionCheckDelay > 0) {
Timestamp actionCheckTs = new Timestamp(System.currentTimeMillis() - actionCheckDelay * 1000);
Timestamp actionLmt = action.getLastCheckTimestamp();
if (actionLmt.after(actionCheckTs)) {
XLog.getLog(getClass()).debug(
"The wf action :" + id + " has been udated recently. Ignoring ActionCheckCommand!");
return null;
}
}
if (workflow.getStatus() == WorkflowJob.Status.RUNNING) {
ActionExecutor executor = Services.get().get(ActionService.class).getExecutor(action.getType());
if (executor != null) {
ActionExecutorContext context = null;
try {
boolean isRetry = false;
context = new ActionCommand.ActionExecutorContext(workflow, action, isRetry);
incrActionCounter(action.getType(), 1);
Instrumentation.Cron cron = new Instrumentation.Cron();
cron.start();
executor.check(context, action);
cron.stop();
addActionCron(action.getType(), cron);
if (action.isExecutionComplete()) {
if (!context.isExecuted()) {
XLog.getLog(getClass()).warn(XLog.OPS,
"Action Completed, ActionExecutor [{0}] must call setExecutionData()",
executor.getType());
action.setErrorInfo(EXEC_DATA_MISSING,
"Execution Complete, but Execution Data Missing from Action");
failJob(context);
action.setLastCheckTime(new Date());
store.updateAction(action);
store.updateWorkflow(workflow);
return null;
}
action.setPending();
queueCallable(new ActionEndCommand(action.getId(), action.getType()));
}
action.setLastCheckTime(new Date());
store.updateAction(action);
store.updateWorkflow(workflow);
}
catch (ActionExecutorException ex) {
XLog.getLog(getClass()).warn(
"Exception while executing check(). Error Code [{0}], Message[{1}]", ex.getErrorCode(),
ex.getMessage(), ex);
switch (ex.getErrorType()) {
case FAILED:
failAction(workflow, action);
break;
}
action.setLastCheckTime(new Date());
store.updateAction(action);
store.updateWorkflow(workflow);
return null;
}
}
}
else {
action.setLastCheckTime(new Date());
store.updateAction(action);
XLog.getLog(getClass()).warn(
"Action [{0}] status is running but WF Job [{1}] status is [{2}]. Expected status is RUNNING.",
action.getId(), workflow.getId(), workflow.getStatus());
}
}
return null;
}
private void failAction(WorkflowJobBean workflow, WorkflowActionBean action) throws CommandException {
XLog.getLog(getClass()).warn("Failing Job [{0}] due to failed action [{1}]", workflow.getId(), action.getId());
action.resetPending();
action.setStatus(Status.FAILED);
workflow.setStatus(WorkflowJob.Status.FAILED);
incrJobCounter(INSTR_FAILED_JOBS_COUNTER, 1);
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
new Services().init();
try {
new ActionCheckCommand("0000001-100122154231282-oozie-dani-W@pig1").call();
Thread.sleep(100000);
}
finally {
new Services().destroy();
}
}
@Override
protected Void execute(WorkflowStore store) throws CommandException, StoreException {
try {
XLog.getLog(getClass()).debug("STARTED ActionCheckCommand for wf actionId=" + id + " priority =" + getPriority());
jobId = Services.get().get(UUIDService.class).getId(id);
if (lock(jobId)) {
call(store);
}
else {
queueCallable(new ActionCheckCommand(id, actionCheckDelay), LOCK_FAILURE_REQUEUE_INTERVAL);
XLog.getLog(getClass()).warn("ActionCheckCommand lock was not acquired - failed {0}", id);
}
}
catch (InterruptedException e) {
queueCallable(new ActionCheckCommand(id, actionCheckDelay), LOCK_FAILURE_REQUEUE_INTERVAL);
XLog.getLog(getClass()).warn("ActionCheckCommand lock was not acquired - interrupted exception failed {0}",
id);
}
XLog.getLog(getClass()).debug("ENDED ActionCheckCommand for wf actionId=" + id + ", jobId=" + jobId);
return null;
}
}