/**
* 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.util.Date;
import javax.servlet.jsp.el.ELException;
import org.apache.hadoop.conf.Configuration;
import org.apache.oozie.ErrorCode;
import org.apache.oozie.FaultInjection;
import org.apache.oozie.WorkflowActionBean;
import org.apache.oozie.WorkflowJobBean;
import org.apache.oozie.XException;
import org.apache.oozie.action.ActionExecutor;
import org.apache.oozie.action.ActionExecutorException;
import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.client.WorkflowJob;
import org.apache.oozie.client.SLAEvent.SlaAppType;
import org.apache.oozie.client.SLAEvent.Status;
import org.apache.oozie.command.CommandException;
import org.apache.oozie.command.coord.CoordActionUpdateCommand;
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.ELEvaluationException;
import org.apache.oozie.util.Instrumentation;
import org.apache.oozie.util.XLog;
import org.apache.oozie.util.XmlUtils;
import org.apache.oozie.util.db.SLADbOperations;
public class ActionStartCommand extends ActionCommand<Void> {
public static final String EL_ERROR = "EL_ERROR";
public static final String EL_EVAL_ERROR = "EL_EVAL_ERROR";
public static final String COULD_NOT_START = "COULD_NOT_START";
public static final String START_DATA_MISSING = "START_DATA_MISSING";
public static final String EXEC_DATA_MISSING = "EXEC_DATA_MISSING";
private String id;
private String jobId;
public ActionStartCommand(String id, String type) {
super("action.start", type, 0);
this.id = id;
}
@Override
protected Void call(WorkflowStore store) throws StoreException, CommandException {
WorkflowJobBean workflow = store.getWorkflow(jobId, false);
setLogInfo(workflow);
WorkflowActionBean action = store.getAction(id, false);
XLog.getLog(getClass()).warn(XLog.STD,
"[***" + action.getId() + "***]" + "In call()....status=" + action.getStatusStr());
setLogInfo(action);
if (action.isPending()
&& (action.getStatus() == WorkflowActionBean.Status.PREP
|| action.getStatus() == WorkflowActionBean.Status.START_RETRY || action.getStatus() == WorkflowActionBean.Status.START_MANUAL)) {
if (workflow.getStatus() == WorkflowJob.Status.RUNNING) {
ActionExecutor executor = Services.get().get(ActionService.class).getExecutor(action.getType());
Configuration conf = workflow.getWorkflowInstance().getConf();
int maxRetries = conf.getInt(OozieClient.ACTION_MAX_RETRIES, executor.getMaxRetries());
long retryInterval = conf.getLong(OozieClient.ACTION_RETRY_INTERVAL, executor.getRetryInterval());
executor.setMaxRetries(maxRetries);
executor.setRetryInterval(retryInterval);
if (executor != null) {
ActionExecutorContext context = null;
try {
boolean isRetry = false;
if (action.getStatus() == WorkflowActionBean.Status.START_RETRY
|| action.getStatus() == WorkflowActionBean.Status.START_MANUAL) {
isRetry = true;
}
context = new ActionCommand.ActionExecutorContext(workflow, action, isRetry);
try {
String tmpActionConf = XmlUtils.removeComments(action.getConf());
String actionConf = context.getELEvaluator().evaluate(tmpActionConf, String.class);
action.setConf(actionConf);
XLog.getLog(getClass()).debug("Start, name [{0}] type [{1}] configuration{E}{E}{2}{E}",
action.getName(), action.getType(), actionConf);
}
catch (ELEvaluationException ex) {
throw new ActionExecutorException(ActionExecutorException.ErrorType.TRANSIENT,
EL_EVAL_ERROR, ex.getMessage(), ex);
}
catch (ELException ex) {
context.setErrorInfo(EL_ERROR, ex.getMessage());
XLog.getLog(getClass()).warn("ELException in ActionStartCommand ", ex.getMessage(), ex);
handleError(context, store, workflow, action);
return null;
}
catch (org.jdom.JDOMException je) {
context.setErrorInfo("ParsingError", je.getMessage());
XLog.getLog(getClass()).warn("JDOMException in ActionStartCommand ", je.getMessage(), je);
handleError(context, store, workflow, action);
return null;
}
catch (Exception ex) {
context.setErrorInfo(EL_ERROR, ex.getMessage());
XLog.getLog(getClass()).warn("Exception in ActionStartCommand ", ex.getMessage(), ex);
handleError(context, store, workflow, action);
return null;
}
action.setErrorInfo(null, null);
incrActionCounter(action.getType(), 1);
Instrumentation.Cron cron = new Instrumentation.Cron();
cron.start();
executor.start(context, action);
cron.stop();
FaultInjection.activate("org.apache.oozie.command.SkipCommitFaultInjection");
addActionCron(action.getType(), cron);
action.setRetries(0);
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);
store.updateAction(action);
store.updateWorkflow(workflow);
return null;
}
action.setPending();
queueCallable(new ActionEndCommand(action.getId(), action.getType()));
}
else {
if (!context.isStarted()) {
XLog.getLog(getClass()).warn(XLog.OPS,
"Action Started, ActionExecutor [{0}] must call setStartData()",
executor.getType());
action.setErrorInfo(START_DATA_MISSING,
"Execution Started, but Start Data Missing from Action");
failJob(context);
store.updateAction(action);
store.updateWorkflow(workflow);
return null;
}
queueCallable(new NotificationCommand(workflow, action));
}
XLog.getLog(getClass()).warn(XLog.STD,
"[***" + action.getId() + "***]" + "Action status=" + action.getStatusStr());
store.updateAction(action);
store.updateWorkflow(workflow);
// Add SLA status event (STARTED) for WF_ACTION
// SLADbOperations.writeSlaStatusEvent(eSla,
// action.getId(), Status.STARTED, store);
SLADbOperations.writeStausEvent(action.getSlaXml(), action.getId(), store, Status.STARTED,
SlaAppType.WORKFLOW_ACTION);
XLog.getLog(getClass()).warn(XLog.STD,
"[***" + action.getId() + "***]" + "Action updated in DB!");
}
catch (ActionExecutorException ex) {
XLog.getLog(getClass()).warn(
"Error starting action [{0}]. ErrorType [{1}], ErrorCode [{2}], Message [{3}]",
action.getName(), ex.getErrorType(), ex.getErrorCode(), ex.getMessage(), ex);
action.setErrorInfo(ex.getErrorCode(), ex.getMessage());
switch (ex.getErrorType()) {
case TRANSIENT:
if (!handleTransient(context, executor, WorkflowAction.Status.START_RETRY)) {
handleNonTransient(store, context, executor, WorkflowAction.Status.START_MANUAL);
action.setPendingAge(new Date());
action.setRetries(0);
action.setStartTime(null);
}
break;
case NON_TRANSIENT:
handleNonTransient(store, context, executor, WorkflowAction.Status.START_MANUAL);
break;
case ERROR:
handleError(context, executor, WorkflowAction.Status.ERROR.toString(), true,
WorkflowAction.Status.DONE);
break;
case FAILED:
try {
failJob(context);
queueCallable(new CoordActionUpdateCommand(workflow));
SLADbOperations.writeStausEvent(action.getSlaXml(), action.getId(), store,
Status.FAILED, SlaAppType.WORKFLOW_ACTION);
SLADbOperations.writeStausEvent(workflow.getSlaXml(), workflow.getId(), store,
Status.FAILED, SlaAppType.WORKFLOW_JOB);
}
catch (XException x) {
XLog.getLog(getClass()).warn("ActionStartCommand - case:FAILED ", x.getMessage());
}
break;
}
store.updateAction(action);
store.updateWorkflow(workflow);
}
}
else {
throw new CommandException(ErrorCode.E0802, action.getType());
}
}
else {
XLog.getLog(getClass()).warn("Job state is not {0}. Skipping Action Execution",
WorkflowJob.Status.RUNNING.toString());
}
}
return null;
}
private void handleError(ActionExecutorContext context, WorkflowStore store, WorkflowJobBean workflow,
WorkflowActionBean action) throws CommandException, StoreException {
failJob(context);
store.updateAction(action);
store.updateWorkflow(workflow);
SLADbOperations.writeStausEvent(action.getSlaXml(), action.getId(), store, Status.FAILED,
SlaAppType.WORKFLOW_ACTION);
SLADbOperations.writeStausEvent(workflow.getSlaXml(), workflow.getId(), store, Status.FAILED,
SlaAppType.WORKFLOW_JOB);
queueCallable(new CoordActionUpdateCommand(workflow));
return;
}
@Override
protected Void execute(WorkflowStore store) throws CommandException, StoreException {
try {
XLog.getLog(getClass()).debug("STARTED ActionStartCommand for wf actionId=" + id);
jobId = Services.get().get(UUIDService.class).getId(id);
if (lock(jobId)) {
call(store);
}
else {
queueCallable(new ActionStartCommand(id, getType()), LOCK_FAILURE_REQUEUE_INTERVAL);
XLog.getLog(getClass()).warn("ActionStartCommand lock was not acquired - failed {0}", id);
}
}
catch (InterruptedException e) {
queueCallable(new ActionStartCommand(id, getType()), LOCK_FAILURE_REQUEUE_INTERVAL);
XLog.getLog(getClass()).warn("ActionStartCommand lock was not acquired - interrupted exception failed {0}",
id);
}
XLog.getLog(getClass()).debug("ENDED ActionStartCommand for wf actionId=" + id + ", jobId=" + jobId);
return null;
}
}