This is an automated email from the ASF dual-hosted git repository.
pefernan pushed a commit to branch main
in repository
https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git
The following commit(s) were added to refs/heads/main by this push:
new 8dde7b8ff5 [incubator-kie-issues#1612] Fix BusinessCalendar behavior
with inconsistent (full/partial) set of properties on `calendar.properties`
(#3788)
8dde7b8ff5 is described below
commit 8dde7b8ff5537c850519d21435ae5abfdfcf1186
Author: Abhiram Gundala <[email protected]>
AuthorDate: Mon Dec 9 06:30:05 2024 -0500
[incubator-kie-issues#1612] Fix BusinessCalendar behavior with inconsistent
(full/partial) set of properties on `calendar.properties` (#3788)
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] TODO setup
* [incubator-kie-issues#1612] Separating different concerns in different
classes: CalendarBean is responsible of calendar.properties file.
BusinessCalendarImpl is responsible of working time evaluation
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Example test
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Extend test coverage. Minor refactoring
related to it.
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Comment on tests
* [incubator-kie-issues#1612] Minor fixes
* [incubator-kie-issues#1612] Extend test coverage. Minor refactoring
related to it.
* [incubator-kie-issues#1612] Minor refactoring
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Fixing tests. Including
incubator-kie-issues#1648 fix
* [incubator-kie-issues#1612] WIP - implementing nightly hour
* [incubator-kie-issues#1612] WIP - Simplify test - moving tested methods
to static
* [incubator-kie-issues#1612] WIP - Cleanup
* [incubator-kie-issues#1612] Working tests.
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Fixed logging
* [incubator-kie-issues#1612] Fixed minute/second reset on calendarRolling
* [incubator-kie-issues#1612] Fixed assertiont JUnit5 -> assertj
* [incubator-kie-issues#1612] Fixed test
* incubator-kie-issues-1612
* incubator-kie-issues-1612
* [incubator-kie-issues#1612] Avoid minute/second reset on rolling hour,
since the minute/second management is based on "add" operation
* [incubator-kie-issues#1612] Fix naming
* [incubator-kie-issues#1612] Add minute / second test/fix
* [incubator-kie-issues#1612] Extend test coverage
* updated logging
* logger update
* logger update
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../org/kie/kogito/calendar/BusinessCalendar.java | 13 +-
.../process/core/timer/BusinessCalendarImpl.java | 622 +++++++++---------
.../org/jbpm/process/core/timer/CalendarBean.java | 399 ++++++++++++
.../process/core/timer/CalendarBeanFactory.java | 60 ++
.../core/timer/BusinessCalendarImplTest.java | 696 +++++++++------------
.../core/timer/CalendarBeanFactoryTest.java | 45 ++
.../jbpm/process/core/timer/CalendarBeanTest.java | 297 +++++++++
.../src/test/resources/calendar.properties | 20 +
jbpm/jbpm-flow/src/test/resources/logback-test.xml | 1 +
....java => BusinessCalendarTimerProcessTest.java} | 24 +-
.../BusinessCalendarProducerQuarkusTemplate.java | 2 +-
.../BusinessCalendarProducerSpringTemplate.java | 2 +-
.../BusinessCalendarProducerQuarkusTemplate.java | 36 --
.../BusinessCalendarProducerSpringTemplate.java | 37 --
14 files changed, 1450 insertions(+), 804 deletions(-)
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
b/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
index 6e15a6cc0e..45ecce21bd 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
+++ b/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
@@ -27,18 +27,21 @@ import java.util.Date;
public interface BusinessCalendar {
/**
- * Calculates given time expression into duration in milliseconds based on
calendar configuration.
- *
+ * Returns the difference, in milliseconds, between the <b>business
date</b> that matches the given
+ * <code>timeExpression</code>, and the current time.
+ * See {@link #calculateBusinessTimeAsDate} for <b>business date</b>
calculation
+ *
* @param timeExpression time expression that is supported by business
calendar implementation.
* @return duration expressed in milliseconds
*/
- public long calculateBusinessTimeAsDuration(String timeExpression);
+ long calculateBusinessTimeAsDuration(String timeExpression);
/**
- * Calculates given time expression into target date based on calendar
configuration.
+ * Returns the first <code>Date</code> that matches the given
<code>timeExpression</code> and falls
+ * into the business calendar working hours.
*
* @param timeExpression time expression that is supported by business
calendar implementation.
* @return date when given time expression will match in the future
*/
- public Date calculateBusinessTimeAsDate(String timeExpression);
+ Date calculateBusinessTimeAsDate(String timeExpression);
}
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendarImpl.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendarImpl.java
index b22f48cbb3..280cc3f6e3 100755
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendarImpl.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendarImpl.java
@@ -18,48 +18,37 @@
*/
package org.jbpm.process.core.timer;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Objects;
-import java.util.Properties;
import java.util.TimeZone;
import java.util.regex.Matcher;
import org.jbpm.util.PatternConstants;
import org.kie.kogito.calendar.BusinessCalendar;
-import org.kie.kogito.timer.SessionClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_PATH;
-
/**
* Default implementation of BusinessCalendar interface that is configured
with properties.
* Following are supported properties:
* <ul>
- * <li>business.days.per.week - specifies number of working days per week
(default 5)</li>
- * <li>business.hours.per.day - specifies number of working hours per day
(default 8)</li>
- * <li>business.start.hour - specifies starting hour of work day (default
9AM)</li>
- * <li>business.end.hour - specifies ending hour of work day (default 5PM)</li>
+ * <li>business.start.hour - specifies starting hour of work day
(mandatory)</li>
+ * <li>business.end.hour - specifies ending hour of work day (mandatory)</li>
* <li>business.holidays - specifies holidays (see format section for details
on how to configure it)</li>
* <li>business.holiday.date.format - specifies holiday date format used
(default yyyy-MM-dd)</li>
- * <li>business.weekend.days - specifies days of the weekend (default Saturday
and Sunday)</li>
+ * <li>business.weekend.days - specifies days of the weekend (default Saturday
(7) and Sunday (1), use 0 to indicate no weekend days)</li>
* <li>business.cal.timezone - specifies time zone to be used (if not given
uses default of the system it runs on)</li>
* </ul>
- *
+ *
* <b>Format</b><br/>
- *
+ *
* Holidays can be given in two formats:
* <ul>
* <li>as date range separated with colon - for instance
2012-05-01:2012-05-15</li>
@@ -68,28 +57,29 @@ import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDA
* each holiday period should be separated from next one with comma:
2012-05-01:2012-05-15,2012-12-24:2012-12-27
* <br/>
* Holiday date format must be given in pattern that is supported by
<code>java.text.SimpleDateFormat</code>.<br/>
- *
- * Weekend days should be given as integer that corresponds to
<code>java.util.Calendar</code> constants.
+ *
+ * Weekend days should be given as integer that corresponds to
<code>java.util.Calendar</code> constants, use 0 to indicate no weekend days
* <br/>
- *
*/
public class BusinessCalendarImpl implements BusinessCalendar {
private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarImpl.class);
- private Properties businessCalendarConfiguration;
-
private static final long HOUR_IN_MILLIS = 60 * 60 * 1000;
- private int daysPerWeek;
- private int hoursInDay;
- private int startHour;
- private int endHour;
- private String timezone;
+ private final int daysPerWeek;
+ private final int hoursInDay;
+ private final int startHour;
+ private final int endHour;
+ private final String timezone;
- private List<TimePeriod> holidays;
- private List<Integer> weekendDays = new ArrayList<>();
- private SessionClock clock;
+ private final List<TimePeriod> holidays;
+ private final List<Integer> weekendDays;
+
+ /**
+ * Testing calendar used only for testing purposes
+ */
+ private final Calendar testingCalendar;
private static final int SIM_WEEK = 3;
private static final int SIM_DAY = 5;
@@ -97,8 +87,6 @@ public class BusinessCalendarImpl implements BusinessCalendar
{
private static final int SIM_MIN = 9;
private static final int SIM_SEC = 11;
- public static final String DAYS_PER_WEEK = "business.days.per.week";
- public static final String HOURS_PER_DAY = "business.hours.per.day";
public static final String START_HOUR = "business.start.hour";
public static final String END_HOUR = "business.end.hour";
// holidays are given as date range and can have more than one value
separated with comma
@@ -108,97 +96,53 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
public static final String WEEKEND_DAYS = "business.weekend.days";
public static final String TIMEZONE = "business.cal.timezone";
- public BusinessCalendarImpl() {
- this(null);
+ public static Builder builder() {
+ return new Builder();
}
- public BusinessCalendarImpl(Properties configuration) {
- this(configuration, null);
+ /**
+ *
+ * @param testingCalendar is used only for testing purpose. It is
<code>null</code> in production and
+ * during normal execution
+ */
+ private BusinessCalendarImpl(Calendar testingCalendar) {
+ this(CalendarBeanFactory.createCalendarBean(), testingCalendar);
}
- public BusinessCalendarImpl(Properties configuration, SessionClock clock) {
- this.clock = clock;
- if (configuration == null) {
- businessCalendarConfiguration = new Properties();
- URL resource =
Thread.currentThread().getContextClassLoader().getResource(BUSINESS_CALENDAR_PATH);
- if (Objects.nonNull(resource)) {
- try (InputStream is = resource.openStream()) {
- businessCalendarConfiguration.load(is);
- } catch (IOException e) {
- logger.error("Error while loading properties for business
calendar", e);
- throw new RuntimeException("Error while loading properties
for business calendar", e);
- }
- }
-
- } else {
- this.businessCalendarConfiguration = configuration;
- }
- init();
- }
-
- protected void init() {
- daysPerWeek = getPropertyAsInt(DAYS_PER_WEEK, "5");
- hoursInDay = getPropertyAsInt(HOURS_PER_DAY, "8");
- startHour = getPropertyAsInt(START_HOUR, "9");
- endHour = getPropertyAsInt(END_HOUR, "17");
- holidays = parseHolidays();
- parseWeekendDays();
- this.timezone = businessCalendarConfiguration.getProperty(TIMEZONE);
- }
-
- protected String adoptISOFormat(String timeExpression) {
-
- try {
- Duration p = null;
- if (DateTimeUtils.isPeriod(timeExpression)) {
- p = Duration.parse(timeExpression);
- } else if (DateTimeUtils.isNumeric(timeExpression)) {
- p = Duration.of(Long.valueOf(timeExpression),
ChronoUnit.MILLIS);
- } else {
- OffsetDateTime dateTime = OffsetDateTime.parse(timeExpression,
DateTimeFormatter.ISO_DATE_TIME);
- p = Duration.between(OffsetDateTime.now(), dateTime);
- }
-
- long days = p.toDays();
- long hours = p.toHours() % 24;
- long minutes = p.toMinutes() % 60;
- long seconds = p.getSeconds() % 60;
- long milis = p.toMillis() % 1000;
-
- StringBuffer time = new StringBuffer();
- if (days > 0) {
- time.append(days + "d");
- }
- if (hours > 0) {
- time.append(hours + "h");
- }
- if (minutes > 0) {
- time.append(minutes + "m");
- }
- if (seconds > 0) {
- time.append(seconds + "s");
- }
- if (milis > 0) {
- time.append(milis + "ms");
- }
-
- return time.toString();
- } catch (Exception e) {
- return timeExpression;
- }
+ private BusinessCalendarImpl(CalendarBean calendarBean, Calendar
testingCalendar) {
+ holidays = calendarBean.getHolidays();
+ weekendDays = calendarBean.getWeekendDays();
+ daysPerWeek = calendarBean.getDaysPerWeek();
+ timezone = calendarBean.getTimezone();
+ startHour = calendarBean.getStartHour();
+ endHour = calendarBean.getEndHour();
+ hoursInDay = calendarBean.getHoursInDay();
+ this.testingCalendar = testingCalendar;
+ logger.debug("\tholidays: {},\n\tweekendDays: {},\n\tdaysPerWeek:
{},\n\ttimezone: {},\n\tstartHour: {},\n\tendHour: {},\n\thoursInDay: {}",
+ holidays, weekendDays, daysPerWeek, timezone, startHour,
endHour, hoursInDay);
}
+ /**
+ * @inheritDoc
+ */
+ @Override
public long calculateBusinessTimeAsDuration(String timeExpression) {
+ logger.trace("timeExpression {}", timeExpression);
timeExpression = adoptISOFormat(timeExpression);
Date calculatedDate = calculateBusinessTimeAsDate(timeExpression);
+ logger.debug("calculatedDate: {}, currentTime: {}, timeExpression: {},
Difference: {} ms",
+ calculatedDate, new Date(getCurrentTime()), timeExpression,
calculatedDate.getTime() - getCurrentTime());
- long calculatedDurationInMs = (calculatedDate.getTime() -
getCurrentTime());
- logger.debug("calculateBusinessTimeAsDuration for expression {}
returns {} seconds", timeExpression, (calculatedDurationInMs / 1000));
- return calculatedDurationInMs;
+ return (calculatedDate.getTime() - getCurrentTime());
}
+ /**
+ * @inheritDoc
+ */
+ @Override
public Date calculateBusinessTimeAsDate(String timeExpression) {
+ logger.trace("timeExpression {}", timeExpression);
timeExpression = adoptISOFormat(timeExpression);
String trimmed = timeExpression.trim();
@@ -208,7 +152,7 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
int min = 0;
int sec = 0;
- if (trimmed.length() > 0) {
+ if (!trimmed.isEmpty()) {
Matcher mat =
PatternConstants.SIMPLE_TIME_DATE_MATCHER.matcher(trimmed);
if (mat.matches()) {
weeks = (mat.group(SIM_WEEK) != null) ?
Integer.parseInt(mat.group(SIM_WEEK)) : 0;
@@ -218,121 +162,208 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
sec = (mat.group(SIM_SEC) != null) ?
Integer.parseInt(mat.group(SIM_SEC)) : 0;
}
}
+ logger.trace("weeks: {}", weeks);
+ logger.trace("days: {}", days);
+ logger.trace("hours: {}", hours);
+ logger.trace("min: {}", min);
+ logger.trace("sec: {}", sec);
int time = 0;
- Calendar c = new GregorianCalendar();
+ Calendar calendar = getCalendar();
+ logger.trace("calendar selected for business calendar: {}",
calendar.getTime());
if (timezone != null) {
- c.setTimeZone(TimeZone.getTimeZone(timezone));
- }
- if (this.clock != null) {
- c.setTimeInMillis(this.clock.getCurrentTime());
+ calendar.setTimeZone(TimeZone.getTimeZone(timezone));
}
// calculate number of weeks
int numberOfWeeks = days / daysPerWeek + weeks;
+ logger.trace("number of weeks: {}", numberOfWeeks);
if (numberOfWeeks > 0) {
- c.add(Calendar.WEEK_OF_YEAR, numberOfWeeks);
+ calendar.add(Calendar.WEEK_OF_YEAR, numberOfWeeks);
}
- handleWeekend(c, hours > 0 || min > 0);
+ logger.trace("calendar WEEK_OF_YEAR: {}",
calendar.get(Calendar.WEEK_OF_YEAR));
+ rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, hours > 0 || min > 0);
hours += (days - (numberOfWeeks * daysPerWeek)) * hoursInDay;
// calculate number of days
int numberOfDays = hours / hoursInDay;
+ logger.trace("numberOfDays: {}", numberOfDays);
if (numberOfDays > 0) {
for (int i = 0; i < numberOfDays; i++) {
- c.add(Calendar.DAY_OF_YEAR, 1);
- handleWeekend(c, false);
- handleHoliday(c, hours > 0 || min > 0);
+ calendar.add(Calendar.DAY_OF_YEAR, 1);
+ boolean resetTime = false;
+ rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, resetTime);
+ logger.trace("calendar after rolling to next working day: {}
when number of days > 0", calendar.getTime());
+ rollCalendarAfterHolidays(calendar, holidays, weekendDays,
hours > 0 || min > 0);
+ logger.trace("calendar after holidays when number of days > 0:
{}", calendar.getTime());
}
}
-
- int currentCalHour = c.get(Calendar.HOUR_OF_DAY);
- if (currentCalHour >= endHour) {
- c.add(Calendar.DAY_OF_YEAR, 1);
- c.add(Calendar.HOUR_OF_DAY, startHour - currentCalHour);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
- } else if (currentCalHour < startHour) {
- c.add(Calendar.HOUR_OF_DAY, startHour - currentCalHour);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
- }
+ int currentCalHour = calendar.get(Calendar.HOUR_OF_DAY);
+ boolean resetMinuteSecond = currentCalHour >= endHour ||
currentCalHour < startHour;
+ rollCalendarToWorkingHour(calendar, resetMinuteSecond);
+ logger.trace("calendar after rolling to working hour: {}",
calendar.getTime());
// calculate remaining hours
time = hours - (numberOfDays * hoursInDay);
- c.add(Calendar.HOUR, time);
- handleWeekend(c, true);
- handleHoliday(c, hours > 0 || min > 0);
-
- currentCalHour = c.get(Calendar.HOUR_OF_DAY);
- if (currentCalHour >= endHour) {
- c.add(Calendar.DAY_OF_YEAR, 1);
- // set hour to the starting one
- c.set(Calendar.HOUR_OF_DAY, startHour);
- c.add(Calendar.HOUR_OF_DAY, currentCalHour - endHour);
- } else if (currentCalHour < startHour) {
- c.add(Calendar.HOUR_OF_DAY, startHour - currentCalHour);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
- }
+ calendar.add(Calendar.HOUR, time);
+ logger.trace("calendar after adding time {}: {}", time,
calendar.getTime());
+ boolean resetTime = true;
+ rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, resetTime);
+ logger.trace("calendar after rolling to next working day: {}",
calendar.getTime());
+ rollCalendarAfterHolidays(calendar, holidays, weekendDays, hours > 0
|| min > 0);
+ logger.trace("calendar after holidays: {}", calendar.getTime());
+ rollCalendarToWorkingHour(calendar, false);
+ logger.trace("calendar after rolling to working hour: {}",
calendar.getTime());
// calculate minutes
int numberOfHours = min / 60;
if (numberOfHours > 0) {
- c.add(Calendar.HOUR, numberOfHours);
+ calendar.add(Calendar.HOUR, numberOfHours);
min = min - (numberOfHours * 60);
}
- c.add(Calendar.MINUTE, min);
+ calendar.add(Calendar.MINUTE, min);
// calculate seconds
int numberOfMinutes = sec / 60;
if (numberOfMinutes > 0) {
- c.add(Calendar.MINUTE, numberOfMinutes);
+ calendar.add(Calendar.MINUTE, numberOfMinutes);
sec = sec - (numberOfMinutes * 60);
}
- c.add(Calendar.SECOND, sec);
+ calendar.add(Calendar.SECOND, sec);
+ logger.trace("calendar after adding {} hour, {} minutes and {}
seconds: {}", numberOfHours, numberOfMinutes, sec, calendar.getTime());
+
+ rollCalendarToWorkingHour(calendar, false);
+ logger.trace("calendar after rolling to next working day: {}",
calendar.getTime());
+
+ // take under consideration weekend
+ resetTime = false;
+ rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, resetTime);
+ logger.trace("calendar after rolling to next working day: {}",
calendar.getTime());
+ // take under consideration holidays
+ rollCalendarAfterHolidays(calendar, holidays, weekendDays, resetTime);
+ logger.trace("calendar after holidays: {}", calendar.getTime());
+
+ return calendar.getTime();
+ }
+
+ /**
+ * Indirection used only for testing purposes
+ *
+ * @return
+ */
+ protected Calendar getCalendar() {
+ String debugMessage = testingCalendar != null ? "Returning clone of
testingCalendar " : "Return new GregorianCalendar";
+ logger.trace(debugMessage);
+ return testingCalendar != null ? (Calendar) testingCalendar.clone() :
new GregorianCalendar();
+ }
- currentCalHour = c.get(Calendar.HOUR_OF_DAY);
+ /**
+ * Rolls the <code>HOUR_OF_DAY</code> of the given <code>Calendar</code>
depending on
+ * given <code>currentCalHour</code>, instance <code>endHour</code>, and
instance <code>startHour</code>
+ *
+ * It also consider if the startHour < endHour (i.e. working daily hours)
or startHour > endHour (i.e. nightly daily hours).
+ *
+ * The case where startHour = endHour is excluded by validation of the
<code>CalendarBean</code>
+ *
+ * @param toRoll
+ * @param resetMinuteSecond if <code>true</code>, set minutes and seconds
to 0
+ */
+ protected void rollCalendarToWorkingHour(Calendar toRoll, boolean
resetMinuteSecond) {
+ logger.trace("toRoll: {}", toRoll.getTime());
+ if (startHour < endHour) {
+ rollCalendarToDailyWorkingHour(toRoll, startHour, endHour);
+ } else {
+ throw new UnsupportedOperationException(String.format("This
feature is not supported yet: %s should be greater than %s", END_HOUR,
START_HOUR));
+ }
+ if (resetMinuteSecond) {
+ toRoll.set(Calendar.MINUTE, 0);
+ toRoll.set(Calendar.SECOND, 0);
+ }
+ }
+
+ /**
+ * Rolls the <code>HOUR_OF_DAY</code> of the given <code>Calendar</code>
to the next "daily" working hour
+ *
+ * @param toRoll
+ * @param startHour
+ * @param endHour
+ */
+ static void rollCalendarToDailyWorkingHour(Calendar toRoll, int startHour,
int endHour) {
+ logger.trace("toRoll: {}", toRoll.getTime());
+ logger.trace("startHour: {}", startHour);
+ logger.trace("endHour: {}", endHour);
+ int currentCalHour = toRoll.get(Calendar.HOUR_OF_DAY);
if (currentCalHour >= endHour) {
- c.add(Calendar.DAY_OF_YEAR, 1);
+ toRoll.add(Calendar.DAY_OF_YEAR, 1);
// set hour to the starting one
- c.set(Calendar.HOUR_OF_DAY, startHour);
- c.add(Calendar.HOUR_OF_DAY, currentCalHour - endHour);
+ toRoll.set(Calendar.HOUR_OF_DAY, startHour);
} else if (currentCalHour < startHour) {
- c.add(Calendar.HOUR_OF_DAY, startHour - currentCalHour);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
+ toRoll.add(Calendar.HOUR_OF_DAY, startHour - currentCalHour);
}
- // take under consideration weekend
- handleWeekend(c, false);
- // take under consideration holidays
- handleHoliday(c, false);
+ logger.trace("calendar after rolling to daily working hour: {}",
toRoll.getTime());
+ }
- return c.getTime();
+ /**
+ * Rolls the <code>HOUR_OF_DAY</code> of the given <code>Calendar</code>
to the next "nightly" working hour
+ *
+ * @param toRoll
+ * @param startHour
+ * @param endHour
+ */
+ static void rollCalendarToNightlyWorkingHour(Calendar toRoll, int
startHour, int endHour) {
+ logger.trace("toRoll: {}", toRoll.getTime());
+ logger.trace("startHour: {}", startHour);
+ logger.trace("endHour: {}", endHour);
+ int currentCalHour = toRoll.get(Calendar.HOUR_OF_DAY);
+ if (currentCalHour < endHour) {
+ toRoll.set(Calendar.HOUR_OF_DAY, endHour);
+ } else if (currentCalHour >= startHour) {
+ toRoll.add(Calendar.DAY_OF_YEAR, 1);
+ toRoll.set(Calendar.HOUR_OF_DAY, endHour);
+ }
+ toRoll.set(Calendar.MINUTE, 0);
+ toRoll.set(Calendar.SECOND, 0);
+ logger.debug("calendar after rolling to nightly working hour: {}",
toRoll.getTime());
}
- protected void handleHoliday(Calendar c, boolean resetTime) {
+ /**
+ * Rolls the given <code>Calendar</code> to the first <b>working day</b>
+ * after configured <code>holidays</code>, if provided.
+ *
+ * Set hour, minute, second and millisecond when
+ * <code>resetTime</code> is <code>true</code>
+ *
+ * @param toRoll
+ * @param holidays
+ * @param resetTime
+ */
+ static void rollCalendarAfterHolidays(Calendar toRoll, List<TimePeriod>
holidays, List<Integer> weekendDays, boolean resetTime) {
+ logger.trace("toRoll: {}", toRoll.getTime());
+ logger.trace("holidays: {}", holidays);
+ logger.trace("weekendDays: {}", weekendDays);
+ logger.trace("resetTime: {}", resetTime);
if (!holidays.isEmpty()) {
- Date current = c.getTime();
+ Date current = toRoll.getTime();
for (TimePeriod holiday : holidays) {
// check each holiday if it overlaps current date and break
after first match
if (current.after(holiday.getFrom()) &&
current.before(holiday.getTo())) {
- Calendar tmp = new GregorianCalendar();
- tmp.setTime(holiday.getTo());
+ Calendar lastHolidayDayTime = new GregorianCalendar();
+ lastHolidayDayTime.setTime(holiday.getTo());
- Calendar tmp2 = new GregorianCalendar();
- tmp2.setTime(current);
- tmp2.set(Calendar.HOUR_OF_DAY, 0);
- tmp2.set(Calendar.MINUTE, 0);
- tmp2.set(Calendar.SECOND, 0);
- tmp2.set(Calendar.MILLISECOND, 0);
+ Calendar currentDayTmp = new GregorianCalendar();
+ currentDayTmp.setTime(current);
+ currentDayTmp.set(Calendar.HOUR_OF_DAY, 0);
+ currentDayTmp.set(Calendar.MINUTE, 0);
+ currentDayTmp.set(Calendar.SECOND, 0);
+ currentDayTmp.set(Calendar.MILLISECOND, 0);
- long difference = tmp.getTimeInMillis() -
tmp2.getTimeInMillis();
+ long difference = lastHolidayDayTime.getTimeInMillis() -
currentDayTmp.getTimeInMillis();
+ int dayDifference = (int) Math.ceil(difference /
(HOUR_IN_MILLIS * 24d));
- c.add(Calendar.HOUR_OF_DAY, (int) (difference /
HOUR_IN_MILLIS));
+ toRoll.add(Calendar.DAY_OF_MONTH, dayDifference);
- handleWeekend(c, resetTime);
+
rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(toRoll, weekendDays,
resetTime);
break;
}
}
@@ -340,122 +371,113 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
}
- protected int getPropertyAsInt(String propertyName, String defaultValue) {
- String value = businessCalendarConfiguration.getProperty(propertyName,
defaultValue);
+ /**
+ * Rolls the given <code>Calendar</code> to the first <b>working day</b>
+ * Set hour, minute, second and millisecond when
+ * <code>resetTime</code> is <code>true</code>
+ *
+ * @param toRoll
+ * @param resetTime
+ */
+ static void rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(Calendar
toRoll, List<Integer> weekendDays, boolean resetTime) {
+ logger.trace("toRoll: {}", toRoll.getTime());
+ logger.trace("weekendDays: {}", weekendDays);
+ logger.trace("resetTime: {}", resetTime);
+ int dayOfTheWeek = toRoll.get(Calendar.DAY_OF_WEEK);
+ logger.trace("dayOfTheWeek: {}", dayOfTheWeek);
+ while (!isWorkingDay(weekendDays, dayOfTheWeek)) {
+ toRoll.add(Calendar.DAY_OF_YEAR, 1);
+ if (resetTime) {
+ toRoll.set(Calendar.HOUR_OF_DAY, 0);
+ toRoll.set(Calendar.MINUTE, 0);
+ toRoll.set(Calendar.SECOND, 0);
+ toRoll.set(Calendar.MILLISECOND, 0);
+ }
+ dayOfTheWeek = toRoll.get(Calendar.DAY_OF_WEEK);
+ }
+ logger.trace("dayOfTheWeek after rolling calendar: {}", dayOfTheWeek);
+ }
- return Integer.parseInt(value);
+ static boolean isWorkingDay(List<Integer> weekendDays, int day) {
+ logger.trace("weekendDays: {}", weekendDays);
+ logger.trace("day: {}", day);
+ return !weekendDays.contains(day);
}
- protected List<TimePeriod> parseHolidays() {
- String holidaysString =
businessCalendarConfiguration.getProperty(HOLIDAYS);
- List<TimePeriod> holidays = new ArrayList<>();
- int currentYear = Calendar.getInstance().get(Calendar.YEAR);
- if (holidaysString != null) {
- String[] hPeriods = holidaysString.split(",");
- SimpleDateFormat sdf = new
SimpleDateFormat(businessCalendarConfiguration.getProperty(HOLIDAY_DATE_FORMAT,
"yyyy-MM-dd"));
- for (String hPeriod : hPeriods) {
- boolean addNextYearHolidays = false;
-
- String[] fromTo = hPeriod.split(":");
- if (fromTo[0].startsWith("*")) {
- addNextYearHolidays = true;
-
- fromTo[0] = fromTo[0].replaceFirst("\\*", currentYear +
"");
- }
- try {
- if (fromTo.length == 2) {
- Calendar tmpFrom = new GregorianCalendar();
- if (timezone != null) {
-
tmpFrom.setTimeZone(TimeZone.getTimeZone(timezone));
- }
- tmpFrom.setTime(sdf.parse(fromTo[0]));
-
- if (fromTo[1].startsWith("*")) {
-
- fromTo[1] = fromTo[1].replaceFirst("\\*",
currentYear + "");
- }
-
- Calendar tmpTo = new GregorianCalendar();
- if (timezone != null) {
- tmpTo.setTimeZone(TimeZone.getTimeZone(timezone));
- }
- tmpTo.setTime(sdf.parse(fromTo[1]));
- Date from = tmpFrom.getTime();
-
- tmpTo.add(Calendar.DAY_OF_YEAR, 1);
-
- if ((tmpFrom.get(Calendar.MONTH) >
tmpTo.get(Calendar.MONTH)) && (tmpFrom.get(Calendar.YEAR) ==
tmpTo.get(Calendar.YEAR))) {
- tmpTo.add(Calendar.YEAR, 1);
- }
-
- Date to = tmpTo.getTime();
- holidays.add(new TimePeriod(from, to));
-
- holidays.add(new TimePeriod(from, to));
- if (addNextYearHolidays) {
- tmpFrom = new GregorianCalendar();
- if (timezone != null) {
-
tmpFrom.setTimeZone(TimeZone.getTimeZone(timezone));
- }
- tmpFrom.setTime(sdf.parse(fromTo[0]));
- tmpFrom.add(Calendar.YEAR, 1);
-
- from = tmpFrom.getTime();
- tmpTo = new GregorianCalendar();
- if (timezone != null) {
-
tmpTo.setTimeZone(TimeZone.getTimeZone(timezone));
- }
- tmpTo.setTime(sdf.parse(fromTo[1]));
- tmpTo.add(Calendar.YEAR, 1);
- tmpTo.add(Calendar.DAY_OF_YEAR, 1);
-
- if ((tmpFrom.get(Calendar.MONTH) >
tmpTo.get(Calendar.MONTH)) && (tmpFrom.get(Calendar.YEAR) ==
tmpTo.get(Calendar.YEAR))) {
- tmpTo.add(Calendar.YEAR, 1);
- }
-
- to = tmpTo.getTime();
- holidays.add(new TimePeriod(from, to));
- }
- } else {
-
- Calendar c = new GregorianCalendar();
- c.setTime(sdf.parse(fromTo[0]));
- c.add(Calendar.DAY_OF_YEAR, 1);
- // handle one day holiday
- holidays.add(new TimePeriod(sdf.parse(fromTo[0]),
c.getTime()));
- if (addNextYearHolidays) {
- Calendar tmp = Calendar.getInstance();
- tmp.setTime(sdf.parse(fromTo[0]));
- tmp.add(Calendar.YEAR, 1);
-
- Date from = tmp.getTime();
- c.add(Calendar.YEAR, 1);
- holidays.add(new TimePeriod(from, c.getTime()));
- }
- }
- } catch (Exception e) {
- logger.error("Error while parsing holiday in business
calendar", e);
- }
+ protected long getCurrentTime() {
+ String debugMessage = testingCalendar != null ? "Returning
testingCalendar time " : "Return System time";
+ return testingCalendar != null ? testingCalendar.getTimeInMillis() :
System.currentTimeMillis();
+ }
+
+ protected String adoptISOFormat(String timeExpression) {
+ logger.trace("timeExpression: {}", timeExpression);
+ try {
+ Duration p = null;
+ if (DateTimeUtils.isPeriod(timeExpression)) {
+ p = Duration.parse(timeExpression);
+ } else if (DateTimeUtils.isNumeric(timeExpression)) {
+ p = Duration.of(Long.valueOf(timeExpression),
ChronoUnit.MILLIS);
+ } else {
+ OffsetDateTime dateTime = OffsetDateTime.parse(timeExpression,
DateTimeFormatter.ISO_DATE_TIME);
+ p = Duration.between(OffsetDateTime.now(), dateTime);
+ }
+
+ long days = p.toDays();
+ long hours = p.toHours() % 24;
+ long minutes = p.toMinutes() % 60;
+ long seconds = p.getSeconds() % 60;
+ long milis = p.toMillis() % 1000;
+
+ StringBuffer time = new StringBuffer();
+ if (days > 0) {
+ time.append(days + "d");
+ }
+ if (hours > 0) {
+ time.append(hours + "h");
+ }
+ if (minutes > 0) {
+ time.append(minutes + "m");
}
+ if (seconds > 0) {
+ time.append(seconds + "s");
+ }
+ if (milis > 0) {
+ time.append(milis + "ms");
+ }
+
+ return time.toString();
+ } catch (Exception e) {
+ return timeExpression;
}
- return holidays;
}
- protected void parseWeekendDays() {
- String weekendDays =
businessCalendarConfiguration.getProperty(WEEKEND_DAYS);
+ public static class Builder {
- if (weekendDays == null) {
- this.weekendDays.add(Calendar.SATURDAY);
- this.weekendDays.add(Calendar.SUNDAY);
- } else {
- String[] days = weekendDays.split(",");
- for (String day : days) {
- this.weekendDays.add(Integer.parseInt(day));
- }
+ private CalendarBean calendarBean;
+ private Calendar testingCalendar;
+
+ public Builder withCalendarBean(CalendarBean calendarBean) {
+ this.calendarBean = calendarBean;
+ return this;
+ }
+
+ /**
+ * Used only for testing purposes.
+ *
+ * @param testingCalendar
+ * @return
+ */
+ public Builder withTestingCalendar(Calendar testingCalendar) {
+ this.testingCalendar = testingCalendar;
+ return this;
+ }
+
+ public BusinessCalendarImpl build() {
+ return calendarBean == null ? new
BusinessCalendarImpl(testingCalendar) : new BusinessCalendarImpl(calendarBean,
testingCalendar);
}
}
- private class TimePeriod {
+ static class TimePeriod {
private Date from;
private Date to;
@@ -471,35 +493,27 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
protected Date getTo() {
return this.to;
}
- }
- protected long getCurrentTime() {
- if (clock != null) {
- return clock.getCurrentTime();
- } else {
- return System.currentTimeMillis();
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof TimePeriod that)) {
+ return false;
+ }
+ return Objects.equals(from, that.from) && Objects.equals(to,
that.to);
}
- }
- protected boolean isWorkingDay(int day) {
- if (weekendDays.contains(day)) {
- return false;
+ @Override
+ public int hashCode() {
+ return Objects.hash(from, to);
}
- return true;
- }
-
- protected void handleWeekend(Calendar c, boolean resetTime) {
- int dayOfTheWeek = c.get(Calendar.DAY_OF_WEEK);
- while (!isWorkingDay(dayOfTheWeek)) {
- c.add(Calendar.DAY_OF_YEAR, 1);
- if (resetTime) {
- c.set(Calendar.HOUR_OF_DAY, 0);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
- }
- dayOfTheWeek = c.get(Calendar.DAY_OF_WEEK);
+ @Override
+ public String toString() {
+ return "TimePeriod{" +
+ "from=" + from +
+ ", to=" + to +
+ '}';
}
}
+
}
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBean.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBean.java
new file mode 100644
index 0000000000..7ca90c904d
--- /dev/null
+++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBean.java
@@ -0,0 +1,399 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+
+public class CalendarBean {
+
+ // Default access for testing purpose
+ static final List<Integer> DEFAULT_WEEKEND_DAYS =
Arrays.asList(Calendar.SATURDAY, Calendar.SUNDAY);
+ static final String DEFAULT_WEEKENDS =
DEFAULT_WEEKEND_DAYS.stream().map(String::valueOf).collect(Collectors.joining(","));
+ static final String DEFAULT_HOLIDAY_DATE_FORMAT = "yyyy-MM-dd";
+ static final String DEFAULT_TIMEZONE = TimeZone.getDefault().getID();
+
+ private static final Logger logger =
LoggerFactory.getLogger(CalendarBean.class);
+ private static final Collection<String> REQUIRED_PROPERTIES =
Arrays.asList(START_HOUR, END_HOUR);
+
+ private static final Map<String, BiConsumer<StringBuilder, Properties>>
FORMAT_VALIDATOR_MAP;
+ private static final List<BiConsumer<StringBuilder, Properties>>
BUSINESS_VALIDATOR_LIST;
+
+ private static final int LOWER_HOUR_BOUND = 0;
+
+ private static final int UPPER_HOUR_BOUND = 24;
+
+ private static final String OUTSIDE_BOUNDARY_ERROR_MESSAGE = "%s %s
outside expected boundaries %s";
+ private static final String INVALID_FORMAT_ERROR_MESSAGE = "%s is not
valid: %s";
+ private static final String REPEATED_VALUES_ERROR_MESSAGE = "There are
repeated values in the given %s %s";
+ private static final String OTHER_VALUES_ERR_MSG = "%s and other values
provided in the given %s %s";
+ private static final String VALUES_SAME_ERR_MSG = "%s %s and %s %s must be
different";
+ private static final String PROPERTY_REQUIRED_ERR_MSG = "Property %s is
required";
+
+ private final Properties calendarConfiguration;
+
+ static {
+ FORMAT_VALIDATOR_MAP = new HashMap<>();
+ FORMAT_VALIDATOR_MAP.put(START_HOUR, (stringBuilder, properties) -> {
+ if (properties.containsKey(START_HOUR)) {
+ try {
+ int hour = getPropertyAsInt(START_HOUR, properties);
+ if (!isInsideValidRange(hour, LOWER_HOUR_BOUND,
UPPER_HOUR_BOUND)) {
+ addMessageToStringBuilder(stringBuilder,
String.format(OUTSIDE_BOUNDARY_ERROR_MESSAGE, START_HOUR, hour, "(0-24)"));
+ }
+ } catch (NumberFormatException e) {
+ addMessageToStringBuilder(stringBuilder,
String.format(INVALID_FORMAT_ERROR_MESSAGE, START_HOUR, e.getMessage()));
+ }
+ }
+ });
+ FORMAT_VALIDATOR_MAP.put(END_HOUR, (stringBuilder, properties) -> {
+ if (properties.containsKey(END_HOUR)) {
+ try {
+ int hour = getPropertyAsInt(END_HOUR, properties);
+ if (!isInsideValidRange(hour, LOWER_HOUR_BOUND,
UPPER_HOUR_BOUND)) {
+ addMessageToStringBuilder(stringBuilder,
String.format(OUTSIDE_BOUNDARY_ERROR_MESSAGE, END_HOUR, hour, "(0-24)"));
+ }
+ } catch (NumberFormatException e) {
+ addMessageToStringBuilder(stringBuilder,
String.format(INVALID_FORMAT_ERROR_MESSAGE, END_HOUR, e.getMessage()));
+ }
+ }
+ });
+ FORMAT_VALIDATOR_MAP.put(HOLIDAYS, (stringBuilder, properties) -> {
+ if (properties.containsKey(HOLIDAYS)) {
+ String originalData = properties.getProperty(HOLIDAYS);
+ String[] allHolidays = originalData.split(",");
+ for (String holiday : allHolidays) {
+ String[] ranges = holiday.split(":");
+ for (String range : ranges) {
+ try {
+ getFormattedDate(range, properties);
+ } catch (ParseException e) {
+ addMessageToStringBuilder(stringBuilder,
String.format(INVALID_FORMAT_ERROR_MESSAGE, HOLIDAYS, e.getMessage()));
+ }
+ }
+ }
+ }
+ });
+ FORMAT_VALIDATOR_MAP.put(HOLIDAY_DATE_FORMAT, (stringBuilder,
properties) -> {
+ if (properties.containsKey(HOLIDAY_DATE_FORMAT)) {
+ try {
+ getSimpleDateFormat((String)
properties.get(HOLIDAY_DATE_FORMAT));
+ } catch (IllegalArgumentException e) {
+ addMessageToStringBuilder(stringBuilder, e.getMessage());
+ }
+ }
+ });
+ FORMAT_VALIDATOR_MAP.put(WEEKEND_DAYS, (stringBuilder, properties) -> {
+ if (properties.containsKey(WEEKEND_DAYS)) {
+ String originalData = properties.getProperty(WEEKEND_DAYS);
+ String[] weekendDays = originalData.split(",\\s?");
+ Set<String> differentValues =
Arrays.stream(weekendDays).collect(Collectors.toSet());
+ if (differentValues.size() < weekendDays.length) {
+ addMessageToStringBuilder(stringBuilder,
String.format(REPEATED_VALUES_ERROR_MESSAGE, WEEKEND_DAYS, originalData));
+ }
+ if (differentValues.contains("0") && differentValues.size() >
1) {
+ addMessageToStringBuilder(stringBuilder,
String.format(OTHER_VALUES_ERR_MSG, "0 (= no weekends)", WEEKEND_DAYS,
originalData));
+ }
+ final List<Integer> intValues = new ArrayList<>();
+ differentValues.forEach(s -> {
+ try {
+ intValues.add(getStringAsInt(s));
+ } catch (NumberFormatException e) {
+ addMessageToStringBuilder(stringBuilder,
e.getMessage());
+ }
+ });
+ if (intValues.stream().anyMatch(value -> value < 0 || value >
7)) {
+ addMessageToStringBuilder(stringBuilder,
String.format(OUTSIDE_BOUNDARY_ERROR_MESSAGE, WEEKEND_DAYS,
intValues.stream().filter(value -> value < 0 || value > 7).toList(), "(0-7)"));
+ }
+ }
+ });
+ FORMAT_VALIDATOR_MAP.put(TIMEZONE, (stringBuilder, properties) -> {
+ if (properties.containsKey(TIMEZONE)) {
+ String originalData = properties.getProperty(TIMEZONE);
+ if
(!Arrays.asList(TimeZone.getAvailableIDs()).contains(originalData)) {
+ addMessageToStringBuilder(stringBuilder,
String.format(INVALID_FORMAT_ERROR_MESSAGE, TIMEZONE, originalData));
+ }
+ }
+ });
+ BUSINESS_VALIDATOR_LIST = new ArrayList<>();
+ BUSINESS_VALIDATOR_LIST.add((stringBuilder, properties) -> {
+ if (properties.containsKey(START_HOUR) &&
properties.containsKey(END_HOUR)) {
+ try {
+ int startHour = getPropertyAsInt(START_HOUR, properties);
+ int endHour = getPropertyAsInt(END_HOUR, properties);
+ if (startHour == endHour) {
+ addMessageToStringBuilder(stringBuilder,
String.format(VALUES_SAME_ERR_MSG, START_HOUR, startHour, END_HOUR, endHour));
+ }
+ } catch (NumberFormatException nfe) {
+ logger.error("Number format exception while checking
equality of start time and end time: {}", nfe.getMessage());
+ }
+ }
+ });
+ }
+
+ public CalendarBean(Properties calendarConfiguration) {
+ this.calendarConfiguration = calendarConfiguration;
+ setup();
+ }
+
+ static void formalValidation(StringBuilder errorMessage, Properties
calendarConfiguration) {
+ requiredPropertyValidation(errorMessage, calendarConfiguration);
+ propertyFormatValidation(errorMessage, calendarConfiguration);
+ }
+
+ static void requiredPropertyValidation(StringBuilder errorMessage,
Properties calendarConfiguration) {
+ REQUIRED_PROPERTIES.forEach(property ->
validateRequiredProperty(property, errorMessage, calendarConfiguration));
+ }
+
+ static void propertyFormatValidation(StringBuilder errorMessage,
Properties calendarConfiguration) {
+
FORMAT_VALIDATOR_MAP.values().forEach(stringBuilderPropertiesBiConsumer ->
stringBuilderPropertiesBiConsumer.accept(errorMessage, calendarConfiguration));
+ }
+
+ static void businessValidation(StringBuilder errorMessage, Properties
calendarConfiguration) {
+ BUSINESS_VALIDATOR_LIST.forEach(stringBuilderPropertiesBiConsumer ->
stringBuilderPropertiesBiConsumer.accept(errorMessage, calendarConfiguration));
+ }
+
+ static void missingDataPopulation(Properties calendarConfiguration) {
+ if (!calendarConfiguration.containsKey(WEEKEND_DAYS)) {
+ calendarConfiguration.put(WEEKEND_DAYS, DEFAULT_WEEKENDS);
+ }
+ if (!calendarConfiguration.containsKey(HOLIDAY_DATE_FORMAT)) {
+ calendarConfiguration.put(HOLIDAY_DATE_FORMAT,
DEFAULT_HOLIDAY_DATE_FORMAT);
+ }
+ if (!calendarConfiguration.containsKey(TIMEZONE)) {
+ calendarConfiguration.put(TIMEZONE, DEFAULT_TIMEZONE);
+ }
+ }
+
+ static int getPropertyAsInt(String propertyName, Properties
calendarConfiguration) {
+ String value = calendarConfiguration.getProperty(propertyName);
+ return getStringAsInt(value);
+ }
+
+ static int getStringAsInt(String value) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException nfe) {
+ logger.error("Number format exception while parsing {} {}", value,
nfe.getMessage());
+ throw nfe;
+ }
+ }
+
+ static Date getFormattedDate(String date, Properties businessCalendar)
throws ParseException {
+ SimpleDateFormat sdf =
+ businessCalendar.containsKey(HOLIDAY_DATE_FORMAT) ?
getSimpleDateFormat(businessCalendar.getProperty(HOLIDAY_DATE_FORMAT)) :
getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+ int currentYear = Calendar.getInstance().get(Calendar.YEAR);
+ if (date.startsWith("*")) {
+ date = date.replaceFirst("\\*", currentYear + "");
+ }
+ return sdf.parse(date);
+ }
+
+ static SimpleDateFormat getSimpleDateFormat(String format) throws
IllegalArgumentException {
+ return new SimpleDateFormat(format);
+ }
+
+ static void validateRequiredProperty(String property, StringBuilder
errorMessage, Properties calendarConfiguration) {
+ String value = calendarConfiguration.getProperty(property);
+ if (Objects.isNull(value)) {
+ addMessageToStringBuilder(errorMessage,
String.format(PROPERTY_REQUIRED_ERR_MSG, property));
+ }
+ }
+
+ static boolean isInsideValidRange(int value, int lowerBound, int
upperBound) {
+ return value >= lowerBound && value <= upperBound;
+ }
+
+ private static void addMessageToStringBuilder(StringBuilder stringBuilder,
String message) {
+ stringBuilder.append(message);
+ stringBuilder.append("\n");
+ }
+
+ public List<BusinessCalendarImpl.TimePeriod> getHolidays() {
+ if (!calendarConfiguration.containsKey(HOLIDAYS)) {
+ return Collections.emptyList();
+ }
+ String timezone = calendarConfiguration.getProperty(TIMEZONE);
+
+ String holidaysString = calendarConfiguration.getProperty(HOLIDAYS);
+ List<BusinessCalendarImpl.TimePeriod> holidays = new ArrayList<>();
+ int currentYear = Calendar.getInstance().get(Calendar.YEAR);
+ String[] hPeriods = holidaysString.split(",");
+
+ for (String hPeriod : hPeriods) {
+ boolean addNextYearHolidays = false;
+
+ String[] fromTo = hPeriod.split(":");
+ if (fromTo[0].startsWith("*")) {
+ addNextYearHolidays = true;
+
+ fromTo[0] = fromTo[0].replaceFirst("\\*", currentYear + "");
+ }
+ try {
+ if (fromTo.length == 2) {
+ Calendar tmpFrom = new GregorianCalendar();
+ if (timezone != null) {
+ tmpFrom.setTimeZone(TimeZone.getTimeZone(timezone));
+ }
+ tmpFrom.setTime(getFormattedDate(fromTo[0],
calendarConfiguration));
+
+ if (fromTo[1].startsWith("*")) {
+
+ fromTo[1] = fromTo[1].replaceFirst("\\*", currentYear
+ "");
+ }
+
+ Calendar tmpTo = new GregorianCalendar();
+ if (timezone != null) {
+ tmpTo.setTimeZone(TimeZone.getTimeZone(timezone));
+ }
+ tmpTo.setTime(getFormattedDate(fromTo[1],
calendarConfiguration));
+ Date from = tmpFrom.getTime();
+
+ tmpTo.add(Calendar.DAY_OF_YEAR, 1);
+
+ if ((tmpFrom.get(Calendar.MONTH) >
tmpTo.get(Calendar.MONTH)) && (tmpFrom.get(Calendar.YEAR) ==
tmpTo.get(Calendar.YEAR))) {
+ tmpTo.add(Calendar.YEAR, 1);
+ }
+
+ Date to = tmpTo.getTime();
+ holidays.add(new BusinessCalendarImpl.TimePeriod(from,
to));
+
+ if (addNextYearHolidays) {
+ tmpFrom = new GregorianCalendar();
+ if (timezone != null) {
+
tmpFrom.setTimeZone(TimeZone.getTimeZone(timezone));
+ }
+ tmpFrom.setTime(getFormattedDate(fromTo[0],
calendarConfiguration));
+ tmpFrom.add(Calendar.YEAR, 1);
+
+ from = tmpFrom.getTime();
+ tmpTo = new GregorianCalendar();
+ if (timezone != null) {
+ tmpTo.setTimeZone(TimeZone.getTimeZone(timezone));
+ }
+ tmpTo.setTime(getFormattedDate(fromTo[1],
calendarConfiguration));
+ tmpTo.add(Calendar.YEAR, 1);
+ tmpTo.add(Calendar.DAY_OF_YEAR, 1);
+
+ if ((tmpFrom.get(Calendar.MONTH) >
tmpTo.get(Calendar.MONTH)) && (tmpFrom.get(Calendar.YEAR) ==
tmpTo.get(Calendar.YEAR))) {
+ tmpTo.add(Calendar.YEAR, 1);
+ }
+
+ to = tmpTo.getTime();
+ holidays.add(new BusinessCalendarImpl.TimePeriod(from,
to));
+ }
+ } else {
+
+ Calendar c = new GregorianCalendar();
+ c.setTime(getFormattedDate(fromTo[0],
calendarConfiguration));
+ c.add(Calendar.DAY_OF_YEAR, 1);
+ // handle one day holiday
+ holidays.add(new
BusinessCalendarImpl.TimePeriod(getFormattedDate(fromTo[0],
calendarConfiguration), c.getTime()));
+ if (addNextYearHolidays) {
+ Calendar tmp = Calendar.getInstance();
+ tmp.setTime(getFormattedDate(fromTo[0],
calendarConfiguration));
+ tmp.add(Calendar.YEAR, 1);
+
+ Date from = tmp.getTime();
+ c.add(Calendar.YEAR, 1);
+ holidays.add(new BusinessCalendarImpl.TimePeriod(from,
c.getTime()));
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Error while parsing holiday in business
calendar", e);
+ }
+ }
+ return holidays;
+ }
+
+ public List<Integer> getWeekendDays() {
+ return parseWeekendDays(calendarConfiguration);
+ }
+
+ public int getDaysPerWeek() {
+ return 7 - parseWeekendDays(calendarConfiguration).size();
+ }
+
+ public String getTimezone() {
+ return calendarConfiguration.getProperty(TIMEZONE);
+ }
+
+ public int getStartHour() {
+ return getPropertyAsInt(START_HOUR);
+ }
+
+ public int getEndHour() {
+ return getPropertyAsInt(END_HOUR);
+ }
+
+ public int getHoursInDay() {
+ int startHour = getStartHour();
+ int endHour = getEndHour();
+ return startHour < endHour ? endHour - startHour : (24 - startHour) +
endHour;
+ }
+
+ protected void setup() {
+ StringBuilder errorMessage = new StringBuilder();
+ formalValidation(errorMessage, calendarConfiguration);
+ missingDataPopulation(calendarConfiguration);
+ businessValidation(errorMessage, calendarConfiguration);
+ if (!errorMessage.isEmpty()) {
+ throw new IllegalArgumentException(errorMessage.toString());
+ }
+ }
+
+ protected List<Integer> parseWeekendDays(Properties calendarConfiguration)
{
+ String weekendDays = calendarConfiguration.getProperty(WEEKEND_DAYS);
+ String[] days = weekendDays.split(",");
+ return Arrays.stream(days).map(day -> Integer.parseInt(day.trim()))
+ .filter(intDay -> intDay != 0)
+ .collect(Collectors.toList());
+ }
+
+ protected int getPropertyAsInt(String propertyName) {
+ return getPropertyAsInt(propertyName, calendarConfiguration);
+ }
+}
\ No newline at end of file
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBeanFactory.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBeanFactory.java
new file mode 100644
index 0000000000..bf0eed797a
--- /dev/null
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/CalendarBeanFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.jbpm.process.core.timer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Objects;
+import java.util.Properties;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_PATH;
+
+public class CalendarBeanFactory {
+
+ private static final Logger logger =
LoggerFactory.getLogger(CalendarBeanFactory.class);
+
+ public static CalendarBean createCalendarBean() {
+ URL resource =
Thread.currentThread().getContextClassLoader().getResource(BUSINESS_CALENDAR_PATH);
+ if (Objects.nonNull(resource)) {
+ logger.debug("URL resource: {}", resource);
+ Properties calendarConfiguration = new Properties();
+ try (InputStream is = resource.openStream()) {
+ calendarConfiguration.load(is);
+ return new CalendarBean(calendarConfiguration);
+ } catch (IOException e) {
+ String errorMessage = "Error while loading properties for
business calendar";
+ logger.error(errorMessage, e);
+ throw new RuntimeException(errorMessage, e);
+ } catch (IllegalArgumentException e) {
+ String errorMessage = "Error while populating properties for
business calendar";
+ logger.error(errorMessage, e);
+ throw e;
+ }
+ } else {
+ String errorMessage = String.format("Missing %s",
BUSINESS_CALENDAR_PATH);
+ logger.error(errorMessage);
+ throw new RuntimeException(errorMessage);
+ }
+ }
+}
diff --git
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java
index 40ad261bdd..e1038c8b39 100755
---
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java
+++
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/BusinessCalendarImplTest.java
@@ -18,491 +18,367 @@
*/
package org.jbpm.process.core.timer;
-import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
+import java.util.function.BiFunction;
+import java.util.stream.IntStream;
import org.jbpm.test.util.AbstractBaseTest;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import org.kie.kogito.timer.SessionPseudoClock;
import org.slf4j.LoggerFactory;
+import static java.time.temporal.ChronoUnit.DAYS;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
-public class BusinessCalendarImplTest extends AbstractBaseTest {
+class BusinessCalendarImplTest extends AbstractBaseTest {
public void addLogger() {
logger = LoggerFactory.getLogger(this.getClass());
}
@Test
- public void testCalculateHours() {
- Properties config = new Properties();
- String expectedDate = "2012-05-04 16:45";
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("3h");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateHoursCustomWorkingHours() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "6");
- String expectedDate = "2012-05-04 15:45";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("8h");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateHoursPassingOverWeekend() {
- Properties config = new Properties();
- String expectedDate = "2012-05-07 12:45";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateHoursPassingOverCustomDefinedWeekend() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY
+ "," + Calendar.SATURDAY);
- String expectedDate = "2012-05-06 12:45";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-03 13:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("7h");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateMinutesPassingOverWeekend() {
- Properties config = new Properties();
- String expectedDate = "2012-05-07 09:15";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-04 16:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateMinutesPassingOverHoliday() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS,
"2012-05-12:2012-05-19");
- String expectedDate = "2012-05-21 09:15";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-11 16:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("30m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateDays() {
- Properties config = new Properties();
- String expectedDate = "2012-05-14 09:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDate("2012-05-04").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void instantiate() {
+ BusinessCalendarImpl retrieved =
BusinessCalendarImpl.builder().build();
+ assertThat(retrieved).isNotNull();
+ retrieved = BusinessCalendarImpl.builder()
+ .withCalendarBean(CalendarBeanFactory.createCalendarBean())
+ .build();
+ assertThat(retrieved).isNotNull();
+
+ Properties calendarConfiguration = new Properties();
+ int startHour = 10;
+ int endHour = 16;
+ calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+ calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+ retrieved = BusinessCalendarImpl.builder()
+ .withCalendarBean(new CalendarBean(calendarConfiguration))
+ .build();
+ assertThat(retrieved).isNotNull();
}
@Test
- public void testCalculateDaysStartingInWeekend() {
- Properties config = new Properties();
- String expectedDate = "2012-05-09 09:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDate("2012-05-05").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("2d");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateInsideDailyWorkingHourWithDelay() {
+ int daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3,
daysToSkip, null, null);
}
@Test
- public void testCalculateDaysCustomWorkingDays() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "4");
- config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, Calendar.FRIDAY
+ "," + Calendar.SATURDAY + "," + Calendar.SUNDAY);
- String expectedDate = "2012-05-15 14:30";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-03 14:30").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateInsideDailyWorkingHourWithoutDelay() {
+ int daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 0,
daysToSkip, null, null);
}
+ @Disabled("TO FIX
https://github.com/apache/incubator-kie-issues/issues/1651")
@Test
- public void testCalculateDaysMiddleDay() {
- Properties config = new Properties();
- String expectedDate = "2012-05-11 12:27";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-03 12:27").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("6d");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateInsideNightlyWorkingHour() {
+ int daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(4, -4, 0, 3,
daysToSkip, null, null);
}
@Test
- public void testCalculateDaysHoursMinutes() {
- Properties config = new Properties();
- String expectedDate = "2012-05-14 14:20";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDate("2012-05-04").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateBeforeWorkingHourWithDelay() {
+ int daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(2, 4, -1, 1,
daysToSkip, null, null);
}
@Test
- public void testCalculateTimeDaysHoursMinutesHolidays() {
+ void calculateBusinessTimeAsDateBeforeWorkingHourWithDelayFineGrained() {
+ // lets pretend 2024-11-28 10:48:33 is the current time
+ Calendar testingCalendar = Calendar.getInstance();
+ testingCalendar.set(Calendar.YEAR, 2024);
+ testingCalendar.set(Calendar.MONTH, Calendar.NOVEMBER);
+ testingCalendar.set(Calendar.DAY_OF_MONTH, 28);
+ testingCalendar.set(Calendar.HOUR_OF_DAY, 10);
+ testingCalendar.set(Calendar.MINUTE, 48);
+ testingCalendar.set(Calendar.SECOND, 33);
+
+ int startHour = 14;
Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS,
"2012-05-10:2012-05-19");
- String expectedDate = "2012-05-21 14:20";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDate("2012-05-04").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("6d4h80m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateTimeDaysHoursMinutesSingleDayHolidays() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-07");
- String expectedDate = "2012-05-08 13:20";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDate("2012-05-04").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void
testCalculateTimeDaysHoursMinutesSingleDayHolidaysInMiddleOfWeek() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
- String expectedDate = "2012-05-10 15:30";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("1d4h20m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateDaysPassingOverHolidayAtYearEnd() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS,
"2012-12-31:2013-01-01");
- String expectedDate = "2013-01-04 09:15";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
- }
+ config.setProperty(BusinessCalendarImpl.START_HOUR,
String.valueOf(startHour));
+ config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
+ config.setProperty(WEEKEND_DAYS, "0");
- @Test
- public void testCalculateDaysPassingOverHolidayAtYearEndWithWildcards() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS, "*-12-31:*-01-01");
- String expectedDate = "2013-01-02 09:15";
+ String delay = "10m";
+ BusinessCalendarImpl businessCal =
BusinessCalendarImpl.builder().withCalendarBean(new CalendarBean(config))
+ .withTestingCalendar(testingCalendar)
+ .build();
+ Date retrieved = businessCal.calculateBusinessTimeAsDate(delay);
+ String expectedDate = "2024-11-28 14:10:00";
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-12-28 16:45").getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ String retrievedTime = sdf.format(retrieved);
+ assertThat(retrievedTime).isEqualTo(expectedDate);
- Date result = businessCal.calculateBusinessTimeAsDate("2d30m");
+ delay = "10s";
+ retrieved = businessCal.calculateBusinessTimeAsDate(delay);
+ expectedDate = "2024-11-28 14:00:10";
+ retrievedTime = sdf.format(retrieved);
+ assertThat(retrievedTime).isEqualTo(expectedDate);
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ delay = "10m 10s";
+ retrieved = businessCal.calculateBusinessTimeAsDate(delay);
+ expectedDate = "2024-11-28 14:10:10";
+ retrievedTime = sdf.format(retrieved);
+ assertThat(retrievedTime).isEqualTo(expectedDate);
}
@Test
- public void testCalculateISOHours() {
- Properties config = new Properties();
- String expectedDate = "2012-05-04 16:45";
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("PT3H");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateBeforeWorkingHourWithoutDelay() {
+ int daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-1, 4, -2, 1,
daysToSkip, null, null);
}
@Test
- public void testCalculateISODaysAndHours() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOLIDAYS, "2012-05-09");
- String expectedDate = "2012-05-10 15:30";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-08 11:10").getTime());
-
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("P1DT4H20M");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateAfterWorkingHour() {
+ int daysToSkip = 1; // because the executionHourDelay is bigger to
endHOurGap, so it goes to next day;
+ commonCalculateBusinessTimeAsDateAssertAtStartHour(-1, 2, 3, 3,
daysToSkip, null, null);
+ commonCalculateBusinessTimeAsDateAssertAtStartHour(0, 6, 1, 5,
daysToSkip, null, null);
}
@Test
- public void testSingleHolidayWithinGivenTime() {
- final Properties props = new Properties();
- props.put(BusinessCalendarImpl.HOLIDAYS, "2015-01-13");
- String expectedDate = "2015-01-15 11:38";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis("2015-01-08
11:38:30.198").getTime());
-
- BusinessCalendarImpl businessCalendarImpl = new
BusinessCalendarImpl(props, clock);
-
- Date result = businessCalendarImpl.calculateBusinessTimeAsDate("4d");
- assertThat(formatDate("yyyy-MM-dd HH:mm",
result)).isEqualTo(expectedDate);
+ void calculateBusinessTimeAsDateWhenTodayAndTomorrowAreHolidays() {
+ String holidayDateFormat = "yyyy-MM-dd";
+ DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+ LocalDate today = LocalDate.now();
+ LocalDate tomorrow = today.plusDays(1);
+ String holidays = sdf.format(today) + "," + sdf.format(tomorrow);
+ int daysToSkip = 2; // because both today and tomorrow are holiday
+ // endHOurGap and executionHourDelay are not relevant in this context
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3,
daysToSkip, holidayDateFormat, holidays);
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 5, 3,
daysToSkip, holidayDateFormat, holidays);
}
@Test
- public void testCalculateMillisecondsAsDefault() {
- Properties config = new Properties();
- String expectedDate = "2012-05-04 16:45:10.000";
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis("2012-05-04
16:45:00.000").getTime());
-
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("10000");
+ void calculateBusinessTimeAsDateWhenNextDayIsHoliday() {
+ String holidayDateFormat = "yyyy-MM-dd";
+ DateTimeFormatter sdf = DateTimeFormatter.ofPattern(holidayDateFormat);
+ LocalDate tomorrow = LocalDate.now().plusDays(1);
+ String holidays = sdf.format(tomorrow);
+ // 1 because the executionHourDelay is equal to endHOurGap, so it goes
to next day;
+ // 1 because next day is holiday
+ int daysToSkip = 2;
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss.SSS",
result)).isEqualTo(expectedDate);
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 4,
daysToSkip, holidayDateFormat, holidays);
+ daysToSkip = 0; // since executionHourDelay falls before endHOurGap
+ commonCalculateBusinessTimeAsDateAssertBetweenHours(-4, 4, 0, 3,
daysToSkip, holidayDateFormat, holidays);
}
@Test
- public void testCalculateMinutesPassingAfterHour() {
- Properties config = new Properties();
- String currentDate = "2018-05-02 19:51:33";
- String expectedDate = "2018-05-03 09:01:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate("1m");
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
- }
-
+ void rollCalendarToDailyWorkingHour() {
+ int startHour = 14;
+ int endHour = 16;
+ Calendar toRoll = Calendar.getInstance();
+ int currentHour = 8;
+ toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+ int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+ BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour,
endHour);
+ assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+ assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+ toRoll = Calendar.getInstance();
+ currentHour = 19;
+ toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+ dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+ BusinessCalendarImpl.rollCalendarToDailyWorkingHour(toRoll, startHour,
endHour);
+ assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+ assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
+ }
+
+ @Disabled("TO FIX
https://github.com/apache/incubator-kie-issues/issues/1651")
@Test
- public void testBusinessCalendarWithoutProvidedConfiguration() {
- assertDoesNotThrow(() -> new BusinessCalendarImpl());
- }
-
- @Test
- public void testCalculateMinutesPassingHoliday() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK, "5");
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "8");
- config.setProperty(BusinessCalendarImpl.START_HOUR, "9");
- config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
- config.setProperty(BusinessCalendarImpl.WEEKEND_DAYS, "1,7"); //
sun,sat
- config.setProperty(BusinessCalendarImpl.HOLIDAYS,
"2018-04-30,2018-05-03:2018-05-05");
- config.setProperty(BusinessCalendarImpl.HOLIDAY_DATE_FORMAT,
"yyyy-MM-dd");
- String currentDate = "2018-05-03 13:51:33";
- String duration = "10m";
- String expectedDate = "2018-05-07 09:10:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
+ void rollCalendarToNightlyWorkingHour() {
+ int startHour = 20;
+ int endHour = 4;
+ Calendar toRoll = Calendar.getInstance();
+ int currentHour = 21;
+ toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+ int dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+ BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll,
startHour, endHour);
+ assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+ assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear);
+
+ toRoll = Calendar.getInstance();
+ currentHour = 3;
+ toRoll.set(Calendar.HOUR_OF_DAY, currentHour);
+ dayOfYear = toRoll.get(Calendar.DAY_OF_YEAR);
+ BusinessCalendarImpl.rollCalendarToNightlyWorkingHour(toRoll,
startHour, endHour);
+ assertThat(toRoll.get(Calendar.HOUR_OF_DAY)).isEqualTo(startHour);
+ assertThat(toRoll.get(Calendar.DAY_OF_YEAR)).isEqualTo(dayOfYear + 1);
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
}
@Test
- public void testCalculateMinutesPassingWeekend() {
- Properties config = new Properties();
- String currentDate = "2018-05-06 13:51:33";
- String duration = "10m";
- String expectedDate = "2018-05-07 09:10:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
+ void rollCalendarAfterHolidays() {
+ Instant now = Instant.now();
+ int holidayLeft = 4;
+ Instant startHolidayInstant = now.minus(2, DAYS);
+ Instant endHolidayInstant = now.plus(holidayLeft, DAYS);
+ Date startHoliday = Date.from(startHolidayInstant);
+ Date endHoliday = Date.from(endHolidayInstant);
+ List<BusinessCalendarImpl.TimePeriod> holidays =
Collections.singletonList(new BusinessCalendarImpl.TimePeriod(startHoliday,
endHoliday));
+ List<Integer> weekendDays = Collections.emptyList();
+ Calendar calendar = Calendar.getInstance();
+ int currentDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
+ BusinessCalendarImpl.rollCalendarAfterHolidays(calendar, holidays,
weekendDays, false);
+ int expected = currentDayOfYear + holidayLeft + 1;
+ assertThat(calendar.get(Calendar.DAY_OF_YEAR)).isEqualTo(expected);
}
@Test
- public void testCalculateMinutesBeforeStartHour() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "4");
- config.setProperty(BusinessCalendarImpl.START_HOUR, "14");
- config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
- String currentDate = "2024-11-28 10:48:33.000";
- String duration = "10m";
- String expectedDate = "2024-11-28 14:10:00";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
+ void rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking() {
+ List<Integer> workingDays = IntStream.range(Calendar.MONDAY,
Calendar.SATURDAY).boxed().toList();
+ List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY,
Calendar.SUNDAY);
+ boolean resetTime = false;
+ workingDays.forEach(workingDay -> {
+ Calendar calendar = getCalendarAtExpectedWeekDay(workingDay);
+
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, resetTime);
+
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(workingDay);
+ });
+ weekendDays.forEach(weekendDay -> {
+ Calendar calendar = getCalendarAtExpectedWeekDay(weekendDay);
+
BusinessCalendarImpl.rollCalendarToNextWorkingDayIfCurrentDayIsNonWorking(calendar,
weekendDays, resetTime);
+
assertThat(calendar.get(Calendar.DAY_OF_WEEK)).isEqualTo(Calendar.MONDAY);
+ });
}
@Test
- public void testCalculateSecondsBeforeStartHour() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "4");
- config.setProperty(BusinessCalendarImpl.START_HOUR, "14");
- config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
- String currentDate = "2024-11-28 10:48:33.000";
- String duration = "10s";
- String expectedDate = "2024-11-28 14:00:10";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
- }
-
- @Test
- public void testCalculateMinutesBeforeEndHour() {
- Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "4");
- config.setProperty(BusinessCalendarImpl.START_HOUR, "14");
- config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
- String currentDate = "2024-11-28 17:58:33.000";
- String duration = "10m";
- String expectedDate = "2024-11-29 14:08:33";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
- }
+ void isWorkingDay() {
+ List<Integer> workingDays = IntStream.range(Calendar.MONDAY,
Calendar.SATURDAY).boxed().toList();
+ List<Integer> weekendDays = Arrays.asList(Calendar.SATURDAY,
Calendar.SUNDAY);
+ workingDays.forEach(workingDay ->
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays,
workingDay)).isTrue());
+ weekendDays.forEach(workingDay ->
assertThat(BusinessCalendarImpl.isWorkingDay(weekendDays,
workingDay)).isFalse());
+ }
+
+ private void commonCalculateBusinessTimeAsDateAssertBetweenHours(int
startHourGap, int endHourGap, int testingCalendarHourGap, int
executionHourDelay, int daysToSkip, String holidayDateFormat,
+ String holidays) {
+ BiFunction<Instant, Instant, Boolean> startBooleanCondition =
(resultInstant, expectedStartTime) -> {
+ logger.debug("Check if {} is after or equal to {} ",
resultInstant, expectedStartTime);
+ return !resultInstant.isBefore(expectedStartTime);
+ };
+ commonCalculateBusinessTimeAsDate(startHourGap,
+ endHourGap,
+ testingCalendarHourGap,
+ executionHourDelay,
+ daysToSkip,
+ holidayDateFormat,
+ holidays,
+ startBooleanCondition);
+ }
+
+ private void commonCalculateBusinessTimeAsDateAssertAtStartHour(int
startHourGap, int endHourGap, int testingCalendarHourGap, int
executionHourDelay, int daysToSkip, String holidayDateFormat,
+ String holidays) {
+ BiFunction<Instant, Instant, Boolean> startBooleanCondition =
(resultInstant, expectedStartTime) -> {
+ logger.debug("Check if {} is equal to {} ", resultInstant,
expectedStartTime);
+ return resultInstant.getEpochSecond() ==
expectedStartTime.getEpochSecond();
+ };
+ commonCalculateBusinessTimeAsDate(startHourGap,
+ endHourGap,
+ testingCalendarHourGap,
+ executionHourDelay,
+ daysToSkip,
+ holidayDateFormat,
+ holidays,
+ startBooleanCondition);
+ }
+
+ private void commonCalculateBusinessTimeAsDate(int startHourGap,
+ int endHourGap, int testingCalendarHourGap,
+ int executionHourDelay, int daysToSkip, String holidayDateFormat,
String holidays,
+ BiFunction<Instant, Instant, Boolean> startBooleanCondition) {
+ logger.debug("startHourGap {}", startHourGap);
+ logger.debug("endHourGap {}", endHourGap);
+ logger.debug("testingCalendarHourGap {}", testingCalendarHourGap);
+ logger.debug("executionHourDelay {}", executionHourDelay);
+ logger.debug("numberOfHolidays {}", daysToSkip);
+ logger.debug("holidayDateFormat {}", holidayDateFormat);
+ logger.debug("holidays {}", holidays);
+
+ // lets pretend 12.00 is the current time
+ Calendar testingCalendar = Calendar.getInstance();
+ testingCalendar.set(Calendar.HOUR_OF_DAY, 12);
+ testingCalendar.set(Calendar.MINUTE, 0);
+ testingCalendar.set(Calendar.SECOND, 0);
+ logger.debug("testingCalendar {}", testingCalendar.getTime());
+ Calendar startCalendar = (Calendar) testingCalendar.clone();
+ startCalendar.add(Calendar.HOUR_OF_DAY, startHourGap);
+ logger.debug("startCalendar {}", startCalendar.getTime());
+ Calendar endCalendar = (Calendar) testingCalendar.clone();
+ endCalendar.add(Calendar.HOUR_OF_DAY, endHourGap);
+ logger.debug("endCalendar {}", endCalendar.getTime());
+
+ int startHour = startCalendar.get(Calendar.HOUR_OF_DAY);
+ int endHour = endCalendar.get(Calendar.HOUR_OF_DAY);
+
+ // We need to reconciliate for daily/working hours and daily/nightly
hours
+ int hoursInDay = startHour < endHour ? endHour - startHour : 24 -
(startHour - endHour);
+ int daysToAdd = daysToSkip;
+ logger.debug("daysToAdd (= numberOfHolidays) {}", daysToAdd);
+ if (executionHourDelay >= hoursInDay) {
+ daysToAdd += executionHourDelay / hoursInDay;
+ logger.debug("daysToAdd += (hourDelay / hoursInDay) {}",
daysToAdd);
+ }
+ if (daysToAdd > 0) {
+ startCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+ endCalendar.add(Calendar.DAY_OF_YEAR, daysToAdd);
+ logger.debug("startCalendar (startCalendar + days to add) {}",
startCalendar.getTime());
+ logger.debug("endCalendar (endCalendar + days to add) {}",
endCalendar.getTime());
+ }
- @Test
- public void testCalculateSecondsBeforeEndHour() {
Properties config = new Properties();
- config.setProperty(BusinessCalendarImpl.HOURS_PER_DAY, "4");
- config.setProperty(BusinessCalendarImpl.START_HOUR, "14");
- config.setProperty(BusinessCalendarImpl.END_HOUR, "18");
- String currentDate = "2024-11-28 17:59:33.000";
- String duration = "50s";
- String expectedDate = "2024-11-29 14:00:23";
-
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTimeAndMillis(currentDate).getTime());
- BusinessCalendarImpl businessCal = new BusinessCalendarImpl(config,
clock);
-
- Date result = businessCal.calculateBusinessTimeAsDate(duration);
-
- assertThat(formatDate("yyyy-MM-dd HH:mm:ss",
result)).isEqualTo(expectedDate);
- }
-
- private Date parseToDate(String dateString) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-
- Date testTime;
- try {
- testTime = sdf.parse(dateString);
-
- return testTime;
- } catch (ParseException e) {
- return null;
+ config.setProperty(START_HOUR, String.valueOf(startHour));
+ config.setProperty(END_HOUR, String.valueOf(endHour));
+ config.setProperty(WEEKEND_DAYS, "0");
+ if (holidayDateFormat != null) {
+ config.setProperty(HOLIDAY_DATE_FORMAT, holidayDateFormat);
}
- }
-
- private Date parseToDateWithTime(String dateString) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
- Date testTime;
- try {
- testTime = sdf.parse(dateString);
-
- return testTime;
- } catch (ParseException e) {
- return null;
+ if (holidays != null) {
+ config.setProperty(HOLIDAYS, holidays);
}
- }
- private Date parseToDateWithTimeAndMillis(String dateString) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ testingCalendar.add(Calendar.HOUR_OF_DAY, testingCalendarHourGap);
+ logger.debug("testingCalendar after testingCalendarHourGap {}",
testingCalendar.getTime());
+ BusinessCalendarImpl businessCal =
BusinessCalendarImpl.builder().withCalendarBean(new CalendarBean(config))
+ .withTestingCalendar(testingCalendar)
+ .build();
+ Date retrieved =
businessCal.calculateBusinessTimeAsDate(String.format("%sh",
executionHourDelay));
+ logger.debug("retrieved {}", retrieved);
- Date testTime;
- try {
- testTime = sdf.parse(dateString);
+ Date expectedStart = startCalendar.getTime();
+ Date expectedEnd = endCalendar.getTime();
- return testTime;
- } catch (ParseException e) {
- return null;
- }
- }
+ Instant retrievedInstant = retrieved.toInstant();
+ Instant expectedStartTime = expectedStart.toInstant();
+ Instant expectedEndTime = expectedEnd.toInstant();
- private String formatDate(String pattern, Date date) {
- SimpleDateFormat sdf = new SimpleDateFormat(pattern);
-
- String testTime = sdf.format(date);
-
- return testTime;
+ logger.debug("retrievedInstant {}", retrievedInstant);
+ logger.debug("expectedStartTime {}", expectedStartTime);
+ logger.debug("expectedEndTime {}", expectedEndTime);
+ assertThat(startBooleanCondition.apply(retrievedInstant,
expectedStartTime)).isTrue();
+ logger.debug("Check if {} is not after {} ", retrievedInstant,
expectedEndTime);
+ assertThat(retrievedInstant.isAfter(expectedEndTime)).isFalse();
}
- private class StaticPseudoClock implements SessionPseudoClock {
-
- private long currentTime;
-
- private StaticPseudoClock(long currenttime) {
- this.currentTime = currenttime;
- }
-
- public long getCurrentTime() {
- return this.currentTime;
- }
-
- public long advanceTime(long amount, TimeUnit unit) {
- throw new UnsupportedOperationException("It is static clock and
does not allow advance time operation");
+ private Calendar getCalendarAtExpectedWeekDay(int weekDay) {
+ Calendar toReturn = Calendar.getInstance();
+ while (toReturn.get(Calendar.DAY_OF_WEEK) != weekDay) {
+ toReturn.add(Calendar.DAY_OF_YEAR, 1);
}
-
+ return toReturn;
}
+
}
diff --git
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanFactoryTest.java
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanFactoryTest.java
new file mode 100644
index 0000000000..0a4f59d59f
--- /dev/null
+++
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanFactoryTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.jbpm.process.core.timer;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CalendarBeanFactoryTest {
+
+ @Test
+ void testCreateCalendarBean() {
+ // This test relies on src/test/resources/calendar.properties:
+ // checked values comes from it
+ CalendarBean calendarBean = CalendarBeanFactory.createCalendarBean();
+ assertThat(calendarBean).isNotNull();
+ assertThat(calendarBean.getStartHour()).isEqualTo(10);
+ assertThat(calendarBean.getEndHour()).isEqualTo(16);
+ assertThat(calendarBean.getHoursInDay()).isEqualTo(6);
+ assertThat(calendarBean.getDaysPerWeek()).isEqualTo(5);
+ assertThat(calendarBean.getWeekendDays()).contains(Calendar.SATURDAY,
Calendar.SUNDAY);
+ assertThat(calendarBean.getHolidays()).isEmpty();
+
assertThat(calendarBean.getTimezone()).isEqualTo(TimeZone.getDefault().getID());
+ }
+}
diff --git
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java
new file mode 100644
index 0000000000..64bf32194e
--- /dev/null
+++
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/core/timer/CalendarBeanTest.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.jbpm.process.core.timer;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.assertj.core.api.ThrowableAssert;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.END_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAYS;
+import static
org.jbpm.process.core.timer.BusinessCalendarImpl.HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.START_HOUR;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.TIMEZONE;
+import static org.jbpm.process.core.timer.BusinessCalendarImpl.WEEKEND_DAYS;
+import static
org.jbpm.process.core.timer.CalendarBean.DEFAULT_HOLIDAY_DATE_FORMAT;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_TIMEZONE;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKENDS;
+import static org.jbpm.process.core.timer.CalendarBean.DEFAULT_WEEKEND_DAYS;
+
+class CalendarBeanTest {
+
+ // Static validation methods
+ @ParameterizedTest
+ @MethodSource("getMissingPropertiesCalendar")
+ void requiredPropertyValidation(Map<String, Object> propertyMap,
List<String> errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonStaticMethodValidation(errorMessage ->
CalendarBean.requiredPropertyValidation(errorMessage, calendarConfiguration),
errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getWronglyFormatPropertiesCalendar")
+ void propertyFormatValidation(Map<String, Object> propertyMap,
List<String> errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonStaticMethodValidation(errorMessage ->
CalendarBean.propertyFormatValidation(errorMessage, calendarConfiguration),
errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getBusinessInvalidPropertiesCalendar")
+ void businessValidation(Map<String, Object> propertyMap, List<String>
errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonStaticMethodValidation(errorMessage ->
CalendarBean.businessValidation(errorMessage, calendarConfiguration),
errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getPartialPropertiesCalendar")
+ void missingDataPopulation(Map<String, Object> propertyMap, Map<String,
Object> defaultValuesMap) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ defaultValuesMap.keySet().forEach(key ->
assertThat(calendarConfiguration.containsKey(key)).isFalse());
+ CalendarBean.missingDataPopulation(calendarConfiguration);
+ defaultValuesMap.forEach((key, value) -> {
+ assertThat(calendarConfiguration.containsKey(key)).isTrue();
+
assertThat(calendarConfiguration.getProperty(key)).isEqualTo(value);
+ });
+ }
+
+ @Test
+ void getPropertyAsInt() {
+ Properties calendarConfiguration = new Properties();
+ String propertyName = "propertyName";
+ int originalValue = 1;
+ String value = "" + originalValue;
+ calendarConfiguration.put(propertyName, value);
+ int retrieved = CalendarBean.getPropertyAsInt(propertyName,
calendarConfiguration);
+ assertThat(retrieved).isEqualTo(originalValue);
+ value = "WRONG";
+ calendarConfiguration.put(propertyName, value);
+ String expectedMessage = "For input string: \"WRONG\"";
+ assertThatThrownBy(() -> CalendarBean.getPropertyAsInt(propertyName,
calendarConfiguration))
+ .isInstanceOf(NumberFormatException.class)
+ .hasMessage(expectedMessage);
+ }
+
+ @Test
+ void validateRequiredProperty() {
+ Properties calendarConfiguration = new Properties();
+ String propertyName = "propertyName";
+ String value = "propertyValue";
+ calendarConfiguration.put(propertyName, value);
+ StringBuilder errorMessage = new StringBuilder();
+ CalendarBean.validateRequiredProperty(propertyName, errorMessage,
calendarConfiguration);
+ assertThat(errorMessage).isEmpty();
+ CalendarBean.validateRequiredProperty("missingProperty", errorMessage,
calendarConfiguration);
+ String[] retrievedErrors = errorMessage.toString().split("\n");
+ assertThat(retrievedErrors).hasSize(1);
+ assertThat(retrievedErrors).contains("Property missingProperty is
required");
+ }
+
+ @Test
+ void getFormattedDate() throws ParseException {
+ Properties calendarConfiguration = new Properties();
+ String dateFormat = "dd-MM-yyyy";
+ String date = "27-11-2024";
+ calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+ Date retrieved = CalendarBean.getFormattedDate(date,
calendarConfiguration);
+ Date expected =
CalendarBean.getSimpleDateFormat(dateFormat).parse(date);
+ assertThat(retrieved).isEqualTo(expected);
+
+ }
+
+ @Test
+ void getSimpleDateFormat() {
+ SimpleDateFormat retrieved =
CalendarBean.getSimpleDateFormat(DEFAULT_HOLIDAY_DATE_FORMAT);
+ assertThat(retrieved).isNotNull();
+ retrieved = CalendarBean.getSimpleDateFormat("dd-MM-yyyy");
+ assertThat(retrieved).isNotNull();
+ String wrong = "WRONG";
+ String expectedMessage = "Illegal pattern character 'R'";
+ assertThatThrownBy(() -> CalendarBean.getSimpleDateFormat(wrong))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(expectedMessage);
+ }
+
+ // Instance methods
+ @ParameterizedTest
+ @MethodSource("getMissingPropertiesCalendar")
+ void requiredPropertyMissing(Map<String, Object> propertyMap, List<String>
errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonIllegalArgumentAssertion(() -> new
CalendarBean(calendarConfiguration), errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getWronglyFormatPropertiesCalendar")
+ void propertyWrongFormat(Map<String, Object> propertyMap, List<String>
errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonIllegalArgumentAssertion(() -> new
CalendarBean(calendarConfiguration), errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getBusinessInvalidPropertiesCalendar")
+ void businessInvalid(Map<String, Object> propertyMap, List<String>
errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonIllegalArgumentAssertion(() -> new
CalendarBean(calendarConfiguration), errorMessages);
+ }
+
+ @Test
+ void instantiationFull() throws ParseException {
+ Properties calendarConfiguration = new Properties();
+ int startHour = 10;
+ int endHour = 16;
+ List<Integer> weekendDays = Arrays.asList(3, 4);
+ String dateFormat = "dd-MM-yyyy";
+ String timezone = "ACT";
+ String holidays = "27-11-2024";
+ calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+ calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+ calendarConfiguration.put(WEEKEND_DAYS,
weekendDays.stream().map(String::valueOf).collect(Collectors.joining(",")));
+ calendarConfiguration.put(HOLIDAY_DATE_FORMAT, dateFormat);
+ calendarConfiguration.put(TIMEZONE, timezone);
+ calendarConfiguration.put(HOLIDAYS, holidays);
+ CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+
+ Date from = CalendarBean.getFormattedDate(holidays,
calendarConfiguration);
+ Date to = CalendarBean.getFormattedDate("28-11-2024",
calendarConfiguration);
+ assertThat(retrieved.getHolidays()).isEqualTo(List.of(new
BusinessCalendarImpl.TimePeriod(from, to)));
+ assertThat(retrieved.getWeekendDays()).isEqualTo(weekendDays);
+ assertThat(retrieved.getDaysPerWeek()).isEqualTo(7 -
weekendDays.size());
+ assertThat(retrieved.getTimezone()).isEqualTo(timezone);
+ assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+ assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+ assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+ }
+
+ @Test
+ void instantiationPartial() {
+ Properties calendarConfiguration = new Properties();
+ int startHour = 10;
+ int endHour = 16;
+ calendarConfiguration.put(START_HOUR, String.valueOf(startHour));
+ calendarConfiguration.put(END_HOUR, String.valueOf(endHour));
+ CalendarBean retrieved = new CalendarBean(calendarConfiguration);
+ assertThat(retrieved.getHolidays()).isEqualTo(Collections.emptyList());
+ assertThat(retrieved.getWeekendDays()).isEqualTo(DEFAULT_WEEKEND_DAYS);
+ assertThat(retrieved.getDaysPerWeek()).isEqualTo(5);
+ assertThat(retrieved.getTimezone()).isEqualTo(DEFAULT_TIMEZONE);
+ assertThat(retrieved.getStartHour()).isEqualTo(startHour);
+ assertThat(retrieved.getEndHour()).isEqualTo(endHour);
+ assertThat(retrieved.getHoursInDay()).isEqualTo(endHour - startHour);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getMissingPropertiesCalendar")
+ void missingProperties(Map<String, Object> propertyMap, List<String>
errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonIllegalArgumentAssertion(() -> new
CalendarBean(calendarConfiguration), errorMessages);
+ }
+
+ @ParameterizedTest
+ @MethodSource("getInvalidPropertiesCalendar")
+ public void invalidProperties(Map<String, Object> propertyMap,
List<String> errorMessages) {
+ Properties calendarConfiguration = new Properties();
+ calendarConfiguration.putAll(propertyMap);
+ commonIllegalArgumentAssertion(() -> new
CalendarBean(calendarConfiguration), errorMessages);
+ }
+
+ // Let's avoid duplication
+ private void commonStaticMethodValidation(Consumer<StringBuilder>
executedMethod,
+ List<String> errorMessages) {
+ StringBuilder errors = new StringBuilder();
+ assertThat(errors).isEmpty();
+ executedMethod.accept(errors);
+ assertThat(errors).isNotEmpty();
+ String[] retrievedErrors = errors.toString().split("\n");
+ assertThat(retrievedErrors).hasSize(errorMessages.size());
+ errorMessages.forEach(msg ->
assertThat(retrievedErrors).contains(msg));
+ }
+
+ private void
commonIllegalArgumentAssertion(ThrowableAssert.ThrowingCallable executedMethod,
List<String> errorMessages) {
+ ThrowableAssert throwableAssert = (ThrowableAssert)
assertThatThrownBy(executedMethod)
+ .isInstanceOf(IllegalArgumentException.class);
+ errorMessages.forEach(throwableAssert::hasMessageContaining);
+ }
+
+ private static Stream<Arguments> getMissingPropertiesCalendar() {
+ return Stream.of(
+ Arguments.of(Map.of(), List.of("Property " + START_HOUR + " is
required", "Property " + END_HOUR + " is required")),
+ Arguments.of(Map.of(START_HOUR, "9"), List.of("Property " +
END_HOUR + " is required")),
+ Arguments.of(Map.of(END_HOUR, "17"), List.of("Property " +
START_HOUR + " is required")));
+ }
+
+ private static Stream<Arguments> getWronglyFormatPropertiesCalendar() {
+
+ return Stream.of(
+ Arguments.of(Map.of(START_HOUR, "9", END_HOUR, "25"),
List.of(END_HOUR + " 25 outside expected boundaries (0-24)")),
+ Arguments.of(Map.of(START_HOUR, "26", END_HOUR, "-2"),
List.of(START_HOUR + " 26 outside expected boundaries (0-24)", END_HOUR + " -2
outside expected boundaries (0-24)")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "1,2,8,9"), List.of(WEEKEND_DAYS + " [8, 9] outside expected
boundaries (0-7)")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "0,1,2"), List.of("0 (= no weekends) and other values provided in
the given " + WEEKEND_DAYS + " 0,1,2")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "1,1,2"), List.of("There are repeated values in the given " +
WEEKEND_DAYS + " 1,1,2")),
+ Arguments.of(Map.of(START_HOUR, "", END_HOUR, ""),
List.of(START_HOUR + " is not valid: For input string: \"\"", END_HOUR + " is
not valid: For input string: \"\"")));
+ }
+
+ private static Stream<Arguments> getBusinessInvalidPropertiesCalendar() {
+
+ return Stream.of(
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "10"),
List.of(START_HOUR + " 10 and " + END_HOUR + " 10 must be different")));
+ }
+
+ private static Stream<Arguments> getPartialPropertiesCalendar() {
+
+ return Stream.of(
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
HOLIDAY_DATE_FORMAT, "dd-mm-YYYY", TIMEZONE, "ACT"), Map.of(WEEKEND_DAYS,
DEFAULT_WEEKENDS)),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "5,6", TIMEZONE, "ACT"), Map.of(HOLIDAY_DATE_FORMAT,
DEFAULT_HOLIDAY_DATE_FORMAT)),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "5,6", HOLIDAY_DATE_FORMAT, "dd-mm-YYYY"), Map.of(TIMEZONE,
DEFAULT_TIMEZONE)));
+ }
+
+ private static Stream<Arguments> getInvalidPropertiesCalendar() {
+
+ return Stream.of(
+ Arguments.of(Map.of(START_HOUR, "9", END_HOUR, "25"),
List.of(END_HOUR + " 25 outside expected boundaries (0-24)")),
+ Arguments.of(Map.of(START_HOUR, "26", END_HOUR, "-2"),
List.of(START_HOUR + " 26 outside expected boundaries (0-24)", END_HOUR + " -2
outside expected boundaries (0-24)")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "1,2,8,9"), List.of(WEEKEND_DAYS + " [8, 9] outside expected
boundaries (0-7)")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "10"),
List.of(START_HOUR + " 10 and " + END_HOUR + " 10 must be different")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "0,1,2"), List.of("0 (= no weekends) and other values provided in
the given " + WEEKEND_DAYS + " 0,1,2")),
+ Arguments.of(Map.of(START_HOUR, "10", END_HOUR, "4",
WEEKEND_DAYS, "1,1,2"), List.of("There are repeated values in the given " +
WEEKEND_DAYS + " 1,1,2")),
+ Arguments.of(Map.of(START_HOUR, "", END_HOUR, ""),
List.of(START_HOUR + " is not valid: For input string: \"\"", END_HOUR + " is
not valid: For input string: \"\"")));
+ }
+}
diff --git a/jbpm/jbpm-flow/src/test/resources/calendar.properties
b/jbpm/jbpm-flow/src/test/resources/calendar.properties
new file mode 100644
index 0000000000..9acb81c21e
--- /dev/null
+++ b/jbpm/jbpm-flow/src/test/resources/calendar.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+business.end.hour=16
+business.start.hour=10
+
diff --git a/jbpm/jbpm-flow/src/test/resources/logback-test.xml
b/jbpm/jbpm-flow/src/test/resources/logback-test.xml
index e269349389..9a48cd358d 100755
--- a/jbpm/jbpm-flow/src/test/resources/logback-test.xml
+++ b/jbpm/jbpm-flow/src/test/resources/logback-test.xml
@@ -29,6 +29,7 @@
</appender>
<logger name="org.jbpm" level="info"/>
+ <logger name="org.jbpm.process.core.timer" level="info"/>
<logger name="com.arjuna" level="error"/>
<logger name="org.hibernate" level="warn"/>
diff --git
a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java
b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTimerProcessTest.java
similarity index 82%
rename from
jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java
rename to
jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTimerProcessTest.java
index 0ca0a9bde8..99704a6e18 100644
---
a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java
+++
b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTimerProcessTest.java
@@ -28,6 +28,7 @@ import java.util.Properties;
import org.jbpm.bpmn2.objects.TestWorkItemHandler;
import org.jbpm.process.core.timer.BusinessCalendarImpl;
+import org.jbpm.process.core.timer.CalendarBean;
import org.jbpm.test.utils.ProcessTestHelper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -38,19 +39,20 @@ import org.kie.kogito.process.impl.AbstractProcessConfig;
import static org.assertj.core.api.Assertions.assertThat;
-public class BusinessCalendarTest {
+public class BusinessCalendarTimerProcessTest {
- private static BusinessCalendar workingDayCalendar;
- private static BusinessCalendar notWorkingDayCalendar;
+ private static Properties notWorkingDayCalendarConfiguration;
+ private static Properties workingDayCalendarConfiguration;
@BeforeAll
public static void createCalendars() {
- workingDayCalendar = configureBusinessCalendar(true);
- notWorkingDayCalendar = configureBusinessCalendar(false);
+ workingDayCalendarConfiguration = configureBusinessCalendar(true);
+ notWorkingDayCalendarConfiguration = configureBusinessCalendar(false);
}
@Test
public void testTimerWithWorkingDayCalendar() throws InterruptedException {
+ BusinessCalendar workingDayCalendar =
BusinessCalendarImpl.builder().withCalendarBean(new
CalendarBean(workingDayCalendarConfiguration)).build();
Application app = ProcessTestHelper.newApplication(new
MockProcessConfig(workingDayCalendar));
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler);
@@ -65,6 +67,7 @@ public class BusinessCalendarTest {
@Test
public void testTimerWithNotWorkingDayCalendar() throws
InterruptedException {
+ BusinessCalendar notWorkingDayCalendar =
BusinessCalendarImpl.builder().withCalendarBean(new
CalendarBean(notWorkingDayCalendarConfiguration)).build();
Application app = ProcessTestHelper.newApplication(new
MockProcessConfig(notWorkingDayCalendar));
TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler);
@@ -77,14 +80,12 @@ public class BusinessCalendarTest {
assertThat(instance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE);
}
- private static BusinessCalendar configureBusinessCalendar(boolean
isWorkingDayCalendar) {
+ private static Properties configureBusinessCalendar(boolean
isWorkingDayCalendar) {
Properties businessCalendarConfiguration = new Properties();
if (isWorkingDayCalendar) {
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.START_HOUR, "0");
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.END_HOUR, "24");
-
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.HOURS_PER_DAY,
"24");
-
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.DAYS_PER_WEEK,
"7");
-
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.WEEKEND_DAYS,
"8,9");
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.WEEKEND_DAYS,
"0");
} else {
Calendar currentCalendar = Calendar.getInstance();
Date today = new Date();
@@ -92,10 +93,13 @@ public class BusinessCalendarTest {
Date tomorrow = currentCalendar.getTime();
String dateFormat = "yyyy-MM-dd";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.START_HOUR, "9");
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.END_HOUR, "17");
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.HOLIDAYS,
sdf.format(today) + "," + sdf.format(tomorrow));
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.WEEKEND_DAYS,
"1,2,3,4,5");
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.HOLIDAY_DATE_FORMAT,
dateFormat);
}
- return new BusinessCalendarImpl(businessCalendarConfiguration);
+ return businessCalendarConfiguration;
}
private static class MockProcessConfig extends AbstractProcessConfig {
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
index 4504af36e1..ff375187a2 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
@@ -31,6 +31,6 @@ public class BusinessCalendarProducer {
@Produces
public BusinessCalendar createBusinessCalendar() {
- return new BusinessCalendarImpl();
+ return BusinessCalendarImpl.builder().build();
}
}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
index 8fd9f766a1..2f0ca351f8 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
@@ -33,6 +33,6 @@ public class BusinessCalendarProducer {
@Bean
public BusinessCalendar createBusinessCalendar() {
- return new BusinessCalendarImpl();
+ return BusinessCalendarImpl.builder().build();
}
}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
deleted file mode 100644
index dd5e5f18ea..0000000000
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 $Package$;
-
-import org.kie.kogito.calendar.BusinessCalendar;
-import org.kie.kogito.calendar.BusinessCalendarImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import jakarta.enterprise.inject.Produces;
-
-public class BusinessCalendarProducer {
-
- private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
-
- @Produces
- public BusinessCalendar createBusinessCalendar() {
- return new BusinessCalendarImpl();
- }
-}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
deleted file mode 100644
index e80c7cb10b..0000000000
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 $Package$;
-
-import org.kie.kogito.calendar.BusinessCalendar;
-import org.kie.kogito.calendar.BusinessCalendarImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class BusinessCalendarProducer {
-
- private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
-
- @Bean
- public BusinessCalendar createBusinessCalendar() {
- return new BusinessCalendarImpl();
- }
-}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]