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    
019    import org.apache.oozie.CoordinatorActionBean;
020    import org.apache.oozie.ErrorCode;
021    import org.apache.oozie.WorkflowJobBean;
022    import org.apache.oozie.XException;
023    import org.apache.oozie.service.JPAService;
024    import org.apache.oozie.service.Services;
025    import org.apache.oozie.util.LogUtils;
026    import org.apache.oozie.util.db.SLADbOperations;
027    import org.apache.oozie.client.CoordinatorAction;
028    import org.apache.oozie.client.WorkflowJob;
029    import org.apache.oozie.client.SLAEvent.SlaAppType;
030    import org.apache.oozie.client.SLAEvent.Status;
031    import org.apache.oozie.command.CommandException;
032    import org.apache.oozie.command.PreconditionException;
033    import org.apache.oozie.executor.jpa.CoordActionGetForExternalIdJPAExecutor;
034    import org.apache.oozie.executor.jpa.CoordActionUpdateJPAExecutor;
035    import org.apache.oozie.executor.jpa.JPAExecutorException;
036    
037    public class CoordActionUpdateXCommand extends CoordinatorXCommand<Void> {
038        private WorkflowJobBean workflow;
039        private CoordinatorActionBean coordAction = null;
040        private JPAService jpaService = null;
041        private int maxRetries = 1;
042    
043        public CoordActionUpdateXCommand(WorkflowJobBean workflow) {
044            super("coord-action-update", "coord-action-update", 1);
045            this.workflow = workflow;
046        }
047    
048        public CoordActionUpdateXCommand(WorkflowJobBean workflow, int maxRetries) {
049            super("coord-action-update", "coord-action-update", 1);
050            this.workflow = workflow;
051            this.maxRetries = maxRetries;
052        }
053    
054        @Override
055        protected Void execute() throws CommandException {
056            try {
057                LOG.debug("STARTED CoordActionUpdateXCommand for wfId=" + workflow.getId());
058    
059                Status slaStatus = null;
060                CoordinatorAction.Status preCoordStatus = coordAction.getStatus();
061                if (workflow.getStatus() == WorkflowJob.Status.SUCCEEDED) {
062                    coordAction.setStatus(CoordinatorAction.Status.SUCCEEDED);
063                    coordAction.setPending(0);
064                    slaStatus = Status.SUCCEEDED;
065                }
066                else if (workflow.getStatus() == WorkflowJob.Status.FAILED) {
067                    coordAction.setStatus(CoordinatorAction.Status.FAILED);
068                    coordAction.setPending(0);
069                    slaStatus = Status.FAILED;
070                }
071                else if (workflow.getStatus() == WorkflowJob.Status.KILLED) {
072                    coordAction.setStatus(CoordinatorAction.Status.KILLED);
073                    coordAction.setPending(0);
074                    slaStatus = Status.KILLED;
075                }
076                else if (workflow.getStatus() == WorkflowJob.Status.SUSPENDED) {
077                    coordAction.setStatus(CoordinatorAction.Status.SUSPENDED);
078                    coordAction.decrementAndGetPending();
079                }
080                else if (workflow.getStatus() == WorkflowJob.Status.RUNNING) {
081                    // resume workflow job and update coord action accordingly
082                    coordAction.setStatus(CoordinatorAction.Status.RUNNING);
083                    coordAction.decrementAndGetPending();
084                }
085                else {
086                    LOG.warn("Unexpected workflow " + workflow.getId() + " STATUS " + workflow.getStatus());
087                    // update lastModifiedTime
088                    coordAction.setLastModifiedTime(new Date());
089                    jpaService.execute(new CoordActionUpdateJPAExecutor(coordAction));
090    
091                    return null;
092                }
093    
094                LOG.info("Updating Coordintaor action id :" + coordAction.getId() + " status from " + preCoordStatus
095                        + " to " + coordAction.getStatus() + ", pending = " + coordAction.getPending());
096    
097                coordAction.setLastModifiedTime(new Date());
098                jpaService.execute(new CoordActionUpdateJPAExecutor(coordAction));
099                if (slaStatus != null) {
100                    SLADbOperations.writeStausEvent(coordAction.getSlaXml(), coordAction.getId(), slaStatus,
101                            SlaAppType.COORDINATOR_ACTION, LOG);
102                }
103                if (workflow.getStatus() != WorkflowJob.Status.SUSPENDED
104                        && workflow.getStatus() != WorkflowJob.Status.RUNNING) {
105                    queue(new CoordActionReadyXCommand(coordAction.getJobId()));
106                }
107                LOG.debug("ENDED CoordActionUpdateXCommand for wfId=" + workflow.getId());
108            }
109            catch (XException ex) {
110                LOG.warn("CoordActionUpdate Failed ", ex.getMessage());
111                throw new CommandException(ex);
112            }
113            return null;
114        }
115    
116        /* (non-Javadoc)
117         * @see org.apache.oozie.command.XCommand#getEntityKey()
118         */
119        @Override
120        protected String getEntityKey() {
121            return coordAction.getJobId();
122        }
123    
124        /* (non-Javadoc)
125         * @see org.apache.oozie.command.XCommand#isLockRequired()
126         */
127        @Override
128        protected boolean isLockRequired() {
129            return true;
130        }
131    
132        /* (non-Javadoc)
133         * @see org.apache.oozie.command.XCommand#eagerLoadState()
134         */
135        @Override
136        protected void eagerLoadState() throws CommandException {
137            jpaService = Services.get().get(JPAService.class);
138            if (jpaService == null) {
139                throw new CommandException(ErrorCode.E0610);
140            }
141    
142            int retries = 0;
143            while (retries++ < maxRetries) {
144                try {
145                    coordAction = jpaService.execute(new CoordActionGetForExternalIdJPAExecutor(workflow.getId()));
146                    if (coordAction != null) {
147                        break;
148                    }
149    
150                    if (retries < maxRetries) {
151                        Thread.sleep(500);
152                    }
153                }
154                catch (JPAExecutorException je) {
155                    LOG.warn("Could not load coord action {0}", je.getMessage(), je);
156                }
157                catch (InterruptedException ex) {
158                    LOG.warn("Retry to load coord action is interrupted {0}", ex.getMessage(), ex);
159                }
160            }
161    
162            if (coordAction != null) {
163                LogUtils.setLogInfo(coordAction, logInfo);
164            }
165        }
166    
167        /* (non-Javadoc)
168         * @see org.apache.oozie.command.XCommand#eagerVerifyPrecondition()
169         */
170        @Override
171        protected void eagerVerifyPrecondition() throws CommandException, PreconditionException {
172            if (coordAction == null) {
173                throw new PreconditionException(ErrorCode.E1100, ", coord action is null");
174            }
175        }
176    
177        /* (non-Javadoc)
178         * @see org.apache.oozie.command.XCommand#loadState()
179         */
180        @Override
181        protected void loadState() throws CommandException {
182        }
183    
184        /* (non-Javadoc)
185         * @see org.apache.oozie.command.XCommand#verifyPrecondition()
186         */
187        @Override
188        protected void verifyPrecondition() throws CommandException, PreconditionException {
189    
190            // if coord action is RUNNING and pending false and workflow is RUNNING, this doesn't need to be updated.
191            if (workflow.getStatus() == WorkflowJob.Status.RUNNING
192                    && coordAction.getStatus() == CoordinatorAction.Status.RUNNING && !coordAction.isPending()) {
193                // update lastModifiedTime
194                coordAction.setLastModifiedTime(new Date());
195                try {
196                    jpaService.execute(new org.apache.oozie.executor.jpa.CoordActionUpdateJPAExecutor(coordAction));
197                }
198                catch (JPAExecutorException je) {
199                    throw new CommandException(je);
200                }
201                throw new PreconditionException(ErrorCode.E1100, ", workflow is RUNNING and coordinator action is RUNNING and pending false");
202            }
203        }
204    
205    }