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.util.Date; 018 019 import org.apache.hadoop.conf.Configuration; 020 import org.apache.oozie.DagELFunctions; 021 import org.apache.oozie.ErrorCode; 022 import org.apache.oozie.WorkflowActionBean; 023 import org.apache.oozie.WorkflowJobBean; 024 import org.apache.oozie.XException; 025 import org.apache.oozie.action.ActionExecutor; 026 import org.apache.oozie.action.ActionExecutorException; 027 import org.apache.oozie.client.OozieClient; 028 import org.apache.oozie.client.WorkflowAction; 029 import org.apache.oozie.client.WorkflowJob; 030 import org.apache.oozie.client.SLAEvent.SlaAppType; 031 import org.apache.oozie.client.SLAEvent.Status; 032 import org.apache.oozie.command.CommandException; 033 import org.apache.oozie.command.PreconditionException; 034 import org.apache.oozie.executor.jpa.JPAExecutorException; 035 import org.apache.oozie.executor.jpa.WorkflowActionGetJPAExecutor; 036 import org.apache.oozie.executor.jpa.WorkflowActionUpdateJPAExecutor; 037 import org.apache.oozie.executor.jpa.WorkflowJobGetJPAExecutor; 038 import org.apache.oozie.executor.jpa.WorkflowJobUpdateJPAExecutor; 039 import org.apache.oozie.service.ActionService; 040 import org.apache.oozie.service.JPAService; 041 import org.apache.oozie.service.Services; 042 import org.apache.oozie.service.UUIDService; 043 import org.apache.oozie.util.Instrumentation; 044 import org.apache.oozie.util.LogUtils; 045 import org.apache.oozie.util.XLog; 046 import org.apache.oozie.util.db.SLADbXOperations; 047 import org.apache.oozie.workflow.WorkflowInstance; 048 049 public class ActionEndXCommand extends ActionXCommand<Void> { 050 public static final String COULD_NOT_END = "COULD_NOT_END"; 051 public static final String END_DATA_MISSING = "END_DATA_MISSING"; 052 053 private String jobId = null; 054 private String actionId = null; 055 private WorkflowJobBean wfJob = null; 056 private WorkflowActionBean wfAction = null; 057 private JPAService jpaService = null; 058 private ActionExecutor executor = null; 059 060 public ActionEndXCommand(String actionId, String type) { 061 super("action.end", type, 0); 062 this.actionId = actionId; 063 this.jobId = Services.get().get(UUIDService.class).getId(actionId); 064 } 065 066 @Override 067 protected boolean isLockRequired() { 068 return true; 069 } 070 071 @Override 072 protected String getEntityKey() { 073 return this.jobId; 074 } 075 076 @Override 077 protected void loadState() throws CommandException { 078 try { 079 jpaService = Services.get().get(JPAService.class); 080 if (jpaService != null) { 081 this.wfJob = jpaService.execute(new WorkflowJobGetJPAExecutor(jobId)); 082 this.wfAction = jpaService.execute(new WorkflowActionGetJPAExecutor(actionId)); 083 LogUtils.setLogInfo(wfJob, logInfo); 084 LogUtils.setLogInfo(wfAction, logInfo); 085 } 086 else { 087 throw new CommandException(ErrorCode.E0610); 088 } 089 } 090 catch (XException ex) { 091 throw new CommandException(ex); 092 } 093 } 094 095 @Override 096 protected void verifyPrecondition() throws CommandException, PreconditionException { 097 if (wfJob == null) { 098 throw new PreconditionException(ErrorCode.E0604, jobId); 099 } 100 if (wfAction == null) { 101 throw new PreconditionException(ErrorCode.E0605, actionId); 102 } 103 if (wfAction.isPending() 104 && (wfAction.getStatus() == WorkflowActionBean.Status.DONE 105 || wfAction.getStatus() == WorkflowActionBean.Status.END_RETRY || wfAction.getStatus() == WorkflowActionBean.Status.END_MANUAL)) { 106 107 if (wfJob.getStatus() != WorkflowJob.Status.RUNNING) { 108 throw new PreconditionException(ErrorCode.E0811, WorkflowJob.Status.RUNNING.toString()); 109 } 110 } 111 else { 112 throw new PreconditionException(ErrorCode.E0812, wfAction.getPending(), wfAction.getStatusStr()); 113 } 114 115 executor = Services.get().get(ActionService.class).getExecutor(wfAction.getType()); 116 if (executor == null) { 117 throw new CommandException(ErrorCode.E0802, wfAction.getType()); 118 } 119 } 120 121 @Override 122 protected Void execute() throws CommandException { 123 LOG.debug("STARTED ActionEndXCommand for action " + actionId); 124 125 Configuration conf = wfJob.getWorkflowInstance().getConf(); 126 int maxRetries = conf.getInt(OozieClient.ACTION_MAX_RETRIES, executor.getMaxRetries()); 127 long retryInterval = conf.getLong(OozieClient.ACTION_RETRY_INTERVAL, executor.getRetryInterval()); 128 executor.setMaxRetries(maxRetries); 129 executor.setRetryInterval(retryInterval); 130 131 boolean isRetry = false; 132 if (wfAction.getStatus() == WorkflowActionBean.Status.END_RETRY 133 || wfAction.getStatus() == WorkflowActionBean.Status.END_MANUAL) { 134 isRetry = true; 135 } 136 ActionExecutorContext context = new ActionXCommand.ActionExecutorContext(wfJob, wfAction, isRetry); 137 try { 138 139 LOG.debug( 140 "End, name [{0}] type [{1}] status[{2}] external status [{3}] signal value [{4}]", 141 wfAction.getName(), wfAction.getType(), wfAction.getStatus(), wfAction.getExternalStatus(), 142 wfAction.getSignalValue()); 143 WorkflowInstance wfInstance = wfJob.getWorkflowInstance(); 144 DagELFunctions.setActionInfo(wfInstance, wfAction); 145 wfJob.setWorkflowInstance(wfInstance); 146 incrActionCounter(wfAction.getType(), 1); 147 148 Instrumentation.Cron cron = new Instrumentation.Cron(); 149 cron.start(); 150 executor.end(context, wfAction); 151 cron.stop(); 152 addActionCron(wfAction.getType(), cron); 153 154 if (!context.isEnded()) { 155 LOG.warn(XLog.OPS, "Action Ended, ActionExecutor [{0}] must call setEndData()", 156 executor.getType()); 157 wfAction.setErrorInfo(END_DATA_MISSING, "Execution Ended, but End Data Missing from Action"); 158 failJob(context); 159 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 160 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 161 return null; 162 } 163 wfAction.setRetries(0); 164 wfAction.setEndTime(new Date()); 165 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 166 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 167 168 Status slaStatus = null; 169 switch (wfAction.getStatus()) { 170 case OK: 171 slaStatus = Status.SUCCEEDED; 172 break; 173 case KILLED: 174 slaStatus = Status.KILLED; 175 break; 176 case FAILED: 177 slaStatus = Status.FAILED; 178 break; 179 case ERROR: 180 LOG.info("ERROR is considered as FAILED for SLA"); 181 slaStatus = Status.KILLED; 182 break; 183 default: 184 slaStatus = Status.FAILED; 185 break; 186 } 187 SLADbXOperations.writeStausEvent(wfAction.getSlaXml(), wfAction.getId(), slaStatus, SlaAppType.WORKFLOW_ACTION); 188 queue(new NotificationXCommand(wfJob, wfAction)); 189 LOG.debug( 190 "Queuing commands for action=" + actionId + ", status=" + wfAction.getStatus() 191 + ", Set pending=" + wfAction.getPending()); 192 queue(new SignalXCommand(jobId, actionId)); 193 } 194 catch (ActionExecutorException ex) { 195 LOG.warn( 196 "Error ending action [{0}]. ErrorType [{1}], ErrorCode [{2}], Message [{3}]", 197 wfAction.getName(), ex.getErrorType(), ex.getErrorCode(), ex.getMessage()); 198 wfAction.setErrorInfo(ex.getErrorCode(), ex.getMessage()); 199 wfAction.setEndTime(null); 200 switch (ex.getErrorType()) { 201 case TRANSIENT: 202 if (!handleTransient(context, executor, WorkflowAction.Status.END_RETRY)) { 203 handleNonTransient(context, executor, WorkflowAction.Status.END_MANUAL); 204 wfAction.setPendingAge(new Date()); 205 wfAction.setRetries(0); 206 } 207 wfAction.setEndTime(null); 208 break; 209 case NON_TRANSIENT: 210 handleNonTransient(context, executor, WorkflowAction.Status.END_MANUAL); 211 wfAction.setEndTime(null); 212 break; 213 case ERROR: 214 handleError(context, executor, COULD_NOT_END, false, WorkflowAction.Status.ERROR); 215 queue(new SignalXCommand(jobId, actionId)); 216 break; 217 case FAILED: 218 failJob(context); 219 break; 220 } 221 try { 222 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 223 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 224 } 225 catch (JPAExecutorException je) { 226 throw new CommandException(je); 227 } 228 } 229 catch (JPAExecutorException je) { 230 throw new CommandException(je); 231 } 232 233 234 LOG.debug("ENDED ActionEndXCommand for action " + actionId); 235 return null; 236 } 237 238 }