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.module.purap.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.module.purap.PurapParameterConstants;
024 import org.kuali.kfs.module.purap.batch.service.PurapRunDateService;
025 import org.kuali.kfs.sys.service.impl.KfsParameterConstants.PURCHASING_BATCH;
026 import org.kuali.rice.kns.service.ParameterService;
027
028 public class PurapRunDateServiceImpl implements PurapRunDateService {
029 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurapRunDateServiceImpl.class);
030
031 private ParameterService parameterService;
032
033 /**
034 * Determines the date to assume when running the batch processes
035 */
036 public Date calculateRunDate(Date executionDate) {
037 Calendar currentCal = Calendar.getInstance();
038 currentCal.setTime(executionDate);
039
040 CutoffTime cutoffTime = parseCutoffTime(retrieveCutoffTimeValue());
041
042 if (isCurrentDateAfterCutoff(currentCal, cutoffTime)) {
043 // go back one day
044 currentCal.add(Calendar.DAY_OF_MONTH, 1);
045 adjustTimeOfDay(currentCal, true);
046 }
047 else {
048 adjustTimeOfDay(currentCal, false);
049 }
050
051 return currentCal.getTime();
052 }
053
054 /**
055 * Adjusts the time of day if necessary, possibly depending on whether the execution time was before or after cutoff time
056 *
057 * @param calendar
058 * @param appliedCutoff true if the execution time was before the cutoff
059 */
060 protected void adjustTimeOfDay(Calendar calendar, boolean appliedCutoff) {
061 calendar.set(Calendar.HOUR_OF_DAY, 0);
062 calendar.set(Calendar.MINUTE, 0);
063 calendar.set(Calendar.SECOND, 0);
064 calendar.set(Calendar.MILLISECOND, 0);
065 }
066 /**
067 * Determines if the given calendar time is before the given cutoff time
068 *
069 * @param currentCal the current time
070 * @param cutoffTime the "start of the day" cut off time
071 * @return true if the current time is before the cutoff, false otherwise
072 */
073 protected boolean isCurrentDateAfterCutoff(Calendar currentCal, CutoffTime cutoffTime) {
074 if (cutoffTime != null) {
075 // if cutoff date is not properly defined
076 // 24 hour clock (i.e. hour is 0 - 23)
077
078 // clone the calendar so we get the same month, day, year
079 // then change the hour, minute, second fields
080 // then see if the cutoff is before or after
081 Calendar cutoffCal = (Calendar) currentCal.clone();
082 cutoffCal.setLenient(false);
083 cutoffCal.set(Calendar.HOUR_OF_DAY, cutoffTime.hour);
084 cutoffCal.set(Calendar.MINUTE, cutoffTime.minute);
085 cutoffCal.set(Calendar.SECOND, cutoffTime.second);
086 cutoffCal.set(Calendar.MILLISECOND, 0);
087
088 return currentCal.after(cutoffCal);
089 }
090 // if cutoff date is not properly defined, then it is considered to be before the cutoff, that is, no cutoff date will be applied
091 return false;
092 }
093
094 /**
095 * Holds the hour, minute, and second of a given cut off time
096 */
097 protected class CutoffTime {
098 /**
099 * 24 hour time, from 0-23, inclusive
100 */
101 protected int hour;
102
103 /**
104 * From 0-59, inclusive
105 */
106 protected int minute;
107
108 /**
109 * From 0-59, inclusive
110 */
111 protected int second;
112
113 /**
114 * Constructs a RunDateServiceImpl instance
115 * @param hour the cutoff hour
116 * @param minute the cutoff minute
117 * @param second the cutoff second
118 */
119 protected CutoffTime(int hour, int minute, int second) {
120 this.hour = hour;
121 this.minute = minute;
122 this.second = second;
123 }
124 }
125
126 /**
127 * Parses a String representation of the cutoff time
128 *
129 * @param cutoffTime the cutoff time String to parse
130 * @return a record holding the cutoff time
131 */
132 protected CutoffTime parseCutoffTime(String cutoffTime) {
133 if (StringUtils.isBlank(cutoffTime)) {
134 return null;
135 }
136 else {
137 cutoffTime = cutoffTime.trim();
138 LOG.debug("Cutoff time value found: " + cutoffTime);
139 StringTokenizer st = new StringTokenizer(cutoffTime, ":", false);
140
141 try {
142 String hourStr = st.nextToken();
143 String minuteStr = st.nextToken();
144 String secondStr = st.nextToken();
145
146 int hourInt = Integer.parseInt(hourStr, 10);
147 int minuteInt = Integer.parseInt(minuteStr, 10);
148 int secondInt = Integer.parseInt(secondStr, 10);
149
150 if (hourInt < 0 || hourInt > 23 || minuteInt < 0 || minuteInt > 59 || secondInt < 0 || secondInt > 59) {
151 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");
152 }
153 return new CutoffTime(hourInt, minuteInt, secondInt);
154 }
155 catch (Exception e) {
156 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.");
157 }
158 }
159 }
160
161 /**
162 * Retrieves the cutoff time from a repository.
163 *
164 * @return a time of day in the format "HH:mm:ss", where HH, mm, ss are defined in the java.text.SimpleDateFormat class. In
165 * particular, 0 <= hour <= 23, 0 <= minute <= 59, and 0 <= second <= 59
166 */
167 protected String retrieveCutoffTimeValue() {
168 String value = parameterService.getParameterValue(PURCHASING_BATCH.class, PurapParameterConstants.PRE_DISBURSEMENT_EXTRACT_CUTOFF_TIME);
169 if (StringUtils.isBlank(value)) {
170 LOG.info("Unable to retrieve parameter for PURAP process cutoff date. Defaulting to no cutoff time (i.e. midnight)");
171 value = null;
172 }
173 return value;
174 }
175
176 public void setParameterService(ParameterService parameterService) {
177 this.parameterService = parameterService;
178 }
179 }