[jbpm-commits] JBoss JBPM SVN: r6592 - in jbpm4/trunk/modules: pvm/src/main/java/org/jbpm/pvm/internal/cal and 1 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Sat Aug 14 23:39:22 EDT 2010


Author: rebody
Date: 2010-08-14 23:39:21 -0400 (Sat, 14 Aug 2010)
New Revision: 6592

Modified:
   jbpm4/trunk/modules/devguide/src/main/docbook/en/modules/ch02-Incubation.xml
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/BusinessCalendarImpl.java
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Day.java
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/DayPart.java
   jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Duration.java
   jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/timer/TimerTest.java
Log:
JBPM-2759 provide subtract for business day.

Modified: jbpm4/trunk/modules/devguide/src/main/docbook/en/modules/ch02-Incubation.xml
===================================================================
--- jbpm4/trunk/modules/devguide/src/main/docbook/en/modules/ch02-Incubation.xml	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/devguide/src/main/docbook/en/modules/ch02-Incubation.xml	2010-08-15 03:39:21 UTC (rev 6592)
@@ -80,7 +80,6 @@
       that only business hours should be taken into account for this duration. Without 
       the indication business, the duration will be interpreted as an absolute time period.
       How to configure business hours is explained in <xref linkend="businesscalendar"/>
-      <emphasis>Note: 'business' is not supported when subtracting from a base date!</emphasis>
       </para>
       
       <section id="baseDate">
@@ -113,7 +112,6 @@
 &lt;reminder name="hitBoss" duedate="#{payRaiseDay} + 3 days" repeat="#{iritationFactor}" /&gt;
         </programlisting>
         
-        <para>Remember, the following example, a subtraction in combination with 'business', is <emphasis>not</emphasis> supported and will throw an exception, as will resulting due dates that will be in the past</para>
         <programlisting language="xml">&lt;reminder name="toGoOrNotToGo" duedate="#{goLive} - 3 business days"/&gt;</programlisting>
       </section>
     </section>

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/BusinessCalendarImpl.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/BusinessCalendarImpl.java	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/BusinessCalendarImpl.java	2010-08-15 03:39:21 UTC (rev 6592)
@@ -57,6 +57,7 @@
   protected long monthInMillis = 30*dayInMillis;
   protected long yearInMillis = 365*dayInMillis;
 
+  //TODO: calculate these numbers based on business calendar config
   protected long businessDayInMillis = 8*hourInMillis;
   protected long businessWeekInMillis = 40*hourInMillis;
   protected long businessMonthInMillis = 21*dayInMillis;
@@ -78,16 +79,27 @@
   }
 
   public Date subtract(Date date, String duration) {
-    if (duration.contains("business")) {
-      throw new JbpmException("Duedate subtraction not supported for business durations");
-    }
     return subtract(date, new Duration(duration));
   }
 
   public Date subtract(Date date, Duration duration) {
     Date end = null;
-    long millis = convertToMillis(duration);
-    end = new Date(date.getTime() - millis);
+    if (duration.isBusinessTime()) {
+        DayPart dayPart = findDayPart(date);
+        boolean isInbusinessHours = (dayPart!=null);
+        if (! isInbusinessHours) {
+          Object[] result = new Object[2];
+          Day day = findDay(date);
+          day.findPreviousDayPartStart((day.getDayParts()!=null ? day.getDayParts().length-1 : 0), date, result);
+          date = (Date) result[0];
+          dayPart = (DayPart) result[1];
+        }
+        long millis = convertToMillis(duration);
+        end = dayPart.subtract(date, millis, duration.isBusinessTime());
+    } else {
+        long millis = convertToMillis(duration);
+        end = new Date(date.getTime()-millis);
+    }
     if (end.before(Clock.getTime())) {
         if (log.isWarnEnabled()) {
           log.warn("Duedate : [" + end + "] in the past of start : [" + date + "]");
@@ -169,6 +181,23 @@
     return date;
   }
 
+  protected Date findStartOfPreviousDay(Date date) {
+        Calendar calendar = createCalendar();
+        calendar.setTime(date);
+        calendar.add(Calendar.DATE, -1);
+        calendar.set(Calendar.HOUR_OF_DAY, 23);
+        calendar.set(Calendar.MINUTE, 59);
+        calendar.set(Calendar.SECOND, 59);
+        calendar.set(Calendar.MILLISECOND, 999);
+        date = calendar.getTime();
+        while(isHoliday(date)) {
+          calendar.setTime(date);
+          calendar.add(Calendar.DATE, -1);
+          date = calendar.getTime();
+        }
+        return date;
+      }
+
   public Calendar createCalendar() {
     return new GregorianCalendar();
   }

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Day.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Day.java	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Day.java	2010-08-15 03:39:21 UTC (rev 6592)
@@ -28,9 +28,9 @@
  * is a day on a business calendar.
  */
 public class Day implements Serializable {
-  
+
   private static final long serialVersionUID = 1L;
-  
+
   protected long oid = -1;
   protected int version = 0;
   protected DayPart[] dayParts = null;
@@ -55,6 +55,25 @@
     }
   }
 
+  public void findPreviousDayPartStart(int dayPartIndex, Date date, Object[] result) {
+        // if there is a day part in this day that starts before the given date
+        if ( (dayParts!=null)
+             && (dayPartIndex >= 0)
+           ) {
+          if (dayParts[dayPartIndex].isEndBefore(date)) {
+            result[0] = dayParts[dayPartIndex].getEndTime(date);
+            result[1] = dayParts[dayPartIndex];
+          } else {
+              findPreviousDayPartStart(dayPartIndex-1, date, result);
+          }
+        } else {
+          // descend recursively
+          date = businessCalendarImpl.findStartOfPreviousDay(date);
+          Day previousDay = businessCalendarImpl.findDay(date);
+          previousDay.findPreviousDayPartStart((previousDay.getDayParts()!=null ? previousDay.getDayParts().length-1 : 0), date, result);
+        }
+      }
+
   public BusinessCalendarImpl getBusinessCalendar() {
     return businessCalendarImpl;
   }

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/DayPart.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/DayPart.java	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/DayPart.java	2010-08-15 03:39:21 UTC (rev 6592)
@@ -26,13 +26,13 @@
 import java.util.Date;
 
 /**
- * is part of a day that can for example be used to represent business hours. 
+ * is part of a day that can for example be used to represent business hours.
  *
  */
 public class DayPart implements Serializable {
 
   private static final long serialVersionUID = 1L;
-  
+
   protected long oid = -1;
   protected int version = 0;
   protected int fromHour = -1;
@@ -44,7 +44,7 @@
 
   public Date add(Date date, long millis, boolean isBusinessTime) {
     Date end = null;
-    
+
     BusinessCalendarImpl businessCalendarImpl = day.getBusinessCalendar();
     Calendar calendar = businessCalendarImpl.createCalendar();
     calendar.setTime(date);
@@ -54,52 +54,97 @@
     long dateMilliseconds = ((hour*60)+minute)*60*1000;
     long dayPartEndMilleseconds = ((toHour*60)+toMinute)*60*1000;
     long millisecondsInThisDayPart = dayPartEndMilleseconds - dateMilliseconds;
-    
+
     if (millis <= millisecondsInThisDayPart) {
       end = new Date( date.getTime() + millis);
     } else {
       long remainderMillis = millis - millisecondsInThisDayPart;
       Date dayPartEndDate = new Date(date.getTime() + millis - remainderMillis);
-      
+
       Object[] result = new Object[2];
       day.findNextDayPartStart(index+1, dayPartEndDate, result);
       Date nextDayPartStart = (Date) result[0];
       DayPart nextDayPart = (DayPart) result[1];
-      
+
       end = nextDayPart.add(nextDayPartStart, remainderMillis, isBusinessTime);
     }
-    
+
     return end;
   }
-  
+
+  public Date subtract(Date date, long millis, boolean isBusinessTime) {
+        Date end = null;
+
+        BusinessCalendarImpl businessCalendarImpl = day.getBusinessCalendar();
+        Calendar calendar = businessCalendarImpl.createCalendar();
+        calendar.setTime(date);
+        int hour = calendar.get(Calendar.HOUR_OF_DAY);
+        int minute = calendar.get(Calendar.MINUTE);
+
+        long dateMilliseconds = ((hour*60)+minute)*60*1000;
+        long dayPartStartMilleseconds = ((fromHour*60)+fromMinute)*60*1000;
+        long millisecondsInThisDayPart = dateMilliseconds - dayPartStartMilleseconds;
+
+        if (millis <= millisecondsInThisDayPart) {
+          end = new Date( date.getTime() - millis);
+        } else {
+          long remainderMillis = millis - millisecondsInThisDayPart;
+          Date dayPartStartDate = new Date(date.getTime() - millis + remainderMillis);
+
+          Object[] result = new Object[2];
+          day.findPreviousDayPartStart(index, dayPartStartDate, result);
+          Date previousDayPartStart = (Date) result[0];
+          DayPart previousDayPart = (DayPart) result[1];
+
+          end = previousDayPart.subtract(previousDayPartStart, remainderMillis, isBusinessTime);
+        }
+
+        return end;
+      }
+
+
   public boolean isStartAfter(Date date) {
     Calendar calendar = day.getBusinessCalendar().createCalendar();
     calendar.setTime(date);
     int hour = calendar.get(Calendar.HOUR_OF_DAY);
     int minute = calendar.get(Calendar.MINUTE);
-    
+
     return ( (hour<fromHour)
              || ( (hour==fromHour)
-                  && (minute<=fromMinute) 
-                ) 
+                  && (minute<=fromMinute)
+                )
            );
   }
 
 
+  public boolean isEndBefore(Date date) {
+        Calendar calendar = day.getBusinessCalendar().createCalendar();
+        calendar.setTime(date);
+        int hour = calendar.get(Calendar.HOUR_OF_DAY);
+        int minute = calendar.get(Calendar.MINUTE);
+
+        return ( (toHour<hour)
+                 || ( (toHour==hour)
+                      && (toMinute<=minute)
+                    )
+               );
+      }
+
+
   public boolean includes(Date date) {
     Calendar calendar = day.getBusinessCalendar().createCalendar();
     calendar.setTime(date);
     int hour = calendar.get(Calendar.HOUR_OF_DAY);
     int minute = calendar.get(Calendar.MINUTE);
-    
+
     return ( ( (fromHour<hour)
                || ( (fromHour==hour)
-                   && (fromMinute<=minute) 
+                   && (fromMinute<=minute)
                  )
              ) &&
              ( (hour<toHour)
                || ( (hour==toHour)
-                    && (minute<=toMinute) 
+                    && (minute<=toMinute)
                   )
              )
            );
@@ -112,7 +157,17 @@
     calendar.set(Calendar.MINUTE, fromMinute);
     return calendar.getTime();
   }
-  
+
+  public Date getEndTime(Date date) {
+        Calendar calendar = day.getBusinessCalendar().createCalendar();
+        calendar.setTime(date);
+        calendar.set(Calendar.HOUR_OF_DAY, toHour);
+        calendar.set(Calendar.MINUTE, toMinute);
+        calendar.set(Calendar.SECOND, 0);
+        calendar.set(Calendar.MILLISECOND, 0);
+        return calendar.getTime();
+  }
+
   public Day getDay() {
     return day;
   }

Modified: jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Duration.java
===================================================================
--- jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Duration.java	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/pvm/src/main/java/org/jbpm/pvm/internal/cal/Duration.java	2010-08-15 03:39:21 UTC (rev 6592)
@@ -165,9 +165,6 @@
       if (durationString == null || durationString.length() == 0) {
         duedate = baseDate;
       } else {
-        if (durationString.contains("business") && durationSeparator == '-') {
-          throw new JbpmException("Invalid duedate, subtraction ('-') not supported if duedate contains 'business'");
-        }
         BusinessCalendar businessCalendar = EnvironmentImpl.getFromCurrent(BusinessCalendar.class);
         if (durationSeparator == '+') {
           duedate = businessCalendar.add(baseDate, durationString);

Modified: jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/timer/TimerTest.java
===================================================================
--- jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/timer/TimerTest.java	2010-08-15 02:44:16 UTC (rev 6591)
+++ jbpm4/trunk/modules/test-db/src/test/java/org/jbpm/test/timer/TimerTest.java	2010-08-15 03:39:21 UTC (rev 6592)
@@ -369,7 +369,7 @@
     } catch (Exception e) {}
   }
 
-  public void testTimerELSubtractBusinessFail() {
+  public void testTimerELSubtractBusiness() {
     deployJpdlXmlString(
       "<process name='Insurance claim' key='ICL'>" +
       "  <start>" +
@@ -378,7 +378,7 @@
       "  <state name='a'>" +
       "    <transition to='b' />" +
       "    <transition name='timeout' to='escalate'>" +
-      "      <timer duedate='#{proc_var} - 6 business days' />" +
+      "      <timer duedate='#{proc_var} - 5 business days' />" +
       "    </transition>" +
       "  </state>" +
       "  <state name='b' />" +
@@ -388,15 +388,25 @@
 
     Map<String, Object> proc_vars = new HashMap<String, Object>();
     Calendar cal = Calendar.getInstance();
-    cal.set(2010, 01, 12, 12, 00, 00); // 12 feb 2010 noon
+    cal.set(2012, 01, 17, 12, 00, 00); // 17 feb 2012 noon
     proc_vars.put("proc_var", cal);
-    try {
-      executionService.startProcessInstanceByKey("ICL", proc_vars, "82436");
-      fail("Should not happen, exception expected");
-    } catch (Exception e) {}
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL", proc_vars, "82436");
+
+    Job job = managementService.createJobQuery()
+      .processInstanceId(processInstance.getId())
+      .uniqueResult();
+
+    Calendar jobDate = Calendar.getInstance();
+    jobDate.setTime(job.getDuedate());
+
+    // 17 feb is friday, 5 businessdays before is friday 10th
+    assertEquals(10 , jobDate.get(Calendar.DAY_OF_MONTH));
+
+    managementService.executeJob(job.getId());
+    assertProcessInstanceEnded(processInstance);
   }
 
-  public void testTimerELSubtractPastFail() {
+  public void testTimerELSubtractBusinessLeap() {
     deployJpdlXmlString(
       "<process name='Insurance claim' key='ICL'>" +
       "  <start>" +
@@ -405,6 +415,76 @@
       "  <state name='a'>" +
       "    <transition to='b' />" +
       "    <transition name='timeout' to='escalate'>" +
+      "      <timer duedate='#{proc_var} - 5 business days' />" +
+      "    </transition>" +
+      "  </state>" +
+      "  <state name='b' />" +
+      "  <end name='escalate' />" +
+      "</process>"
+    );
+
+    Map<String, Object> proc_vars = new HashMap<String, Object>();
+    Calendar cal = Calendar.getInstance();
+    cal.set(2012, 2, 5, 12, 00, 00); // 5 march 2012 noon
+    proc_vars.put("proc_var", cal);
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL", proc_vars, "82436");
+
+    Job job = managementService.createJobQuery().processInstanceId(processInstance.getId()).uniqueResult();
+
+    Calendar jobDate = Calendar.getInstance();
+    jobDate.setTime(job.getDuedate());
+
+    // 5 mar is monday, 5 businessdays before is monday feb 27th (29-02-12 is leap day)
+    assertEquals(27, jobDate.get(Calendar.DAY_OF_MONTH));
+
+    managementService.executeJob(job.getId());
+    assertProcessInstanceEnded(processInstance);
+  }
+
+  public void testTimerELSubtractBusinessMonthBoundary() {
+    deployJpdlXmlString(
+      "<process name='Insurance claim' key='ICL'>" +
+      "  <start>" +
+      "    <transition to='a' />" +
+      "  </start>" +
+      "  <state name='a'>" +
+      "    <transition to='b' />" +
+      "    <transition name='timeout' to='escalate'>" +
+      "      <timer duedate='#{proc_var} - 10 business days' />" +
+      "    </transition>" +
+      "  </state>" +
+      "  <state name='b' />" +
+      "  <end name='escalate' />" +
+      "</process>"
+    );
+
+    Map<String, Object> proc_vars = new HashMap<String, Object>();
+    Calendar cal = Calendar.getInstance();
+    cal.set(2012, 02, 13, 21, 00, 00); // 13 mar 2012
+    proc_vars.put("proc_var", cal);
+    ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL", proc_vars, "82436");
+
+    Job job = managementService.createJobQuery().processInstanceId(processInstance.getId()).uniqueResult();
+
+    Calendar jobDate = Calendar.getInstance();
+    jobDate.setTime(job.getDuedate());
+
+    // 13 mar is tuesday, 10 businessdays before is tuesday feb 28th (29-02-12 is leap day)
+    assertEquals(28, jobDate.get(Calendar.DAY_OF_MONTH));
+
+    managementService.executeJob(job.getId());
+    assertProcessInstanceEnded(processInstance);
+  }
+
+  public void testTimerELSubtractPast() {
+    deployJpdlXmlString(
+      "<process name='Insurance claim' key='ICL'>" +
+      "  <start>" +
+      "    <transition to='a' />" +
+      "  </start>" +
+      "  <state name='a'>" +
+      "    <transition to='b' />" +
+      "    <transition name='timeout' to='escalate'>" +
       "      <timer duedate='#{proc_var} - 3 days' />" +
       "    </transition>" +
       "  </state>" +



More information about the jbpm-commits mailing list