org.kitesdk.apps.spi.oozie.CronConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.kitesdk.apps.spi.oozie.CronConverter.java

Source

/**
 * Copyright 2015 Cerner Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kitesdk.apps.spi.oozie;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Instant;

import java.util.regex.Pattern;

/**
 * Utility class for converting cron-based definitions to Oozie frequencies.
 *
 * This may be replaced in the future should Oozie support cron definitions
 * in frequencies. See https://issues.apache.org/jira/browse/OOZIE-1431.
 */
public class CronConverter {

    private static final Pattern NUMBER = Pattern.compile("[0-9]+");

    private static final Pattern INTERVAL = Pattern.compile("\\*/[0-9]+");

    private static void validate(String cronSchedule) {

        String[] parts = cronSchedule.split(" ");

        if (parts.length != 5)
            throw new IllegalArgumentException("Invalid cron schedule:" + cronSchedule);

        // Minute, hour, day must all be valid
        if (!isValid(parts[0]) || !isValid(parts[1]) || !isValid(parts[2]))
            throw new IllegalArgumentException("Invalid cron schedule: " + cronSchedule);

        // Only wildcards are currently supported in month and day-of-week parts.
        if (!isWildCard(parts[3]) || !isWildCard(parts[4]))
            throw new IllegalArgumentException(
                    "Month or day-of-week cron schedules " + "not currently supported. Schedule: " + cronSchedule);

    }

    private static boolean isWildCard(String cronPart) {
        return cronPart.equals("*");
    }

    private static boolean isConstant(String cronPart) {
        return NUMBER.matcher(cronPart).matches();
    }

    private static boolean isInterval(String cronPart) {
        return INTERVAL.matcher(cronPart).matches();
    }

    private static boolean isValid(String cronPart) {
        return isConstant(cronPart) || isInterval(cronPart) || isWildCard(cronPart);
    }

    /**
     * Returns the interval value. The given cron part
     * must be a valid interval (i.e., isInterval(...) returns true)
     */
    private static int getInterval(String cronPart) {

        String intervalPart = cronPart.substring(cronPart.indexOf("/") + 1);

        return Integer.parseInt(intervalPart);
    }

    /**
     * Converts a given cron-style schedule to an Oozie frequency,
     * if supported. Throws an IllegalArgumentException
     * if a conversion isn't possible.
     */
    public static String toFrequency(String cronSchedule) {

        validate(cronSchedule);

        String[] splits = cronSchedule.split(" ");

        String minute = splits[0];
        String hour = splits[1];
        String day = splits[2];

        if (isWildCard(minute)) {
            return "1";
        }

        if (isInterval(minute)) {
            return Integer.toString(getInterval(minute));
        }

        if (isWildCard(hour)) {
            return "${coord:hours(1)}";
        }

        if (isInterval(hour)) {
            return "${coord:hours(" + getInterval(hour) + ")}";
        }

        if (isWildCard(day)) {
            return "${coord:days(1)}";
        }

        // The day must be an interval. If it wasn't, then
        // the schedule was malformed and should have yielded
        // an exception.
        assert (isInterval(day));

        return "${coord:days(" + getInterval(day) + ")}";
    }

    public static Instant nextInstant(String cronSchedule, Instant current) {

        DateTime currentTime = new DateTime(current, DateTimeZone.UTC).withSecondOfMinute(0).withMillisOfSecond(0);

        validate(cronSchedule);

        String[] splits = cronSchedule.split(" ");

        String minutePart = splits[0];
        String hourPart = splits[1];
        String dayPart = splits[2];

        // TODO: fold these together like the hour.
        if (isWildCard(minutePart)) {
            return currentTime.plusMinutes(1).toInstant();
        }

        if (isInterval(minutePart)) {

            // Roll minutes forward until we hit a start time
            // that matches the cron interval.
            int interval = getInterval(minutePart);

            DateTime next = currentTime.withSecondOfMinute(0).plusMinutes(1);

            while (!(next.getMinuteOfHour() % interval == 0)) {

                next = next.plusMinutes(1);
            }

            return next.toInstant();
        }

        assert (isConstant(minutePart));

        // The minute part must be a constant, so
        // simply get the value.
        int minute = Integer.parseInt(minutePart);

        // The schedule is based on hours.
        if (!isConstant(hourPart)) {

            int hourModulus = isWildCard(hourPart) ? 1 : getInterval(hourPart);

            DateTime next = currentTime.withMinuteOfHour(minute);

            while (next.isBefore(current) || !(next.getHourOfDay() % hourModulus == 0)) {

                next = next.plusHours(1);
            }

            return next.toInstant();
        }

        int hour = Integer.parseInt(hourPart);

        // The schedule is based on days, and therfore the day cannot
        // be a constant. This is is checked in validation as well.
        assert (!isConstant(dayPart));

        DateTime next = currentTime.withMinuteOfHour(minute).withHourOfDay(hour);

        while (next.isBefore(current)) {

            next = next.plusDays(1);
        }

        return next.toInstant();
    }
}