You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by rk...@apache.org on 2013/02/04 19:02:13 UTC

svn commit: r1442238 - in /oozie/branches/branch-3.3: ./ core/src/main/java/org/apache/oozie/command/coord/ core/src/main/java/org/apache/oozie/coord/ core/src/main/resources/ core/src/test/java/org/apache/oozie/coord/ docs/src/site/twiki/ examples/src...

Author: rkanter
Date: Mon Feb  4 18:02:12 2013
New Revision: 1442238

URL: http://svn.apache.org/viewvc?rev=1442238&view=rev
Log:
OOZIE-1028 Add EL function to allow date ranges to be used for dataset ranges (rkanter via tucu)

Modified:
    oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/command/coord/CoordCommandUtils.java
    oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/coord/CoordELFunctions.java
    oozie/branches/branch-3.3/core/src/main/resources/oozie-default.xml
    oozie/branches/branch-3.3/core/src/test/java/org/apache/oozie/coord/TestCoordELFunctions.java
    oozie/branches/branch-3.3/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
    oozie/branches/branch-3.3/examples/src/main/apps/aggregator/job.properties
    oozie/branches/branch-3.3/release-log.txt

Modified: oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/command/coord/CoordCommandUtils.java
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/command/coord/CoordCommandUtils.java?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/command/coord/CoordCommandUtils.java (original)
+++ oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/command/coord/CoordCommandUtils.java Mon Feb  4 18:02:12 2013
@@ -18,6 +18,7 @@
 package org.apache.oozie.command.coord;
 
 import java.io.StringReader;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 
@@ -44,6 +45,7 @@ public class CoordCommandUtils {
     public static int CURRENT = 0;
     public static int LATEST = 1;
     public static int FUTURE = 2;
+    public static int OFFSET = 3;
     public static int UNEXPECTED = -1;
     public static final String RESOLVED_UNRESOLVED_SEPARATOR = ";";
 
@@ -112,6 +114,9 @@ public class CoordCommandUtils {
         else if (function.indexOf("future") >= 0) {
             return FUTURE;
         }
+        else if (function.indexOf("offset") >= 0) {
+            return OFFSET;
+        }
         return UNEXPECTED;
         // throw new RuntimeException("Unexpected instance name "+ function);
     }
@@ -124,7 +129,7 @@ public class CoordCommandUtils {
     public static void checkIfBothSameType(String startInst, String endInst) throws CommandException {
         if (getFuncType(startInst) != getFuncType(endInst)) {
             throw new CommandException(ErrorCode.E1010,
-                    " start-instance and end-instance both should be either latest or current or future\n"
+                    " start-instance and end-instance both should be either latest or current or future or offset\n"
                             + " start " + startInst + " and end " + endInst);
         }
     }
@@ -174,38 +179,68 @@ public class CoordCommandUtils {
                                                          // future
                                                          // function
             int startIndex = getInstanceNumber(strStart, event, appInst, conf, restArg);
+            String startRestArg = restArg.toString();
             restArg.delete(0, restArg.length());
             int endIndex = getInstanceNumber(strEnd, event, appInst, conf, restArg);
-            if (startIndex > endIndex) {
-                throw new CommandException(ErrorCode.E1010,
-                        " start-instance should be equal or earlier than the end-instance \n"
-                                + XmlUtils.prettyPrint(event));
-            }
+            String endRestArg = restArg.toString();
             int funcType = getFuncType(strStart);
-            if (funcType == CURRENT) {
-                // Everything could be resolved NOW. no latest() ELs
-                for (int i = endIndex; i >= startIndex; i--) {
-                    String matInstance = materializeInstance(event, "${coord:current(" + i + ")}", appInst, conf, eval);
-                    if (matInstance == null || matInstance.length() == 0) {
-                        // Earlier than dataset's initial instance
-                        break;
-                    }
-                    if (instances.length() > 0) {
-                        instances.append(CoordELFunctions.INSTANCE_SEPARATOR);
+            if (funcType == OFFSET) {
+                TimeUnit startU = TimeUnit.valueOf(startRestArg);
+                TimeUnit endU = TimeUnit.valueOf(endRestArg);
+                if (startU.getCalendarUnit() * startIndex > endU.getCalendarUnit() * endIndex) {
+                    throw new CommandException(ErrorCode.E1010,
+                            " start-instance should be equal or earlier than the end-instance \n"
+                                    + XmlUtils.prettyPrint(event));
+                }
+                Calendar startCal = CoordELFunctions.resolveOffsetRawTime(startIndex, startU, eval);
+                Calendar endCal = CoordELFunctions.resolveOffsetRawTime(endIndex, endU, eval);
+                if (startCal != null && endCal != null) {
+                    List<Integer> expandedFreqs = CoordELFunctions.expandOffsetTimes(startCal, endCal, eval);
+                    for (int i = expandedFreqs.size() - 1; i >= 0; i--) {
+                        String matInstance = materializeInstance(event, "${coord:offset(" + expandedFreqs.get(i) + ", \"MINUTE\")}",
+                                                                    appInst, conf, eval);
+                        if (matInstance == null || matInstance.length() == 0) {
+                            // Earlier than dataset's initial instance
+                            break;
+                        }
+                        if (instances.length() > 0) {
+                            instances.append(CoordELFunctions.INSTANCE_SEPARATOR);
+                        }
+                        instances.append(matInstance);
                     }
-                    instances.append(matInstance);
                 }
             }
-            else { // latest(n)/future() EL is present
-                for (; startIndex <= endIndex; startIndex++) {
-                    if (instances.length() > 0) {
-                        instances.append(CoordELFunctions.INSTANCE_SEPARATOR);
-                    }
-                    if (funcType == LATEST) {
-                        instances.append("${coord:latest(" + startIndex + ")}");
+            else {
+                if (startIndex > endIndex) {
+                    throw new CommandException(ErrorCode.E1010,
+                            " start-instance should be equal or earlier than the end-instance \n"
+                                    + XmlUtils.prettyPrint(event));
+                }
+                if (funcType == CURRENT) {
+                    // Everything could be resolved NOW. no latest() ELs
+                    for (int i = endIndex; i >= startIndex; i--) {
+                        String matInstance = materializeInstance(event, "${coord:current(" + i + ")}", appInst, conf, eval);
+                        if (matInstance == null || matInstance.length() == 0) {
+                            // Earlier than dataset's initial instance
+                            break;
+                        }
+                        if (instances.length() > 0) {
+                            instances.append(CoordELFunctions.INSTANCE_SEPARATOR);
+                        }
+                        instances.append(matInstance);
                     }
-                    else { // For future
-                        instances.append("${coord:future(" + startIndex + ",'" + restArg + "')}");
+                }
+                else { // latest(n)/future() EL is present
+                    for (; startIndex <= endIndex; startIndex++) {
+                        if (instances.length() > 0) {
+                            instances.append(CoordELFunctions.INSTANCE_SEPARATOR);
+                        }
+                        if (funcType == LATEST) {
+                            instances.append("${coord:latest(").append(startIndex).append(")}");
+                        }
+                        else if (funcType == FUTURE) {
+                            instances.append("${coord:future(").append(startIndex).append(",'").append(endRestArg).append("')}");
+                        }
                     }
                 }
             }

Modified: oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/coord/CoordELFunctions.java
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/coord/CoordELFunctions.java?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/coord/CoordELFunctions.java (original)
+++ oozie/branches/branch-3.3/core/src/main/java/org/apache/oozie/coord/CoordELFunctions.java Mon Feb  4 18:02:12 2013
@@ -18,8 +18,10 @@
 package org.apache.oozie.coord;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.List;
 import java.util.TimeZone;
 
 import org.apache.hadoop.conf.Configuration;
@@ -432,6 +434,25 @@ public class CoordELFunctions {
     }
 
     /**
+     * Determine the date-time in Oozie processing timezone of the given offset from the dataset effective nominal time. <p/> It
+     * depends on: <p> 1. Data set frequency <p/> 2. Data set Time Unit <p/> 3. Data set Time zone/DST
+     * <p/> 4. Data set initial instance <p/> 5. Action Creation Time
+     *
+     * @param n offset amount (integer)
+     * @param timeUnit TimeUnit for offset n ("MINUTE", "HOUR", "DAY", "MONTH", "YEAR")
+     * @return date-time in Oozie processing timezone of the given offset from the dataset effective nominal time
+     * @throws Exception if there was a problem formatting
+     */
+    public static String ph2_coord_offset(int n, String timeUnit) throws Exception {
+        if (isSyncDataSet()) { // For Sync Dataset
+            return coord_offset_sync(n, timeUnit);
+        }
+        else {
+            throw new UnsupportedOperationException("Asynchronous Dataset is not supported yet");
+        }
+    }
+
+    /**
      * Determine how many hours is on the date of n-th dataset instance. <p/> It depends on: <p/> 1. Data set frequency
      * <p/> 2. Data set Time unit (day, month, minute) <p/> 3. Data set Time zone/DST <p/> 4. End Day/Month flag <p/> 5.
      * Data set initial instance <p/> 6. Action Creation Time
@@ -560,10 +581,18 @@ public class CoordELFunctions {
         return echoUnResolved("current", n);
     }
 
+    public static String ph1_coord_offset_echo(String n, String timeUnit) {
+        return echoUnResolved("offset", n + " , " + timeUnit);
+    }
+
     public static String ph2_coord_current_echo(String n) {
         return echoUnResolved("current", n);
     }
 
+    public static String ph2_coord_offset_echo(String n, String timeUnit) {
+        return echoUnResolved("offset", n + " , " + timeUnit);
+    }
+
     public static String ph1_coord_dateOffset_echo(String n, String offset, String unit) {
         return echoUnResolved("dateOffset", n + " , " + offset + " , " + unit);
     }
@@ -728,6 +757,68 @@ public class CoordELFunctions {
     }
 
     /**
+     *
+     * @param n offset amount (integer)
+     * @param timeUnit TimeUnit for offset n ("MINUTE", "HOUR", "DAY", "MONTH", "YEAR")
+     * @return the offset time from the effective nominal time <p/> return empty string ("") if the Action_Creation_time or the
+     *         offset instance <p/> is earlier than the Initial_Instance of dataset.
+     * @throws Exception
+     */
+    private static String coord_offset_sync(int n, String timeUnit) throws Exception {
+        Calendar rawCal = resolveOffsetRawTime(n, TimeUnit.valueOf(timeUnit), null);
+        if (rawCal == null) {
+            // warning already logged by resolveOffsetRawTime()
+            return "";
+        }
+
+        int freq = getDSFrequency();
+        TimeUnit freqUnit = getDSTimeUnit();
+        int freqCount = 0;
+        // We're going to manually turn back/forward cal by decrements/increments of freq and then check that it gives the same
+        // time as rawCal; this is to check that the offset time resolves to a frequency offset of the effective nominal time
+        // In other words, that there exists an integer x, such that coord:offset(n, timeUnit) == coord:current(x) is true
+        // If not, then we'll "rewind" rawCal to the latest instance earlier than rawCal and use that.
+        Calendar cal = getInitialInstanceCal();
+        if (rawCal.before(cal)) {
+            while (cal.after(rawCal)) {
+                cal.add(freqUnit.getCalendarUnit(), -freq);
+                freqCount--;
+            }
+        }
+        else if (rawCal.after(cal)) {
+            while (cal.before(rawCal)) {
+                cal.add(freqUnit.getCalendarUnit(), freq);
+                freqCount++;
+            }
+        }
+        if (cal.before(rawCal)) {
+            rawCal = cal;
+        }
+        else if (cal.after(rawCal)) {
+            cal.add(freqUnit.getCalendarUnit(), -freq);
+            rawCal = cal;
+            freqCount--;
+        }
+        String rawCalStr = DateUtils.formatDateOozieTZ(rawCal);
+
+        Calendar nominalInstanceCal = getInitialInstanceCal();
+        nominalInstanceCal.add(freqUnit.getCalendarUnit(), freq * freqCount);
+        if (nominalInstanceCal.getTime().compareTo(getInitialInstance()) < 0) {
+            XLog.getLog(CoordELFunctions.class).warn("If the initial instance of the dataset is later than the offset instance"
+                    + " specified, such as coord:offset({0}, {1}) in this case, an empty string is returned. This means that no"
+                    + " data is available at the offset instance specified by the user and the user could try modifying his"
+                    + " initial-instance to an earlier time.", n, timeUnit);
+            return "";
+        }
+        String nominalCalStr = DateUtils.formatDateOozieTZ(nominalInstanceCal);
+
+        if (!rawCalStr.equals(nominalCalStr)) {
+            throw new RuntimeException("Shouldn't happen");
+        }
+        return rawCalStr;
+    }
+
+    /**
      * @param offset
      * @return n-th available latest instance Date-Time for SYNC data-set
      * @throws Exception
@@ -884,7 +975,15 @@ public class CoordELFunctions {
      * @return the initial instance of a DataSet in DATE
      */
     private static Date getInitialInstance() {
-        return getInitialInstanceCal().getTime();
+        ELEvaluator eval = ELEvaluator.getCurrent();
+        return getInitialInstance(eval);
+    }
+
+    /**
+     * @return the initial instance of a DataSet in DATE
+     */
+    private static Date getInitialInstance(ELEvaluator eval) {
+        return getInitialInstanceCal(eval).getTime();
         // return ds.getInitInstance();
     }
 
@@ -893,6 +992,13 @@ public class CoordELFunctions {
      */
     private static Calendar getInitialInstanceCal() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getInitialInstanceCal(eval);
+    }
+
+    /**
+     * @return the initial instance of a DataSet in Calendar
+     */
+    private static Calendar getInitialInstanceCal(ELEvaluator eval) {
         SyncCoordDataset ds = (SyncCoordDataset) eval.getVariable(DATASET);
         if (ds == null) {
             throw new RuntimeException("Associated Dataset should be defined with key " + DATASET);
@@ -901,7 +1007,7 @@ public class CoordELFunctions {
         effInitTS.setTime(ds.getInitInstance());
         effInitTS.setTimeZone(ds.getTimeZone());
         // To adjust EOD/EOM
-        DateUtils.moveToEnd(effInitTS, getDSEndOfFlag());
+        DateUtils.moveToEnd(effInitTS, getDSEndOfFlag(eval));
         return effInitTS;
         // return ds.getInitInstance();
     }
@@ -911,6 +1017,13 @@ public class CoordELFunctions {
      */
     private static Date getActionCreationtime() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getActionCreationtime(eval);
+    }
+
+    /**
+     * @return Nominal or action creation Time when all the dependencies of an application instance are met.
+     */
+    private static Date getActionCreationtime(ELEvaluator eval) {
         SyncCoordAction coordAction = (SyncCoordAction) eval.getVariable(COORD_ACTION);
         if (coordAction == null) {
             throw new RuntimeException("Associated Application instance should be defined with key " + COORD_ACTION);
@@ -949,9 +1062,21 @@ public class CoordELFunctions {
      *         the dataset.
      */
     private static Calendar getCurrentInstance(Date effectiveTime, int instanceCount[]) {
-        Date datasetInitialInstance = getInitialInstance();
-        TimeUnit dsTimeUnit = getDSTimeUnit();
-        TimeZone dsTZ = getDatasetTZ();
+        ELEvaluator eval = ELEvaluator.getCurrent();
+        return getCurrentInstance(effectiveTime, instanceCount, eval);
+    }
+
+    /**
+     * Find the current instance based on effectiveTime (i.e Action_Creation_Time or Action_Start_Time)
+     *
+     * @return current instance i.e. current(0) returns null if effectiveTime is earlier than Initial Instance time of
+     *         the dataset.
+     */
+    private static Calendar getCurrentInstance(Date effectiveTime, int instanceCount[], ELEvaluator eval) {
+        Date datasetInitialInstance = getInitialInstance(eval);
+        TimeUnit dsTimeUnit = getDSTimeUnit(eval);
+        TimeZone dsTZ = getDatasetTZ(eval);
+        int dsFreq = getDSFrequency(eval);
         // Convert Date to Calendar for corresponding TZ
         Calendar current = Calendar.getInstance();
         current.setTime(datasetInitialInstance);
@@ -960,24 +1085,23 @@ public class CoordELFunctions {
         Calendar calEffectiveTime = Calendar.getInstance();
         calEffectiveTime.setTime(effectiveTime);
         calEffectiveTime.setTimeZone(dsTZ);
+        if (instanceCount == null) {    // caller doesn't care about this value
+            instanceCount = new int[1];
+        }
         instanceCount[0] = 0;
         if (current.compareTo(calEffectiveTime) > 0) {
-            // Nominal Time < initial Instance
-            // TODO: getClass() call doesn't work from static method.
-            // XLog.getLog("CoordELFunction.class").warn("ACTION CREATED BEFORE INITIAL INSTACE "+
-            // current.getTime());
             return null;
         }
         Calendar origCurrent = (Calendar) current.clone();
         while (current.compareTo(calEffectiveTime) <= 0) {
             current = (Calendar) origCurrent.clone();
             instanceCount[0]++;
-            current.add(dsTimeUnit.getCalendarUnit(), instanceCount[0] * getDSFrequency());
+            current.add(dsTimeUnit.getCalendarUnit(), instanceCount[0] * dsFreq);
         }
         instanceCount[0]--;
 
         current = (Calendar) origCurrent.clone();
-        current.add(dsTimeUnit.getCalendarUnit(), instanceCount[0] * getDSFrequency());
+        current.add(dsTimeUnit.getCalendarUnit(), instanceCount[0] * dsFreq);
         return current;
     }
 
@@ -1007,6 +1131,13 @@ public class CoordELFunctions {
      */
     private static int getDSFrequency() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getDSFrequency(eval);
+    }
+
+    /**
+     * @return dataset frequency in minutes
+     */
+    private static int getDSFrequency(ELEvaluator eval) {
         SyncCoordDataset ds = (SyncCoordDataset) eval.getVariable(DATASET);
         if (ds == null) {
             throw new RuntimeException("Associated Dataset should be defined with key " + DATASET);
@@ -1019,6 +1150,13 @@ public class CoordELFunctions {
      */
     private static TimeUnit getDSTimeUnit() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getDSTimeUnit(eval);
+    }
+
+    /**
+     * @return dataset TimeUnit
+     */
+    private static TimeUnit getDSTimeUnit(ELEvaluator eval) {
         SyncCoordDataset ds = (SyncCoordDataset) eval.getVariable(DATASET);
         if (ds == null) {
             throw new RuntimeException("Associated Dataset should be defined with key " + DATASET);
@@ -1031,6 +1169,13 @@ public class CoordELFunctions {
      */
     private static TimeZone getDatasetTZ() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getDatasetTZ(eval);
+    }
+
+    /**
+     * @return dataset TimeZone
+     */
+    private static TimeZone getDatasetTZ(ELEvaluator eval) {
         SyncCoordDataset ds = (SyncCoordDataset) eval.getVariable(DATASET);
         if (ds == null) {
             throw new RuntimeException("Associated Dataset should be defined with key " + DATASET);
@@ -1043,6 +1188,13 @@ public class CoordELFunctions {
      */
     private static TimeUnit getDSEndOfFlag() {
         ELEvaluator eval = ELEvaluator.getCurrent();
+        return getDSEndOfFlag(eval);
+    }
+
+    /**
+     * @return dataset TimeUnit
+     */
+    private static TimeUnit getDSEndOfFlag(ELEvaluator eval) {
         SyncCoordDataset ds = (SyncCoordDataset) eval.getVariable(DATASET);
         if (ds == null) {
             throw new RuntimeException("Associated Dataset should be defined with key " + DATASET);
@@ -1070,4 +1222,76 @@ public class CoordELFunctions {
         ELEvaluator eval = ELEvaluator.getCurrent();
         return (String) eval.getVariable(OozieClient.USER_NAME);
     }
+
+    /**
+     * Takes two offset times and returns a list of multiples of the frequency offset from the effective nominal time that occur
+     * between them.  The caller should make sure that startCal is earlier than endCal.
+     * <p>
+     * As a simple example, assume its the same day: startCal is 1:00, endCal is 2:00, frequency is 20min, and effective nominal
+     * time is 1:20 -- then this method would return a list containing: -20, 0, 20, 40, 60
+     *
+     * @param startCal The earlier offset time
+     * @param endCal The later offset time
+     * @param eval The ELEvaluator to use; cannot be null
+     * @return A list of multiple of the frequency offset from the effective nominal time that occur between the startCal and endCal
+     */
+    public static List<Integer> expandOffsetTimes(Calendar startCal, Calendar endCal, ELEvaluator eval) {
+        List<Integer> expandedFreqs = new ArrayList<Integer>();
+        // Use eval because the "current" eval isn't set
+        int freq = getDSFrequency(eval);
+        TimeUnit freqUnit = getDSTimeUnit(eval);
+        Calendar cal = getCurrentInstance(getActionCreationtime(eval), null, eval);
+        int totalFreq = 0;
+        if (startCal.before(cal)) {
+            while (cal.after(startCal)) {
+                cal.add(freqUnit.getCalendarUnit(), -freq);
+                totalFreq += -freq;
+            }
+            if (cal.before(startCal)) {
+                cal.add(freqUnit.getCalendarUnit(), freq);
+                totalFreq += freq;
+            }
+        }
+        else if (startCal.after(cal)) {
+            while (cal.before(startCal)) {
+                cal.add(freqUnit.getCalendarUnit(), freq);
+                totalFreq += freq;
+            }
+        }
+        // At this point, cal is the smallest multiple of the dataset frequency that is >= to the startCal and offset from the
+        // effective nominal time.  Now we can find all of the instances that occur between startCal and endCal, inclusive.
+        while (cal.before(endCal) || cal.equals(endCal)) {
+            expandedFreqs.add(totalFreq);
+            cal.add(freqUnit.getCalendarUnit(), freq);
+            totalFreq += freq;
+        }
+        return expandedFreqs;
+    }
+
+    /**
+     * Resolve the offset time from the effective nominal time
+     *
+     * @param n offset amount (integer)
+     * @param timeUnit TimeUnit for offset n ("MINUTE", "HOUR", "DAY", "MONTH", "YEAR")
+     * @param eval The ELEvaluator to use; or null to use the "current" eval
+     * @return A Calendar of the offset time
+     */
+    public static Calendar resolveOffsetRawTime(int n, TimeUnit timeUnit, ELEvaluator eval) {
+        // Use eval if given (for when the "current" eval isn't set)
+        Calendar cal;
+        if (eval == null) {
+            cal = getCurrentInstance(getActionCreationtime(), null);
+        }
+        else {
+            cal = getCurrentInstance(getActionCreationtime(eval), null, eval);
+        }
+        if (cal == null) {
+            XLog.getLog(CoordELFunctions.class).warn("If the initial instance of the dataset is later than the nominal time, an"
+                    + " empty string is returned. This means that no data is available at the offset instance specified by the user"
+                    + " and the user could try modifying his or her initial-instance to an earlier time.");
+            return null;
+        }
+        cal.add(timeUnit.getCalendarUnit(), n);
+        return cal;
+    }
 }

Modified: oozie/branches/branch-3.3/core/src/main/resources/oozie-default.xml
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/core/src/main/resources/oozie-default.xml?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/core/src/main/resources/oozie-default.xml (original)
+++ oozie/branches/branch-3.3/core/src/main/resources/oozie-default.xml Mon Feb  4 18:02:12 2013
@@ -682,6 +682,7 @@
             coord:daysInMonth=org.apache.oozie.coord.CoordELFunctions#ph1_coord_daysInMonth_echo,
             coord:tzOffset=org.apache.oozie.coord.CoordELFunctions#ph1_coord_tzOffset_echo,
             coord:current=org.apache.oozie.coord.CoordELFunctions#ph1_coord_current_echo,
+            coord:offset=org.apache.oozie.coord.CoordELFunctions#ph1_coord_offset_echo,
             coord:latest=org.apache.oozie.coord.CoordELFunctions#ph1_coord_latest_echo,
             coord:future=org.apache.oozie.coord.CoordELFunctions#ph1_coord_future_echo,
             coord:formatTime=org.apache.oozie.coord.CoordELFunctions#ph1_coord_formatTime_echo,
@@ -827,6 +828,7 @@
             coord:daysInMonth=org.apache.oozie.coord.CoordELFunctions#ph2_coord_daysInMonth,
             coord:tzOffset=org.apache.oozie.coord.CoordELFunctions#ph2_coord_tzOffset,
             coord:current=org.apache.oozie.coord.CoordELFunctions#ph2_coord_current,
+            coord:offset=org.apache.oozie.coord.CoordELFunctions#ph2_coord_offset,
             coord:latest=org.apache.oozie.coord.CoordELFunctions#ph2_coord_latest_echo,
             coord:future=org.apache.oozie.coord.CoordELFunctions#ph2_coord_future_echo,
             coord:actionId=org.apache.oozie.coord.CoordELFunctions#ph2_coord_actionId,
@@ -879,6 +881,7 @@
             coord:daysInMonth=org.apache.oozie.coord.CoordELFunctions#ph2_coord_daysInMonth,
             coord:tzOffset=org.apache.oozie.coord.CoordELFunctions#ph2_coord_tzOffset,
             coord:current=org.apache.oozie.coord.CoordELFunctions#ph2_coord_current_echo,
+            coord:offset=org.apache.oozie.coord.CoordELFunctions#ph2_coord_offset_echo,
             coord:latest=org.apache.oozie.coord.CoordELFunctions#ph2_coord_latest_echo,
             coord:future=org.apache.oozie.coord.CoordELFunctions#ph2_coord_future_echo,
             coord:formatTime=org.apache.oozie.coord.CoordELFunctions#ph2_coord_formatTime,

Modified: oozie/branches/branch-3.3/core/src/test/java/org/apache/oozie/coord/TestCoordELFunctions.java
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/core/src/test/java/org/apache/oozie/coord/TestCoordELFunctions.java?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/core/src/test/java/org/apache/oozie/coord/TestCoordELFunctions.java (original)
+++ oozie/branches/branch-3.3/core/src/test/java/org/apache/oozie/coord/TestCoordELFunctions.java Mon Feb  4 18:02:12 2013
@@ -676,6 +676,109 @@ public class TestCoordELFunctions extend
         assertEquals("2009-06-01T07:00Z", CoordELFunctions.evalAndWrap(eval, expr));
     }
 
+    public void testOffset() throws Exception {
+        init("coord-action-create");
+        String expr = "${coord:offset(-1440, \"MINUTE\")}";
+        assertEquals("2009-09-08T23:59Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(-24, \"HOUR\")}";
+        assertEquals("2009-09-08T23:59Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(-1, \"DAY\")}";
+        assertEquals("2009-09-08T23:59Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(1, \"MONTH\")}";
+        assertEquals("2009-10-09T23:59Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(1, \"YEAR\")}";
+        assertEquals("2010-09-09T23:59Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(-10, \"DAY\")}";
+        assertEquals("", CoordELFunctions.evalAndWrap(eval, expr));
+
+        appInst.setNominalTime(DateUtils.parseDateOozieTZ("2015-01-02T00:45Z"));
+        ds.setFrequency(1);
+        ds.setTimeUnit(TimeUnit.YEAR);
+        ds.setInitInstance(DateUtils.parseDateOozieTZ("2010-01-02T00:01Z"));
+        ds.setTimeZone(DateUtils.getTimeZone("America/Los_Angeles"));
+        // Year
+        expr = "${coord:offset(0, \"YEAR\")} ${coord:offset(1, \"YEAR\")} ${coord:offset(-1, \"YEAR\")}"
+                + " ${coord:offset(-3, \"YEAR\")}";
+        assertEquals("2015-01-02T00:01Z 2016-01-02T00:01Z 2014-01-02T00:01Z 2012-01-02T00:01Z", eval.evaluate(expr, String.class));
+        // Month
+        expr = "${coord:offset(0, \"MONTH\")} ${coord:offset(12, \"MONTH\")} ${coord:offset(-12, \"MONTH\")}"
+                + " ${coord:offset(-36, \"MONTH\")}";
+        assertEquals("2015-01-02T00:01Z 2016-01-02T00:01Z 2014-01-02T00:01Z 2012-01-02T00:01Z", eval.evaluate(expr, String.class));
+        // Day
+        expr = "${coord:offset(0, \"DAY\")} ${coord:offset(365, \"DAY\")} ${coord:offset(-365, \"DAY\")}"
+                + " ${coord:offset(-1096, \"DAY\")}";   // its -1096 instead of -1095 because of DST (extra 1 day)
+        assertEquals("2015-01-02T00:01Z 2016-01-02T00:01Z 2014-01-02T00:01Z 2012-01-02T00:01Z", eval.evaluate(expr, String.class));
+        // Hour
+        expr = "${coord:offset(0, \"HOUR\")} ${coord:offset(8760, \"HOUR\")} ${coord:offset(-8760, \"HOUR\")}"
+                + " ${coord:offset(-26304, \"HOUR\")}"; // its -26304 instead of -26280 because of DST (extra 24 hours)
+        assertEquals("2015-01-02T00:01Z 2016-01-02T00:01Z 2014-01-02T00:01Z 2012-01-02T00:01Z", eval.evaluate(expr, String.class));
+        // Minute
+        expr = "${coord:offset(0, \"MINUTE\")} ${coord:offset(525600, \"MINUTE\")} ${coord:offset(-525600, \"MINUTE\")}"
+                + " ${coord:offset(-1578240, \"MINUTE\")}"; // its -1578240 instead of -1576800 because of DST (extra 1440 minutes)
+        assertEquals("2015-01-02T00:01Z 2016-01-02T00:01Z 2014-01-02T00:01Z 2012-01-02T00:01Z", eval.evaluate(expr, String.class));
+
+        appInst.setNominalTime(DateUtils.parseDateOozieTZ("2015-01-02T00:45Z"));
+        ds.setFrequency(1);
+        ds.setTimeUnit(TimeUnit.MINUTE);
+        ds.setInitInstance(DateUtils.parseDateOozieTZ("2010-01-02T00:01Z"));
+        ds.setTimeZone(DateUtils.getTimeZone("America/Los_Angeles"));
+        // Minute
+        expr = "${coord:offset(0, \"MINUTE\")} ${coord:offset(1, \"MINUTE\")} ${coord:offset(-1, \"MINUTE\")}"
+                + " ${coord:offset(-3, \"MINUTE\")}";
+        assertEquals("2015-01-02T00:45Z 2015-01-02T00:46Z 2015-01-02T00:44Z 2015-01-02T00:42Z", eval.evaluate(expr, String.class));
+        // Hour
+        expr = "${coord:offset(0, \"HOUR\")} ${coord:offset(1, \"HOUR\")} ${coord:offset(-1, \"HOUR\")}"
+                + " ${coord:offset(-3, \"HOUR\")}";
+        assertEquals("2015-01-02T00:45Z 2015-01-02T01:45Z 2015-01-01T23:45Z 2015-01-01T21:45Z", eval.evaluate(expr, String.class));
+        // Day
+        expr = "${coord:offset(0, \"DAY\")} ${coord:offset(1, \"DAY\")} ${coord:offset(-1, \"DAY\")}"
+                + " ${coord:offset(-3, \"DAY\")}";
+        assertEquals("2015-01-02T00:45Z 2015-01-03T00:45Z 2015-01-01T00:45Z 2014-12-30T00:45Z", eval.evaluate(expr, String.class));
+        // Month
+        expr = "${coord:offset(0, \"MONTH\")} ${coord:offset(1, \"MONTH\")} ${coord:offset(-1, \"MONTH\")}"
+                + " ${coord:offset(-3, \"MONTH\")}";
+        assertEquals("2015-01-02T00:45Z 2015-02-02T00:45Z 2014-12-02T00:45Z 2014-10-01T23:45Z", eval.evaluate(expr, String.class));
+        // Year
+        expr = "${coord:offset(0, \"YEAR\")} ${coord:offset(1, \"YEAR\")} ${coord:offset(-1, \"YEAR\")}"
+                + " ${coord:offset(-3, \"YEAR\")}";
+        assertEquals("2015-01-02T00:45Z 2016-01-02T00:45Z 2014-01-02T00:45Z 2012-01-02T00:45Z", eval.evaluate(expr, String.class));
+
+        // Test rewinding when the given offset isn't a multiple of the frequency
+        appInst.setNominalTime(DateUtils.parseDateOozieTZ("2015-01-02T00:45Z"));
+        ds.setFrequency(4);
+        ds.setTimeUnit(TimeUnit.HOUR);
+        ds.setInitInstance(DateUtils.parseDateOozieTZ("2010-01-02T00:01Z"));
+        ds.setTimeZone(DateUtils.getTimeZone("America/Los_Angeles"));
+        expr = "${coord:offset(5, \"MINUTE\")}";
+        assertEquals("2015-01-02T00:01Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(1, \"HOUR\")}";
+        assertEquals("2015-01-02T00:01Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(7, \"HOUR\")}";
+        assertEquals("2015-01-02T04:01Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(-2, \"HOUR\")}";
+        assertEquals("2015-01-01T20:01Z", CoordELFunctions.evalAndWrap(eval, expr));
+        expr = "${coord:offset(-43825, \"HOUR\")}";
+        assertEquals("", CoordELFunctions.evalAndWrap(eval, expr));
+
+        // "blah" is not a valid TimeUnit
+        expr = "${coord:offset(1, \"blah\")}";
+        try {
+            CoordELFunctions.evalAndWrap(eval, expr);
+            fail("eval of " + expr + " should have thrown an exception");
+        } catch(Exception e) {
+            assertTrue(e.getMessage().contains("Unable to evaluate"));
+        }
+
+        // 4.5 is not a valid integer
+        expr = "${coord:offset(4.5, \"blah\")}";
+        try {
+            CoordELFunctions.evalAndWrap(eval, expr);
+            fail("eval of " + expr + " should have thrown an exception");
+        } catch(Exception e) {
+            assertTrue(e.getMessage().contains("Unable to evaluate"));
+        }
+    }
+
     public void testLatest() throws Exception {
         init("coord-action-start");
         String expr = "${coord:latest(0)}";

Modified: oozie/branches/branch-3.3/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki (original)
+++ oozie/branches/branch-3.3/docs/src/site/twiki/CoordinatorFunctionalSpec.twiki Mon Feb  4 18:02:12 2013
@@ -1525,7 +1525,139 @@ The 'app-coord-daily' coordinator applic
 
 The output datasets from the 'app-coord-hourly' coordinator application are the input to the 'app-coord-daily' coordinator application thereby forming a simple data-pipeline application.
 
----++++ 6.6.2. coord:hoursInDay(int n) EL Function for Synchronous Datasets
+---++++ 6.6.2. coord:offset(int n, String timeUnit) EL Function for Synchronous Datasets
+
+=${coord:offset(int n, String timeUnit)}= represents the n<sup>th</sup> timeUnit, relative to the coordinator action creation
+(materialization) time. The coordinator action creation (materialization) time is computed based on the coordinator job start time
+and its frequency.
+
+It is an alternative to the =${coord:current(int n)}= command (see previous section) and can be used anywhere
+=${coord:current(int n)}= can be used. The difference between the two functions is that =${coord:current(int n)}= computes an offset
+based on the n<sup>th</sup> multiple of the frequency, while =${coord:offset(int n, String timeUnit)}= computes an offset based on
+the n<sup>th</sup> multiple of =timeUnit=.
+
+=n= can be a negative integer, zero or a positive integer.
+
+=timeUnit= can be any one of the following constants: ="MINUTE"=, ="HOUR"=, ="DAY"=, ="MONTH"=, ="YEAR"=
+
+=${coord:offset(int n, String timeUnit)}= returns the nominal datetime for n<sup>th</sup> timeUnit relative to the coordinator
+action creation (materialization) time.
+
+When used directly, =${coord:offset(int n, String timeUnit)}= performs the following calculation:
+
+<verbatim>
+DS_FREQ: dataset frequency (minutes)
+CA_NT = coordinator action creation (materialization) nominal time
+coord:offset(int n, String timeUnit) = CA_NT + floor(timeUnit * n div DS_FREQ) * DS_FREQ
+</verbatim>
+
+NOTE: The formula above is not 100% correct, because DST changes the calculation has to account for hour shifts. Oozie Coordinator
+must make the correct calculation accounting for DTS hour shifts.
+
+When used in 'instance' or 'end-instance' XML elements, the above equation is used; the effect of the floor function is to
+"rewind" the resolved datetime to match the latest instance before the resolved time.
+When used in 'start-instance' XML elements, a slight modification to the above equation is used; instead of being "rewinded", the
+resolved datetime is "fastforwarded" to match the earliest instance after the resolved time.
+See the next two examples for more information.
+
+*%GREEN% Examples: %ENDCOLOR%*
+
+1. *=${coord:offset(int n, String timeUnit)}= datetime calculation:*
+
+Datasets Definition:
+
+<verbatim>
+<datasets>
+.
+  <dataset name="logs" frequency="${coord:days(1)}"
+           initial-instance="2009-01-01T24:00Z" timezone="UTC">
+    <uri-template>hdfs://bar:8020/app/logs/${YEAR}${MONTH}/${DAY}</uri-template>
+  </dataset>
+.
+  <dataset name="weeklySiteAccessStats" frequency="${coord:days(7)}"
+           initial-instance="2009-01-07T24:00Z" timezone="UTC">
+    <uri-template>hdfs://bar:8020/app/weeklystats/${YEAR}/${MONTH}/${DAY}</uri-template>
+  </dataset>
+.
+</datasets>
+</verbatim>
+
+For a coordinator action creation time: =2009-05-29T24:00Z= the =${coord:offset(int n, String timeUnit)}= EL function would resolve
+to the following datetime values for the 'logs' and 'weeklySiteStats' datasets:
+
+| *${coord:offset(int n, String timeUnit)}* | *Dataset 'logs'* | *Dataset 'weeklySiteAccessStats'* |
+| =${coord:offset(0, "MINUTE")}= <br/> =${coord:offset(0, "HOUR")}= <br/> =${coord:offset(0, "DAY")}= <br/> =${coord:offset(0, "MONTH")}= <br/> =${coord:offset(0, "YEAR")}= | =2009-05-29T24:00Z= | =2009-05-27T24:00Z= |
+| =${coord:offset(1440, "MINUTE")}= <br/> =${coord:offset(24, "HOUR")}= <br/> =${coord:offset(1, "DAY")}= | =2009-05-30T24:00Z= | =2009-05-27T24:00Z= |
+| =${coord:offset(-1440, "MINUTE")}= <br/> =${coord:offset(-24, "HOUR")}= <br/> =${coord:offset(-1, "DAY")}= | =2009-05-28T24:00Z= | =2009-05-20T24:00Z= |
+| =${coord:offset(-4320, "MINUTE")}= <br/> =${coord:offset(-72, "HOUR")}= <br/> =${coord:offset(-3, "DAY")}= | =2009-05-26T24:00Z= | =2009-05-20T24:00Z= |
+| =${coord:offset(11520, "MINUTE")}= <br/> =${coord:offset(192, "HOUR")}= <br/> =${coord:offset(8, "DAY")}= | =2009-06-06T24:00Z= | =2009-06-03T24:00Z= |
+| =${coord:offset(10, "MINUTE")}= | =2009-05-29T24:00Z= | =2009-05-27T24:00Z= |
+
+Some things to note about the above example:
+   1. When =n= is 0, the =timeUnit= doesn't really matter because 0 minutes is the same as 0 hours, 0 days, etc
+   2. There are multiple ways to express the same value (e.g. ${coord:offset(24, "HOUR")}= is equivalent to =${coord:offset(1, "DAY")}=)
+   3. The datetimes resolved for the 2 datasets differ when the =${coord:offset(int n, String timeUnit)}= function is invoked with the same arguments. This is because the =${coord:offset(int n, String timeUnit)}= function takes into consideration the initial-time and the frequency for the dataset for which is performing the calculation.
+   4. As mentioned before, if the resolved time doesn't fall exactly on an instance, it will get "rewinded" to match the latest instance before the resolved time.  For example, =${coord:offset(1, "DAY")}= is resolved to =2009-05-27T24:00Z= for the 'weeklysiteStats' dataset even though this is the same as =${coord:offset(0, "DAY")}=; this is because the frequency is 7 days, so =${coord:offset(1, "DAY")}= had to be "rewinded".
+
+2. *"fastforwarding" in <start-instance> =${coord:offset(int n, String timeUnit)}= calculation:*
+
+When specifying dataset instances, keep in mind that the resolved value of =${coord:offset(int n, String timeUnit)}= must line up
+with an offset of a multiple of the frequency when used in an 'instance' XML element.
+However, when used in ='start-instance'= and ='end-instance'= XML elements, this is not a requirement.  In this case, the function
+will automatically resolve the range of instances to match the offset of a multiple of the frequency that would fall between the
+='start-instance'= and ='end-instance'= XML elements; in other words, ='start-instance'= XML element is "fastforwarded" while
+='end-instance'= XML element is "rewinded".  So, in the example below, the frequency of the "logs" dataset is 1 hour while the
+='start-instance'= XML element is =${coord:offset(-90, "MINUTE")}= (-1.5 hours).  If this were in an ='instance'= XML element, it
+would be "rewinded", but here it is effectively equivalent to =${coord:offset(-60, "MINUTE")}= or =${coord:current(-1)}= as we are
+dealing with a range.
+
+Datasets Definition file 'datasets.xml':
+
+<verbatim>
+<datasets>
+.
+  <dataset name="logs" frequency="${coord:hours(1)}"
+           initial-instance="2009-01-01T01:00Z" timezone="UTC">
+    <uri-template>hdfs://bar:8020/app/logs/${YEAR}/${MONTH}/${DAY}/${HOUR}</uri-template>
+  </dataset>
+.
+  <dataset name="stats" frequency="${coord:days(1)}"
+           initial-instance="2009-01-01T24:00Z" timezone="UTC">
+    <uri-template>hdfs://bar:8020/app/logs/${YEAR}/${MONTH}/${DAY}</uri-template>
+  </dataset>
+.
+</datasets>
+</verbatim>
+
+Coordinator application definition:
+
+<verbatim>
+   <coordinator-app name="app-coord" frequency="${coord:days(1)}"
+                    start="2009-01-01T24:00Z" end="2009-12-31T24:00Z" timezone="UTC"
+                    xmlns="uri:oozie:coordinator:0.1">
+      <datasets>
+        <include>hdfs://foo:8020/app/dataset-definitions/datasets.xml</include>
+      </datasets>
+      <input-events>
+        <data-in name="input" dataset="logs">
+          <start-instance>${coord:offset(-90, "MINUTE")}</start-instance>
+          <end-instance>${coord:offset(0, "DAY")}</end-instance>
+        </data-in>
+      </input-events>
+      <output-events>
+        <data-out name="output" dataset="stats">
+          <instance>${coord:offset(0, "DAY")}</instance>
+        </data-out>
+      </output-events>
+      <action>
+        <workflow>
+        ...
+       </workflow>
+      </action>
+   </coordinator-app>
+</verbatim>
+
+---++++ 6.6.3. coord:hoursInDay(int n) EL Function for Synchronous Datasets
 
 The =${coord:hoursInDay(int n)}= EL function returns the number of hours for the specified day, in a timezone/daylight-saving sensitive way.
 
@@ -1586,7 +1718,7 @@ For timezones observing daylight saving,
 
 For timezones not observing daylight saving, it always returns =24=.
 
----++++ 6.6.3. coord:daysInMonth(int n) EL Function for Synchronous Datasets
+---++++ 6.6.4. coord:daysInMonth(int n) EL Function for Synchronous Datasets
 
 The =${coord:daysInMonth(int n)}= EL function returns the number of days for month of the specified day.
 
@@ -1644,7 +1776,7 @@ Coordinator application definition:
 
 This example is a coordinator application that runs monthly, and consumes the daily feeds for the last month.
 
----++++ 6.6.4. coord:tzOffset() EL Function for Synchronous Datasets
+---++++ 6.6.5. coord:tzOffset() EL Function for Synchronous Datasets
 
 =${coord:tzOffset()}= EL function returns the difference in *minutes* between a dataset timezone and the coordinator job timezone at the current nominal time. This EL function is useful when dealing with datasets from multiple timezones, but execute in a different timezone.
 
@@ -1663,7 +1795,7 @@ IMPORTANT: While the offset is multiples
 
 Refer to section #7, 3nd use case for a detailed example.
 
----++++ 6.6.5. coord:latest(int n) EL Function for Synchronous Datasets
+---++++ 6.6.6. coord:latest(int n) EL Function for Synchronous Datasets
 
 =${coord:latest(int n)}= represents the n<sup>th</sup> latest currently available instance of a *synchronous* dataset.
 
@@ -1729,7 +1861,7 @@ Then, the dataset instances for the inpu
   hdfs://bar:8020/app/logs/2009/01/10
 </verbatim>
 
----++++ 6.6.6. coord:future(int n, int limit) EL Function for Synchronous Datasets
+---++++ 6.6.7. coord:future(int n, int limit) EL Function for Synchronous Datasets
 
 =${coord:future(int n, int limit)}= represents the n<sup>th</sup> currently available future instance of a *synchronous* dataset while looking ahead for 'limit' number of instances.
 
@@ -1800,13 +1932,13 @@ Then, the dataset instances for the inpu
   hdfs://bar:8020/app/logs/2009/02/07
 </verbatim>
 
----++++ 6.6.7. coord:version(int n) EL Function for Asynchronous Datasets
+---++++ 6.6.8. coord:version(int n) EL Function for Asynchronous Datasets
    * TBD
 
----++++ 6.6.8. coord:latest(int n) EL Function for Asynchronous Datasets
+---++++ 6.6.9. coord:latest(int n) EL Function for Asynchronous Datasets
    * TBD
 
----++++ 6.6.9. Dataset Instance Resolution for Instances Before the Initial Instance
+---++++ 6.6.10. Dataset Instance Resolution for Instances Before the Initial Instance
 
 When defining input events that refer to dataset instances it may be possible that the resolution of instances is out of it lower bound. This is scenario is likely to happen when the instance resolution is very close to the initial-instance. This is useful for bootstrapping the application.
 

Modified: oozie/branches/branch-3.3/examples/src/main/apps/aggregator/job.properties
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/examples/src/main/apps/aggregator/job.properties?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/examples/src/main/apps/aggregator/job.properties (original)
+++ oozie/branches/branch-3.3/examples/src/main/apps/aggregator/job.properties Mon Feb  4 18:02:12 2013
@@ -21,7 +21,7 @@ jobTracker=localhost:8021
 queueName=default
 examplesRoot=examples
 
-oozie.coord.application.path=${nameNode}/user/${user.name}/${examplesRoot}/apps/aggregator
+oozie.coord.application.path=${nameNode}/user/${user.name}/${examplesRoot}/apps/aggregator/coordinator.xml
 start=2010-01-01T01:00Z
 end=2010-01-01T03:00Z
 

Modified: oozie/branches/branch-3.3/release-log.txt
URL: http://svn.apache.org/viewvc/oozie/branches/branch-3.3/release-log.txt?rev=1442238&r1=1442237&r2=1442238&view=diff
==============================================================================
--- oozie/branches/branch-3.3/release-log.txt (original)
+++ oozie/branches/branch-3.3/release-log.txt Mon Feb  4 18:02:12 2013
@@ -1,5 +1,6 @@
 -- Oozie 3.3.2 (unreleased)
 
+OOZIE-1028 Add EL function to allow date ranges to be used for dataset ranges (rkanter via tucu)
 OOZIE-1048 Enable propagation of native libraries as a VM argument using java.library.path (venkatesh via tucu)
 OOZIE-1014 Coordinator action failure error not propagated (mona)
 OOZIE-1029 MiniMRCluster fails to start when used against YARN (rkanter via tucu)