001/*
002 * Copyright 2007 The Kuali Foundation
003 * 
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 * http://www.opensource.org/licenses/ecl2.php
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.ole.gl.batch.service.impl;
017
018import java.util.Calendar;
019import java.util.Date;
020import java.util.StringTokenizer;
021
022import org.apache.commons.lang.StringUtils;
023import org.kuali.ole.gl.GeneralLedgerConstants;
024import org.kuali.ole.gl.batch.ScrubberStep;
025import org.kuali.ole.gl.batch.service.RunDateService;
026import org.kuali.rice.coreservice.framework.parameter.ParameterService;
027
028/**
029 * The default implementation of RunDateService
030 */
031public class RunDateServiceImpl implements RunDateService {
032    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(RunDateServiceImpl.class);
033
034    private ParameterService parameterService;
035
036    /**
037     * 
038     * @see org.kuali.ole.gl.batch.service.RunDateService#calculateRunDate(java.util.Date)
039     */
040    public Date calculateRunDate(Date executionDate) {
041        Calendar currentCal = Calendar.getInstance();
042        currentCal.setTime(executionDate);
043
044        CutoffTime cutoffTime = parseCutoffTime(retrieveCutoffTimeValue());
045
046        if (isCurrentDateBeforeCutoff(currentCal, cutoffTime)) {
047            // time to set the date to the previous day's last minute/second
048            currentCal.add(Calendar.DAY_OF_MONTH, -1);
049            // per old COBOL code (see KULRNE-70),
050            // the time is set to 23:59:59 (assuming 0 ms)
051            currentCal.set(Calendar.HOUR_OF_DAY, 23);
052            currentCal.set(Calendar.MINUTE, 59);
053            currentCal.set(Calendar.SECOND, 59);
054            currentCal.set(Calendar.MILLISECOND, 0);
055            return new Date(currentCal.getTimeInMillis());
056        }
057        return new Date(executionDate.getTime());
058    }
059
060    /**
061     * Determines if the given calendar time is before the given cutoff time
062     * 
063     * @param currentCal the current time
064     * @param cutoffTime the "start of the day" cut off time
065     * @return true if the current time is before the cutoff, false otherwise
066     */
067    protected boolean isCurrentDateBeforeCutoff(Calendar currentCal, CutoffTime cutoffTime) {
068        if (cutoffTime != null) {
069            // if cutoff date is not properly defined
070            // 24 hour clock (i.e. hour is 0 - 23)
071
072            // clone the calendar so we get the same month, day, year
073            // then change the hour, minute, second fields
074            // then see if the cutoff is before or after
075            Calendar cutoffCal = (Calendar) currentCal.clone();
076            cutoffCal.setLenient(false);
077            cutoffCal.set(Calendar.HOUR_OF_DAY, cutoffTime.hour);
078            cutoffCal.set(Calendar.MINUTE, cutoffTime.minute);
079            cutoffCal.set(Calendar.SECOND, cutoffTime.second);
080            cutoffCal.set(Calendar.MILLISECOND, 0);
081
082            return currentCal.before(cutoffCal);
083        }
084        // if cutoff date is not properly defined, then it is considered to be after the cutoff
085        return false;
086    }
087
088    /**
089     * Holds the hour, minute, and second of a given cut off time
090     */
091    protected class CutoffTime {
092        /**
093         * 24 hour time, from 0-23, inclusive
094         */
095        protected int hour;
096
097        /**
098         * From 0-59, inclusive
099         */
100        protected int minute;
101
102        /**
103         * From 0-59, inclusive
104         */
105        protected int second;
106
107        /**
108         * Constructs a RunDateServiceImpl instance
109         * @param hour the cutoff hour
110         * @param minute the cutoff minute
111         * @param second the cutoff second
112         */
113        protected CutoffTime(int hour, int minute, int second) {
114            this.hour = hour;
115            this.minute = minute;
116            this.second = second;
117        }
118    }
119
120    /**
121     * Parses a String representation of the cutoff time
122     * 
123     * @param cutoffTime the cutoff time String to parse
124     * @return a record holding the cutoff time
125     */
126    protected CutoffTime parseCutoffTime(String cutoffTime) {
127        if (StringUtils.isBlank(cutoffTime)) {
128            return new CutoffTime(0, 0, 0);
129        }
130        else {
131            cutoffTime = cutoffTime.trim();
132            if (LOG.isDebugEnabled()) {
133                LOG.debug("Cutoff time value found: " + cutoffTime);
134            }
135            StringTokenizer st = new StringTokenizer(cutoffTime, ":", false);
136
137            try {
138                String hourStr = st.nextToken();
139                String minuteStr = st.nextToken();
140                String secondStr = st.nextToken();
141
142                int hourInt = Integer.parseInt(hourStr, 10);
143                int minuteInt = Integer.parseInt(minuteStr, 10);
144                int secondInt = Integer.parseInt(secondStr, 10);
145
146                if (hourInt < 0 || hourInt > 23 || minuteInt < 0 || minuteInt > 59 || secondInt < 0 || secondInt > 59) {
147                    throw new IllegalArgumentException("Cutoff time must be in the format \"HH:mm:ss\", where HH, mm, ss are defined in the java.text.SimpleDateFormat class.  In particular, 0 <= hour <= 23, 0 <= minute <= 59, and 0 <= second <= 59");
148                }
149                return new CutoffTime(hourInt, minuteInt, secondInt);
150            }
151            catch (Exception e) {
152                throw new IllegalArgumentException("Cutoff time should either be null, or in the format \"HH:mm:ss\", where HH, mm, ss are defined in the java.text.SimpleDateFormat class.");
153            }
154        }
155    }
156
157    /**
158     * Retrieves the cutoff time from a repository.
159     * 
160     * @return a time of day in the format "HH:mm:ss", where HH, mm, ss are defined in the java.text.SimpleDateFormat class. In
161     *         particular, 0 <= hour <= 23, 0 <= minute <= 59, and 0 <= second <= 59
162     */
163    protected String retrieveCutoffTimeValue() {
164        String value = parameterService.getParameterValueAsString(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.SCRUBBER_CUTOFF_TIME);
165        if (StringUtils.isBlank(value)) {
166            LOG.error("Unable to retrieve parameter for GL process cutoff date.  Defaulting to no cutoff time (i.e. midnight)");
167            value = null;
168        }
169        return value;
170    }
171
172    public void setParameterService(ParameterService parameterService) {
173        this.parameterService = parameterService;
174    }
175
176}