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.ErrorCode; 021 import org.apache.oozie.WorkflowActionBean; 022 import org.apache.oozie.WorkflowJobBean; 023 import org.apache.oozie.XException; 024 import org.apache.oozie.action.ActionExecutor; 025 import org.apache.oozie.action.ActionExecutorException; 026 import org.apache.oozie.client.WorkflowJob; 027 import org.apache.oozie.client.WorkflowAction.Status; 028 import org.apache.oozie.command.CommandException; 029 import org.apache.oozie.command.PreconditionException; 030 import org.apache.oozie.executor.jpa.JPAExecutorException; 031 import org.apache.oozie.executor.jpa.WorkflowActionGetJPAExecutor; 032 import org.apache.oozie.executor.jpa.WorkflowActionUpdateJPAExecutor; 033 import org.apache.oozie.executor.jpa.WorkflowJobGetJPAExecutor; 034 import org.apache.oozie.executor.jpa.WorkflowJobUpdateJPAExecutor; 035 import org.apache.oozie.service.ActionService; 036 import org.apache.oozie.service.JPAService; 037 import org.apache.oozie.service.Services; 038 import org.apache.oozie.service.UUIDService; 039 import org.apache.oozie.util.InstrumentUtils; 040 import org.apache.oozie.util.Instrumentation; 041 import org.apache.oozie.util.LogUtils; 042 import org.apache.oozie.util.XLog; 043 044 /** 045 * Executes the check command for ActionHandlers. </p> Ensures the action is in 046 * RUNNING state before executing 047 * {@link ActionExecutor#check(org.apache.oozie.action.ActionExecutor.Context, org.apache.oozie.client.WorkflowAction)} 048 */ 049 public class ActionCheckXCommand extends ActionXCommand<Void> { 050 public static final String EXEC_DATA_MISSING = "EXEC_DATA_MISSING"; 051 private String actionId; 052 private String jobId; 053 private int actionCheckDelay; 054 private WorkflowJobBean wfJob = null; 055 private WorkflowActionBean wfAction = null; 056 private JPAService jpaService = null; 057 private ActionExecutor executor = null; 058 059 public ActionCheckXCommand(String actionId) { 060 this(actionId, -1); 061 } 062 063 public ActionCheckXCommand(String actionId, int priority, int checkDelay) { 064 super("action.check", "action.check", priority); 065 this.actionId = actionId; 066 this.actionCheckDelay = checkDelay; 067 this.jobId = Services.get().get(UUIDService.class).getId(actionId); 068 } 069 070 public ActionCheckXCommand(String actionId, int checkDelay) { 071 this(actionId, 0, checkDelay); 072 } 073 074 @Override 075 protected void eagerLoadState() throws CommandException { 076 try { 077 jpaService = Services.get().get(JPAService.class); 078 if (jpaService != null) { 079 this.wfJob = jpaService.execute(new WorkflowJobGetJPAExecutor(jobId)); 080 this.wfAction = jpaService.execute(new WorkflowActionGetJPAExecutor(actionId)); 081 LogUtils.setLogInfo(wfJob, logInfo); 082 LogUtils.setLogInfo(wfAction, logInfo); 083 } 084 else { 085 throw new CommandException(ErrorCode.E0610); 086 } 087 } 088 catch (XException ex) { 089 throw new CommandException(ex); 090 } 091 } 092 093 @Override 094 protected void eagerVerifyPrecondition() throws CommandException, PreconditionException { 095 if (wfJob == null) { 096 throw new PreconditionException(ErrorCode.E0604, jobId); 097 } 098 if (wfAction == null) { 099 throw new PreconditionException(ErrorCode.E0605, actionId); 100 } 101 // if the action has been updated, quit this command 102 if (actionCheckDelay > 0) { 103 Timestamp actionCheckTs = new Timestamp(System.currentTimeMillis() - actionCheckDelay * 1000); 104 Timestamp actionLmt = wfAction.getLastCheckTimestamp(); 105 if (actionLmt.after(actionCheckTs)) { 106 throw new PreconditionException(ErrorCode.E0817, actionId); 107 } 108 } 109 110 executor = Services.get().get(ActionService.class).getExecutor(wfAction.getType()); 111 if (executor == null) { 112 throw new CommandException(ErrorCode.E0802, wfAction.getType()); 113 } 114 } 115 116 @Override 117 protected boolean isLockRequired() { 118 return true; 119 } 120 121 @Override 122 protected String getEntityKey() { 123 return this.jobId; 124 } 125 126 @Override 127 protected void loadState() throws CommandException { 128 } 129 130 @Override 131 protected void verifyPrecondition() throws CommandException, PreconditionException { 132 if (!wfAction.isPending() || wfAction.getStatus() != WorkflowActionBean.Status.RUNNING) { 133 throw new PreconditionException(ErrorCode.E0815, wfAction.getPending(), wfAction.getStatusStr()); 134 } 135 if (wfJob.getStatus() != WorkflowJob.Status.RUNNING) { 136 wfAction.setLastCheckTime(new Date()); 137 try { 138 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 139 } 140 catch (JPAExecutorException e) { 141 throw new CommandException(e); 142 } 143 throw new PreconditionException(ErrorCode.E0818, wfAction.getId(), wfJob.getId(), wfJob.getStatus()); 144 } 145 } 146 147 @Override 148 protected Void execute() throws CommandException { 149 LOG.debug("STARTED ActionCheckXCommand for wf actionId=" + actionId + " priority =" + getPriority()); 150 151 ActionExecutorContext context = null; 152 try { 153 boolean isRetry = false; 154 context = new ActionXCommand.ActionExecutorContext(wfJob, wfAction, isRetry); 155 incrActionCounter(wfAction.getType(), 1); 156 157 Instrumentation.Cron cron = new Instrumentation.Cron(); 158 cron.start(); 159 executor.check(context, wfAction); 160 cron.stop(); 161 addActionCron(wfAction.getType(), cron); 162 163 if (wfAction.isExecutionComplete()) { 164 if (!context.isExecuted()) { 165 LOG.warn(XLog.OPS, "Action Completed, ActionExecutor [{0}] must call setExecutionData()", executor 166 .getType()); 167 wfAction.setErrorInfo(EXEC_DATA_MISSING, 168 "Execution Complete, but Execution Data Missing from Action"); 169 failJob(context); 170 wfAction.setLastCheckTime(new Date()); 171 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 172 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 173 return null; 174 } 175 wfAction.setPending(); 176 queue(new ActionEndXCommand(wfAction.getId(), wfAction.getType())); 177 } 178 wfAction.setLastCheckTime(new Date()); 179 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 180 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 181 } 182 catch (ActionExecutorException ex) { 183 LOG.warn("Exception while executing check(). Error Code [{0}], Message[{1}]", ex.getErrorCode(), ex 184 .getMessage(), ex); 185 186 switch (ex.getErrorType()) { 187 case FAILED: 188 failAction(wfJob, wfAction); 189 break; 190 } 191 wfAction.setLastCheckTime(new Date()); 192 try { 193 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 194 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 195 } 196 catch (JPAExecutorException e) { 197 throw new CommandException(e); 198 } 199 return null; 200 } 201 catch (JPAExecutorException e) { 202 throw new CommandException(e); 203 } 204 205 LOG.debug("ENDED ActionCheckXCommand for wf actionId=" + actionId + ", jobId=" + jobId); 206 return null; 207 } 208 209 private void failAction(WorkflowJobBean workflow, WorkflowActionBean action) throws CommandException { 210 LOG.warn("Failing Job [{0}] due to failed action [{1}]", workflow.getId(), action.getId()); 211 action.resetPending(); 212 action.setStatus(Status.FAILED); 213 workflow.setStatus(WorkflowJob.Status.FAILED); 214 InstrumentUtils.incrJobCounter(INSTR_FAILED_JOBS_COUNTER, 1, getInstrumentation()); 215 } 216 }