Repository: oozie
Updated Branches:
  refs/heads/master 885676214 -> f6b3f0e8c


OOZIE-1703 User should be able to set coord end-time before start time (puru 
via rohini)


Project: http://git-wip-us.apache.org/repos/asf/oozie/repo
Commit: http://git-wip-us.apache.org/repos/asf/oozie/commit/f6b3f0e8
Tree: http://git-wip-us.apache.org/repos/asf/oozie/tree/f6b3f0e8
Diff: http://git-wip-us.apache.org/repos/asf/oozie/diff/f6b3f0e8

Branch: refs/heads/master
Commit: f6b3f0e8c38e185b34bc2cd3b4f16533cfa505fc
Parents: 8856762
Author: Rohini Palaniswamy <[email protected]>
Authored: Tue Apr 15 10:25:22 2014 -0700
Committer: Rohini Palaniswamy <[email protected]>
Committed: Tue Apr 15 10:25:22 2014 -0700

----------------------------------------------------------------------
 .../command/coord/CoordChangeXCommand.java      | 170 ++++++++---------
 .../TestCoordActionInputCheckXCommand.java      |   2 +-
 .../command/coord/TestCoordChangeXCommand.java  | 181 +++++++++++++++----
 .../org/apache/oozie/test/XDataTestCase.java    |  43 ++++-
 docs/src/site/twiki/DG_CommandLineTool.twiki    |  22 ++-
 release-log.txt                                 |   1 +
 6 files changed, 273 insertions(+), 146 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/core/src/main/java/org/apache/oozie/command/coord/CoordChangeXCommand.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/oozie/command/coord/CoordChangeXCommand.java 
b/core/src/main/java/org/apache/oozie/command/coord/CoordChangeXCommand.java
index 5ecd5d5..fb31e9a 100644
--- a/core/src/main/java/org/apache/oozie/command/coord/CoordChangeXCommand.java
+++ b/core/src/main/java/org/apache/oozie/command/coord/CoordChangeXCommand.java
@@ -18,14 +18,12 @@
 package org.apache.oozie.command.coord;
 
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-
 import org.apache.commons.lang.StringUtils;
 import org.apache.oozie.CoordinatorActionBean;
 import org.apache.oozie.CoordinatorJobBean;
@@ -34,13 +32,11 @@ import org.apache.oozie.XException;
 import org.apache.oozie.client.CoordinatorAction;
 import org.apache.oozie.client.CoordinatorJob;
 import org.apache.oozie.client.Job;
-import org.apache.oozie.client.Job.Status;
 import org.apache.oozie.client.OozieClient;
 import org.apache.oozie.client.rest.JsonBean;
 import org.apache.oozie.command.CommandException;
 import org.apache.oozie.command.PreconditionException;
 import org.apache.oozie.command.bundle.BundleStatusUpdateXCommand;
-import org.apache.oozie.coord.TimeUnit;
 import org.apache.oozie.executor.jpa.CoordActionGetJPAExecutor;
 import 
org.apache.oozie.executor.jpa.CoordJobGetActionByActionNumberJPAExecutor;
 import org.apache.oozie.executor.jpa.CoordJobGetJPAExecutor;
@@ -170,21 +166,6 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
     }
 
     /**
-     * Returns the actual last action time(one instance before 
coordJob.lastActionTime)
-     * @return Date - last action time if coordJob.getLastActionTime() is not 
null, null otherwise
-     */
-    private Date getLastActionTime() {
-        if(coordJob.getLastActionTime() == null)
-            return null;
-
-        Calendar d = 
Calendar.getInstance(DateUtils.getTimeZone(coordJob.getTimeZone()));
-        d.setTime(coordJob.getLastActionTime());
-        TimeUnit timeUnit = TimeUnit.valueOf(coordJob.getTimeUnitStr());
-        d.add(timeUnit.getCalendarUnit(), 
-Integer.valueOf(coordJob.getFrequency()));
-        return d.getTime();
-    }
-
-    /**
      * Check if new end time is valid.
      *
      * @param coordJob coordinator job id.
@@ -192,21 +173,7 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
      * @throws CommandException thrown if new end time is not valid.
      */
     private void checkEndTime(CoordinatorJobBean coordJob, Date newEndTime) 
throws CommandException {
-        // New endTime cannot be before coordinator job's start time.
-        Date startTime = coordJob.getStartTime();
-        if (newEndTime.before(startTime)) {
-            throw new CommandException(ErrorCode.E1015, newEndTime, "cannot be 
before coordinator job's start time ["
-                    + startTime + "]");
-        }
-
-        // New endTime cannot be before coordinator job's last action time.
-        Date lastActionTime = getLastActionTime();
-        if (lastActionTime != null) {
-            if (!newEndTime.after(lastActionTime)) {
-                throw new CommandException(ErrorCode.E1015, newEndTime,
-                        "must be after coordinator job's last action time [" + 
lastActionTime + "]");
-            }
-        }
+        //It's ok to set end date before start date.
     }
 
     /**
@@ -220,8 +187,7 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
     private void checkPauseTime(CoordinatorJobBean coordJob, Date newPauseTime)
             throws CommandException {
         // New pauseTime has to be a non-past time.
-        Date d = new Date();
-        if (newPauseTime.before(d)) {
+        if (newPauseTime.before(coordJob.getStartTime())) {
             throw new CommandException(ErrorCode.E1015, newPauseTime, "must be 
a non-past time");
         }
     }
@@ -253,39 +219,24 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
      *
      * @param coordJob coordinator job
      * @param newPauseTime new pause time
+     * @throws JPAExecutorException, CommandException
      */
-    private void processLookaheadActions(CoordinatorJobBean coordJob, Date 
newPauseTime) throws CommandException {
-        Date lastActionTime = coordJob.getLastActionTime();
+    private void processLookaheadActions(CoordinatorJobBean coordJob, Date 
newTime) throws CommandException,
+            JPAExecutorException {
+        int lastActionNumber = coordJob.getLastActionNumber();
+        Date  lastActionTime = null;
+        Date  tempDate = null;
+
+        while ((tempDate = deleteAction(lastActionNumber, newTime)) != null) {
+            lastActionNumber--;
+            lastActionTime = tempDate;
+        }
         if (lastActionTime != null) {
-            // d is the real last action time.
-            Calendar d = 
Calendar.getInstance(DateUtils.getTimeZone(coordJob.getTimeZone()));
-            d.setTime(getLastActionTime());
-            TimeUnit timeUnit = TimeUnit.valueOf(coordJob.getTimeUnitStr());
-
-            int lastActionNumber = coordJob.getLastActionNumber();
-
-            boolean hasChanged = false;
-            while (true) {
-                if (!newPauseTime.after(d.getTime())) {
-                    deleteAction(lastActionNumber);
-                    d.add(timeUnit.getCalendarUnit(), 
-Integer.valueOf(coordJob.getFrequency()));
-                    lastActionNumber = lastActionNumber - 1;
-
-                    hasChanged = true;
-                }
-                else {
-                    break;
-                }
-            }
-
-            if (hasChanged == true) {
-                coordJob.setLastActionNumber(lastActionNumber);
-                d.add(timeUnit.getCalendarUnit(), 
Integer.valueOf(coordJob.getFrequency()));
-                Date d1 = d.getTime();
-                coordJob.setLastActionTime(d1);
-                coordJob.setNextMaterializedTime(d1);
-                coordJob.resetDoneMaterialization();
-            }
+            LOG.debug("New pause/end date is : " + newTime + " and last action 
number is : " + lastActionNumber);
+            coordJob.setLastActionNumber(lastActionNumber);
+            coordJob.setLastActionTime(lastActionTime);
+            coordJob.setNextMaterializedTime(lastActionTime);
+            coordJob.resetDoneMaterialization();
         }
     }
 
@@ -294,31 +245,42 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
      *
      * @param actionNum coordinator action number
      */
-    private void deleteAction(int actionNum) throws CommandException {
+    private Date deleteAction(int actionNum, Date afterDate) throws 
CommandException {
         try {
+            if (actionNum <= 0) {
+                return null;
+            }
+
             String actionId = jpaService.execute(new 
CoordJobGetActionByActionNumberJPAExecutor(jobId, actionNum));
             CoordinatorActionBean bean = jpaService.execute(new 
CoordActionGetJPAExecutor(actionId));
-            // delete SLA registration entry (if any) for action
-            if (SLAService.isEnabled()) {
-                
Services.get().get(SLAService.class).removeRegistration(actionId);
-            }
-            SLARegistrationBean slaReg = jpaService.execute(new 
SLARegistrationGetJPAExecutor(actionId));
-            if (slaReg != null) {
-                LOG.debug("Deleting registration bean corresponding to action 
" + slaReg.getId());
-                deleteList.add(slaReg);
-            }
-            SLASummaryBean slaSummaryBean = jpaService.execute(new 
SLASummaryGetJPAExecutor(actionId));
-            if (slaSummaryBean != null) {
-                LOG.debug("Deleting summary bean corresponding to action " + 
slaSummaryBean.getId());
-                deleteList.add(slaSummaryBean);
-            }
-            if (bean.getStatus() == CoordinatorAction.Status.WAITING
-                    || bean.getStatus() == CoordinatorAction.Status.READY) {
-                deleteList.add(bean);
+            if (afterDate.compareTo(bean.getNominalTime()) <= 0) {
+                // delete SLA registration entry (if any) for action
+                if (SLAService.isEnabled()) {
+                    
Services.get().get(SLAService.class).removeRegistration(actionId);
+                }
+                SLARegistrationBean slaReg = jpaService.execute(new 
SLARegistrationGetJPAExecutor(actionId));
+                if (slaReg != null) {
+                    LOG.debug("Deleting registration bean corresponding to 
action " + slaReg.getId());
+                    deleteList.add(slaReg);
+                }
+                SLASummaryBean slaSummaryBean = jpaService.execute(new 
SLASummaryGetJPAExecutor(actionId));
+                if (slaSummaryBean != null) {
+                    LOG.debug("Deleting summary bean corresponding to action " 
+ slaSummaryBean.getId());
+                    deleteList.add(slaSummaryBean);
+                }
+                if (bean.getStatus() == CoordinatorAction.Status.WAITING
+                        || bean.getStatus() == CoordinatorAction.Status.READY) 
{
+                    deleteList.add(bean);
+                }
+                else {
+                    throw new CommandException(ErrorCode.E1022, bean.getId());
+                }
+                return bean.getNominalTime();
             }
             else {
-                throw new CommandException(ErrorCode.E1022, bean.getId());
+                return null;
             }
+
         }
         catch (JPAExecutorException e) {
             throw new CommandException(e);
@@ -374,19 +336,33 @@ public class CoordChangeXCommand extends 
CoordinatorXCommand<Void> {
                         && 
coordJob.getNextMaterializedTime().after(newEndTime);
                 if (!dontChange) {
                     coordJob.setEndTime(newEndTime);
-                    if (coordJob.getStatus() == 
CoordinatorJob.Status.SUCCEEDED) {
-                        coordJob.setStatus(CoordinatorJob.Status.RUNNING);
+                    // OOZIE-1703, we should SUCCEEDED the coord, if it's in 
PREP and new endtime is before start time
+                    if (coordJob.getStatus() == CoordinatorJob.Status.PREP && 
coordJob.getStartTime().after(newEndTime)) {
+                        LOG.info("Changing coord status to SUCCEEDED, because 
it's in PREP and new end time is before start time. "
+                                + "Startime is " + coordJob.getStartTime() + " 
and new end time is " + newEndTime);
+                        coordJob.setStatus(CoordinatorJob.Status.SUCCEEDED);
+                        coordJob.setDoneMaterialization();
+                        coordJob.resetPending();
                     }
-                    if (coordJob.getStatus() == 
CoordinatorJob.Status.DONEWITHERROR
-                            || coordJob.getStatus() == 
CoordinatorJob.Status.FAILED) {
-                        // Check for backward compatibility for Oozie versions 
(3.2 and before)
-                        // when RUNNINGWITHERROR, SUSPENDEDWITHERROR and
-                        // PAUSEDWITHERROR is not supported
-                        coordJob.setStatus(StatusUtils
-                                
.getStatusIfBackwardSupportTrue(CoordinatorJob.Status.RUNNINGWITHERROR));
+                    else {
+                        //move it to running iff neendtime is after starttime.
+                        if (newEndTime.after(coordJob.getStartTime())) {
+                            if (coordJob.getStatus() == 
CoordinatorJob.Status.SUCCEEDED) {
+                                
coordJob.setStatus(CoordinatorJob.Status.RUNNING);
+                            }
+                            if (coordJob.getStatus() == 
CoordinatorJob.Status.DONEWITHERROR
+                                    || coordJob.getStatus() == 
CoordinatorJob.Status.FAILED) {
+                                // Check for backward compatibility for Oozie 
versions (3.2 and before)
+                                // when RUNNINGWITHERROR, SUSPENDEDWITHERROR 
and
+                                // PAUSEDWITHERROR is not supported
+                                coordJob.setStatus(StatusUtils
+                                        
.getStatusIfBackwardSupportTrue(CoordinatorJob.Status.RUNNINGWITHERROR));
+                            }
+                            coordJob.setPending();
+                            coordJob.resetDoneMaterialization();
+                            processLookaheadActions(coordJob, newEndTime);
+                        }
                     }
-                    coordJob.setPending();
-                    coordJob.resetDoneMaterialization();
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/core/src/test/java/org/apache/oozie/command/coord/TestCoordActionInputCheckXCommand.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/oozie/command/coord/TestCoordActionInputCheckXCommand.java
 
b/core/src/test/java/org/apache/oozie/command/coord/TestCoordActionInputCheckXCommand.java
index d1c5752..ebf5081 100644
--- 
a/core/src/test/java/org/apache/oozie/command/coord/TestCoordActionInputCheckXCommand.java
+++ 
b/core/src/test/java/org/apache/oozie/command/coord/TestCoordActionInputCheckXCommand.java
@@ -809,7 +809,7 @@ public class TestCoordActionInputCheckXCommand extends 
XDataTestCase {
 
     protected CoordinatorActionBean 
addRecordToCoordActionTableForWaiting(String jobId, int actionNum,
             CoordinatorAction.Status status, String resourceXmlName) throws 
Exception {
-        CoordinatorActionBean action = createCoordAction(jobId, actionNum, 
status, resourceXmlName, 0, TZ);
+        CoordinatorActionBean action = createCoordAction(jobId, actionNum, 
status, resourceXmlName, 0, TZ, null);
         String missDeps = getTestCaseFileUri("2009/01/29/_SUCCESS") + "#"
                 + getTestCaseFileUri("2009/01/22/_SUCCESS") + "#"
                 + getTestCaseFileUri("2009/01/15/_SUCCESS") + "#"

http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/core/src/test/java/org/apache/oozie/command/coord/TestCoordChangeXCommand.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/oozie/command/coord/TestCoordChangeXCommand.java
 
b/core/src/test/java/org/apache/oozie/command/coord/TestCoordChangeXCommand.java
index fdc0a55..327ec90 100644
--- 
a/core/src/test/java/org/apache/oozie/command/coord/TestCoordChangeXCommand.java
+++ 
b/core/src/test/java/org/apache/oozie/command/coord/TestCoordChangeXCommand.java
@@ -196,12 +196,9 @@ public class TestCoordChangeXCommand extends XDataTestCase 
{
 
         try {
             new CoordChangeXCommand(jobId, "endtime=1900-12-20T05:00Z").call();
-            fail("Should not reach here.");
         }
         catch (CommandException ex) {
-            if (ex.getErrorCode() != ErrorCode.E1015) {
-                fail("Error code should be E1015.");
-            }
+            fail("Should not throw exception");
         }
 
         try {
@@ -215,7 +212,7 @@ public class TestCoordChangeXCommand extends XDataTestCase {
         }
 
         try {
-            new CoordChangeXCommand(jobId, 
"pausetime=2009-02-01T01:03Z").call();
+            new CoordChangeXCommand(jobId, 
"pausetime=2009-02-01T00:00Z").call();
             fail("Should not reach here.");
         }
         catch (CommandException ex) {
@@ -235,7 +232,7 @@ public class TestCoordChangeXCommand extends XDataTestCase {
         }
 
         try {
-            new CoordChangeXCommand(jobId, 
"pausetime=2009-02-01T01:08Z").call();
+            new CoordChangeXCommand(jobId, 
"pausetime=2009-02-01T00:08Z").call();
             fail("Should not reach here.");
         }
         catch (CommandException ex) {
@@ -398,14 +395,123 @@ public class TestCoordChangeXCommand extends 
XDataTestCase {
         assertTrue(coordJob.isDoneMaterialization());
 
         String newEndTime = convertDateToString(startTime.getTime() + 20 * 60 
* 1000);
-
         new CoordChangeXCommand(coordJob.getId(), "endtime=" + 
newEndTime).call();
-
         coordJob = jpaService.execute(coordGetCmd);
         assertFalse(Job.Status.RUNNING == coordJob.getStatus());
         assertFalse(coordJob.isPending());
         assertTrue(coordJob.isDoneMaterialization());
+    }
+
+    // Testcase to test deletion of lookahead action in case of end-date change
+    public void testCoordChangeEndTimeDeleteAction() throws Exception {
+        Date startTime = DateUtils.parseDateOozieTZ("2013-08-01T00:00Z");
+        Date endTime = DateUtils.parseDateOozieTZ("2013-08-01T05:00Z");
+        Date changeEndTime = DateUtils.parseDateOozieTZ("2013-08-01T02:00Z");
+        String endTimeChangeStr = "endtime=" + 
DateUtils.formatDateOozieTZ(changeEndTime);
+        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.RUNNING,
+                startTime, endTime, endTime, true, false, 4);
+        addRecordToCoordActionTable(job.getId(), 1, 
CoordinatorAction.Status.SUCCEEDED, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 2, 
CoordinatorAction.Status.RUNNING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T01:00Z"));
+        addRecordToCoordActionTable(job.getId(), 3, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T02:00Z"));
+        addRecordToCoordActionTable(job.getId(), 4, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T03:00Z"));
+        JPAService jpaService = Services.get().get(JPAService.class);
+        new CoordChangeXCommand(job.getId(), endTimeChangeStr).call();
+        CoordJobGetJPAExecutor coordGetCmd = new 
CoordJobGetJPAExecutor(job.getId());
+        CoordinatorJobBean coordJob = jpaService.execute(coordGetCmd);
+        assertEquals(coordJob.getEndTime(), changeEndTime);
+        assertEquals(Job.Status.RUNNING, coordJob.getStatus());
+        assertEquals(2, coordJob.getLastActionNumber());
+        try {
+            jpaService.execute(new 
CoordJobGetActionByActionNumberJPAExecutor(job.getId(), 2));
+        }
+        catch (JPAExecutorException jpae) {
+            fail(" Action should be there");
+        }
+        try {
+            jpaService.execute(new 
CoordJobGetActionByActionNumberJPAExecutor(job.getId(), 3));
+            fail("Expected to fail as action 3 should have been deleted");
+        }
+        catch (JPAExecutorException jpae) {
+            assertEquals(ErrorCode.E0603, jpae.getErrorCode());
+        }
+        assertEquals(DateUtils.parseDateOozieTZ("2013-08-01T02:00Z"), 
coordJob.getNextMaterializedTime());
+        assertEquals(DateUtils.parseDateOozieTZ("2013-08-01T02:00Z"), 
coordJob.getLastActionTime());
+    }
+
+    // Testcase to test deletion of lookahead action in case of end-date change
+    // Added one more test case to test processLookaheadActions with day 
frequency and SUSPENDED status.
+    public void testProcessLookaheadActions() throws Exception {
+        Date startTime = DateUtils.parseDateOozieTZ("2013-08-01T00:00Z");
+        Date endTime = DateUtils.parseDateOozieTZ("2013-08-29T00:00Z");
+
+        Date changeEndTime = DateUtils.parseDateOozieTZ("2013-08-05T00:00Z");
+        String endTimeChangeStr = "endtime=" + 
DateUtils.formatDateOozieTZ(changeEndTime);
+        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.SUSPENDED,
+                startTime, endTime, endTime, true, false, 6);
+        addRecordToCoordActionTable(job.getId(), 1, 
CoordinatorAction.Status.SUCCEEDED, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 2, 
CoordinatorAction.Status.RUNNING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-02T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 3, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-03T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 4, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-04T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 5, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-05T00:00Z"));
+        addRecordToCoordActionTable(job.getId(), 6, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-06T00:00Z"));
+
+        job.setFrequency("1");
+        job.setTimeUnit(Timeunit.DAY);
+        
CoordJobQueryExecutor.getInstance().executeUpdate(CoordJobQuery.UPDATE_COORD_JOB,
 job);
+
+        JPAService jpaService = Services.get().get(JPAService.class);
+        new CoordChangeXCommand(job.getId(), endTimeChangeStr).call();
+
+        CoordJobGetJPAExecutor coordGetCmd = new 
CoordJobGetJPAExecutor(job.getId());
+        CoordinatorJobBean coordJob = jpaService.execute(coordGetCmd);
+        assertEquals(coordJob.getEndTime(), changeEndTime);
+        assertEquals(Job.Status.SUSPENDED, coordJob.getStatus());
+        assertEquals(4, coordJob.getLastActionNumber());
+        assertEquals(DateUtils.parseDateOozieTZ("2013-08-05T00:00Z"), 
coordJob.getNextMaterializedTime());
+        assertEquals(DateUtils.parseDateOozieTZ("2013-08-05T00:00Z"), 
coordJob.getLastActionTime());
+        assertEquals(changeEndTime, coordJob.getEndTime());
+    }
+
+    // Testcase to check status for coord whose enddate is set before 
startdate.
+    public void testCoordChangeEndTimeBeforeStart() throws Exception {
+        Date start = new Date();
+        Date end = new Date(start.getTime() + (4 * 60 * 60 * 1000)); // 4 hrs
+        Date endTime = new Date(start.getTime() - 3000);
 
+        String endTimeChangeStr = "endtime=" + 
DateUtils.formatDateOozieTZ(endTime);
+        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.PREP, start, end,
+                end, true, false, 0);
+
+        JPAService jpaService = Services.get().get(JPAService.class);
+        CoordJobGetJPAExecutor coordGetCmd = new 
CoordJobGetJPAExecutor(job.getId());
+        CoordinatorJobBean coordJob = jpaService.execute(coordGetCmd);
+
+        assertEquals(Job.Status.PREP, coordJob.getStatus());
+        assertEquals(0, coordJob.getLastActionNumber());
+
+        new CoordChangeXCommand(job.getId(), endTimeChangeStr).call();
+
+        coordGetCmd = new CoordJobGetJPAExecutor(job.getId());
+        coordJob = jpaService.execute(coordGetCmd);
+        assertEquals(DateUtils.formatDateOozieTZ(coordJob.getEndTime()), 
DateUtils.formatDateOozieTZ(endTime));
+        assertEquals(Job.Status.SUCCEEDED, coordJob.getStatus());
+        Date newEndTime = new Date(start.getTime() - 2000);
+        endTimeChangeStr = "endtime=" + 
DateUtils.formatDateOozieTZ(newEndTime);
+        new CoordChangeXCommand(job.getId(), endTimeChangeStr).call();
+        coordGetCmd = new CoordJobGetJPAExecutor(job.getId());
+        coordJob = jpaService.execute(coordGetCmd);
+        assertEquals(DateUtils.formatDateOozieTZ(coordJob.getEndTime()), 
DateUtils.formatDateOozieTZ(newEndTime));
+        assertEquals(Job.Status.SUCCEEDED, coordJob.getStatus());
     }
 
     /**
@@ -442,20 +548,21 @@ public class TestCoordChangeXCommand extends 
XDataTestCase {
      * @throws Exception
      */
     public void testCoordChangePauseTime() throws Exception {
-        Date start = new Date();
-        Date end = new Date(start.getTime() + (4 * 60 * 60 * 1000));    //4 hrs
-        Date pauseTime = new Date(start.getTime() + (2 * 60 * 60 * 1000));  
//2 hrs
+        Date startTime = DateUtils.parseDateOozieTZ("2013-08-01T00:00Z");
+        Date endTime = DateUtils.parseDateOozieTZ("2013-08-01T04:59Z");
+
+        Date pauseTime = new Date(startTime.getTime() + (3 * 60 * 60 * 1000)); 
 //2 hrs
         String pauseTimeChangeStr = "pausetime=" + 
DateUtils.formatDateOozieTZ(pauseTime);
-        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.RUNNING, start,
-                end, end, true, false, 4);
+        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.RUNNING, 
startTime,
+                endTime, endTime, true, false, 4);
         CoordinatorActionBean ca1 = addRecordToCoordActionTable(job.getId(), 
1, CoordinatorAction.Status.SUCCEEDED,
                 "coord-action-get.xml", 0);
         CoordinatorActionBean ca2 = addRecordToCoordActionTable(job.getId(), 
2, CoordinatorAction.Status.SUCCEEDED,
-                "coord-action-get.xml", 0);
+                "coord-action-get.xml", 0, 
DateUtils.parseDateOozieTZ("2013-08-01T02:00Z"));
         CoordinatorActionBean ca3 = addRecordToCoordActionTable(job.getId(), 
3, CoordinatorAction.Status.WAITING,
-                "coord-action-get.xml", 0);
+                "coord-action-get.xml", 0, 
DateUtils.parseDateOozieTZ("2013-08-01T03:00Z"));
         CoordinatorActionBean ca4 = addRecordToCoordActionTable(job.getId(), 
4, CoordinatorAction.Status.WAITING,
-                "coord-action-get.xml", 0);
+                "coord-action-get.xml", 0, 
DateUtils.parseDateOozieTZ("2013-08-01T04:00Z"));
 
         SLARegistrationBean slaRegBean1 = new SLARegistrationBean();
         slaRegBean1.setId(ca1.getId());
@@ -493,7 +600,6 @@ public class TestCoordChangeXCommand extends XDataTestCase {
         }
         catch (JPAExecutorException jpae) {
             assertEquals(ErrorCode.E0603, jpae.getErrorCode());
-            jpae.printStackTrace();
         }
 
         try {
@@ -502,7 +608,6 @@ public class TestCoordChangeXCommand extends XDataTestCase {
         }
         catch (JPAExecutorException jpae) {
             assertEquals(ErrorCode.E0603, jpae.getErrorCode());
-            jpae.printStackTrace();
         }
 
         slaRegBean1 = jpaService.execute(new 
SLARegistrationGetJPAExecutor(slaRegBean1.getId()));
@@ -519,24 +624,37 @@ public class TestCoordChangeXCommand extends 
XDataTestCase {
         assertNotNull(slaSummaryBean1);
     }
 
-    //Checks that RUNNING coord action is not deleted
-    public void testCoordActionDelete() throws Exception {
-        Date start = new Date();
-        Date end = new Date(start.getTime() + (4 * 60 * 60 * 1000));    //4 hrs
-        Date pauseTime = new Date(start.getTime() + (2 * 60 * 60 * 1000));  
//2 hrs
+    // Checks that RUNNING coord action is not deleted
+    public void testChangeTimeDeleteRunning() throws Exception {
+        Date startTime = DateUtils.parseDateOozieTZ("2013-08-01T00:00Z");
+        Date endTime = DateUtils.parseDateOozieTZ("2013-08-01T04:59Z");
+        Date pauseTime = new Date(startTime.getTime() + (2 * 60 * 60 * 1000)); 
// 2 hrs
         String pauseTimeChangeStr = "pausetime=" + 
DateUtils.formatDateOozieTZ(pauseTime);
-        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.RUNNING, start,
-                end, end, true, false, 4);
+        final CoordinatorJobBean job = 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status.RUNNING,
+                startTime, endTime, endTime, true, false, 4);
         addRecordToCoordActionTable(job.getId(), 1, 
CoordinatorAction.Status.SUCCEEDED, "coord-action-get.xml", 0);
-        addRecordToCoordActionTable(job.getId(), 2, 
CoordinatorAction.Status.SUCCEEDED, "coord-action-get.xml", 0);
-        addRecordToCoordActionTable(job.getId(), 3, 
CoordinatorAction.Status.RUNNING, "coord-action-get.xml", 0);
-        addRecordToCoordActionTable(job.getId(), 4, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0);
-
+        addRecordToCoordActionTable(job.getId(), 2, 
CoordinatorAction.Status.SUCCEEDED, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T02:00Z"));
+        addRecordToCoordActionTable(job.getId(), 3, 
CoordinatorAction.Status.RUNNING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T03:00Z"));
+        addRecordToCoordActionTable(job.getId(), 4, 
CoordinatorAction.Status.WAITING, "coord-action-get.xml", 0,
+                DateUtils.parseDateOozieTZ("2013-08-01T04:00Z"));
         try {
             new CoordChangeXCommand(job.getId(), pauseTimeChangeStr).call();
             fail("Should not reach here.");
-        } catch(CommandException e) {
-            if(e.getErrorCode() != ErrorCode.E1022) {
+        }
+        catch (CommandException e) {
+            if (e.getErrorCode() != ErrorCode.E1022) {
+                fail("Error code should be E1022");
+            }
+        }
+        String endTimeChangeStr = "endtime=" + 
DateUtils.formatDateOozieTZ(pauseTime);
+        try {
+            new CoordChangeXCommand(job.getId(), endTimeChangeStr).call();
+            fail("Should not reach here.");
+        }
+        catch (CommandException e) {
+            if (e.getErrorCode() != ErrorCode.E1022) {
                 fail("Error code should be E1022");
             }
         }
@@ -640,7 +758,6 @@ public class TestCoordChangeXCommand extends XDataTestCase {
             assertTrue(e.getMessage().contains("Cannot change a killed 
coordinator job"));
         }
     }
-
     protected CoordinatorJobBean 
addRecordToCoordJobTableForPauseTimeTest(CoordinatorJob.Status status, Date 
start,
             Date end, Date lastActionTime, boolean pending, boolean doneMatd, 
int lastActionNum) throws Exception {
         CoordinatorJobBean coordJob = createCoordJob(status, start, end, 
pending, doneMatd, lastActionNum);

http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/core/src/test/java/org/apache/oozie/test/XDataTestCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/oozie/test/XDataTestCase.java 
b/core/src/test/java/org/apache/oozie/test/XDataTestCase.java
index 423944a..4f500da 100644
--- a/core/src/test/java/org/apache/oozie/test/XDataTestCase.java
+++ b/core/src/test/java/org/apache/oozie/test/XDataTestCase.java
@@ -512,7 +512,26 @@ public abstract class XDataTestCase extends XHCatTestCase {
      */
     protected CoordinatorActionBean addRecordToCoordActionTable(String jobId, 
int actionNum,
             CoordinatorAction.Status status, String resourceXmlName, int 
pending) throws Exception {
-        CoordinatorActionBean action = createCoordAction(jobId, actionNum, 
status, resourceXmlName, pending);
+        return addRecordToCoordActionTable(jobId, actionNum, status, 
resourceXmlName, pending, null);
+    }
+
+    /**
+     * Insert coord action for testing.
+     *
+     * @param jobId coord job id
+     * @param actionNum action number
+     * @param status coord action status
+     * @param resourceXmlName xml file name
+     * @param pending pending counter
+     * @param action nominal time
+     * @return coord action bean
+     * @throws Exception thrown if unable to create coord action bean
+     */
+    protected CoordinatorActionBean addRecordToCoordActionTable(String jobId, 
int actionNum,
+            CoordinatorAction.Status status, String resourceXmlName, int 
pending, Date actionNominalTime)
+            throws Exception {
+        CoordinatorActionBean action = createCoordAction(jobId, actionNum, 
status, resourceXmlName, pending,
+                actionNominalTime);
 
         try {
             JPAService jpaService = Services.get().get(JPAService.class);
@@ -569,9 +588,15 @@ public abstract class XDataTestCase extends XHCatTestCase {
 
     protected CoordinatorActionBean createCoordAction(String jobId, int 
actionNum, CoordinatorAction.Status status,
             String resourceXmlName, int pending) throws Exception {
-        return createCoordAction(jobId, actionNum, status, resourceXmlName, 
pending, "Z");
+        return createCoordAction(jobId, actionNum, status, resourceXmlName, 
pending, "Z", null);
     }
 
+    protected CoordinatorActionBean createCoordAction(String jobId, int 
actionNum, CoordinatorAction.Status status,
+            String resourceXmlName, int pending, Date actionNominalTime) 
throws Exception {
+        return createCoordAction(jobId, actionNum, status, resourceXmlName, 
pending, "Z", actionNominalTime);
+    }
+
+
     /**
      * Create coord action bean
      *
@@ -584,12 +609,11 @@ public abstract class XDataTestCase extends XHCatTestCase 
{
      * @throws Exception thrown if unable to create coord action bean
      */
     protected CoordinatorActionBean createCoordAction(String jobId, int 
actionNum, CoordinatorAction.Status status,
-            String resourceXmlName, int pending, String oozieTimeZoneMask) 
throws Exception {
+            String resourceXmlName, int pending, String oozieTimeZoneMask, 
Date actionNominalTime) throws Exception {
         String actionId = 
Services.get().get(UUIDService.class).generateChildId(jobId, actionNum + "");
         Path appPath = new Path(getFsTestCaseDir(), "coord");
         String actionXml = getCoordActionXml(appPath, resourceXmlName);
         actionXml = actionXml.replace("${TZ}", oozieTimeZoneMask);
-        String actionNominalTime = getActionNominalTime(actionXml);
 
         CoordinatorActionBean action = new CoordinatorActionBean();
         action.setId(actionId);
@@ -598,7 +622,14 @@ public abstract class XDataTestCase extends XHCatTestCase {
         action.setActionNumber(actionNum);
         action.setPending(pending);
         try {
-            
action.setNominalTime(DateUtils.parseDateOozieTZ(actionNominalTime));
+            if (actionNominalTime == null) {
+                String nominalTime = getActionNominalTime(actionXml);
+
+                action.setNominalTime(DateUtils.parseDateOozieTZ(nominalTime));
+            }
+            else {
+                action.setNominalTime(actionNominalTime);
+            }
         }
         catch (Exception e) {
             e.printStackTrace();
@@ -1504,7 +1535,7 @@ public abstract class XDataTestCase extends XHCatTestCase 
{
             CoordinatorAction.Status status, String resourceXmlName, String 
missingDependencies,
             String pushMissingDependencies, String oozieTimeZoneMask) throws 
Exception {
         CoordinatorActionBean action = createCoordAction(jobId, actionNum, 
status, resourceXmlName, 0,
-                oozieTimeZoneMask);
+                oozieTimeZoneMask, null);
         action.setMissingDependencies(missingDependencies);
         action.setPushMissingDependencies(pushMissingDependencies);
         try {

http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/docs/src/site/twiki/DG_CommandLineTool.twiki
----------------------------------------------------------------------
diff --git a/docs/src/site/twiki/DG_CommandLineTool.twiki 
b/docs/src/site/twiki/DG_CommandLineTool.twiki
index c1bd08a..5819b71 100644
--- a/docs/src/site/twiki/DG_CommandLineTool.twiki
+++ b/docs/src/site/twiki/DG_CommandLineTool.twiki
@@ -345,16 +345,18 @@ Valid value names are:
    * pausetime: the pause time of the coordinator job.
    * status: new status for coordinator job.
 
-Repeated value names are not allowed. New end time must not be before job's 
start time and last action time. Currently status only
-takes RUNNING and can be used to change the status of FAILED or KILLED 
coordinator job to RUNNING and resuming materialization.
-This status change command does not affect the status of already materialized 
actions in the coordinator. If there are FAILED or
-KILLED coordinator actions they have to be rerun separately.
-
-New concurrency value has to be a valid integer. All lookahead actions will be 
revoked according to the new pause time. Also empty string "" can be used to 
reset pause time to none.
+Conditions and usage:
+   * Repeated value names are not allowed.
+   * New end time should not be before job's start time and last action time.
+   * If end time is before job start time and if the job has not materialized 
any actions, then job status is changed to SUCCEEDED.
+   * Currently status only takes RUNNING and can be used to change the status 
of FAILED or KILLED coordinator job to RUNNING and resuming materialization. 
This status change command does not affect the status of already materialized 
actions in the coordinator. If there are FAILED or KILLED coordinator actions 
they have to be rerun separately.
+   * New concurrency value has to be a valid integer.
+   * All lookahead actions which are in WAITING/READY state will be revoked 
according to the new pause/end time. If any action after new pause/end time is 
not in WAITING/READY state, an exception will be thrown.
+   * Also empty string "" can be used to reset pause time to none.
 
 After the command is executed the job's end time, concurrency or pause time 
should be changed. If an already-succeeded job changes its end time, its status 
will become running.
 
----+++ Changing pausetime of a Bundle Job
+---+++ Changing endtime/pausetime of a Bundle Job
 
 Example:
 
@@ -366,11 +368,11 @@ The =change= option changes a bundle job that is not in 
=KILLED= status.
 
 Valid value names are:
    * pausetime: the pause time of the bundle job.
+   * endtime: the end time of the bundle job.
 
-Repeated value names are not allowed. An empty string "" can be used to reset 
pause time to none.
-
-After the command is executed the job's pause time should be changed.
+Repeated value names are not allowed. An empty string "" can be used to reset 
pause time to none. New end time should not be before job's kickoff time.
 
+Bundle will execute pause/end date change command on each coordinator job. 
Refer conditions and usage section of coordinator change command for more 
details 
[[DG_CommandLineTool#Changing_endtimeconcurrencypausetimestatus_of_a_Coordinator_Job][Coordinator
 job change command]].
 ---+++ Rerunning a Workflow Job
 
 Example:

http://git-wip-us.apache.org/repos/asf/oozie/blob/f6b3f0e8/release-log.txt
----------------------------------------------------------------------
diff --git a/release-log.txt b/release-log.txt
index 3db5020..413932b 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
 -- Oozie 4.1.0 release (trunk - unreleased)
 
+OOZIE-1703 User should be able to set coord end-time before start time (puru 
via rohini)
 OOZIE-1719 v1/jobs api returns null for parentId even when it exists (ryota)
 OOZIE-1773 bulk API returns total = 0 when it's not (ryota)
 OOZIE-1774 Expected/Actual Duration on UI SLA Tab doesn't show correct 
information (ryota)

Reply via email to