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.oozie.client.WorkflowJob;
018    import org.apache.oozie.client.SLAEvent.SlaAppType;
019    import org.apache.oozie.client.SLAEvent.Status;
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.command.CommandException;
025    import org.apache.oozie.command.PreconditionException;
026    import org.apache.oozie.command.coord.CoordActionUpdateXCommand;
027    import org.apache.oozie.executor.jpa.JPAExecutorException;
028    import org.apache.oozie.executor.jpa.WorkflowActionUpdateJPAExecutor;
029    import org.apache.oozie.executor.jpa.WorkflowActionsGetForJobJPAExecutor;
030    import org.apache.oozie.executor.jpa.WorkflowJobGetJPAExecutor;
031    import org.apache.oozie.executor.jpa.WorkflowJobUpdateJPAExecutor;
032    import org.apache.oozie.service.JPAService;
033    import org.apache.oozie.service.Services;
034    import org.apache.oozie.workflow.WorkflowException;
035    import org.apache.oozie.workflow.WorkflowInstance;
036    import org.apache.oozie.workflow.lite.LiteWorkflowInstance;
037    import org.apache.oozie.util.InstrumentUtils;
038    import org.apache.oozie.util.LogUtils;
039    import org.apache.oozie.util.ParamChecker;
040    import org.apache.oozie.util.db.SLADbXOperations;
041    
042    import java.util.Date;
043    import java.util.List;
044    
045    /**
046     * Kill workflow job and its workflow instance and queue a {@link WorkflowActionKillXCommand} to kill the workflow
047     * actions.
048     */
049    public class KillXCommand extends WorkflowXCommand<Void> {
050    
051        private String wfId;
052        private WorkflowJobBean wfJob;
053        private List<WorkflowActionBean> actionList;
054        private JPAService jpaService = null;
055    
056        public KillXCommand(String wfId) {
057            super("kill", "kill", 1);
058            this.wfId = ParamChecker.notEmpty(wfId, "wfId");
059        }
060    
061        @Override
062        protected boolean isLockRequired() {
063            return true;
064        }
065    
066        @Override
067        protected String getEntityKey() {
068            return this.wfId;
069        }
070    
071        @Override
072        protected void loadState() throws CommandException {
073            try {
074                jpaService = Services.get().get(JPAService.class);
075                if (jpaService != null) {
076                    this.wfJob = jpaService.execute(new WorkflowJobGetJPAExecutor(wfId));
077                    this.actionList = jpaService.execute(new WorkflowActionsGetForJobJPAExecutor(wfId));
078                    LogUtils.setLogInfo(wfJob, logInfo);
079                }
080                else {
081                    throw new CommandException(ErrorCode.E0610);
082                }
083            }
084            catch (XException ex) {
085                throw new CommandException(ex);
086            }
087        }
088    
089        @Override
090        protected void verifyPrecondition() throws CommandException, PreconditionException {
091            if (wfJob.getStatus() != WorkflowJob.Status.PREP && wfJob.getStatus() != WorkflowJob.Status.RUNNING
092                    && wfJob.getStatus() != WorkflowJob.Status.SUSPENDED && wfJob.getStatus() != WorkflowJob.Status.FAILED) {
093                throw new PreconditionException(ErrorCode.E0725, wfJob.getId());
094            }
095        }
096    
097        @Override
098        protected Void execute() throws CommandException {
099            LOG.info("STARTED WorkflowKillXCommand for jobId=" + wfId);
100    
101            wfJob.setEndTime(new Date());
102    
103            if (wfJob.getStatus() != WorkflowJob.Status.FAILED) {
104                InstrumentUtils.incrJobCounter(getName(), 1, getInstrumentation());
105                wfJob.setStatus(WorkflowJob.Status.KILLED);
106                SLADbXOperations.writeStausEvent(wfJob.getSlaXml(), wfJob.getId(), Status.KILLED, SlaAppType.WORKFLOW_JOB);
107                try {
108                    wfJob.getWorkflowInstance().kill();
109                }
110                catch (WorkflowException e) {
111                    throw new CommandException(ErrorCode.E0725, e.getMessage(), e);
112                }
113                WorkflowInstance wfInstance = wfJob.getWorkflowInstance();
114                ((LiteWorkflowInstance) wfInstance).setStatus(WorkflowInstance.Status.KILLED);
115                wfJob.setWorkflowInstance(wfInstance);
116            }
117            try {
118                for (WorkflowActionBean action : actionList) {
119                    if (action.getStatus() == WorkflowActionBean.Status.RUNNING
120                            || action.getStatus() == WorkflowActionBean.Status.DONE) {
121                        action.setPending();
122                        action.setStatus(WorkflowActionBean.Status.KILLED);
123    
124                        jpaService.execute(new WorkflowActionUpdateJPAExecutor(action));
125    
126                        queue(new ActionKillXCommand(action.getId(), action.getType()));
127                    }
128                    if (action.getStatus() == WorkflowActionBean.Status.PREP
129                            || action.getStatus() == WorkflowActionBean.Status.START_RETRY
130                            || action.getStatus() == WorkflowActionBean.Status.START_MANUAL
131                            || action.getStatus() == WorkflowActionBean.Status.END_RETRY
132                            || action.getStatus() == WorkflowActionBean.Status.END_MANUAL) {
133    
134                        action.setStatus(WorkflowActionBean.Status.KILLED);
135                        action.resetPending();
136                        SLADbXOperations.writeStausEvent(action.getSlaXml(), action.getId(), Status.KILLED,
137                                SlaAppType.WORKFLOW_ACTION);
138                        jpaService.execute(new WorkflowActionUpdateJPAExecutor(action));
139                    }
140                }
141                jpaService.execute(new WorkflowJobUpdateJPAExecutor(wfJob));
142                queue(new NotificationXCommand(wfJob));
143            }
144            catch (JPAExecutorException je) {
145                throw new CommandException(je);
146            }
147            finally {
148                if(wfJob.getStatus() == WorkflowJob.Status.KILLED) {
149                     new WfEndXCommand(wfJob).call(); //To delete the WF temp dir
150                }
151                // update coordinator action
152                new CoordActionUpdateXCommand(wfJob).call();
153            }
154    
155            LOG.info("ENDED WorkflowKillXCommand for jobId=" + wfId);
156            return null;
157        }
158    
159    }