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.coord;
020
021import org.apache.commons.lang.StringUtils;
022import org.apache.hadoop.conf.Configuration;
023import org.apache.oozie.AppType;
024import org.apache.oozie.CoordinatorActionBean;
025import org.apache.oozie.CoordinatorJobBean;
026import org.apache.oozie.ErrorCode;
027import org.apache.oozie.SLAEventBean;
028import org.apache.oozie.client.CoordinatorJob;
029import org.apache.oozie.client.Job;
030import org.apache.oozie.client.SLAEvent.SlaAppType;
031import org.apache.oozie.client.rest.JsonBean;
032import org.apache.oozie.command.CommandException;
033import org.apache.oozie.command.MaterializeTransitionXCommand;
034import org.apache.oozie.command.PreconditionException;
035import org.apache.oozie.command.bundle.BundleStatusUpdateXCommand;
036import org.apache.oozie.coord.CoordUtils;
037import org.apache.oozie.coord.TimeUnit;
038import org.apache.oozie.executor.jpa.BatchQueryExecutor;
039import org.apache.oozie.executor.jpa.BatchQueryExecutor.UpdateEntry;
040import org.apache.oozie.executor.jpa.CoordActionsActiveCountJPAExecutor;
041import org.apache.oozie.executor.jpa.CoordJobQueryExecutor;
042import org.apache.oozie.executor.jpa.CoordJobQueryExecutor.CoordJobQuery;
043import org.apache.oozie.executor.jpa.JPAExecutorException;
044import org.apache.oozie.service.ConfigurationService;
045import org.apache.oozie.service.CoordMaterializeTriggerService;
046import org.apache.oozie.service.EventHandlerService;
047import org.apache.oozie.service.JPAService;
048import org.apache.oozie.service.Service;
049import org.apache.oozie.service.Services;
050import org.apache.oozie.sla.SLAOperations;
051import org.apache.oozie.util.DateUtils;
052import org.apache.oozie.util.Instrumentation;
053import org.apache.oozie.util.LogUtils;
054import org.apache.oozie.util.ParamChecker;
055import org.apache.oozie.util.StatusUtils;
056import org.apache.oozie.util.XConfiguration;
057import org.apache.oozie.util.XmlUtils;
058import org.apache.oozie.util.db.SLADbOperations;
059import org.jdom.Element;
060import org.jdom.JDOMException;
061
062import java.io.IOException;
063import java.io.StringReader;
064import java.sql.Timestamp;
065import java.util.Calendar;
066import java.util.Date;
067import java.util.TimeZone;
068
069/**
070 * Materialize actions for specified start and end time for coordinator job.
071 */
072@SuppressWarnings("deprecation")
073public class CoordMaterializeTransitionXCommand extends MaterializeTransitionXCommand {
074
075    private JPAService jpaService = null;
076    private CoordinatorJobBean coordJob = null;
077    private String jobId = null;
078    private Date startMatdTime = null;
079    private Date endMatdTime = null;
080    private final int materializationWindow;
081    private int lastActionNumber = 1; // over-ride by DB value
082    private CoordinatorJob.Status prevStatus = null;
083
084    static final private int lookAheadWindow = ConfigurationService.getInt(CoordMaterializeTriggerService
085            .CONF_LOOKUP_INTERVAL);
086
087    /**
088     * Default MAX timeout in minutes, after which coordinator input check will timeout
089     */
090    public static final String CONF_DEFAULT_MAX_TIMEOUT = Service.CONF_PREFIX + "coord.default.max.timeout";
091
092    /**
093     * The constructor for class {@link CoordMaterializeTransitionXCommand}
094     *
095     * @param jobId coordinator job id
096     * @param materializationWindow materialization window to calculate end time
097     */
098    public CoordMaterializeTransitionXCommand(String jobId, int materializationWindow) {
099        super("coord_mater", "coord_mater", 1);
100        this.jobId = ParamChecker.notEmpty(jobId, "jobId");
101        this.materializationWindow = materializationWindow;
102    }
103
104    public CoordMaterializeTransitionXCommand(CoordinatorJobBean coordJob, int materializationWindow, Date startTime,
105                                              Date endTime) {
106        super("coord_mater", "coord_mater", 1);
107        this.jobId = ParamChecker.notEmpty(coordJob.getId(), "jobId");
108        this.materializationWindow = materializationWindow;
109        this.coordJob = coordJob;
110        this.startMatdTime = startTime;
111        this.endMatdTime = endTime;
112    }
113
114    /* (non-Javadoc)
115     * @see org.apache.oozie.command.MaterializeTransitionXCommand#transitToNext()
116     */
117    @Override
118    public void transitToNext() throws CommandException {
119    }
120
121    /* (non-Javadoc)
122     * @see org.apache.oozie.command.TransitionXCommand#updateJob()
123     */
124    @Override
125    public void updateJob() throws CommandException {
126        updateList.add(new UpdateEntry(CoordJobQuery.UPDATE_COORD_JOB_MATERIALIZE,coordJob));
127    }
128
129    /* (non-Javadoc)
130     * @see org.apache.oozie.command.MaterializeTransitionXCommand#performWrites()
131     */
132    @Override
133    public void performWrites() throws CommandException {
134        try {
135            BatchQueryExecutor.getInstance().executeBatchInsertUpdateDelete(insertList, updateList, null);
136            // register the partition related dependencies of actions
137            for (JsonBean actionBean : insertList) {
138                if (actionBean instanceof CoordinatorActionBean) {
139                    CoordinatorActionBean coordAction = (CoordinatorActionBean) actionBean;
140                    if (EventHandlerService.isEnabled()) {
141                        CoordinatorXCommand.generateEvent(coordAction, coordJob.getUser(), coordJob.getAppName(), null);
142                    }
143
144                    // TODO: time 100s should be configurable
145                    queue(new CoordActionNotificationXCommand(coordAction), 100);
146
147                    //Delay for input check = (nominal time - now)
148                    long checkDelay = coordAction.getNominalTime().getTime() - new Date().getTime();
149                    queue(new CoordActionInputCheckXCommand(coordAction.getId(), coordAction.getJobId()),
150                        Math.max(checkDelay, 0));
151
152                    if (!StringUtils.isEmpty(coordAction.getPushMissingDependencies())) {
153                        // TODO: Delay in catchup mode?
154                        queue(new CoordPushDependencyCheckXCommand(coordAction.getId(), true), 100);
155                    }
156                }
157            }
158        }
159        catch (JPAExecutorException jex) {
160            throw new CommandException(jex);
161        }
162    }
163
164    /* (non-Javadoc)
165     * @see org.apache.oozie.command.XCommand#getEntityKey()
166     */
167    @Override
168    public String getEntityKey() {
169        return this.jobId;
170    }
171
172    @Override
173    protected boolean isLockRequired() {
174        return true;
175    }
176
177    /* (non-Javadoc)
178     * @see org.apache.oozie.command.XCommand#loadState()
179     */
180    @Override
181    protected void loadState() throws CommandException {
182        jpaService = Services.get().get(JPAService.class);
183        if (jpaService == null) {
184            LOG.error(ErrorCode.E0610);
185        }
186
187        try {
188            coordJob = CoordJobQueryExecutor.getInstance().get(CoordJobQuery.GET_COORD_JOB_MATERIALIZE, jobId);
189            prevStatus = coordJob.getStatus();
190        }
191        catch (JPAExecutorException jex) {
192            throw new CommandException(jex);
193        }
194
195        // calculate start materialize and end materialize time
196        calcMatdTime();
197
198        LogUtils.setLogInfo(coordJob);
199    }
200
201    /**
202     * Calculate startMatdTime and endMatdTime from job's start time if next materialized time is null
203     *
204     * @throws CommandException thrown if failed to calculate startMatdTime and endMatdTime
205     */
206    protected void calcMatdTime() throws CommandException {
207        Timestamp startTime = coordJob.getNextMaterializedTimestamp();
208        if (startTime == null) {
209            startTime = coordJob.getStartTimestamp();
210        }
211        // calculate end time by adding materializationWindow to start time.
212        // need to convert materializationWindow from secs to milliseconds
213        long startTimeMilli = startTime.getTime();
214        long endTimeMilli = startTimeMilli + (materializationWindow * 1000);
215
216        startMatdTime = DateUtils.toDate(new Timestamp(startTimeMilli));
217        endMatdTime = DateUtils.toDate(new Timestamp(endTimeMilli));
218        endMatdTime = getMaterializationTimeForCatchUp(endMatdTime);
219        // if MaterializationWindow end time is greater than endTime
220        // for job, then set it to endTime of job
221        Date jobEndTime = coordJob.getEndTime();
222        if (endMatdTime.compareTo(jobEndTime) > 0) {
223            endMatdTime = jobEndTime;
224        }
225
226        LOG.debug("Materializing coord job id=" + jobId + ", start=" + DateUtils.formatDateOozieTZ(startMatdTime) + ", end="
227                + DateUtils.formatDateOozieTZ(endMatdTime)
228                + ", window=" + materializationWindow);
229    }
230
231    /**
232     * Get materialization for window for catch-up jobs. for current jobs,it reruns currentMatdate, For catch-up, end
233     * Mataterilized Time = startMatdTime + MatThrottling * frequency; unless LAST_ONLY execution order is set, in which
234     * case it returns now (to materialize all actions in the past)
235     *
236     * @param currentMatTime
237     * @return Date returns materialization for window for catch-up jobs
238     * @throws CommandException
239     * @throws JDOMException
240     */
241    private Date getMaterializationTimeForCatchUp(Date currentMatTime) throws CommandException {
242        if (currentMatTime.after(new Date())) {
243            return currentMatTime;
244        }
245        if (coordJob.getExecutionOrder().equals(CoordinatorJob.Execution.LAST_ONLY) ||
246                coordJob.getExecutionOrder().equals(CoordinatorJob.Execution.NONE)) {
247            return new Date();
248        }
249        final int frequency;
250        try {
251            frequency = Integer.parseInt(coordJob.getFrequency());
252        }
253        catch (final NumberFormatException e) {
254            // Cron based frequency: catching up at maximum till the coordinator job's end time,
255            // bounded also by the throttle parameter, aka the number of coordinator actions to materialize
256            return coordJob.getEndTime();
257        }
258
259        TimeZone appTz = DateUtils.getTimeZone(coordJob.getTimeZone());
260        TimeUnit freqTU = TimeUnit.valueOf(coordJob.getTimeUnitStr());
261        Calendar startInstance = Calendar.getInstance(appTz);
262        startInstance.setTime(startMatdTime);
263        Calendar endMatInstance = null;
264        Calendar previousInstance = startInstance;
265        for (int i = 1; i <= coordJob.getMatThrottling(); i++) {
266            endMatInstance = (Calendar) startInstance.clone();
267            endMatInstance.add(freqTU.getCalendarUnit(), i * frequency);
268            if (endMatInstance.getTime().compareTo(new Date()) >= 0) {
269                if (previousInstance.getTime().after(currentMatTime)) {
270                    return previousInstance.getTime();
271                }
272                else {
273                    return currentMatTime;
274                }
275            }
276            previousInstance = endMatInstance;
277        }
278        if (endMatInstance == null) {
279            return currentMatTime;
280        }
281        else {
282            return endMatInstance.getTime();
283        }
284    }
285
286    /* (non-Javadoc)
287     * @see org.apache.oozie.command.XCommand#verifyPrecondition()
288     */
289    @Override
290    protected void verifyPrecondition() throws CommandException, PreconditionException {
291        if (!(coordJob.getStatus() == CoordinatorJobBean.Status.PREP || coordJob.getStatus() == CoordinatorJobBean.Status.RUNNING
292                || coordJob.getStatus() == CoordinatorJobBean.Status.RUNNINGWITHERROR)) {
293            throw new PreconditionException(ErrorCode.E1100, "CoordMaterializeTransitionXCommand for jobId=" + jobId
294                    + " job is not in PREP or RUNNING but in " + coordJob.getStatus());
295        }
296
297        if (coordJob.isDoneMaterialization()) {
298            throw new PreconditionException(ErrorCode.E1100, "CoordMaterializeTransitionXCommand for jobId =" + jobId
299                    + " job is already materialized");
300        }
301
302        if (coordJob.getNextMaterializedTimestamp() != null
303                && coordJob.getNextMaterializedTimestamp().compareTo(coordJob.getEndTimestamp()) >= 0) {
304            throw new PreconditionException(ErrorCode.E1100, "CoordMaterializeTransitionXCommand for jobId=" + jobId
305                    + " job is already materialized");
306        }
307
308        Timestamp startTime = coordJob.getNextMaterializedTimestamp();
309        if (startTime == null) {
310            startTime = coordJob.getStartTimestamp();
311
312            if (startTime.after(new Timestamp(System.currentTimeMillis() + lookAheadWindow * 1000))) {
313                throw new PreconditionException(ErrorCode.E1100, "CoordMaterializeTransitionXCommand for jobId="
314                        + jobId + " job's start time is not reached yet - nothing to materialize");
315            }
316        }
317
318        if (coordJob.getNextMaterializedTimestamp() != null
319                && coordJob.getNextMaterializedTimestamp().after(
320                        new Timestamp(System.currentTimeMillis() + lookAheadWindow * 1000))) {
321            throw new PreconditionException(ErrorCode.E1100, "CoordMaterializeTransitionXCommand for jobId=" + jobId
322                    + " Request is for future time. Lookup time is  "
323                    + new Timestamp(System.currentTimeMillis() + lookAheadWindow * 1000) + " mat time is "
324                    + coordJob.getNextMaterializedTimestamp());
325        }
326
327        if (coordJob.getLastActionTime() != null && coordJob.getLastActionTime().compareTo(coordJob.getEndTime()) >= 0) {
328            throw new PreconditionException(ErrorCode.E1100, "ENDED Coordinator materialization for jobId = " + jobId
329                    + ", all actions have been materialized from start time = " + coordJob.getStartTime()
330                    + " to end time = " + coordJob.getEndTime() + ", job status = " + coordJob.getStatusStr());
331        }
332
333        if (coordJob.getLastActionTime() != null && coordJob.getLastActionTime().compareTo(endMatdTime) >= 0) {
334            throw new PreconditionException(ErrorCode.E1100, "ENDED Coordinator materialization for jobId = " + jobId
335                    + ", action is *already* materialized for Materialization start time = " + startMatdTime
336                    + ", materialization end time = " + endMatdTime + ", job status = " + coordJob.getStatusStr());
337        }
338
339        if (endMatdTime.after(coordJob.getEndTime())) {
340            throw new PreconditionException(ErrorCode.E1100, "ENDED Coordinator materialization for jobId = " + jobId
341                    + " materialization end time = " + endMatdTime + " surpasses coordinator job's end time = "
342                    + coordJob.getEndTime() + " job status = " + coordJob.getStatusStr());
343        }
344
345        if (coordJob.getPauseTime() != null && !startMatdTime.before(coordJob.getPauseTime())) {
346            throw new PreconditionException(ErrorCode.E1100, "ENDED Coordinator materialization for jobId = " + jobId
347                    + ", materialization start time = " + startMatdTime
348                    + " is after or equal to coordinator job's pause time = " + coordJob.getPauseTime()
349                    + ", job status = " + coordJob.getStatusStr());
350        }
351
352    }
353
354    /* (non-Javadoc)
355     * @see org.apache.oozie.command.MaterializeTransitionXCommand#materialize()
356     */
357    @Override
358    protected void materialize() throws CommandException {
359        Instrumentation.Cron cron = new Instrumentation.Cron();
360        cron.start();
361        try {
362            materializeActions(false);
363            updateJobMaterializeInfo(coordJob);
364        }
365        catch (CommandException ex) {
366            LOG.warn("Exception occurred:" + ex.getMessage() + " Making the job failed ", ex);
367            coordJob.setStatus(Job.Status.FAILED);
368            coordJob.resetPending();
369            // remove any materialized actions and slaEvents
370            insertList.clear();
371        }
372        catch (Exception e) {
373            LOG.error("Exception occurred:" + e.getMessage() + " Making the job failed ", e);
374            coordJob.setStatus(Job.Status.FAILED);
375            try {
376                CoordJobQueryExecutor.getInstance().executeUpdate(CoordJobQuery.UPDATE_COORD_JOB_MATERIALIZE, coordJob);
377            }
378            catch (JPAExecutorException jex) {
379                throw new CommandException(ErrorCode.E1011, jex);
380            }
381            throw new CommandException(ErrorCode.E1012, e.getMessage(), e);
382        } finally {
383            cron.stop();
384            instrumentation.addCron(INSTRUMENTATION_GROUP, getName() + ".materialize", cron);
385        }
386    }
387
388    /**
389     * Create action instances starting from "startMatdTime" to "endMatdTime" and store them into coord action table.
390     *
391     * @param dryrun if this is a dry run
392     * @throws Exception thrown if failed to materialize actions
393     */
394    protected String materializeActions(boolean dryrun) throws Exception {
395
396        Configuration jobConf = null;
397        try {
398            jobConf = new XConfiguration(new StringReader(coordJob.getConf()));
399        }
400        catch (IOException ioe) {
401            LOG.warn("Configuration parse error. read from DB :" + coordJob.getConf(), ioe);
402            throw new CommandException(ErrorCode.E1005, ioe.getMessage(), ioe);
403        }
404
405        String jobXml = coordJob.getJobXml();
406        Element eJob = XmlUtils.parseXml(jobXml);
407        TimeZone appTz = DateUtils.getTimeZone(coordJob.getTimeZone());
408
409        String frequency = coordJob.getFrequency();
410        TimeUnit freqTU = TimeUnit.valueOf(coordJob.getTimeUnitStr());
411        TimeUnit endOfFlag = TimeUnit.valueOf(eJob.getAttributeValue("end_of_duration"));
412        Calendar start = Calendar.getInstance(appTz);
413        start.setTime(startMatdTime);
414        DateUtils.moveToEnd(start, endOfFlag);
415        Calendar end = Calendar.getInstance(appTz);
416        end.setTime(endMatdTime);
417        lastActionNumber = coordJob.getLastActionNumber();
418        //Intentionally printing dates in their own timezone, not Oozie timezone
419        LOG.info("materialize actions for tz=" + appTz.getDisplayName() + ",\n start=" + start.getTime() + ", end="
420                + end.getTime() + ",\n timeUnit " + freqTU.getCalendarUnit() + ",\n frequency :" + frequency + ":"
421                + freqTU + ",\n lastActionNumber " + lastActionNumber);
422        // Keep the actual start time
423        Calendar origStart = Calendar.getInstance(appTz);
424        origStart.setTime(coordJob.getStartTimestamp());
425        // Move to the End of duration, if needed.
426        DateUtils.moveToEnd(origStart, endOfFlag);
427
428        StringBuilder actionStrings = new StringBuilder();
429        Date jobPauseTime = coordJob.getPauseTime();
430        Calendar pause = null;
431        if (jobPauseTime != null) {
432            pause = Calendar.getInstance(appTz);
433            pause.setTime(DateUtils.convertDateToTimestamp(jobPauseTime));
434        }
435
436        String action = null;
437        int numWaitingActions = dryrun ? 0 : jpaService.execute(new CoordActionsActiveCountJPAExecutor(coordJob.getId()));
438        int maxActionToBeCreated = coordJob.getMatThrottling() - numWaitingActions;
439        // If LAST_ONLY and all materialization is in the past, ignore maxActionsToBeCreated
440        boolean ignoreMaxActions =
441                (coordJob.getExecutionOrder().equals(CoordinatorJob.Execution.LAST_ONLY) ||
442                        coordJob.getExecutionOrder().equals(CoordinatorJob.Execution.NONE))
443                        && endMatdTime.before(new Date());
444        LOG.debug("Coordinator job :" + coordJob.getId() + ", maxActionToBeCreated :" + maxActionToBeCreated
445                + ", Mat_Throttle :" + coordJob.getMatThrottling() + ", numWaitingActions :" + numWaitingActions);
446
447        boolean isCronFrequency = false;
448
449        Calendar effStart = (Calendar) start.clone();
450        try {
451            int intFrequency = Integer.parseInt(coordJob.getFrequency());
452            effStart = (Calendar) origStart.clone();
453            effStart.add(freqTU.getCalendarUnit(), lastActionNumber * intFrequency);
454        }
455        catch (NumberFormatException e) {
456            isCronFrequency = true;
457        }
458
459        boolean firstMater = true;
460
461        end = new DaylightOffsetCalculator(startMatdTime, endMatdTime).calculate(appTz, end);
462
463        while (effStart.compareTo(end) < 0 && (ignoreMaxActions || maxActionToBeCreated-- > 0)) {
464            if (pause != null && effStart.compareTo(pause) >= 0) {
465                break;
466            }
467
468            Date nextTime = effStart.getTime();
469
470            if (isCronFrequency) {
471                if (effStart.getTime().compareTo(startMatdTime) == 0 && firstMater) {
472                    effStart.add(Calendar.MINUTE, -1);
473                    firstMater = false;
474                }
475
476                nextTime = CoordCommandUtils.getNextValidActionTimeForCronFrequency(effStart.getTime(), coordJob);
477                effStart.setTime(nextTime);
478            }
479
480            if (effStart.compareTo(end) < 0) {
481
482                if (pause != null && effStart.compareTo(pause) >= 0) {
483                    break;
484                }
485                CoordinatorActionBean actionBean = new CoordinatorActionBean();
486                lastActionNumber++;
487
488                int timeout = coordJob.getTimeout();
489                LOG.debug("Materializing action for time=" + DateUtils.formatDateOozieTZ(effStart.getTime())
490                        + ", lastactionnumber=" + lastActionNumber + " timeout=" + timeout + " minutes");
491                Date actualTime = new Date();
492                action = CoordCommandUtils.materializeOneInstance(jobId, dryrun, (Element) eJob.clone(),
493                        nextTime, actualTime, lastActionNumber, jobConf, actionBean);
494                actionBean.setTimeOut(timeout);
495                if (!dryrun) {
496                    storeToDB(actionBean, action, jobConf); // Storing to table
497
498                }
499                else {
500                    actionStrings.append("action for new instance");
501                    actionStrings.append(action);
502                }
503            }
504            else {
505                break;
506            }
507
508            if (!isCronFrequency) {
509                effStart = (Calendar) origStart.clone();
510                effStart.add(freqTU.getCalendarUnit(), lastActionNumber * Integer.parseInt(coordJob.getFrequency()));
511            }
512        }
513
514        if (isCronFrequency) {
515            if (effStart.compareTo(end) < 0 && !(ignoreMaxActions || maxActionToBeCreated-- > 0)) {
516                //Since we exceed the throttle, we need to move the nextMadtime forward
517                //to avoid creating duplicate actions
518                if (!firstMater) {
519                    effStart.setTime(CoordCommandUtils.getNextValidActionTimeForCronFrequency(effStart.getTime(), coordJob));
520                }
521            }
522        }
523
524        endMatdTime = effStart.getTime();
525
526        if (!dryrun) {
527            return action;
528        }
529        else {
530            return actionStrings.toString();
531        }
532    }
533
534    private void storeToDB(CoordinatorActionBean actionBean, String actionXml, Configuration jobConf) throws Exception {
535        LOG.debug("In storeToDB() coord action id = " + actionBean.getId() + ", size of actionXml = "
536                + actionXml.length());
537        actionBean.setActionXml(actionXml);
538        insertList.add(actionBean);
539        writeActionSlaRegistration(actionXml, actionBean, jobConf);
540    }
541
542    private void writeActionSlaRegistration(String actionXml, CoordinatorActionBean actionBean, Configuration jobConf)
543            throws Exception {
544        Element eAction = XmlUtils.parseXml(actionXml);
545        Element eSla = eAction.getChild("action", eAction.getNamespace()).getChild("info", eAction.getNamespace("sla"));
546                SLAEventBean slaEvent = SLADbOperations.createSlaRegistrationEvent(eSla, actionBean.getId(),
547                                 SlaAppType.COORDINATOR_ACTION, coordJob.getUser(), coordJob.getGroup(), LOG);
548                         if (slaEvent != null) {
549            insertList.add(slaEvent);
550        }
551        // inserting into new table also
552        SLAOperations.createSlaRegistrationEvent(eSla, actionBean.getId(), actionBean.getJobId(),
553                AppType.COORDINATOR_ACTION, coordJob.getUser(), coordJob.getAppName(), LOG, false,
554                CoordUtils.isSlaAlertDisabled(actionBean, coordJob.getAppName(), jobConf));
555    }
556
557    private void updateJobMaterializeInfo(CoordinatorJobBean job) throws CommandException {
558        job.setLastActionTime(endMatdTime);
559        job.setLastActionNumber(lastActionNumber);
560        // if the job endtime == action endtime, we don't need to materialize this job anymore
561        Date jobEndTime = job.getEndTime();
562
563
564        if (job.getStatus() == CoordinatorJob.Status.PREP){
565            LOG.info("[" + job.getId() + "]: Update status from " + job.getStatus() + " to RUNNING");
566            job.setStatus(Job.Status.RUNNING);
567        }
568        job.setPending();
569        Calendar end = Calendar.getInstance();
570        end.setTime(jobEndTime);
571
572        end = calculateEndTimeWithDSTOffset(end);
573
574        if (end.getTime().compareTo(endMatdTime) <= 0) {
575            LOG.info("[" + job.getId() + "]: all actions have been materialized, set pending to true");
576            // set doneMaterialization to true when materialization is done
577            job.setDoneMaterialization();
578        }
579        job.setStatus(StatusUtils.getStatus(job));
580        LOG.info("Coord Job status updated to = " + job.getStatus());
581        job.setNextMaterializedTime(endMatdTime);
582    }
583
584    private Calendar calculateEndTimeWithDSTOffset(final Calendar endTime) {
585        final TimeZone appTz = DateUtils.getTimeZone(coordJob.getTimeZone());
586        final Calendar start = Calendar.getInstance(appTz);
587        start.setTime(startMatdTime);
588
589        return new DaylightOffsetCalculator(startMatdTime, endMatdTime).calculate(appTz, endTime);
590    }
591
592    /* (non-Javadoc)
593     * @see org.apache.oozie.command.XCommand#getKey()
594     */
595    @Override
596    public String getKey() {
597        return getName() + "_" + jobId;
598    }
599
600    /* (non-Javadoc)
601     * @see org.apache.oozie.command.TransitionXCommand#notifyParent()
602     */
603    @Override
604    public void notifyParent() throws CommandException {
605        // update bundle action only when status changes in coord job
606        if (this.coordJob.getBundleId() != null) {
607            if (!prevStatus.equals(coordJob.getStatus())) {
608                BundleStatusUpdateXCommand bundleStatusUpdate = new BundleStatusUpdateXCommand(coordJob, prevStatus);
609                bundleStatusUpdate.call();
610            }
611        }
612    }
613}