001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.command.bundle; 020 021import java.util.Date; 022import java.util.HashMap; 023import java.util.List; 024 025import org.apache.oozie.BundleActionBean; 026import org.apache.oozie.BundleJobBean; 027import org.apache.oozie.ErrorCode; 028import org.apache.oozie.client.Job; 029import org.apache.oozie.client.Job.Status; 030import org.apache.oozie.command.CommandException; 031import org.apache.oozie.command.PreconditionException; 032import org.apache.oozie.command.StatusTransitXCommand; 033import org.apache.oozie.executor.jpa.BundleActionQueryExecutor; 034import org.apache.oozie.executor.jpa.BundleJobQueryExecutor; 035import org.apache.oozie.executor.jpa.BundleActionQueryExecutor.BundleActionQuery; 036import org.apache.oozie.executor.jpa.BundleJobQueryExecutor.BundleJobQuery; 037import org.apache.oozie.executor.jpa.JPAExecutorException; 038import org.apache.oozie.util.LogUtils; 039import org.apache.oozie.util.StatusUtils; 040 041/** 042 * BundleStatusTransitXCommand update job's status according to its child actions' status. If all child actions' pending 043 * flag equals 0 (job done), we reset the job's pending flag to 0. If all child actions are succeeded, we set the job's 044 * status to SUCCEEDED. 045 */ 046public class BundleStatusTransitXCommand extends StatusTransitXCommand { 047 048 private String jobId; 049 private List<BundleActionBean> bundleActions; 050 private BundleJobBean bundleJob; 051 private boolean foundPending; 052 private HashMap<Job.Status, Integer> bundleActionStatus = new HashMap<Job.Status, Integer>(); 053 054 public BundleStatusTransitXCommand(String id) { 055 super("bundle_status_transit", "bundle_status_transit", 0); 056 this.jobId = id; 057 } 058 059 @Override 060 public String getEntityKey() { 061 return jobId; 062 } 063 064 @Override 065 protected void loadState() throws CommandException { 066 try { 067 bundleJob = BundleJobQueryExecutor.getInstance().get( 068 BundleJobQuery.GET_BUNDLE_JOB_ID_STATUS_PENDING_MOD_PAUSE_SUSPEND_TIME, jobId); 069 070 bundleActions = BundleActionQueryExecutor.getInstance().getList( 071 BundleActionQuery.GET_BUNDLE_UNIGNORED_ACTION_STATUS_PENDING_FOR_BUNDLE, jobId); 072 for (BundleActionBean bAction : bundleActions) { 073 int counter = 0; 074 if (bundleActionStatus.containsKey(bAction.getStatus())) { 075 counter = getActionStatusCount(bAction.getStatus()) + 1; 076 } 077 else { 078 ++counter; 079 } 080 bundleActionStatus.put(bAction.getStatus(), counter); 081 if (bAction.getCoordId() == null 082 && (bAction.getStatus() == Job.Status.FAILED || bAction.getStatus() == Job.Status.KILLED) ) { 083 new BundleKillXCommand(jobId).call(); 084 bundleJob = BundleJobQueryExecutor.getInstance().get( 085 BundleJobQuery.GET_BUNDLE_JOB_ID_STATUS_PENDING_MOD_PAUSE_SUSPEND_TIME, jobId); 086 bundleJob.setStatus(Job.Status.FAILED); 087 bundleJob.setLastModifiedTime(new Date()); 088 BundleJobQueryExecutor.getInstance().executeUpdate(BundleJobQuery.UPDATE_BUNDLE_JOB_STATUS, 089 bundleJob); 090 } 091 092 if (bAction.isPending()) { 093 foundPending = true; 094 } 095 } 096 LogUtils.setLogInfo(bundleJob); 097 } 098 catch (JPAExecutorException e) { 099 throw new CommandException(ErrorCode.E1322, e); 100 } 101 } 102 103 @Override 104 protected Job.Status getJobStatus() throws CommandException { 105 Job.Status jobStatus = super.getJobStatus(); 106 if (jobStatus == null) { 107 if (isPrepRunningState()) { 108 return getPrepRunningStatus(); 109 } 110 } 111 112 return jobStatus; 113 } 114 115 @Override 116 protected boolean isTerminalState() { 117 return !foundPending 118 && bundleActions.size() == getActionStatusCount(Job.Status.SUCCEEDED) 119 + getActionStatusCount(Job.Status.FAILED) + getActionStatusCount(Job.Status.KILLED) 120 + getActionStatusCount(Job.Status.DONEWITHERROR); 121 } 122 123 @Override 124 protected Job.Status getTerminalStatus() { 125 126 // If all bundle action is done and bundle is killed, then don't change the status. 127 if (bundleJob.getStatus().equals(Job.Status.KILLED)) { 128 return Job.Status.KILLED; 129 130 } 131 // If all the bundle actions are succeeded then bundle job should be succeeded. 132 if (bundleActions.size() == getActionStatusCount(Job.Status.SUCCEEDED)) { 133 return Job.Status.SUCCEEDED; 134 135 } 136 else if (bundleActions.size() == getActionStatusCount(Job.Status.KILLED)) { 137 // If all the bundle actions are KILLED then bundle job should be KILLED. 138 return Job.Status.KILLED; 139 } 140 else if (bundleActions.size() == getActionStatusCount(Job.Status.FAILED)) { 141 // If all the bundle actions are FAILED then bundle job should be FAILED. 142 return Job.Status.FAILED; 143 } 144 else { 145 return Job.Status.DONEWITHERROR; 146 147 } 148 } 149 150 @Override 151 protected boolean isPausedState() { 152 //If bundle is paused then timestamp will be set. 153 //If bundleJob.getPauseTime() is not set, that means that status has to be computed from bottom-up. 154 if (bundleJob.getStatus() == Job.Status.PAUSED || bundleJob.getStatus() == Job.Status.PAUSEDWITHERROR 155 && bundleJob.getPauseTime() != null) { 156 return true; 157 } 158 else { 159 return getBottomUpPauseStatus() != null; 160 } 161 162 } 163 164 @Override 165 protected Job.Status getPausedState() { 166 if (bundleJob.getStatus() == Job.Status.PAUSED || bundleJob.getStatus() == Job.Status.PAUSEDWITHERROR) { 167 if (hasTerminatedActions() || bundleActionStatus.containsKey(Job.Status.SUSPENDEDWITHERROR) 168 || bundleActionStatus.containsKey(Job.Status.RUNNINGWITHERROR) 169 || bundleActionStatus.containsKey(Job.Status.PAUSEDWITHERROR)) { 170 return Job.Status.PAUSEDWITHERROR; 171 } 172 else { 173 return Job.Status.PAUSED; 174 } 175 } 176 return getBottomUpPauseStatus(); 177 178 } 179 180 @Override 181 protected boolean isSuspendedState() { 182 //If bundle is suspended then timestamp will be set. 183 //If bundleJob.getSuspendedTimestamp() is not set, that means that status has to be computed from bottom-up. 184 if ((bundleJob.getStatus() == Job.Status.SUSPENDED || bundleJob.getStatus() == Job.Status.SUSPENDEDWITHERROR) 185 && bundleJob.getSuspendedTimestamp() != null) { 186 return true; 187 } 188 189 return getBottomUpSuspendedState() != null; 190 191 } 192 193 @Override 194 protected Job.Status getSuspendedStatus() { 195 if (bundleJob.getStatus() == Job.Status.SUSPENDED || bundleJob.getStatus() == Job.Status.SUSPENDEDWITHERROR) { 196 if (hasTerminatedActions() || bundleActionStatus.containsKey(Job.Status.SUSPENDEDWITHERROR) 197 || bundleActionStatus.containsKey(Job.Status.PAUSEDWITHERROR)) { 198 return Job.Status.SUSPENDEDWITHERROR; 199 } 200 else { 201 return Job.Status.SUSPENDED; 202 } 203 204 } 205 return getBottomUpSuspendedState(); 206 207 } 208 209 @Override 210 protected boolean isRunningState() { 211 return true; 212 } 213 214 @Override 215 protected Status getRunningState() { 216 if (bundleJob.getStatus() != Job.Status.PREP) { 217 return getRunningStatus(bundleActionStatus); 218 } 219 else 220 return null; 221 } 222 223 @Override 224 protected void updateJobStatus(Job.Status bundleStatus) throws JPAExecutorException { 225 LOG.info("Set bundle job [" + jobId + "] status to '" + bundleStatus + "' from '" + bundleJob.getStatus() + "'"); 226 227 String jobId = bundleJob.getId(); 228 // Update the Bundle Job 229 // Check for backward support when RUNNINGWITHERROR, SUSPENDEDWITHERROR and 230 // PAUSEDWITHERROR is not supported 231 bundleJob.setStatus(StatusUtils.getStatusIfBackwardSupportTrue(bundleStatus)); 232 bundleJob.setLastModifiedTime(new Date()); 233 if (foundPending) { 234 bundleJob.setPending(); 235 LOG.info("Bundle job [" + jobId + "] Pending set to TRUE"); 236 } 237 else { 238 bundleJob.resetPending(); 239 LOG.info("Bundle job [" + jobId + "] Pending set to FALSE"); 240 } 241 BundleJobQueryExecutor.getInstance().executeUpdate(BundleJobQuery.UPDATE_BUNDLE_JOB_STATUS_PENDING_MODTIME, 242 bundleJob); 243 } 244 245 /** 246 * bottom up; check the status of parent through their children. 247 * 248 * @return the bottom up pause status 249 */ 250 private Job.Status getBottomUpPauseStatus() { 251 252 if (bundleActionStatus.containsKey(Job.Status.PAUSED) 253 && bundleActions.size() == getActionStatusCount(Job.Status.PAUSED)) { 254 return Job.Status.PAUSED; 255 256 } 257 else if (bundleActionStatus.containsKey(Job.Status.PAUSEDWITHERROR) 258 && bundleActions.size() == getActionStatusCount(Job.Status.PAUSED) 259 + getActionStatusCount(Job.Status.PAUSEDWITHERROR)) { 260 return Job.Status.PAUSEDWITHERROR; 261 } 262 263 return null; 264 } 265 266 /** 267 * Bottom up update status of parent from the status of its children. 268 * 269 * @return the bottom up suspended state 270 */ 271 private Job.Status getBottomUpSuspendedState() { 272 273 if (!foundPending && bundleActionStatus.containsKey(Job.Status.SUSPENDED) 274 || bundleActionStatus.containsKey(Job.Status.SUSPENDEDWITHERROR)) { 275 276 if (bundleActions.size() == getActionStatusCount(Job.Status.SUSPENDED) 277 + getActionStatusCount(Job.Status.SUCCEEDED)) { 278 return Job.Status.SUSPENDED; 279 } 280 else if (bundleActions.size() == getActionStatusCount(Job.Status.SUSPENDEDWITHERROR) 281 + getActionStatusCount(Job.Status.SUSPENDED) + getActionStatusCount(Job.Status.SUCCEEDED) 282 + getActionStatusCount(Job.Status.KILLED) + getActionStatusCount(Job.Status.FAILED) 283 + getActionStatusCount(Job.Status.DONEWITHERROR)) { 284 return Job.Status.SUSPENDEDWITHERROR; 285 286 } 287 } 288 return null; 289 } 290 291 private boolean hasTerminatedActions() { 292 return bundleActionStatus.containsKey(Job.Status.KILLED) || bundleActionStatus.containsKey(Job.Status.FAILED) 293 || bundleActionStatus.containsKey(Job.Status.DONEWITHERROR); 294 295 } 296 297 private boolean isPrepRunningState() { 298 return !foundPending && bundleActionStatus.containsKey(Job.Status.PREP) 299 && bundleActions.size() > getActionStatusCount(Job.Status.PREP); 300 } 301 302 private Status getPrepRunningStatus() { 303 return getRunningStatus(bundleActionStatus); 304 305 } 306 307 private int getActionStatusCount(final Job.Status status) { 308 309 if (bundleActionStatus.containsKey(status)) { 310 return bundleActionStatus.get(status); 311 } 312 else { 313 return 0; 314 } 315 } 316 317 private Job.Status getRunningStatus(HashMap<Job.Status, Integer> actionStatus) { 318 if (actionStatus.containsKey(Job.Status.FAILED) || actionStatus.containsKey(Job.Status.KILLED) 319 || actionStatus.containsKey(Job.Status.DONEWITHERROR) 320 || actionStatus.containsKey(Job.Status.RUNNINGWITHERROR)) { 321 return Job.Status.RUNNINGWITHERROR; 322 } 323 else { 324 return Job.Status.RUNNING; 325 } 326 } 327 328 @Override 329 protected void verifyPrecondition() throws CommandException, PreconditionException { 330 } 331 332}