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 java.util.Date;
018    import java.util.List;
019    
020    import org.apache.oozie.CoordinatorActionBean;
021    import org.apache.oozie.CoordinatorJobBean;
022    import org.apache.oozie.ErrorCode;
023    import org.apache.oozie.XException;
024    import org.apache.oozie.client.CoordinatorJob;
025    import org.apache.oozie.client.Job;
026    import org.apache.oozie.command.CommandException;
027    import org.apache.oozie.command.PreconditionException;
028    import org.apache.oozie.command.SuspendTransitionXCommand;
029    import org.apache.oozie.command.bundle.BundleStatusUpdateXCommand;
030    import org.apache.oozie.command.wf.SuspendXCommand;
031    import org.apache.oozie.executor.jpa.CoordActionUpdateJPAExecutor;
032    import org.apache.oozie.executor.jpa.CoordJobGetActionsJPAExecutor;
033    import org.apache.oozie.executor.jpa.CoordJobGetJPAExecutor;
034    import org.apache.oozie.executor.jpa.CoordJobUpdateJPAExecutor;
035    import org.apache.oozie.executor.jpa.JPAExecutorException;
036    import org.apache.oozie.service.JPAService;
037    import org.apache.oozie.service.Services;
038    import org.apache.oozie.util.InstrumentUtils;
039    import org.apache.oozie.util.LogUtils;
040    import org.apache.oozie.util.ParamChecker;
041    import org.apache.oozie.util.StatusUtils;
042    
043    /**
044     * Suspend coordinator job and actions.
045     *
046     */
047    public class CoordSuspendXCommand extends SuspendTransitionXCommand {
048        private final String jobId;
049        private CoordinatorJobBean coordJob;
050        private JPAService jpaService;
051        private boolean exceptionOccured = false;
052        private CoordinatorJob.Status prevStatus = null;
053    
054        public CoordSuspendXCommand(String id) {
055            super("coord_suspend", "coord_suspend", 1);
056            this.jobId = ParamChecker.notEmpty(id, "id");
057        }
058    
059        /* (non-Javadoc)
060         * @see org.apache.oozie.command.XCommand#getEntityKey()
061         */
062        @Override
063        protected String getEntityKey() {
064            return jobId;
065        }
066    
067        /* (non-Javadoc)
068         * @see org.apache.oozie.command.XCommand#isLockRequired()
069         */
070        @Override
071        protected boolean isLockRequired() {
072            return true;
073        }
074    
075        /* (non-Javadoc)
076         * @see org.apache.oozie.command.XCommand#loadState()
077         */
078        @Override
079        protected void loadState() throws CommandException {
080            super.eagerLoadState();
081            try {
082                jpaService = Services.get().get(JPAService.class);
083                if (jpaService != null) {
084                    this.coordJob = jpaService.execute(new CoordJobGetJPAExecutor(this.jobId));
085                    prevStatus = coordJob.getStatus();
086                }
087                else {
088                    throw new CommandException(ErrorCode.E0610);
089                }
090            }
091            catch (Exception ex) {
092                throw new CommandException(ErrorCode.E0603, ex);
093            }
094            LogUtils.setLogInfo(this.coordJob, logInfo);
095        }
096    
097        /* (non-Javadoc)
098         * @see org.apache.oozie.command.XCommand#verifyPrecondition()
099         */
100        @Override
101        protected void verifyPrecondition() throws CommandException, PreconditionException {
102            super.eagerVerifyPrecondition();
103            if (coordJob.getStatus() == CoordinatorJob.Status.SUCCEEDED
104                    || coordJob.getStatus() == CoordinatorJob.Status.FAILED
105                    || coordJob.getStatus() == CoordinatorJob.Status.KILLED) {
106                LOG.info("CoordSuspendXCommand is not going to execute because "
107                        + "job finished or failed or killed, id = " + jobId + ", status = " + coordJob.getStatus());
108                throw new PreconditionException(ErrorCode.E0728, jobId, coordJob.getStatus().toString());
109            }
110        }
111    
112        /* (non-Javadoc)
113         * @see org.apache.oozie.command.SuspendTransitionXCommand#suspendChildren()
114         */
115        @Override
116        public void suspendChildren() throws CommandException {
117            try {
118                List<CoordinatorActionBean> actionList = jpaService.execute(new CoordJobGetActionsJPAExecutor(jobId));
119                for (CoordinatorActionBean action : actionList) {
120                    if (action.getStatus() == CoordinatorActionBean.Status.RUNNING) {
121                        // queue a SuspendXCommand
122                        if (action.getExternalId() != null) {
123                            queue(new SuspendXCommand(action.getExternalId()));
124                            updateCoordAction(action);
125                            LOG.debug("Suspend coord action = [{0}], new status = [{1}], pending = [{2}] and queue SuspendXCommand for [{3}]",
126                                            action.getId(), action.getStatus(), action.getPending(), action.getExternalId());
127                        } else {
128                            updateCoordAction(action);
129                            LOG.debug("Suspend coord action = [{0}], new status = [{1}], pending = [{2}] and external id is null",
130                                    action.getId(), action.getStatus(), action.getPending());
131                        }
132    
133                    }
134                }
135                LOG.debug("Suspended coordinator actions for the coordinator=[{0}]", jobId);
136            }
137            catch (XException ex) {
138                exceptionOccured = true;
139                throw new CommandException(ex);
140            }
141            finally {
142                if (exceptionOccured) {
143                    coordJob.setStatus(CoordinatorJob.Status.FAILED);
144                    coordJob.resetPending();
145                    LOG.debug("Exception happened, fail coordinator job id = " + jobId + ", status = " + coordJob.getStatus());
146                    try {
147                        jpaService.execute(new CoordJobUpdateJPAExecutor(coordJob));
148                    }
149                    catch (JPAExecutorException je) {
150                        LOG.error("Failed to update coordinator job : " + jobId, je);
151                    }
152                }
153            }
154        }
155    
156        /* (non-Javadoc)
157         * @see org.apache.oozie.command.TransitionXCommand#notifyParent()
158         */
159        @Override
160        public void notifyParent() throws CommandException {
161            // update bundle action
162            if (this.coordJob.getBundleId() != null) {
163                BundleStatusUpdateXCommand bundleStatusUpdate = new BundleStatusUpdateXCommand(coordJob, prevStatus);
164                bundleStatusUpdate.call();
165            }
166        }
167    
168        /* (non-Javadoc)
169         * @see org.apache.oozie.command.TransitionXCommand#updateJob()
170         */
171        @Override
172        public void updateJob() throws CommandException {
173            InstrumentUtils.incrJobCounter(getName(), 1, getInstrumentation());
174            coordJob.setLastModifiedTime(new Date());
175            coordJob.setSuspendedTime(new Date());
176            LOG.debug("Suspend coordinator job id = " + jobId + ", status = " + coordJob.getStatus() + ", pending = " + coordJob.isPending());
177            try {
178                jpaService.execute(new CoordJobUpdateJPAExecutor(coordJob));
179            }
180            catch (JPAExecutorException e) {
181                throw new CommandException(e);
182            }
183        }
184    
185        private void updateCoordAction(CoordinatorActionBean action) throws CommandException {
186            action.setStatus(CoordinatorActionBean.Status.SUSPENDED);
187            action.incrementAndGetPending();
188            action.setLastModifiedTime(new Date());
189            try {
190                jpaService.execute(new CoordActionUpdateJPAExecutor(action));
191            }
192            catch (JPAExecutorException e) {
193                throw new CommandException(e);
194            }
195        }
196    
197        /* (non-Javadoc)
198         * @see org.apache.oozie.command.TransitionXCommand#getJob()
199         */
200        @Override
201        public Job getJob() {
202            return coordJob;
203        }
204    
205        /**
206         * Transit job to suspended from running or to prepsuspended from prep.
207         *
208         * @see org.apache.oozie.command.TransitionXCommand#transitToNext()
209         */
210        @Override
211        public void transitToNext() {
212            if (coordJob == null) {
213                coordJob = (CoordinatorJobBean) this.getJob();
214            }
215            if (coordJob.getStatus() == Job.Status.PREP) {
216                coordJob.setStatus(Job.Status.PREPSUSPENDED);
217                coordJob.setStatus(StatusUtils.getStatus(coordJob));
218            }
219            else if (coordJob.getStatus() == Job.Status.RUNNING) {
220                coordJob.setStatus(Job.Status.SUSPENDED);
221            }
222            coordJob.setPending();
223        }
224    
225    }