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 org.apache.hadoop.conf.Configuration; 018 import org.apache.oozie.client.WorkflowJob; 019 import org.apache.oozie.client.SLAEvent.SlaAppType; 020 import org.apache.oozie.client.SLAEvent.Status; 021 import org.apache.oozie.WorkflowActionBean; 022 import org.apache.oozie.WorkflowJobBean; 023 import org.apache.oozie.ErrorCode; 024 import org.apache.oozie.XException; 025 import org.apache.oozie.command.CommandException; 026 import org.apache.oozie.command.PreconditionException; 027 import org.apache.oozie.command.coord.CoordActionUpdateXCommand; 028 import org.apache.oozie.executor.jpa.JPAExecutorException; 029 import org.apache.oozie.executor.jpa.WorkflowActionGetJPAExecutor; 030 import org.apache.oozie.executor.jpa.WorkflowActionInsertJPAExecutor; 031 import org.apache.oozie.executor.jpa.WorkflowActionUpdateJPAExecutor; 032 import org.apache.oozie.executor.jpa.WorkflowJobGetJPAExecutor; 033 import org.apache.oozie.executor.jpa.WorkflowJobUpdateJPAExecutor; 034 import org.apache.oozie.service.ELService; 035 import org.apache.oozie.service.JPAService; 036 import org.apache.oozie.service.SchemaService; 037 import org.apache.oozie.service.Services; 038 import org.apache.oozie.service.UUIDService; 039 import org.apache.oozie.service.WorkflowStoreService; 040 import org.apache.oozie.workflow.WorkflowException; 041 import org.apache.oozie.workflow.WorkflowInstance; 042 import org.apache.oozie.util.ELEvaluator; 043 import org.apache.oozie.util.InstrumentUtils; 044 import org.apache.oozie.util.LogUtils; 045 import org.apache.oozie.util.XConfiguration; 046 import org.apache.oozie.util.ParamChecker; 047 import org.apache.oozie.util.XmlUtils; 048 import org.apache.oozie.util.db.SLADbXOperations; 049 import org.jdom.Element; 050 import org.jdom.Namespace; 051 052 import java.io.StringReader; 053 import java.util.Date; 054 import java.util.List; 055 import java.util.Map; 056 057 public class SignalXCommand extends WorkflowXCommand<Void> { 058 059 protected static final String INSTR_SUCCEEDED_JOBS_COUNTER_NAME = "succeeded"; 060 061 private JPAService jpaService = null; 062 private String jobId; 063 private String actionId; 064 private WorkflowJobBean wfJob; 065 private WorkflowActionBean wfAction; 066 067 public SignalXCommand(String name, int priority, String jobId) { 068 super(name, name, priority); 069 this.jobId = ParamChecker.notEmpty(jobId, "jobId"); 070 } 071 072 public SignalXCommand(String jobId, String actionId) { 073 this("signal", 1, jobId); 074 this.actionId = ParamChecker.notEmpty(actionId, "actionId"); 075 } 076 077 @Override 078 protected boolean isLockRequired() { 079 return true; 080 } 081 082 @Override 083 protected String getEntityKey() { 084 return this.jobId; 085 } 086 087 @Override 088 protected void loadState() throws CommandException { 089 try { 090 jpaService = Services.get().get(JPAService.class); 091 if (jpaService != null) { 092 this.wfJob = jpaService.execute(new WorkflowJobGetJPAExecutor(jobId)); 093 LogUtils.setLogInfo(wfJob, logInfo); 094 if (actionId != null) { 095 this.wfAction = jpaService.execute(new WorkflowActionGetJPAExecutor(actionId)); 096 LogUtils.setLogInfo(wfAction, logInfo); 097 } 098 } 099 else { 100 throw new CommandException(ErrorCode.E0610); 101 } 102 } 103 catch (XException ex) { 104 throw new CommandException(ex); 105 } 106 } 107 108 @Override 109 protected void verifyPrecondition() throws CommandException, PreconditionException { 110 if ((wfAction == null) || (wfAction.isComplete() && wfAction.isPending())) { 111 if (wfJob.getStatus() != WorkflowJob.Status.RUNNING && wfJob.getStatus() != WorkflowJob.Status.PREP) { 112 throw new PreconditionException(ErrorCode.E0813, wfJob.getStatusStr()); 113 } 114 } 115 else { 116 throw new PreconditionException(ErrorCode.E0814, actionId, wfAction.getStatusStr(), wfAction.isPending()); 117 } 118 } 119 120 @Override 121 protected Void execute() throws CommandException { 122 LOG.debug("STARTED SignalCommand for jobid=" + jobId + ", actionId=" + actionId); 123 WorkflowInstance workflowInstance = wfJob.getWorkflowInstance(); 124 workflowInstance.setTransientVar(WorkflowStoreService.WORKFLOW_BEAN, wfJob); 125 boolean completed = false; 126 boolean skipAction = false; 127 if (wfAction == null) { 128 if (wfJob.getStatus() == WorkflowJob.Status.PREP) { 129 try { 130 completed = workflowInstance.start(); 131 } 132 catch (WorkflowException e) { 133 throw new CommandException(e); 134 } 135 wfJob.setStatus(WorkflowJob.Status.RUNNING); 136 wfJob.setStartTime(new Date()); 137 wfJob.setWorkflowInstance(workflowInstance); 138 // 1. Add SLA status event for WF-JOB with status STARTED 139 // 2. Add SLA registration events for all WF_ACTIONS 140 SLADbXOperations.writeStausEvent(wfJob.getSlaXml(), jobId, Status.STARTED, SlaAppType.WORKFLOW_JOB); 141 writeSLARegistrationForAllActions(workflowInstance.getApp().getDefinition(), wfJob.getUser(), wfJob 142 .getGroup(), wfJob.getConf()); 143 queue(new NotificationXCommand(wfJob)); 144 } 145 else { 146 throw new CommandException(ErrorCode.E0801, wfJob.getId()); 147 } 148 } 149 else { 150 String skipVar = workflowInstance.getVar(wfAction.getName() + WorkflowInstance.NODE_VAR_SEPARATOR 151 + ReRunCommand.TO_SKIP); 152 if (skipVar != null) { 153 skipAction = skipVar.equals("true"); 154 } 155 try { 156 completed = workflowInstance.signal(wfAction.getExecutionPath(), wfAction.getSignalValue()); 157 } 158 catch (WorkflowException e) { 159 throw new CommandException(e); 160 } 161 wfJob.setWorkflowInstance(workflowInstance); 162 wfAction.resetPending(); 163 if (!skipAction) { 164 wfAction.setTransition(workflowInstance.getTransition(wfAction.getName())); 165 } 166 try { 167 jpaService.execute(new WorkflowActionUpdateJPAExecutor(wfAction)); 168 } 169 catch (JPAExecutorException je) { 170 throw new CommandException(je); 171 } 172 } 173 174 if (completed) { 175 try { 176 for (String actionToKillId : WorkflowStoreService.getActionsToKill(workflowInstance)) { 177 WorkflowActionBean actionToKill; 178 179 actionToKill = jpaService.execute(new WorkflowActionGetJPAExecutor(actionToKillId)); 180 181 actionToKill.setPending(); 182 actionToKill.setStatus(WorkflowActionBean.Status.KILLED); 183 jpaService.execute(new WorkflowActionUpdateJPAExecutor(actionToKill)); 184 queue(new ActionKillXCommand(actionToKill.getId(), actionToKill.getType())); 185 } 186 187 for (String actionToFailId : WorkflowStoreService.getActionsToFail(workflowInstance)) { 188 WorkflowActionBean actionToFail = jpaService.execute(new WorkflowActionGetJPAExecutor( 189 actionToFailId)); 190 actionToFail.resetPending(); 191 actionToFail.setStatus(WorkflowActionBean.Status.FAILED); 192 SLADbXOperations.writeStausEvent(wfAction.getSlaXml(), wfAction.getId(), Status.FAILED, 193 SlaAppType.WORKFLOW_ACTION); 194 jpaService.execute(new WorkflowActionUpdateJPAExecutor(actionToFail)); 195 } 196 } 197 catch (JPAExecutorException je) { 198 throw new CommandException(je); 199 } 200 201 wfJob.setStatus(WorkflowJob.Status.valueOf(workflowInstance.getStatus().toString())); 202 wfJob.setEndTime(new Date()); 203 wfJob.setWorkflowInstance(workflowInstance); 204 Status slaStatus = Status.SUCCEEDED; 205 switch (wfJob.getStatus()) { 206 case SUCCEEDED: 207 slaStatus = Status.SUCCEEDED; 208 break; 209 case KILLED: 210 slaStatus = Status.KILLED; 211 break; 212 case FAILED: 213 slaStatus = Status.FAILED; 214 break; 215 default: // TODO SUSPENDED 216 break; 217 } 218 SLADbXOperations.writeStausEvent(wfJob.getSlaXml(), jobId, slaStatus, SlaAppType.WORKFLOW_JOB); 219 queue(new NotificationXCommand(wfJob)); 220 if (wfJob.getStatus() == WorkflowJob.Status.SUCCEEDED) { 221 InstrumentUtils.incrJobCounter(INSTR_SUCCEEDED_JOBS_COUNTER_NAME, 1, getInstrumentation()); 222 } 223 } 224 else { 225 for (WorkflowActionBean newAction : WorkflowStoreService.getStartedActions(workflowInstance)) { 226 String skipVar = workflowInstance.getVar(newAction.getName() + WorkflowInstance.NODE_VAR_SEPARATOR 227 + ReRunCommand.TO_SKIP); 228 boolean skipNewAction = false; 229 if (skipVar != null) { 230 skipNewAction = skipVar.equals("true"); 231 } 232 try { 233 if (skipNewAction) { 234 WorkflowActionBean oldAction; 235 236 oldAction = jpaService.execute(new WorkflowActionGetJPAExecutor(newAction.getId())); 237 238 oldAction.setPending(); 239 jpaService.execute(new WorkflowActionUpdateJPAExecutor(oldAction)); 240 241 queue(new SignalXCommand(jobId, oldAction.getId())); 242 } 243 else { 244 newAction.setPending(); 245 String actionSlaXml = getActionSLAXml(newAction.getName(), workflowInstance.getApp() 246 .getDefinition(), wfJob.getConf()); 247 newAction.setSlaXml(actionSlaXml); 248 jpaService.execute(new WorkflowActionInsertJPAExecutor(newAction)); 249 LOG.debug("SignalXCommand: Name: "+ newAction.getName() + ", Id: " +newAction.getId() + ", Authcode:" + newAction.getCred()); 250 queue(new ActionStartXCommand(newAction.getId(), newAction.getType())); 251 } 252 } 253 catch (JPAExecutorException je) { 254 throw new CommandException(je); 255 } 256 } 257 } 258 259 try { 260 jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob)); 261 } 262 catch (JPAExecutorException je) { 263 throw new CommandException(je); 264 } 265 LOG.debug( 266 "Updated the workflow status to " + wfJob.getId() + " status =" + wfJob.getStatusStr()); 267 if (wfJob.getStatus() != WorkflowJob.Status.RUNNING && wfJob.getStatus() != WorkflowJob.Status.SUSPENDED) { 268 // update coordinator action 269 new CoordActionUpdateXCommand(wfJob).call(); 270 new WfEndXCommand(wfJob).call(); //To delete the WF temp dir 271 } 272 LOG.debug("ENDED SignalCommand for jobid=" + jobId + ", actionId=" + actionId); 273 return null; 274 } 275 276 public static ELEvaluator createELEvaluatorForGroup(Configuration conf, String group) { 277 ELEvaluator eval = Services.get().get(ELService.class).createEvaluator(group); 278 for (Map.Entry<String, String> entry : conf) { 279 eval.setVariable(entry.getKey(), entry.getValue()); 280 } 281 return eval; 282 } 283 284 @SuppressWarnings("unchecked") 285 private String getActionSLAXml(String actionName, String wfXml, String wfConf) throws CommandException { 286 String slaXml = null; 287 try { 288 Element eWfJob = XmlUtils.parseXml(wfXml); 289 for (Element action : (List<Element>) eWfJob.getChildren("action", eWfJob.getNamespace())) { 290 if (action.getAttributeValue("name").equals(actionName) == false) { 291 continue; 292 } 293 Element eSla = action.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI)); 294 if (eSla != null) { 295 slaXml = XmlUtils.prettyPrint(eSla).toString(); 296 break; 297 } 298 } 299 } 300 catch (Exception e) { 301 throw new CommandException(ErrorCode.E1004, e.getMessage(), e); 302 } 303 return slaXml; 304 } 305 306 private String resolveSla(Element eSla, Configuration conf) throws CommandException { 307 String slaXml = null; 308 try { 309 ELEvaluator evalSla = SubmitCommand.createELEvaluatorForGroup(conf, "wf-sla-submit"); 310 slaXml = SubmitCommand.resolveSla(eSla, evalSla); 311 } 312 catch (Exception e) { 313 throw new CommandException(ErrorCode.E1004, e.getMessage(), e); 314 } 315 return slaXml; 316 } 317 318 @SuppressWarnings("unchecked") 319 private void writeSLARegistrationForAllActions(String wfXml, String user, String group, String strConf) 320 throws CommandException { 321 try { 322 Element eWfJob = XmlUtils.parseXml(wfXml); 323 Configuration conf = new XConfiguration(new StringReader(strConf)); 324 for (Element action : (List<Element>) eWfJob.getChildren("action", eWfJob.getNamespace())) { 325 Element eSla = action.getChild("info", Namespace.getNamespace(SchemaService.SLA_NAME_SPACE_URI)); 326 if (eSla != null) { 327 String slaXml = resolveSla(eSla, conf); 328 eSla = XmlUtils.parseXml(slaXml); 329 String actionId = Services.get().get(UUIDService.class).generateChildId(jobId, 330 action.getAttributeValue("name") + ""); 331 SLADbXOperations.writeSlaRegistrationEvent(eSla, actionId, SlaAppType.WORKFLOW_ACTION, user, group); 332 } 333 } 334 } 335 catch (Exception e) { 336 throw new CommandException(ErrorCode.E1007, "workflow:Actions " + jobId, e); 337 } 338 339 } 340 341 }