001    /*
002     * Copyright 2011 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     */
016    package org.kuali.kfs.gl.batch.service.impl;
017    
018    import java.util.Calendar;
019    import java.util.Date;
020    import java.util.StringTokenizer;
021    
022    import org.apache.commons.lang.StringUtils;
023    import org.kuali.kfs.gl.GeneralLedgerConstants;
024    import org.kuali.kfs.gl.batch.ScrubberStep;
025    import org.kuali.kfs.gl.batch.service.RunDateService;
026    import org.kuali.rice.kns.service.ParameterService;
027    
028    /**
029     * The default implementation of RunDateService
030     */
031    public 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.kfs.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                LOG.debug("Cutoff time value found: " + cutoffTime);
133                StringTokenizer st = new StringTokenizer(cutoffTime, ":", false);
134    
135                try {
136                    String hourStr = st.nextToken();
137                    String minuteStr = st.nextToken();
138                    String secondStr = st.nextToken();
139    
140                    int hourInt = Integer.parseInt(hourStr, 10);
141                    int minuteInt = Integer.parseInt(minuteStr, 10);
142                    int secondInt = Integer.parseInt(secondStr, 10);
143    
144                    if (hourInt < 0 || hourInt > 23 || minuteInt < 0 || minuteInt > 59 || secondInt < 0 || secondInt > 59) {
145                        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");
146                    }
147                    return new CutoffTime(hourInt, minuteInt, secondInt);
148                }
149                catch (Exception e) {
150                    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.");
151                }
152            }
153        }
154    
155        /**
156         * Retrieves the cutoff time from a repository.
157         * 
158         * @return a time of day in the format "HH:mm:ss", where HH, mm, ss are defined in the java.text.SimpleDateFormat class. In
159         *         particular, 0 <= hour <= 23, 0 <= minute <= 59, and 0 <= second <= 59
160         */
161        protected String retrieveCutoffTimeValue() {
162            String value = parameterService.getParameterValue(ScrubberStep.class, GeneralLedgerConstants.GlScrubberGroupParameters.SCRUBBER_CUTOFF_TIME);
163            if (StringUtils.isBlank(value)) {
164                LOG.error("Unable to retrieve parameter for GL process cutoff date.  Defaulting to no cutoff time (i.e. midnight)");
165                value = null;
166            }
167            return value;
168        }
169    
170        public void setParameterService(ParameterService parameterService) {
171            this.parameterService = parameterService;
172        }
173    
174    }