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.coord;
016    
017    import org.apache.oozie.client.CoordinatorJob;
018    import org.apache.oozie.client.Job;
019    import org.apache.oozie.CoordinatorActionBean;
020    import org.apache.oozie.CoordinatorJobBean;
021    import org.apache.oozie.ErrorCode;
022    import org.apache.oozie.XException;
023    import org.apache.oozie.command.bundle.BundleStatusUpdateXCommand;
024    import org.apache.oozie.command.wf.KillXCommand;
025    import org.apache.oozie.command.CommandException;
026    import org.apache.oozie.command.KillTransitionXCommand;
027    import org.apache.oozie.command.PreconditionException;
028    import org.apache.oozie.executor.jpa.CoordActionUpdateJPAExecutor;
029    import org.apache.oozie.executor.jpa.CoordJobGetActionsJPAExecutor;
030    import org.apache.oozie.executor.jpa.CoordJobGetJPAExecutor;
031    import org.apache.oozie.executor.jpa.CoordJobUpdateJPAExecutor;
032    import org.apache.oozie.executor.jpa.JPAExecutorException;
033    import org.apache.oozie.service.JPAService;
034    import org.apache.oozie.service.Services;
035    import org.apache.oozie.util.LogUtils;
036    import org.apache.oozie.util.ParamChecker;
037    import org.apache.oozie.util.StatusUtils;
038    
039    import java.util.Date;
040    import java.util.List;
041    
042    public class CoordKillXCommand extends KillTransitionXCommand {
043    
044        private final String jobId;
045        private CoordinatorJobBean coordJob;
046        private List<CoordinatorActionBean> actionList;
047        private JPAService jpaService = null;
048        private CoordinatorJob.Status prevStatus = null;
049    
050        public CoordKillXCommand(String id) {
051            super("coord_kill", "coord_kill", 1);
052            this.jobId = ParamChecker.notEmpty(id, "id");
053        }
054    
055        @Override
056        protected boolean isLockRequired() {
057            return true;
058        }
059    
060        @Override
061        protected String getEntityKey() {
062            return this.jobId;
063        }
064    
065        @Override
066        protected void loadState() throws CommandException {
067            try {
068                jpaService = Services.get().get(JPAService.class);
069    
070                if (jpaService != null) {
071                    this.coordJob = jpaService.execute(new CoordJobGetJPAExecutor(jobId));
072                    this.actionList = jpaService.execute(new CoordJobGetActionsJPAExecutor(jobId));
073                    prevStatus = coordJob.getStatus();
074                    LogUtils.setLogInfo(coordJob, logInfo);
075                }
076                else {
077                    throw new CommandException(ErrorCode.E0610);
078                }
079            }
080            catch (XException ex) {
081                throw new CommandException(ex);
082            }
083        }
084    
085        @Override
086        protected void verifyPrecondition() throws CommandException, PreconditionException {
087            // if namespace 0.1 is used and backward support is true, SUCCEEDED coord job can be killed
088            if (StatusUtils.isV1CoordjobKillable(coordJob)) {
089                return;
090            }
091            if (coordJob.getStatus() == CoordinatorJob.Status.SUCCEEDED
092                    || coordJob.getStatus() == CoordinatorJob.Status.FAILED
093                    || coordJob.getStatus() == CoordinatorJob.Status.DONEWITHERROR) {
094                LOG.info("CoordKillXCommand not killed - job either finished SUCCEEDED, FAILED or DONEWITHERROR, job id = "
095                        + jobId + ", status = " + coordJob.getStatus());
096                throw new PreconditionException(ErrorCode.E1020, jobId);
097            }
098        }
099    
100        private void updateCoordAction(CoordinatorActionBean action) throws CommandException {
101            action.setStatus(CoordinatorActionBean.Status.KILLED);
102            action.incrementAndGetPending();
103            action.setLastModifiedTime(new Date());
104            try {
105                jpaService.execute(new CoordActionUpdateJPAExecutor(action));
106            }
107            catch (JPAExecutorException e) {
108                throw new CommandException(e);
109            }
110        }
111    
112        @Override
113        public void killChildren() throws CommandException {
114            try {
115                if (actionList != null) {
116                    for (CoordinatorActionBean action : actionList) {
117                        if (action.getStatus() != CoordinatorActionBean.Status.FAILED
118                                && action.getStatus() != CoordinatorActionBean.Status.TIMEDOUT
119                                && action.getStatus() != CoordinatorActionBean.Status.SUCCEEDED
120                                && action.getStatus() != CoordinatorActionBean.Status.KILLED) {
121                            // queue a WorkflowKillXCommand to delete the workflow job and actions
122                            if (action.getExternalId() != null) {
123                                queue(new KillXCommand(action.getExternalId()));
124                                updateCoordAction(action);
125                                LOG.debug("Killed coord action = [{0}], new status = [{1}], pending = [{2}] and queue KillXCommand for [{3}]",
126                                                action.getId(), action.getStatus(), action.getPending(), action.getExternalId());
127                            }
128                            else {
129                                updateCoordAction(action);
130                                LOG.debug("Killed coord action = [{0}], current status = [{1}], pending = [{2}]", action.getId(), action
131                                        .getStatus(), action.getPending());
132                            }
133                        }
134                    }
135                }
136                jpaService.execute(new CoordJobUpdateJPAExecutor(coordJob));
137    
138                LOG.debug("Killed coord actions for the coordinator=[{0}]", jobId);
139            }
140            catch (JPAExecutorException ex) {
141                throw new CommandException(ex);
142            }
143        }
144    
145        @Override
146        public void notifyParent() throws CommandException {
147            // update bundle action
148            if (coordJob.getBundleId() != null) {
149                BundleStatusUpdateXCommand bundleStatusUpdate = new BundleStatusUpdateXCommand(coordJob, prevStatus);
150                bundleStatusUpdate.call();
151            }
152        }
153    
154        @Override
155        public void updateJob() throws CommandException {
156            try {
157                coordJob.setEndTime(new Date());
158                jpaService.execute(new CoordJobUpdateJPAExecutor(coordJob));
159            }
160            catch (JPAExecutorException ex) {
161                throw new CommandException(ex);
162            }
163        }
164    
165        /* (non-Javadoc)
166         * @see org.apache.oozie.command.TransitionXCommand#getJob()
167         */
168        @Override
169        public Job getJob() {
170            return coordJob;
171        }
172    
173    }