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.bundle; 016 017 import java.util.Date; 018 import java.util.HashSet; 019 import java.util.List; 020 import java.util.Map; 021 import java.util.Set; 022 023 import org.apache.oozie.BundleActionBean; 024 import org.apache.oozie.BundleJobBean; 025 import org.apache.oozie.ErrorCode; 026 import org.apache.oozie.XException; 027 import org.apache.oozie.client.Job; 028 import org.apache.oozie.client.OozieClient; 029 import org.apache.oozie.command.CommandException; 030 import org.apache.oozie.command.PreconditionException; 031 import org.apache.oozie.command.XCommand; 032 import org.apache.oozie.command.coord.CoordChangeXCommand; 033 import org.apache.oozie.executor.jpa.BundleActionUpdateJPAExecutor; 034 import org.apache.oozie.executor.jpa.BundleActionsGetJPAExecutor; 035 import org.apache.oozie.executor.jpa.BundleJobGetJPAExecutor; 036 import org.apache.oozie.executor.jpa.BundleJobUpdateJPAExecutor; 037 import org.apache.oozie.service.JPAService; 038 import org.apache.oozie.service.Services; 039 import org.apache.oozie.util.DateUtils; 040 import org.apache.oozie.util.JobUtils; 041 import org.apache.oozie.util.LogUtils; 042 import org.apache.oozie.util.ParamChecker; 043 044 public class BundleJobChangeXCommand extends XCommand<Void> { 045 private String jobId; 046 private String changeValue; 047 private JPAService jpaService; 048 private List<BundleActionBean> bundleActions; 049 private BundleJobBean bundleJob; 050 private Date newPauseTime = null; 051 private Date newEndTime = null; 052 boolean isChangePauseTime = false; 053 boolean isChangeEndTime = false; 054 055 private static final Set<String> ALLOWED_CHANGE_OPTIONS = new HashSet<String>(); 056 static { 057 ALLOWED_CHANGE_OPTIONS.add("pausetime"); 058 ALLOWED_CHANGE_OPTIONS.add("endtime"); 059 } 060 061 /** 062 * @param id bundle job id 063 * @param changeValue change value 064 * 065 * @throws CommandException thrown if failed to change bundle 066 */ 067 public BundleJobChangeXCommand(String id, String changeValue) throws CommandException { 068 super("bundle_change", "bundle_change", 1); 069 this.jobId = ParamChecker.notEmpty(id, "id"); 070 this.changeValue = ParamChecker.notEmpty(changeValue, "changeValue"); 071 } 072 073 /** 074 * Check if new pause time is future time. 075 * 076 * @param newPauseTime new pause time. 077 * @throws CommandException thrown if new pause time is not valid. 078 */ 079 private void checkPauseTime(Date newPauseTime) throws CommandException { 080 // New pauseTime has to be a non-past time. 081 Date d = new Date(); 082 if (newPauseTime.before(d)) { 083 throw new CommandException(ErrorCode.E1317, newPauseTime, "must be a non-past time"); 084 } 085 } 086 087 /** 088 * Check if new pause time is future time. 089 * 090 * @param newEndTime new end time, can be null meaning no change on end time. 091 * @throws CommandException thrown if new end time is not valid. 092 */ 093 private void checkEndTime(Date newEndTime) throws CommandException { 094 // New endTime has to be a non-past start time. 095 Date startTime = bundleJob.getKickoffTime(); 096 if (startTime != null && newEndTime.before(startTime)) { 097 throw new CommandException(ErrorCode.E1317, newEndTime, "must be greater then kickoff time"); 098 } 099 } 100 101 /** 102 * validate if change value is valid. 103 * 104 * @param changeValue change value. 105 * @throws CommandException thrown if changeValue cannot be parsed properly. 106 */ 107 private void validateChangeValue(String changeValue) throws CommandException { 108 Map<String, String> map = JobUtils.parseChangeValue(changeValue); 109 110 if (map.size() > ALLOWED_CHANGE_OPTIONS.size() || !(map.containsKey(OozieClient.CHANGE_VALUE_PAUSETIME) || map.containsKey(OozieClient.CHANGE_VALUE_ENDTIME))) { 111 throw new CommandException(ErrorCode.E1317, changeValue, "can only change pausetime or end time"); 112 } 113 114 if (map.containsKey(OozieClient.CHANGE_VALUE_PAUSETIME)) { 115 isChangePauseTime = true; 116 } 117 else if(map.containsKey(OozieClient.CHANGE_VALUE_ENDTIME)){ 118 isChangeEndTime = true; 119 } 120 else { 121 throw new CommandException(ErrorCode.E1317, changeValue, "should change pausetime or endtime"); 122 } 123 124 if(isChangePauseTime){ 125 String value = map.get(OozieClient.CHANGE_VALUE_PAUSETIME); 126 if (!value.equals("")) { 127 try { 128 newPauseTime = DateUtils.parseDateUTC(value); 129 } 130 catch (Exception ex) { 131 throw new CommandException(ErrorCode.E1317, value, "is not a valid date"); 132 } 133 134 checkPauseTime(newPauseTime); 135 } 136 } 137 else if (isChangeEndTime){ 138 String value = map.get(OozieClient.CHANGE_VALUE_ENDTIME); 139 if (!value.equals("")) { 140 try { 141 newEndTime = DateUtils.parseDateUTC(value); 142 } 143 catch (Exception ex) { 144 throw new CommandException(ErrorCode.E1317, value, "is not a valid date"); 145 } 146 147 checkEndTime(newEndTime); 148 } 149 } 150 } 151 152 /* (non-Javadoc) 153 * @see org.apache.oozie.command.XCommand#execute() 154 */ 155 @Override 156 protected Void execute() throws CommandException { 157 try { 158 if (isChangePauseTime || isChangeEndTime) { 159 if (isChangePauseTime) { 160 bundleJob.setPauseTime(newPauseTime); 161 } 162 else if (isChangeEndTime) { 163 bundleJob.setEndTime(newEndTime); 164 bundleJob.setStatus(Job.Status.RUNNING); 165 } 166 for (BundleActionBean action : this.bundleActions) { 167 // queue coord change commands; 168 if (action.getStatus() != Job.Status.KILLED && action.getCoordId() != null) { 169 queue(new CoordChangeXCommand(action.getCoordId(), changeValue)); 170 LOG.info("Queuing CoordChangeXCommand coord job = " + action.getCoordId() + " to change " 171 + changeValue); 172 action.setPending(action.getPending() + 1); 173 jpaService.execute(new BundleActionUpdateJPAExecutor(action)); 174 } 175 } 176 jpaService.execute(new BundleJobUpdateJPAExecutor(bundleJob)); 177 } 178 return null; 179 } 180 catch (XException ex) { 181 throw new CommandException(ex); 182 } 183 } 184 185 /* (non-Javadoc) 186 * @see org.apache.oozie.command.XCommand#getEntityKey() 187 */ 188 @Override 189 protected String getEntityKey() { 190 return this.jobId; 191 } 192 193 /* (non-Javadoc) 194 * @see org.apache.oozie.command.XCommand#isLockRequired() 195 */ 196 @Override 197 protected boolean isLockRequired() { 198 return true; 199 } 200 201 /* (non-Javadoc) 202 * @see org.apache.oozie.command.XCommand#loadState() 203 */ 204 @Override 205 protected void loadState() throws CommandException { 206 try{ 207 eagerLoadState(); 208 this.bundleActions = jpaService.execute(new BundleActionsGetJPAExecutor(jobId)); 209 } 210 catch(Exception Ex){ 211 throw new CommandException(ErrorCode.E1311,this.jobId); 212 } 213 } 214 215 /* (non-Javadoc) 216 * @see org.apache.oozie.command.XCommand#verifyPrecondition() 217 */ 218 @Override 219 protected void verifyPrecondition() throws CommandException, PreconditionException { 220 } 221 222 /* (non-Javadoc) 223 * @see org.apache.oozie.command.XCommand#eagerLoadState() 224 */ 225 @Override 226 protected void eagerLoadState() throws CommandException { 227 try { 228 jpaService = Services.get().get(JPAService.class); 229 230 if (jpaService != null) { 231 this.bundleJob = jpaService.execute(new BundleJobGetJPAExecutor(jobId)); 232 LogUtils.setLogInfo(bundleJob, logInfo); 233 } 234 else { 235 throw new CommandException(ErrorCode.E0610); 236 } 237 } 238 catch (XException ex) { 239 throw new CommandException(ex); 240 } 241 } 242 243 /* (non-Javadoc) 244 * @see org.apache.oozie.command.XCommand#eagerVerifyPrecondition() 245 */ 246 @Override 247 protected void eagerVerifyPrecondition() throws CommandException, PreconditionException { 248 validateChangeValue(changeValue); 249 250 if (bundleJob == null) { 251 LOG.info("BundleChangeCommand not succeeded - " + "job " + jobId + " does not exist"); 252 throw new PreconditionException(ErrorCode.E1314, jobId); 253 } 254 if (isChangePauseTime) { 255 if (bundleJob.getStatus() == Job.Status.SUCCEEDED || bundleJob.getStatus() == Job.Status.FAILED 256 || bundleJob.getStatus() == Job.Status.KILLED || bundleJob.getStatus() == Job.Status.DONEWITHERROR 257 || bundleJob == null) { 258 LOG.info("BundleChangeCommand not succeeded for changing pausetime- " + "job " + jobId + " finished, status is " 259 + bundleJob.getStatusStr()); 260 throw new PreconditionException(ErrorCode.E1312, jobId, bundleJob.getStatus().toString()); 261 } 262 } 263 else if(isChangeEndTime){ 264 if (bundleJob.getStatus() == Job.Status.KILLED || bundleJob == null) { 265 LOG.info("BundleChangeCommand not succeeded for changing endtime- " + "job " + jobId + " finished, status is " 266 + bundleJob.getStatusStr()); 267 throw new PreconditionException(ErrorCode.E1312, jobId, bundleJob.getStatus().toString()); 268 } 269 } 270 } 271 }