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.List;
018    
019    import org.apache.oozie.CoordinatorActionBean;
020    import org.apache.oozie.CoordinatorJobBean;
021    import org.apache.oozie.ErrorCode;
022    import org.apache.oozie.client.CoordinatorAction;
023    import org.apache.oozie.client.Job;
024    import org.apache.oozie.command.CommandException;
025    import org.apache.oozie.command.PreconditionException;
026    import org.apache.oozie.executor.jpa.CoordJobGetReadyActionsJPAExecutor;
027    import org.apache.oozie.executor.jpa.CoordJobGetRunningActionsCountJPAExecutor;
028    import org.apache.oozie.executor.jpa.JPAExecutorException;
029    import org.apache.oozie.service.JPAService;
030    import org.apache.oozie.service.Services;
031    import org.apache.oozie.util.LogUtils;
032    import org.apache.oozie.util.XLog;
033    
034    public class CoordActionReadyXCommand extends CoordinatorXCommand<Void> {
035        private final String jobId;
036        private final XLog log = getLog();
037        private CoordinatorJobBean coordJob = null;
038        private JPAService jpaService = null;
039    
040        public CoordActionReadyXCommand(String id) {
041            super("coord_action_ready", "coord_action_ready", 1);
042            this.jobId = id;
043        }
044    
045        @Override
046        /**
047         * Check for READY actions and change state to SUBMITTED by a command to submit the job to WF engine.
048         * This method checks all the actions associated with a jobId to figure out which actions
049         * to start (based on concurrency and execution order [FIFO, LIFO, LAST_ONLY])
050         *
051         */
052        protected Void execute() throws CommandException {
053            // number of actions to start (-1 means start ALL)
054            int numActionsToStart = -1;
055    
056            // get execution setting for this job (FIFO, LIFO, LAST_ONLY)
057            String jobExecution = coordJob.getExecution();
058            // get concurrency setting for this job
059            int jobConcurrency = coordJob.getConcurrency();
060            // if less than 0, then UNLIMITED concurrency
061            if (jobConcurrency >= 0) {
062                // count number of actions that are already RUNNING or SUBMITTED
063                // subtract from CONCURRENCY to calculate number of actions to start
064                // in WF engine
065    
066                int numRunningJobs;
067                try {
068                    numRunningJobs = jpaService.execute(new CoordJobGetRunningActionsCountJPAExecutor(jobId));
069                }
070                catch (JPAExecutorException je) {
071                    throw new CommandException(je);
072                }
073    
074                numActionsToStart = jobConcurrency - numRunningJobs;
075                if (numActionsToStart < 0) {
076                    numActionsToStart = 0;
077                }
078                log.debug("concurrency=" + jobConcurrency + ", execution=" + jobExecution + ", numRunningJobs="
079                        + numRunningJobs + ", numLeftover=" + numActionsToStart);
080                // no actions to start
081                if (numActionsToStart == 0) {
082                    log.warn("No actions to start! for jobId=" + jobId);
083                    return null;
084                }
085            }
086            // get list of actions that are READY and fit in the concurrency and execution
087    
088            List<CoordinatorActionBean> actions;
089            try {
090                actions = jpaService.execute(new CoordJobGetReadyActionsJPAExecutor(jobId, numActionsToStart, jobExecution));
091            }
092            catch (JPAExecutorException je) {
093                throw new CommandException(je);
094            }
095            log.debug("Number of READY actions = " + actions.size());
096            String user = coordJob.getUser();
097            String authToken = coordJob.getAuthToken();
098            // make sure auth token is not null
099            // log.denug("user=" + user + ", token=" + authToken);
100            int counter = 0;
101            for (CoordinatorActionBean action : actions) {
102                // continue if numActionsToStart is negative (no limit on number of
103                // actions), or if the counter is less than numActionsToStart
104                if ((numActionsToStart < 0) || (counter < numActionsToStart)) {
105                    log.debug("Set status to SUBMITTED for id: " + action.getId());
106                    // change state of action to SUBMITTED
107                    action.setStatus(CoordinatorAction.Status.SUBMITTED);
108                    // queue action to start action
109                    queue(new CoordActionStartXCommand(action.getId(), user, authToken), 100);
110                    try {
111                        jpaService.execute(new org.apache.oozie.executor.jpa.CoordActionUpdateJPAExecutor(action));
112                    }
113                    catch (JPAExecutorException je) {
114                        throw new CommandException(je);
115                    }
116                }
117                else {
118                    break;
119                }
120                counter++;
121    
122            }
123            return null;
124        }
125    
126        @Override
127        protected String getEntityKey() {
128            return jobId;
129        }
130    
131        @Override
132        protected boolean isLockRequired() {
133            return true;
134        }
135    
136        @Override
137        protected void loadState() throws CommandException {
138            jpaService = Services.get().get(JPAService.class);
139            if (jpaService == null) {
140                throw new CommandException(ErrorCode.E0610);
141            }
142            try {
143                coordJob = jpaService.execute(new org.apache.oozie.executor.jpa.CoordJobGetJPAExecutor(jobId));
144            }
145            catch (JPAExecutorException e) {
146                throw new CommandException(e);
147            }
148            LogUtils.setLogInfo(coordJob, logInfo);
149        }
150    
151        @Override
152        protected void verifyPrecondition() throws CommandException, PreconditionException {
153            if (coordJob.getStatus() != Job.Status.RUNNING && coordJob.getStatus() != Job.Status.SUCCEEDED && coordJob.getStatus() != Job.Status.PAUSED && coordJob.getStatus() != Job.Status.PAUSEDWITHERROR) {
154                throw new PreconditionException(ErrorCode.E1100, "[" + jobId
155                        + "]::CoordActionReady:: Ignoring job. Coordinator job is not in RUNNING state, but state="
156                        + coordJob.getStatus());
157            }
158        }
159    }