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 33a0075900 [incubator-kie-issues#1554] Fix Business Calendar Usage
(#3686)
33a0075900 is described below
commit 33a00759008f81b0b39833844410369d93738d46
Author: Abhiram Gundala <[email protected]>
AuthorDate: Mon Oct 28 18:19:51 2024 -0400
[incubator-kie-issues#1554] Fix Business Calendar Usage (#3686)
* business-calendar
* business-calendar
* business-calendar
* business-calendar
* business-calendar
* business-calendar
* [gitgabrio_business-calendar] Minor refactoring AbstractProcessRuntime
and some tests
* [gitgabrio_business-calendar] Minor refactoring AbstractProcessRuntime
and some tests
* business-calendar
* business-calendar
* business-calendar
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../org/kie/kogito/calendar}/BusinessCalendar.java | 2 +-
.../java/org/kie/kogito/process/ProcessConfig.java | 3 +
.../process/core/constants/CalendarConstants.java | 25 +--
.../process/core/timer/BusinessCalendarImpl.java | 50 +++---
.../process/instance/AbstractProcessRuntime.java | 90 +++++++++++
.../process/instance/DummyKnowledgeRuntime.java | 9 ++
.../jbpm/process/instance/LightProcessRuntime.java | 80 +---------
.../jbpm/process/instance/ProcessRuntimeImpl.java | 80 +---------
.../instance/impl/WorkflowProcessInstanceImpl.java | 7 +-
.../instance/node/StateBasedNodeInstance.java | 9 +-
.../kogito/process/impl/AbstractProcessConfig.java | 11 +-
.../kogito/process/impl/StaticProcessConfig.java | 16 +-
.../core/timer/BusinessCalendarImplTest.java | 12 +-
.../process/instance/LightProcessRuntimeTest.java | 6 +
.../instance/RuleFlowProcessInstanceTest.java | 3 +-
.../process/impl/AbstractProcessConfigTest.java | 2 +-
.../calendar/BPMN2-BusinessCalendarTimer.bpmn2 | 172 +++++++++++++++++++++
.../java/org/jbpm/bpmn2/ProcessFactoryTest.java | 6 +
.../jbpm/bpmn2/calendar/BusinessCalendarTest.java | 115 ++++++++++++++
.../src/test/resources/calendar.properties | 6 +
.../kie/kogito/codegen/process/ProcessCodegen.java | 16 +-
...StaticDependencyInjectionProducerGenerator.java | 12 +-
.../config/ProcessConfigQuarkusTemplate.java | 11 +-
.../config/ProcessConfigSpringTemplate.java | 7 +-
.../BusinessCalendarProducerQuarkusTemplate.java | 31 ++--
.../BusinessCalendarProducerSpringTemplate.java | 33 ++--
.../kogito/codegen/process/ProcessCodegenTest.java | 43 +++++-
.../BusinessCalendarProducerQuarkusTemplate.java | 31 ++--
.../BusinessCalendarProducerSpringTemplate.java | 34 ++--
29 files changed, 604 insertions(+), 318 deletions(-)
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendar.java
b/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
old mode 100755
new mode 100644
similarity index 97%
rename from
jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendar.java
rename to
api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
index 544fbcb6ef..6e15a6cc0e
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/timer/BusinessCalendar.java
+++ b/api/kogito-api/src/main/java/org/kie/kogito/calendar/BusinessCalendar.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.jbpm.process.core.timer;
+package org.kie.kogito.calendar;
import java.util.Date;
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
index 40549bbcd9..b365c3d764 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++ b/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
@@ -20,6 +20,7 @@ package org.kie.kogito.process;
import org.kie.kogito.KogitoConfig;
import org.kie.kogito.auth.IdentityProvider;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.jobs.JobsService;
import org.kie.kogito.signal.SignalManagerHub;
import org.kie.kogito.uow.UnitOfWorkManager;
@@ -38,4 +39,6 @@ public interface ProcessConfig extends KogitoConfig {
ProcessVersionResolver versionResolver();
IdentityProvider identityProvider();
+
+ BusinessCalendar getBusinessCalendar();
}
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/constants/CalendarConstants.java
similarity index 57%
copy from api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
copy to
jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/constants/CalendarConstants.java
index 40549bbcd9..036902eabb 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/core/constants/CalendarConstants.java
@@ -16,26 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.kie.kogito.process;
+package org.jbpm.process.core.constants;
-import org.kie.kogito.KogitoConfig;
-import org.kie.kogito.auth.IdentityProvider;
-import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.signal.SignalManagerHub;
-import org.kie.kogito.uow.UnitOfWorkManager;
-
-public interface ProcessConfig extends KogitoConfig {
- WorkItemHandlerConfig workItemHandlers();
-
- ProcessEventListenerConfig processEventListeners();
-
- SignalManagerHub signalManagerHub();
-
- UnitOfWorkManager unitOfWorkManager();
-
- JobsService jobsService();
-
- ProcessVersionResolver versionResolver();
-
- IdentityProvider identityProvider();
+public class CalendarConstants {
+ public static final String BUSINESS_CALENDAR_PATH = "calendar.properties";
+ public static final String BUSINESS_CALENDAR_ENVIRONMENT_KEY =
"jbpm.business.calendar";
}
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 29017a6230..fbb650e3c3 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
@@ -20,6 +20,7 @@ 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;
@@ -30,15 +31,19 @@ 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:
@@ -103,46 +108,35 @@ public class BusinessCalendarImpl implements
BusinessCalendar {
public static final String WEEKEND_DAYS = "business.weekend.days";
public static final String TIMEZONE = "business.cal.timezone";
- private static final String DEFAULT_PROPERTIES_NAME =
"/jbpm.business.calendar.properties";
-
public BusinessCalendarImpl() {
- String propertiesLocation =
System.getProperty("jbpm.business.calendar.properties");
-
- if (propertiesLocation == null) {
- propertiesLocation = DEFAULT_PROPERTIES_NAME;
- }
- businessCalendarConfiguration = new Properties();
-
- InputStream in =
this.getClass().getResourceAsStream(propertiesLocation);
- if (in != null) {
-
- try {
- businessCalendarConfiguration.load(in);
- } catch (IOException e) {
- logger.error("Error while loading properties for business
calendar", e);
-
- }
- }
- init();
-
+ this(null);
}
public BusinessCalendarImpl(Properties configuration) {
- this.businessCalendarConfiguration = configuration;
- init();
+ this(configuration, null);
}
public BusinessCalendarImpl(Properties configuration, SessionClock clock) {
- this.businessCalendarConfiguration = configuration;
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() {
- if (this.businessCalendarConfiguration == null) {
- throw new IllegalArgumentException("BusinessCalendar configuration
was not provided.");
- }
-
daysPerWeek = getPropertyAsInt(DAYS_PER_WEEK, "5");
hoursInDay = getPropertyAsInt(HOURS_PER_DAY, "8");
startHour = getPropertyAsInt(START_HOUR, "9");
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java
index 4ef63f4376..cb388bb361 100644
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/AbstractProcessRuntime.java
@@ -18,22 +18,43 @@
*/
package org.jbpm.process.instance;
+import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import org.drools.base.time.TimeUtils;
+import org.drools.core.common.InternalKnowledgeRuntime;
+import org.jbpm.process.core.timer.DateTimeUtils;
+import org.jbpm.process.core.timer.Timer;
import org.jbpm.process.instance.event.KogitoProcessEventListenerAdapter;
import org.jbpm.process.instance.event.KogitoProcessEventSupportImpl;
+import org.jbpm.ruleflow.core.RuleFlowProcess;
+import org.jbpm.workflow.core.node.StartNode;
+import org.kie.api.definition.process.Process;
import org.kie.api.event.process.ProcessEventListener;
import org.kie.kogito.Application;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.internal.process.event.KogitoProcessEventListener;
import org.kie.kogito.internal.process.event.KogitoProcessEventSupport;
import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
+import org.kie.kogito.jobs.DurationExpirationTime;
+import org.kie.kogito.jobs.ExactExpirationTime;
+import org.kie.kogito.jobs.ExpirationTime;
+import org.kie.kogito.jobs.JobsService;
+import org.kie.kogito.jobs.ProcessJobDescription;
+import org.kie.kogito.signal.SignalManager;
+
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
public abstract class AbstractProcessRuntime implements InternalProcessRuntime
{
protected KogitoProcessEventSupport processEventSupport;
protected KogitoProcessRuntimeImpl kogitoProcessRuntime = new
KogitoProcessRuntimeImpl(this);
+
+ protected SignalManager signalManager;
+ protected JobsService jobService;
+
private final Application application;
private final Map<ProcessEventListener, KogitoProcessEventListener>
listenersMap = new IdentityHashMap<>();
@@ -72,6 +93,20 @@ public abstract class AbstractProcessRuntime implements
InternalProcessRuntime {
return (List<ProcessEventListener>) (Object)
((KogitoProcessEventSupportImpl) this.processEventSupport).getEventListeners();
}
+ protected void initStartTimers(Collection<Process> processes,
InternalKnowledgeRuntime kruntime) {
+ for (Process process : processes) {
+ RuleFlowProcess p = (RuleFlowProcess) process;
+ List<StartNode> startNodes = p.getTimerStart();
+ if (startNodes != null && !startNodes.isEmpty()) {
+ for (StartNode startNode : startNodes) {
+ if (startNode != null && startNode.getTimer() != null) {
+
jobService.scheduleProcessJob(ProcessJobDescription.of(createTimerInstance(startNode.getTimer(),
kruntime), p.getId()));
+ }
+ }
+ }
+ }
+ }
+
private KogitoProcessEventListener
asKogitoProcessEventListener(ProcessEventListener processEventListener) {
if (processEventListener instanceof KogitoProcessEventListener) {
return ((KogitoProcessEventListener) processEventListener);
@@ -85,4 +120,59 @@ public abstract class AbstractProcessRuntime implements
InternalProcessRuntime {
}
return listenersMap.remove(processEventListener);
}
+
+ protected ExpirationTime createTimerInstance(Timer timer,
InternalKnowledgeRuntime kruntime) {
+
+ if (kruntime != null &&
kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY) != null) {
+ BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY);
+
+ long delay =
businessCalendar.calculateBusinessTimeAsDuration(timer.getDelay());
+
+ if (timer.getPeriod() == null) {
+ return DurationExpirationTime.repeat(delay);
+ } else {
+ long period =
businessCalendar.calculateBusinessTimeAsDuration(timer.getPeriod());
+
+ return DurationExpirationTime.repeat(delay, period);
+ }
+ } else {
+ return configureTimerInstance(timer);
+ }
+ }
+
+ private ExpirationTime configureTimerInstance(Timer timer) {
+ long duration = -1;
+ switch (timer.getTimeType()) {
+ case Timer.TIME_CYCLE:
+ // when using ISO date/time period is not set
+ long[] repeatValues =
DateTimeUtils.parseRepeatableDateTime(timer.getDelay());
+ if (repeatValues.length == 3) {
+ return DurationExpirationTime.repeat(repeatValues[1],
repeatValues[2]);
+ } else {
+ long delay = repeatValues[0];
+ long period = -1;
+ try {
+ period = TimeUtils.parseTimeString(timer.getPeriod());
+
+ } catch (RuntimeException e) {
+ period = repeatValues[0];
+ }
+
+ return DurationExpirationTime.repeat(delay, period);
+ }
+
+ case Timer.TIME_DURATION:
+
+ duration = DateTimeUtils.parseDuration(timer.getDelay());
+ return DurationExpirationTime.repeat(duration);
+
+ case Timer.TIME_DATE:
+
+ return ExactExpirationTime.of(timer.getDate());
+
+ default:
+ throw new UnsupportedOperationException("Not supported timer
definition");
+ }
+
+ }
}
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/DummyKnowledgeRuntime.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/DummyKnowledgeRuntime.java
index 2f7da3c086..1ae292bbd3 100644
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/DummyKnowledgeRuntime.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/DummyKnowledgeRuntime.java
@@ -21,6 +21,7 @@ package org.jbpm.process.instance;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import org.drools.core.common.EndOperationListener;
import org.drools.core.common.InternalAgenda;
@@ -53,11 +54,15 @@ import org.kie.api.runtime.rule.QueryResults;
import org.kie.api.runtime.rule.ViewChangedEventListener;
import org.kie.api.time.SessionClock;
import org.kie.kogito.Application;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.internal.process.event.KogitoProcessEventSupport;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
import org.kie.kogito.jobs.JobsService;
+import org.kie.kogito.process.ProcessConfig;
+
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
/**
* A severely limited implementation of the WorkingMemory interface.
@@ -72,6 +77,10 @@ class DummyKnowledgeRuntime implements
InternalKnowledgeRuntime, KogitoProcessRu
this.processRuntime = processRuntime;
this.environment = new EnvironmentImpl();
// register codegen-based node instances factories
+ BusinessCalendar calendar =
processRuntime.getApplication().config().get(ProcessConfig.class).getBusinessCalendar();
+ if (Objects.nonNull(calendar)) {
+ environment.set(BUSINESS_CALENDAR_ENVIRONMENT_KEY, calendar);
+ }
environment.set("NodeInstanceFactoryRegistry", new
CodegenNodeInstanceFactoryRegistry());
}
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java
index df1b7c5ba6..a972d72daf 100755
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightProcessRuntime.java
@@ -24,16 +24,12 @@ import java.util.List;
import java.util.Map;
import org.drools.base.definitions.rule.impl.RuleImpl;
-import org.drools.base.time.TimeUtils;
import org.drools.core.common.InternalKnowledgeRuntime;
import org.drools.core.common.ReteEvaluator;
import org.drools.core.common.WorkingMemoryAction;
import org.drools.core.phreak.PropagationEntry;
import org.jbpm.process.core.event.EventFilter;
import org.jbpm.process.core.event.EventTypeFilter;
-import org.jbpm.process.core.timer.BusinessCalendar;
-import org.jbpm.process.core.timer.DateTimeUtils;
-import org.jbpm.process.core.timer.Timer;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.workflow.core.impl.DataAssociation;
import org.jbpm.workflow.core.impl.NodeIoHelper;
@@ -54,11 +50,7 @@ import org.kie.internal.runtime.StatefulKnowledgeSession;
import org.kie.kogito.Application;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
-import org.kie.kogito.jobs.DurationExpirationTime;
-import org.kie.kogito.jobs.ExactExpirationTime;
-import org.kie.kogito.jobs.ExpirationTime;
import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.jobs.ProcessJobDescription;
import org.kie.kogito.process.Processes;
import org.kie.kogito.services.jobs.impl.InMemoryJobService;
import org.kie.kogito.signal.SignalManager;
@@ -71,8 +63,6 @@ public class LightProcessRuntime extends
AbstractProcessRuntime {
private final InternalKnowledgeRuntime knowledgeRuntime;
private ProcessInstanceManager processInstanceManager;
- private SignalManager signalManager;
- private JobsService jobService;
private final KogitoWorkItemManager workItemManager;
private UnitOfWorkManager unitOfWorkManager;
@@ -102,20 +92,7 @@ public class LightProcessRuntime extends
AbstractProcessRuntime {
}
public void initStartTimers() {
- Collection<Process> processes = runtimeContext.getProcesses();
- for (Process process : processes) {
- RuleFlowProcess p = (RuleFlowProcess) process;
- List<StartNode> startNodes = p.getTimerStart();
- if (startNodes != null && !startNodes.isEmpty()) {
- for (StartNode startNode : startNodes) {
- if (startNode != null && startNode.getTimer() != null) {
-
-
jobService.scheduleProcessJob(ProcessJobDescription.of(createTimerInstance(startNode.getTimer(),
knowledgeRuntime), p.getId()));
-
- }
- }
- }
- }
+ initStartTimers(runtimeContext.getProcesses(), knowledgeRuntime);
}
@Override
@@ -424,61 +401,6 @@ public class LightProcessRuntime extends
AbstractProcessRuntime {
return runtimeContext.isActive();
}
- protected ExpirationTime createTimerInstance(Timer timer,
InternalKnowledgeRuntime kruntime) {
-
- if (kruntime != null &&
kruntime.getEnvironment().get("jbpm.business.calendar") != null) {
- BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get("jbpm.business.calendar");
-
- long delay =
businessCalendar.calculateBusinessTimeAsDuration(timer.getDelay());
-
- if (timer.getPeriod() == null) {
- return DurationExpirationTime.repeat(delay);
- } else {
- long period =
businessCalendar.calculateBusinessTimeAsDuration(timer.getPeriod());
-
- return DurationExpirationTime.repeat(delay, period);
- }
- } else {
- return configureTimerInstance(timer);
- }
- }
-
- private ExpirationTime configureTimerInstance(Timer timer) {
- long duration = -1;
- switch (timer.getTimeType()) {
- case Timer.TIME_CYCLE:
- // when using ISO date/time period is not set
- long[] repeatValues =
DateTimeUtils.parseRepeatableDateTime(timer.getDelay());
- if (repeatValues.length == 3) {
- return DurationExpirationTime.repeat(repeatValues[1],
repeatValues[2]);
- } else {
- long delay = repeatValues[0];
- long period = -1;
- try {
- period = TimeUtils.parseTimeString(timer.getPeriod());
-
- } catch (RuntimeException e) {
- period = repeatValues[0];
- }
-
- return DurationExpirationTime.repeat(delay, period);
- }
-
- case Timer.TIME_DURATION:
-
- duration = DateTimeUtils.parseDuration(timer.getDelay());
- return DurationExpirationTime.repeat(duration);
-
- case Timer.TIME_DATE:
-
- return ExactExpirationTime.of(timer.getDate());
-
- default:
- throw new UnsupportedOperationException("Not supported timer
definition");
- }
-
- }
-
public class SignalManagerSignalAction extends
PropagationEntry.AbstractPropagationEntry implements WorkingMemoryAction {
private String type;
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/ProcessRuntimeImpl.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/ProcessRuntimeImpl.java
index 3eaec2fce3..0cd50cde8d 100755
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/ProcessRuntimeImpl.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/ProcessRuntimeImpl.java
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Map;
import org.drools.base.definitions.rule.impl.RuleImpl;
-import org.drools.base.time.TimeUtils;
import org.drools.core.common.InternalKnowledgeRuntime;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.ReteEvaluator;
@@ -35,9 +34,6 @@ import
org.drools.core.time.impl.CommandServiceTimerJobFactoryManager;
import org.drools.core.time.impl.ThreadSafeTrackableTimeJobFactoryManager;
import org.jbpm.process.core.event.EventFilter;
import org.jbpm.process.core.event.EventTypeFilter;
-import org.jbpm.process.core.timer.BusinessCalendar;
-import org.jbpm.process.core.timer.DateTimeUtils;
-import org.jbpm.process.core.timer.Timer;
import org.jbpm.process.instance.event.DefaultSignalManagerFactory;
import org.jbpm.process.instance.event.KogitoProcessEventSupportImpl;
import org.jbpm.process.instance.impl.DefaultProcessInstanceManagerFactory;
@@ -47,7 +43,6 @@ import org.jbpm.workflow.core.impl.NodeIoHelper;
import org.jbpm.workflow.core.node.EventTrigger;
import org.jbpm.workflow.core.node.StartNode;
import org.jbpm.workflow.core.node.Trigger;
-import org.kie.api.KieBase;
import org.kie.api.command.ExecutableCommand;
import org.kie.api.definition.process.Node;
import org.kie.api.definition.process.Process;
@@ -66,11 +61,7 @@ import org.kie.internal.runtime.StatefulKnowledgeSession;
import org.kie.kogito.Application;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
-import org.kie.kogito.jobs.DurationExpirationTime;
-import org.kie.kogito.jobs.ExactExpirationTime;
-import org.kie.kogito.jobs.ExpirationTime;
import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.jobs.ProcessJobDescription;
import org.kie.kogito.services.identity.NoOpIdentityProvider;
import org.kie.kogito.services.jobs.impl.LegacyInMemoryJobService;
import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory;
@@ -84,8 +75,6 @@ public class ProcessRuntimeImpl extends
AbstractProcessRuntime {
private InternalKnowledgeRuntime kruntime;
private ProcessInstanceManager processInstanceManager;
- private SignalManager signalManager;
- private JobsService jobService;
private UnitOfWorkManager unitOfWorkManager;
public ProcessRuntimeImpl(Application application, InternalWorkingMemory
workingMemory) {
@@ -109,20 +98,7 @@ public class ProcessRuntimeImpl extends
AbstractProcessRuntime {
}
public void initStartTimers() {
- KieBase kbase = kruntime.getKieBase();
- Collection<Process> processes = kbase.getProcesses();
- for (Process process : processes) {
- RuleFlowProcess p = (RuleFlowProcess) process;
- List<StartNode> startNodes = p.getTimerStart();
- if (startNodes != null && !startNodes.isEmpty()) {
-
- for (StartNode startNode : startNodes) {
- if (startNode != null && startNode.getTimer() != null) {
-
jobService.scheduleProcessJob(ProcessJobDescription.of(createTimerInstance(startNode.getTimer(),
kruntime), p.getId()));
- }
- }
- }
- }
+ initStartTimers(kruntime.getKieBase().getProcesses(), kruntime);
}
private void initProcessInstanceManager() {
@@ -408,60 +384,6 @@ public class ProcessRuntimeImpl extends
AbstractProcessRuntime {
return active.booleanValue();
}
- protected ExpirationTime createTimerInstance(Timer timer,
InternalKnowledgeRuntime kruntime) {
- if (kruntime != null &&
kruntime.getEnvironment().get("jbpm.business.calendar") != null) {
- BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get("jbpm.business.calendar");
-
- long delay =
businessCalendar.calculateBusinessTimeAsDuration(timer.getDelay());
-
- if (timer.getPeriod() == null) {
- return DurationExpirationTime.repeat(delay);
- } else {
- long period =
businessCalendar.calculateBusinessTimeAsDuration(timer.getPeriod());
-
- return DurationExpirationTime.repeat(delay, period);
- }
- } else {
- return configureTimerInstance(timer);
- }
- }
-
- private ExpirationTime configureTimerInstance(Timer timer) {
- long duration = -1;
- switch (timer.getTimeType()) {
- case Timer.TIME_CYCLE:
- // when using ISO date/time period is not set
- long[] repeatValues =
DateTimeUtils.parseRepeatableDateTime(timer.getDelay());
- if (repeatValues.length == 3) {
- int parsedReapedCount = (int) repeatValues[0];
-
- return DurationExpirationTime.repeat(repeatValues[1],
repeatValues[2], parsedReapedCount);
- } else {
- long delay = repeatValues[0];
- long period = -1;
- try {
- period = TimeUtils.parseTimeString(timer.getPeriod());
- } catch (RuntimeException e) {
- period = repeatValues[0];
- }
-
- return DurationExpirationTime.repeat(delay, period);
- }
-
- case Timer.TIME_DURATION:
-
- duration = DateTimeUtils.parseDuration(timer.getDelay());
- return DurationExpirationTime.after(duration);
-
- case Timer.TIME_DATE:
-
- return ExactExpirationTime.of(timer.getDate());
-
- default:
- throw new UnsupportedOperationException("Not supported timer
definition");
- }
- }
-
@Override
public InternalKnowledgeRuntime getInternalKieRuntime() {
return this.kruntime;
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
index 93554930fa..25a0cd0467 100755
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/impl/WorkflowProcessInstanceImpl.java
@@ -44,7 +44,6 @@ import org.jbpm.process.core.ContextContainer;
import org.jbpm.process.core.ContextResolver;
import org.jbpm.process.core.context.variable.Variable;
import org.jbpm.process.core.context.variable.VariableScope;
-import org.jbpm.process.core.timer.BusinessCalendar;
import org.jbpm.process.core.timer.DateTimeUtils;
import org.jbpm.process.core.timer.Timer;
import org.jbpm.process.instance.ContextInstance;
@@ -81,6 +80,7 @@ import org.kie.api.definition.process.NodeContainer;
import org.kie.api.definition.process.WorkflowElementIdentifier;
import org.kie.api.runtime.rule.AgendaFilter;
import org.kie.internal.process.CorrelationKey;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.internal.process.event.KogitoEventListener;
import org.kie.kogito.internal.process.runtime.KogitoNodeInstance;
import org.kie.kogito.internal.process.runtime.KogitoNodeInstanceContainer;
@@ -102,6 +102,7 @@ import org.mvel2.integration.VariableResolverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
import static org.jbpm.ruleflow.core.Metadata.COMPENSATION;
import static org.jbpm.ruleflow.core.Metadata.CONDITION;
import static org.jbpm.ruleflow.core.Metadata.CORRELATION_KEY;
@@ -565,8 +566,8 @@ public abstract class WorkflowProcessInstanceImpl extends
ProcessInstanceImpl im
logger.debug("SLA due date is set to {}", slaDueDateExpression);
InternalKnowledgeRuntime kruntime = getKnowledgeRuntime();
long duration;
- if (kruntime.getEnvironment().get("jbpm.business.calendar") != null) {
- BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get("jbpm.business.calendar");
+ if (kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY)
!= null) {
+ BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY);
duration =
businessCalendar.calculateBusinessTimeAsDuration(slaDueDateExpression);
} else {
duration = DateTimeUtils.parseDuration(slaDueDateExpression);
diff --git
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
index 554da5acc5..6b69126d81 100755
---
a/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
+++
b/jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/StateBasedNodeInstance.java
@@ -29,7 +29,6 @@ import java.util.Optional;
import org.drools.core.common.InternalAgenda;
import org.drools.core.common.ReteEvaluator;
import org.drools.core.rule.consequence.InternalMatch;
-import org.jbpm.process.core.timer.BusinessCalendar;
import org.jbpm.process.core.timer.DateTimeUtils;
import org.jbpm.process.core.timer.Timer;
import org.jbpm.process.instance.InternalProcessRuntime;
@@ -44,6 +43,7 @@ import
org.jbpm.workflow.instance.impl.ExtendedNodeInstanceImpl;
import org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl;
import org.kie.api.event.rule.MatchCreatedEvent;
import org.kie.api.runtime.KieRuntime;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.internal.process.event.KogitoEventListener;
import org.kie.kogito.internal.process.runtime.KogitoNodeInstance;
import org.kie.kogito.internal.process.runtime.KogitoProcessContext;
@@ -60,6 +60,7 @@ import org.kie.kogito.timer.TimerInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_ENVIRONMENT_KEY;
import static org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE;
import static
org.jbpm.workflow.instance.node.TimerNodeInstance.TIMER_TRIGGERED_EVENT;
@@ -145,8 +146,8 @@ public abstract class StateBasedNodeInstance extends
ExtendedNodeInstanceImpl im
protected ExpirationTime createTimerInstance(Timer timer) {
KieRuntime kruntime = getProcessInstance().getKnowledgeRuntime();
- if (kruntime != null &&
kruntime.getEnvironment().get("jbpm.business.calendar") != null) {
- BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get("jbpm.business.calendar");
+ if (kruntime != null &&
kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY) != null) {
+ BusinessCalendar businessCalendar = (BusinessCalendar)
kruntime.getEnvironment().get(BUSINESS_CALENDAR_ENVIRONMENT_KEY);
String delay = null;
switch (timer.getTimeType()) {
case Timer.TIME_CYCLE:
@@ -183,7 +184,7 @@ public abstract class StateBasedNodeInstance extends
ExtendedNodeInstanceImpl im
case Timer.TIME_DURATION:
delay = resolveTimerExpression(timer.getDelay());
- return
DurationExpirationTime.repeat(businessCalendar.calculateBusinessTimeAsDuration(delay));
+ return
DurationExpirationTime.after(businessCalendar.calculateBusinessTimeAsDuration(delay));
case Timer.TIME_DATE:
// even though calendar is available concrete date was
provided so it shall be used
return ExactExpirationTime.of(timer.getDate());
diff --git
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java
index 25d278f628..ac13e65942 100644
---
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java
+++
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/AbstractProcessConfig.java
@@ -28,6 +28,7 @@ import java.util.stream.StreamSupport;
import org.kie.api.event.process.ProcessEventListener;
import org.kie.kogito.Addons;
import org.kie.kogito.auth.IdentityProvider;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.event.EventPublisher;
import org.kie.kogito.jobs.JobsService;
import org.kie.kogito.process.ProcessConfig;
@@ -51,6 +52,7 @@ public abstract class AbstractProcessConfig implements
ProcessConfig {
private final JobsService jobsService;
private final ProcessVersionResolver versionResolver;
private final IdentityProvider identityProvider;
+ private final BusinessCalendar businessCalendar;
protected AbstractProcessConfig(
Iterable<WorkItemHandlerConfig> workItemHandlerConfig,
@@ -62,7 +64,8 @@ public abstract class AbstractProcessConfig implements
ProcessConfig {
String kogitoService,
Iterable<UnitOfWorkEventListener> unitOfWorkListeners,
Iterable<ProcessVersionResolver> versionResolver,
- Iterable<IdentityProvider> identityProvider) {
+ Iterable<IdentityProvider> identityProvider,
+ Iterable<BusinessCalendar> businessCalendar) {
this.workItemHandlerConfig =
mergeWorkItemHandler(workItemHandlerConfig, DefaultWorkItemHandlerConfig::new);
this.processEventListenerConfig = merge(processEventListenerConfigs,
processEventListeners);
@@ -72,6 +75,7 @@ public abstract class AbstractProcessConfig implements
ProcessConfig {
this.jobsService = orDefault(jobsService, () -> null);
this.versionResolver = orDefault(versionResolver, () -> null);
this.identityProvider = orDefault(identityProvider,
NoOpIdentityProvider::new);
+ this.businessCalendar = orDefault(businessCalendar, () -> null);
eventPublishers.forEach(publisher ->
unitOfWorkManager().eventManager().addPublisher(publisher));
unitOfWorkListeners.forEach(listener ->
unitOfWorkManager().register(listener));
@@ -124,6 +128,11 @@ public abstract class AbstractProcessConfig implements
ProcessConfig {
return identityProvider;
}
+ @Override
+ public BusinessCalendar getBusinessCalendar() {
+ return this.businessCalendar;
+ }
+
public org.kie.kogito.Addons addons() {
return Addons.EMTPY;
}
diff --git
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java
index c540407c4f..343f232a04 100644
---
a/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java
+++
b/jbpm/jbpm-flow/src/main/java/org/kie/kogito/process/impl/StaticProcessConfig.java
@@ -19,6 +19,7 @@
package org.kie.kogito.process.impl;
import org.kie.kogito.auth.IdentityProvider;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.jobs.JobsService;
import org.kie.kogito.process.ProcessConfig;
import org.kie.kogito.process.ProcessEventListenerConfig;
@@ -41,12 +42,13 @@ public class StaticProcessConfig implements ProcessConfig {
private final ProcessVersionResolver versionResolver;
private final IdentityProvider identityProvider;
+ private final BusinessCalendar businessCalendar;
public StaticProcessConfig(
WorkItemHandlerConfig workItemHandlerConfig,
ProcessEventListenerConfig processEventListenerConfig,
UnitOfWorkManager unitOfWorkManager) {
- this(workItemHandlerConfig, processEventListenerConfig,
unitOfWorkManager, null, null, new NoOpIdentityProvider());
+ this(workItemHandlerConfig, processEventListenerConfig,
unitOfWorkManager, null, null, new NoOpIdentityProvider(), null);
}
public StaticProcessConfig(
@@ -55,7 +57,8 @@ public class StaticProcessConfig implements ProcessConfig {
UnitOfWorkManager unitOfWorkManager,
JobsService jobsService,
ProcessVersionResolver versionResolver,
- IdentityProvider identityProvider) {
+ IdentityProvider identityProvider,
+ BusinessCalendar calendar) {
this.unitOfWorkManager = unitOfWorkManager;
this.workItemHandlerConfig = workItemHandlerConfig;
this.processEventListenerConfig = processEventListenerConfig;
@@ -63,6 +66,7 @@ public class StaticProcessConfig implements ProcessConfig {
this.jobsService = jobsService;
this.versionResolver = versionResolver;
this.identityProvider = identityProvider;
+ this.businessCalendar = calendar;
}
public StaticProcessConfig() {
@@ -71,7 +75,8 @@ public class StaticProcessConfig implements ProcessConfig {
new DefaultUnitOfWorkManager(new
CollectingUnitOfWorkFactory()),
null,
null,
- new NoOpIdentityProvider());
+ new NoOpIdentityProvider(),
+ null);
}
@Override
@@ -108,4 +113,9 @@ public class StaticProcessConfig implements ProcessConfig {
public IdentityProvider identityProvider() {
return identityProvider;
}
+
+ @Override
+ public BusinessCalendar getBusinessCalendar() {
+ return this.businessCalendar;
+ }
}
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 1fc0852da9..3d398d753d 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
@@ -31,7 +31,7 @@ import org.kie.kogito.timer.SessionPseudoClock;
import org.slf4j.LoggerFactory;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class BusinessCalendarImplTest extends AbstractBaseTest {
@@ -327,14 +327,8 @@ public class BusinessCalendarImplTest extends
AbstractBaseTest {
}
@Test
- public void testMissingConfigurationDualArgConstructor() {
- SessionPseudoClock clock = new
StaticPseudoClock(parseToDateWithTime("2012-05-04 13:45").getTime());
-
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new
BusinessCalendarImpl(null, clock));
- }
-
- @Test
- public void testMissingConfigurationSingleArgConstructor() {
-
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> new
BusinessCalendarImpl(null));
+ public void testBusinessCalendarWithoutProvidedConfiguration() {
+ assertDoesNotThrow(() -> new BusinessCalendarImpl());
}
@Test
diff --git
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/instance/LightProcessRuntimeTest.java
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/instance/LightProcessRuntimeTest.java
index 61a3c1f28a..7e3b18e691 100644
---
a/jbpm/jbpm-flow/src/test/java/org/jbpm/process/instance/LightProcessRuntimeTest.java
+++
b/jbpm/jbpm-flow/src/test/java/org/jbpm/process/instance/LightProcessRuntimeTest.java
@@ -26,9 +26,12 @@ import
org.jbpm.ruleflow.core.WorkflowElementIdentifierFactory;
import org.junit.jupiter.api.Test;
import org.kie.api.definition.process.WorkflowElementIdentifier;
import org.kie.kogito.Application;
+import org.kie.kogito.Config;
import org.kie.kogito.process.Processes;
+import org.kie.kogito.process.impl.AbstractProcessConfig;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -66,6 +69,9 @@ class LightProcessRuntimeTest {
LightProcessRuntimeContext rtc = new
LightProcessRuntimeContext(Collections.singletonList(myProcess.process));
Application application = mock(Application.class);
+ Config config = mock(Config.class);
+ when(application.config()).thenReturn(config);
+ when(config.get(any())).thenReturn(mock(AbstractProcessConfig.class));
when(application.get(Processes.class)).thenReturn(mock(Processes.class));
LightProcessRuntime rt = new LightProcessRuntime(rtc, services,
application);
diff --git
a/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/instance/RuleFlowProcessInstanceTest.java
b/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/instance/RuleFlowProcessInstanceTest.java
index d0b8ced9e9..68d2e177e4 100755
---
a/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/instance/RuleFlowProcessInstanceTest.java
+++
b/jbpm/jbpm-flow/src/test/java/org/jbpm/ruleflow/instance/RuleFlowProcessInstanceTest.java
@@ -27,7 +27,8 @@ import
org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
import org.slf4j.LoggerFactory;
-import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class RuleFlowProcessInstanceTest extends AbstractBaseTest {
diff --git
a/jbpm/jbpm-flow/src/test/java/org/kie/kogito/process/impl/AbstractProcessConfigTest.java
b/jbpm/jbpm-flow/src/test/java/org/kie/kogito/process/impl/AbstractProcessConfigTest.java
index 2f0b75255d..6a3f26da38 100644
---
a/jbpm/jbpm-flow/src/test/java/org/kie/kogito/process/impl/AbstractProcessConfigTest.java
+++
b/jbpm/jbpm-flow/src/test/java/org/kie/kogito/process/impl/AbstractProcessConfigTest.java
@@ -34,7 +34,7 @@ public class AbstractProcessConfigTest {
private static class MockProcessConfig extends AbstractProcessConfig {
protected MockProcessConfig(Iterable<WorkItemHandlerConfig>
workItemHandlerConfig) {
super(workItemHandlerConfig, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(),
- Collections.emptyList(), null, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList());
+ Collections.emptyList(), null, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
}
}
diff --git
a/jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/calendar/BPMN2-BusinessCalendarTimer.bpmn2
b/jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/calendar/BPMN2-BusinessCalendarTimer.bpmn2
new file mode 100644
index 0000000000..490924b1f1
--- /dev/null
+++
b/jbpm/jbpm-tests/src/test/bpmn/org/jbpm/bpmn2/calendar/BPMN2-BusinessCalendarTimer.bpmn2
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.omg.org/bpmn20"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:bpsim="http://www.bpsim.org/schemas/1.0"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:drools="http://www.jboss.org/drools" id="_02EK0ABVEeSwDJQJw6Rb7Q"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/201 [...]
+ <bpmn2:process id="BusinessCalendarTimer"
drools:packageName="org.jbpm.bpmn2.calendar"
+ drools:version="1.0" name="BusinessCalendar-timer" isExecutable="true">
+ <bpmn2:startEvent id="processStartEvent" drools:bgcolor="#9acd32"
drools:selectable="true" name="">
+ <bpmn2:outgoing>_BD039EC9-BD1C-44EB-8DB7-347E412D0F7A</bpmn2:outgoing>
+ </bpmn2:startEvent>
+ <bpmn2:userTask id="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF"
drools:selectable="true" drools:scriptFormat="http://www.java.com/java"
name="Human Task">
+ <bpmn2:incoming>_BD039EC9-BD1C-44EB-8DB7-347E412D0F7A</bpmn2:incoming>
+ <bpmn2:outgoing>_AA98E9A1-3635-40E0-8991-471E24785D03</bpmn2:outgoing>
+ <bpmn2:ioSpecification id="_02EK0QBVEeSwDJQJw6Rb7Q">
+ <bpmn2:dataInput
id="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF_TaskNameInputX" name="TaskName"/>
+ <bpmn2:inputSet id="_02Ex4ABVEeSwDJQJw6Rb7Q"/>
+ <bpmn2:outputSet id="_02Ex4QBVEeSwDJQJw6Rb7Q"/>
+ </bpmn2:ioSpecification>
+ <bpmn2:dataInputAssociation id="_02Ex4gBVEeSwDJQJw6Rb7Q">
+
<bpmn2:targetRef>_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF_TaskNameInputX</bpmn2:targetRef>
+ <bpmn2:assignment id="_02Ex4wBVEeSwDJQJw6Rb7Q">
+ <bpmn2:from xsi:type="bpmn2:tFormalExpression"
id="_02Ex5ABVEeSwDJQJw6Rb7Q">HumanTask</bpmn2:from>
+ <bpmn2:to xsi:type="bpmn2:tFormalExpression"
id="_02Ex5QBVEeSwDJQJw6Rb7Q">_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF_TaskNameInputX</bpmn2:to>
+ </bpmn2:assignment>
+ </bpmn2:dataInputAssociation>
+ <bpmn2:potentialOwner id="_02Ex5gBVEeSwDJQJw6Rb7Q">
+ <bpmn2:resourceAssignmentExpression id="_02Ex5wBVEeSwDJQJw6Rb7Q">
+ <bpmn2:formalExpression
id="_02Ex6ABVEeSwDJQJw6Rb7Q">john</bpmn2:formalExpression>
+ </bpmn2:resourceAssignmentExpression>
+ </bpmn2:potentialOwner>
+ </bpmn2:userTask>
+ <bpmn2:sequenceFlow id="_BD039EC9-BD1C-44EB-8DB7-347E412D0F7A"
drools:bgcolor="#000000" drools:selectable="true" sourceRef="processStartEvent"
targetRef="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF"/>
+ <bpmn2:endEvent id="_03613543-8CE0-406A-9777-46BC93776DFB"
drools:bgcolor="#ff6347" drools:selectable="true" name="">
+ <bpmn2:incoming>_AA98E9A1-3635-40E0-8991-471E24785D03</bpmn2:incoming>
+ </bpmn2:endEvent>
+ <bpmn2:sequenceFlow id="_AA98E9A1-3635-40E0-8991-471E24785D03"
drools:bgcolor="#000000" drools:selectable="true"
sourceRef="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF"
targetRef="_03613543-8CE0-406A-9777-46BC93776DFB"/>
+ <bpmn2:sequenceFlow id="_9C180400-8EAA-4C89-A148-AE8872CFD116"
drools:bgcolor="#000000" drools:selectable="true"
sourceRef="_BAFDAD64-4B68-44FE-B486-4159DC144C00"
targetRef="_C1F7D38E-261F-4113-BB78-590253E0C959"/>
+ <bpmn2:endEvent id="_C1F7D38E-261F-4113-BB78-590253E0C959"
drools:bgcolor="#ff6347" drools:selectable="true" name="">
+ <bpmn2:incoming>_9C180400-8EAA-4C89-A148-AE8872CFD116</bpmn2:incoming>
+ <bpmn2:terminateEventDefinition id="_02Ex6QBVEeSwDJQJw6Rb7Q"/>
+ </bpmn2:endEvent>
+ <bpmn2:boundaryEvent id="_BAFDAD64-4B68-44FE-B486-4159DC144C00"
drools:bgcolor="#f5deb3" drools:selectable="true" drools:bordercolor="#a0522d"
drools:boundaryca="false" name=""
attachedToRef="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF" cancelActivity="false">
+ <bpmn2:outgoing>_9C180400-8EAA-4C89-A148-AE8872CFD116</bpmn2:outgoing>
+ <bpmn2:timerEventDefinition id="_02Ex6gBVEeSwDJQJw6Rb7Q">
+ <bpmn2:timeDuration xsi:type="bpmn2:tFormalExpression"
id="_02Ex6wBVEeSwDJQJw6Rb7Q">1s</bpmn2:timeDuration>
+ </bpmn2:timerEventDefinition>
+ </bpmn2:boundaryEvent>
+ </bpmn2:process>
+ <bpmndi:BPMNDiagram id="_02Ex7ABVEeSwDJQJw6Rb7Q">
+ <bpmndi:BPMNPlane id="_02Ex7QBVEeSwDJQJw6Rb7Q"
bpmnElement="org.jbpm.test.regression.BusinessCalendar-timer">
+ <bpmndi:BPMNShape id="_02Ex7gBVEeSwDJQJw6Rb7Q"
bpmnElement="processStartEvent">
+ <dc:Bounds height="30.0" width="30.0" x="120.0" y="165.0"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="_02Ex7wBVEeSwDJQJw6Rb7Q"
bpmnElement="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF">
+ <dc:Bounds height="80.0" width="100.0" x="195.0" y="140.0"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge id="_02Ex8ABVEeSwDJQJw6Rb7Q"
bpmnElement="_BD039EC9-BD1C-44EB-8DB7-347E412D0F7A">
+ <di:waypoint xsi:type="dc:Point" x="135.0" y="180.0"/>
+ <di:waypoint xsi:type="dc:Point" x="245.0" y="180.0"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNShape id="_02Ex8QBVEeSwDJQJw6Rb7Q"
bpmnElement="_03613543-8CE0-406A-9777-46BC93776DFB">
+ <dc:Bounds height="28.0" width="28.0" x="338.0" y="164.0"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge id="_02Ex8gBVEeSwDJQJw6Rb7Q"
bpmnElement="_AA98E9A1-3635-40E0-8991-471E24785D03">
+ <di:waypoint xsi:type="dc:Point" x="245.0" y="180.0"/>
+ <di:waypoint xsi:type="dc:Point" x="352.0" y="178.0"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNEdge id="_02Ex8wBVEeSwDJQJw6Rb7Q"
bpmnElement="_9C180400-8EAA-4C89-A148-AE8872CFD116">
+ <di:waypoint xsi:type="dc:Point" x="255.0" y="220.0"/>
+ <di:waypoint xsi:type="dc:Point" x="255.0" y="287.0"/>
+ <di:waypoint xsi:type="dc:Point" x="352.0" y="285.0"/>
+ </bpmndi:BPMNEdge>
+ <bpmndi:BPMNShape id="_02Ex9ABVEeSwDJQJw6Rb7Q"
bpmnElement="_C1F7D38E-261F-4113-BB78-590253E0C959">
+ <dc:Bounds height="28.0" width="28.0" x="338.0" y="271.0"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNShape id="_02Ex9QBVEeSwDJQJw6Rb7Q"
bpmnElement="_BAFDAD64-4B68-44FE-B486-4159DC144C00">
+ <dc:Bounds height="30.0" width="30.0" x="240.0" y="205.0"/>
+ </bpmndi:BPMNShape>
+ <bpmndi:BPMNEdge id="_02Ex9gBVEeSwDJQJw6Rb7Q"
bpmnElement="_BAFDAD64-4B68-44FE-B486-4159DC144C00">
+ <di:waypoint xsi:type="dc:Point" x="60.0" y="77.0"/>
+ <di:waypoint xsi:type="dc:Point" x="60.0" y="77.0"/>
+ </bpmndi:BPMNEdge>
+ </bpmndi:BPMNPlane>
+ </bpmndi:BPMNDiagram>
+ <bpmn2:relationship id="_02Ex9wBVEeSwDJQJw6Rb7Q" type="BPSimData">
+ <bpmn2:extensionElements>
+ <bpsim:BPSimData>
+ <bpsim:Scenario xsi:type="bpsim:Scenario" id="default"
name="Simulationscenario">
+ <bpsim:ScenarioParameters xsi:type="bpsim:ScenarioParameters"
baseTimeUnit="min"/>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_7C1AEE0C-F9CC-46D8-BFC0-554A3046F3BF" id="_02Ex-ABVEeSwDJQJw6Rb7Q">
+ <bpsim:TimeParameters xsi:type="bpsim:TimeParameters">
+ <bpsim:ProcessingTime xsi:type="bpsim:Parameter">
+ <bpsim:UniformDistribution max="10.0" min="5.0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ResourceParameters xsi:type="bpsim:ResourceParameters">
+ <bpsim:Availability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="8.0"/>
+ </bpsim:Availability>
+ <bpsim:Quantity xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="1.0"/>
+ </bpsim:Quantity>
+ </bpsim:ResourceParameters>
+ <bpsim:CostParameters xsi:type="bpsim:CostParameters">
+ <bpsim:UnitCost xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="0.0"/>
+ </bpsim:UnitCost>
+ </bpsim:CostParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_03613543-8CE0-406A-9777-46BC93776DFB" id="_02Ex-QBVEeSwDJQJw6Rb7Q">
+ <bpsim:TimeParameters xsi:type="bpsim:TimeParameters">
+ <bpsim:ProcessingTime xsi:type="bpsim:Parameter">
+ <bpsim:UniformDistribution max="10.0" min="5.0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="processStartEvent" id="_02Ex-gBVEeSwDJQJw6Rb7Q">
+ <bpsim:TimeParameters xsi:type="bpsim:TimeParameters">
+ <bpsim:ProcessingTime xsi:type="bpsim:Parameter">
+ <bpsim:UniformDistribution max="10.0" min="5.0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ControlParameters xsi:type="bpsim:ControlParameters">
+ <bpsim:Probability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="100.0"/>
+ </bpsim:Probability>
+ </bpsim:ControlParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_BAFDAD64-4B68-44FE-B486-4159DC144C00" id="_02FY8ABVEeSwDJQJw6Rb7Q">
+ <bpsim:TimeParameters xsi:type="bpsim:TimeParameters">
+ <bpsim:ProcessingTime xsi:type="bpsim:Parameter">
+ <bpsim:UniformDistribution max="10.0" min="5.0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ <bpsim:ControlParameters xsi:type="bpsim:ControlParameters">
+ <bpsim:Probability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="100.0"/>
+ </bpsim:Probability>
+ </bpsim:ControlParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_C1F7D38E-261F-4113-BB78-590253E0C959" id="_02FY8QBVEeSwDJQJw6Rb7Q">
+ <bpsim:TimeParameters xsi:type="bpsim:TimeParameters">
+ <bpsim:ProcessingTime xsi:type="bpsim:Parameter">
+ <bpsim:UniformDistribution max="10.0" min="5.0"/>
+ </bpsim:ProcessingTime>
+ </bpsim:TimeParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_BD039EC9-BD1C-44EB-8DB7-347E412D0F7A" id="_02FY8gBVEeSwDJQJw6Rb7Q">
+ <bpsim:ControlParameters xsi:type="bpsim:ControlParameters">
+ <bpsim:Probability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="100.0"/>
+ </bpsim:Probability>
+ </bpsim:ControlParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_AA98E9A1-3635-40E0-8991-471E24785D03" id="_02FY8wBVEeSwDJQJw6Rb7Q">
+ <bpsim:ControlParameters xsi:type="bpsim:ControlParameters">
+ <bpsim:Probability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="100.0"/>
+ </bpsim:Probability>
+ </bpsim:ControlParameters>
+ </bpsim:ElementParameters>
+ <bpsim:ElementParameters xsi:type="bpsim:ElementParameters"
elementRef="_9C180400-8EAA-4C89-A148-AE8872CFD116" id="_02FY9ABVEeSwDJQJw6Rb7Q">
+ <bpsim:ControlParameters xsi:type="bpsim:ControlParameters">
+ <bpsim:Probability xsi:type="bpsim:Parameter">
+ <bpsim:FloatingParameter value="100.0"/>
+ </bpsim:Probability>
+ </bpsim:ControlParameters>
+ </bpsim:ElementParameters>
+ </bpsim:Scenario>
+ </bpsim:BPSimData>
+ </bpmn2:extensionElements>
+ <bpmn2:source>_02EK0ABVEeSwDJQJw6Rb7Q</bpmn2:source>
+ <bpmn2:target>_02EK0ABVEeSwDJQJw6Rb7Q</bpmn2:target>
+ </bpmn2:relationship>
+</bpmn2:definitions>
diff --git
a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ProcessFactoryTest.java
b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ProcessFactoryTest.java
index 2a78663004..5739164f83 100755
--- a/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ProcessFactoryTest.java
+++ b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/ProcessFactoryTest.java
@@ -41,12 +41,14 @@ import org.kie.api.io.Resource;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.io.ResourceFactory;
import org.kie.kogito.Application;
+import org.kie.kogito.Config;
import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener;
import org.kie.kogito.internal.process.event.KogitoProcessEventListener;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.process.Processes;
import org.kie.kogito.process.bpmn2.BpmnProcess;
import org.kie.kogito.process.bpmn2.BpmnProcesses;
+import org.kie.kogito.process.impl.AbstractProcessConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.jbpm.ruleflow.core.Metadata.CANCEL_ACTIVITY;
@@ -56,6 +58,7 @@ import static
org.jbpm.ruleflow.core.Metadata.EVENT_TYPE_TIMER;
import static org.jbpm.ruleflow.core.Metadata.HAS_ERROR_EVENT;
import static org.jbpm.ruleflow.core.Metadata.TIME_CYCLE;
import static org.jbpm.ruleflow.core.Metadata.TIME_DURATION;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -417,6 +420,9 @@ public class ProcessFactoryTest extends JbpmBpmn2TestCase {
final RuleFlowProcess process = factory.validate().getProcess();
Application application = mock(Application.class);
+ Config config = mock(Config.class);
+ when(application.config()).thenReturn(config);
+ when(config.get(any())).thenReturn(mock(AbstractProcessConfig.class));
when(application.get(Processes.class)).thenReturn(new
BpmnProcesses().addProcess(new BpmnProcess(process)));
final LightProcessRuntime processRuntime =
LightProcessRuntime.of(application, Collections.singletonList(process), new
LightProcessRuntimeServiceProvider());
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/BusinessCalendarTest.java
new file mode 100644
index 0000000000..c89cc7a824
--- /dev/null
+++
b/jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/calendar/BusinessCalendarTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.bpmn2.calendar;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import org.jbpm.bpmn2.objects.TestWorkItemHandler;
+import org.jbpm.process.core.timer.BusinessCalendarImpl;
+import org.jbpm.test.utils.ProcessTestHelper;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.kie.kogito.Application;
+import org.kie.kogito.StaticApplication;
+import org.kie.kogito.StaticConfig;
+import org.kie.kogito.calendar.BusinessCalendar;
+import org.kie.kogito.process.ProcessConfig;
+import org.kie.kogito.process.ProcessInstance;
+import org.kie.kogito.process.bpmn2.BpmnProcesses;
+import org.kie.kogito.process.impl.AbstractProcessConfig;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class BusinessCalendarTest {
+
+ private static BusinessCalendar workingDayCalendar;
+ private static BusinessCalendar notWorkingDayCalendar;
+
+ @BeforeAll
+ public static void createCalendars() {
+ workingDayCalendar = configureBusinessCalendar(true);
+ notWorkingDayCalendar = configureBusinessCalendar(false);
+ }
+
+ @Test
+ public void testTimerWithWorkingDayCalendar() throws InterruptedException {
+ BpmnProcesses bpmnProcesses = new BpmnProcesses();
+ ProcessConfig config = new MockProcessConfig(workingDayCalendar);
+ Application app = new StaticApplication(new StaticConfig(null,
config), bpmnProcesses);
+ TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
+ ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler);
+ org.kie.kogito.process.Process<BusinessCalendarTimerModel>
processDefinition = BusinessCalendarTimerProcess.newProcess(app);
+ BusinessCalendarTimerModel model = processDefinition.createModel();
+ org.kie.kogito.process.ProcessInstance<BusinessCalendarTimerModel>
instance = processDefinition.createInstance(model);
+ instance.start();
+ assertThat(instance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE);
+ Thread.sleep(2000);
+
assertThat(instance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED);
+ }
+
+ @Test
+ public void testTimerWithNotWorkingDayCalendar() throws
InterruptedException {
+ BpmnProcesses bpmnProcesses = new BpmnProcesses();
+ ProcessConfig config = new MockProcessConfig(notWorkingDayCalendar);
+ Application app = new StaticApplication(new StaticConfig(null,
config), bpmnProcesses);
+ TestWorkItemHandler workItemHandler = new TestWorkItemHandler();
+ ProcessTestHelper.registerHandler(app, "Human Task", workItemHandler);
+ org.kie.kogito.process.Process<BusinessCalendarTimerModel>
processDefinition = BusinessCalendarTimerProcess.newProcess(app);
+ BusinessCalendarTimerModel model = processDefinition.createModel();
+ org.kie.kogito.process.ProcessInstance<BusinessCalendarTimerModel>
instance = processDefinition.createInstance(model);
+ instance.start();
+ assertThat(instance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE);
+ Thread.sleep(2000);
+ assertThat(instance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE);
+ }
+
+ private static BusinessCalendar 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");
+ } else {
+ Calendar currentCalendar = Calendar.getInstance();
+ Date today = new Date();
+ currentCalendar.add(Calendar.DATE, 1);
+ Date tomorrow = currentCalendar.getTime();
+ String dateFormat = "yyyy-MM-dd";
+ SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.HOLIDAYS,
sdf.format(today) + "," + sdf.format(tomorrow));
+
businessCalendarConfiguration.setProperty(BusinessCalendarImpl.HOLIDAY_DATE_FORMAT,
dateFormat);
+ }
+ return new BusinessCalendarImpl(businessCalendarConfiguration);
+ }
+
+ private static class MockProcessConfig extends AbstractProcessConfig {
+ private MockProcessConfig(BusinessCalendar businessCalendar) {
+ super(Collections.emptyList(), Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(),
+ Collections.emptyList(), null, Collections.emptyList(),
Collections.emptyList(), Collections.emptyList(), List.of(businessCalendar));
+ }
+ }
+}
diff --git a/jbpm/jbpm-tests/src/test/resources/calendar.properties
b/jbpm/jbpm-tests/src/test/resources/calendar.properties
new file mode 100644
index 0000000000..e2ab07c9f0
--- /dev/null
+++ b/jbpm/jbpm-tests/src/test/resources/calendar.properties
@@ -0,0 +1,6 @@
+business.end.hour=24
+business.hours.per.day=24
+business.start.hour=0
+business.holiday.date.format=yyyy-MM-dd
+business.days.per.week =7
+#business.cal.timezone= system default timezone..
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
index 528a9e081a..14bab62579 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
@@ -77,6 +78,7 @@ import
com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
+import static
org.jbpm.process.core.constants.CalendarConstants.BUSINESS_CALENDAR_PATH;
import static
org.kie.kogito.grafana.GrafanaConfigurationWriter.buildDashboardName;
import static
org.kie.kogito.grafana.GrafanaConfigurationWriter.generateOperationalDashboard;
import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeClassName;
@@ -100,7 +102,8 @@ public class ProcessCodegen extends AbstractGenerator {
private static final String GLOBAL_OPERATIONAL_DASHBOARD_TEMPLATE =
"/grafana-dashboard-template/processes/global-operational-dashboard-template.json";
private static final String PROCESS_OPERATIONAL_DASHBOARD_TEMPLATE =
"/grafana-dashboard-template/processes/process-operational-dashboard-template.json";
-
+ public static final String BUSINESS_CALENDAR_PRODUCER_TEMPLATE =
"BusinessCalendarProducer";
+ private static final String IS_BUSINESS_CALENDAR_PRESENT =
"isBusinessCalendarPresent";
static {
ProcessValidatorRegistry.getInstance().registerAdditonalValidator(JavaRuleFlowProcessValidator.getInstance());
BPMN_SEMANTIC_MODULES.addSemanticModule(new BPMNSemanticModule());
@@ -144,6 +147,7 @@ public class ProcessCodegen extends AbstractGenerator {
if (useSvgAddon) {
context.addContextAttribute(ContextAttributesConstants.PROCESS_AUTO_SVG_MAPPING,
processSVGMap);
}
+ context.addContextAttribute(IS_BUSINESS_CALENDAR_PRESENT,
resources.stream().anyMatch(resource ->
resource.resource().getSourcePath().endsWith(BUSINESS_CALENDAR_PATH)));
handleValidation(context, processesErrors);
@@ -436,10 +440,16 @@ public class ProcessCodegen extends AbstractGenerator {
}
//Generating the Producer classes for Dependency Injection
- StaticDependencyInjectionProducerGenerator.of(context())
- .generate()
+ StaticDependencyInjectionProducerGenerator
staticDependencyInjectionProducerGenerator =
StaticDependencyInjectionProducerGenerator.of(context());
+
+ staticDependencyInjectionProducerGenerator.generate()
.entrySet()
.forEach(entry -> storeFile(PRODUCER_TYPE, entry.getKey(),
entry.getValue()));
+ Boolean isBusinessCalendarPresent =
context().getContextAttribute(IS_BUSINESS_CALENDAR_PRESENT, Boolean.class);
+ if (Objects.nonNull(isBusinessCalendarPresent) &&
isBusinessCalendarPresent) {
+
staticDependencyInjectionProducerGenerator.generate(List.of(BUSINESS_CALENDAR_PRODUCER_TEMPLATE))
+ .forEach((key, value) -> storeFile(PRODUCER_TYPE, key,
value));
+ }
if (context().hasRESTForGenerator(this)) {
for (ProcessResourceGenerator resourceGenerator : rgs) {
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/StaticDependencyInjectionProducerGenerator.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/StaticDependencyInjectionProducerGenerator.java
index 9c147303e7..9529b99cbc 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/StaticDependencyInjectionProducerGenerator.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/StaticDependencyInjectionProducerGenerator.java
@@ -49,11 +49,19 @@ public class StaticDependencyInjectionProducerGenerator {
* @return Map with the generated resources
*/
public Map<String, String> generate() {
+ return generate(producerTemplates);
+ }
+
+ /**
+ * Key is the FilePath, Value is the content
+ *
+ * @return Map with the generated resources
+ */
+ public Map<String, String> generate(List<String> templates) {
if (!context.hasDI()) {
return Collections.emptyMap();
}
- return producerTemplates.stream()
- .map(this::buildProducerTemplatedGenerator)
+ return templates.stream().map(this::buildProducerTemplatedGenerator)
.collect(Collectors.toMap(TemplatedGenerator::generatedFilePath,
generator ->
generator.compilationUnitOrThrow().toString()));
}
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigQuarkusTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigQuarkusTemplate.java
index 19491e3b91..98daf8fbdc 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigQuarkusTemplate.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigQuarkusTemplate.java
@@ -18,10 +18,9 @@
*/
package $Package$;
-import jakarta.enterprise.inject.Instance;
-
import org.kie.api.event.process.ProcessEventListener;
import org.kie.kogito.auth.IdentityProvider;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.event.EventPublisher;
import org.kie.kogito.jobs.JobsService;
import org.kie.kogito.process.ProcessEventListenerConfig;
@@ -30,6 +29,8 @@ import org.kie.kogito.process.WorkItemHandlerConfig;
import org.kie.kogito.uow.UnitOfWorkManager;
import org.kie.kogito.uow.events.UnitOfWorkEventListener;
+import jakarta.enterprise.inject.Instance;
+
@jakarta.inject.Singleton
public class ProcessConfig extends
org.kie.kogito.process.impl.AbstractProcessConfig {
@@ -44,7 +45,8 @@ public class ProcessConfig extends
org.kie.kogito.process.impl.AbstractProcessCo
org.kie.kogito.config.ConfigBean configBean,
Instance<UnitOfWorkEventListener> unitOfWorkEventListeners,
Instance<ProcessVersionResolver> versionResolver,
- Instance<IdentityProvider> identityProvider) {
+ Instance<IdentityProvider> identityProvider,
+ Instance<BusinessCalendar> businessCalendar) {
super(workItemHandlerConfig,
processEventListenerConfigs,
@@ -55,7 +57,8 @@ public class ProcessConfig extends
org.kie.kogito.process.impl.AbstractProcessCo
configBean.getServiceUrl(),
unitOfWorkEventListeners,
versionResolver,
- identityProvider);
+ identityProvider,
+ businessCalendar);
}
}
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigSpringTemplate.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigSpringTemplate.java
index df77c11433..8152091578 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigSpringTemplate.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/config/ProcessConfigSpringTemplate.java
@@ -22,6 +22,7 @@ import java.util.List;
import org.kie.api.event.process.ProcessEventListener;
import org.kie.kogito.auth.IdentityProvider;
+import org.kie.kogito.calendar.BusinessCalendar;
import org.kie.kogito.event.EventPublisher;
import org.kie.kogito.jobs.JobsService;
import org.kie.kogito.process.ProcessEventListenerConfig;
@@ -44,7 +45,8 @@ public class ProcessConfig extends
org.kie.kogito.process.impl.AbstractProcessCo
org.kie.kogito.config.ConfigBean configBean,
List<UnitOfWorkEventListener> unitOfWorkEventListeners,
List<ProcessVersionResolver> versionResolver,
- List<IdentityProvider> identityProvider) {
+ List<IdentityProvider> identityProvider,
+ List<BusinessCalendar> businessCalendar) {
super(workItemHandlerConfig,
processEventListenerConfigs,
@@ -55,6 +57,7 @@ public class ProcessConfig extends
org.kie.kogito.process.impl.AbstractProcessCo
configBean.getServiceUrl(),
unitOfWorkEventListeners,
versionResolver,
- identityProvider);
+ identityProvider,
+ businessCalendar);
}
}
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
similarity index 57%
copy from api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
copy to
kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
index 40549bbcd9..4504af36e1 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
@@ -16,26 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.kie.kogito.process;
+package $Package$;
-import org.kie.kogito.KogitoConfig;
-import org.kie.kogito.auth.IdentityProvider;
-import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.signal.SignalManagerHub;
-import org.kie.kogito.uow.UnitOfWorkManager;
+import org.jbpm.process.core.timer.BusinessCalendarImpl;
+import org.kie.kogito.calendar.BusinessCalendar;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public interface ProcessConfig extends KogitoConfig {
- WorkItemHandlerConfig workItemHandlers();
+import jakarta.enterprise.inject.Produces;
- ProcessEventListenerConfig processEventListeners();
+public class BusinessCalendarProducer {
- SignalManagerHub signalManagerHub();
+ private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
- UnitOfWorkManager unitOfWorkManager();
-
- JobsService jobsService();
-
- ProcessVersionResolver versionResolver();
-
- IdentityProvider identityProvider();
-}
+ @Produces
+ public BusinessCalendar createBusinessCalendar() {
+ return new BusinessCalendarImpl();
+ }
+}
\ No newline at end of file
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
similarity index 57%
copy from api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
copy to
kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
index 40549bbcd9..8fd9f766a1 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
@@ -16,26 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.kie.kogito.process;
+package $Package$;
-import org.kie.kogito.KogitoConfig;
-import org.kie.kogito.auth.IdentityProvider;
-import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.signal.SignalManagerHub;
-import org.kie.kogito.uow.UnitOfWorkManager;
+import org.kie.kogito.calendar.BusinessCalendar;
+import org.jbpm.process.core.timer.BusinessCalendarImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
-public interface ProcessConfig extends KogitoConfig {
- WorkItemHandlerConfig workItemHandlers();
+@Configuration
+public class BusinessCalendarProducer {
- ProcessEventListenerConfig processEventListeners();
+ private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
- SignalManagerHub signalManagerHub();
+ @Bean
+ public BusinessCalendar createBusinessCalendar() {
- UnitOfWorkManager unitOfWorkManager();
-
- JobsService jobsService();
-
- ProcessVersionResolver versionResolver();
-
- IdentityProvider identityProvider();
-}
+ return new BusinessCalendarImpl();
+ }
+}
\ No newline at end of file
diff --git
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
index b2472ec758..d1d930a116 100644
---
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
@@ -23,17 +23,24 @@ import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.drools.codegen.common.GeneratedFile;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.kie.kogito.codegen.api.AddonsConfig;
import org.kie.kogito.codegen.api.context.KogitoBuildContext;
+import org.kie.kogito.codegen.api.context.impl.JavaKogitoBuildContext;
+import org.kie.kogito.codegen.api.context.impl.QuarkusKogitoBuildContext;
+import org.kie.kogito.codegen.api.context.impl.SpringBootKogitoBuildContext;
import org.kie.kogito.codegen.core.DashboardGeneratedFileUtils;
import org.kie.kogito.codegen.core.io.CollectedResourceProducer;
import static org.assertj.core.api.Assertions.assertThat;
+import static
org.kie.kogito.codegen.process.ProcessCodegen.BUSINESS_CALENDAR_PRODUCER_TEMPLATE;
import static
org.kie.kogito.grafana.utils.GrafanaDashboardUtils.DISABLED_OPERATIONAL_DASHBOARDS;
class ProcessCodegenTest {
@@ -99,16 +106,44 @@ class ProcessCodegenTest {
generateTestDashboards(codeGenerator, 0);
}
- private List<GeneratedFile> generateTestDashboards(ProcessCodegen
codeGenerator, int expectedDashboards) {
+ @ParameterizedTest
+ @MethodSource("contextBuildersForBusinessCalendar")
+ public void
whenCalendarPropertiesFoundGenerateBusinessCalendar(KogitoBuildContext.Builder
contextBuilder, String dependencyAnnotation) {
+ KogitoBuildContext context = contextBuilder.build();
+ StaticDependencyInjectionProducerGenerator
staticDependencyInjectionProducerGenerator =
StaticDependencyInjectionProducerGenerator.of(context);
+ Map<String, String> businessCalendarProducerSourcesMap =
staticDependencyInjectionProducerGenerator.generate(List.of(BUSINESS_CALENDAR_PRODUCER_TEMPLATE));
+ String expectedKey =
"org/kie/kogito/app/BusinessCalendarProducer.java";
+
assertThat(businessCalendarProducerSourcesMap).hasSize(1).containsKey(expectedKey);
+ String generatedContent =
businessCalendarProducerSourcesMap.get(expectedKey);
+
assertThat(generatedContent).isNotNull().isNotEmpty().contains(dependencyAnnotation);
+ }
- Collection<GeneratedFile> generatedFiles = codeGenerator.generate();
+ @ParameterizedTest
+ @MethodSource("contextBuildersNotDI")
+ public void whenBuildingNotDIContext(KogitoBuildContext.Builder
contextBuilder) {
+ KogitoBuildContext context = contextBuilder.build();
+ StaticDependencyInjectionProducerGenerator
staticDependencyInjectionProducerGenerator =
StaticDependencyInjectionProducerGenerator.of(context);
+ Map<String, String> businessCalendarProducer =
staticDependencyInjectionProducerGenerator.generate(List.of(BUSINESS_CALENDAR_PRODUCER_TEMPLATE));
+ assertThat(businessCalendarProducer).isEmpty();
+ }
+ private List<GeneratedFile> generateTestDashboards(ProcessCodegen
codeGenerator, int expectedDashboards) {
+ Collection<GeneratedFile> generatedFiles = codeGenerator.generate();
List<GeneratedFile> dashboards = generatedFiles.stream()
.filter(x ->
x.type().equals(DashboardGeneratedFileUtils.DASHBOARD_TYPE))
.collect(Collectors.toList());
-
assertThat(dashboards).hasSize(expectedDashboards);
-
return dashboards;
}
+
+ private static Stream<Arguments> contextBuildersForBusinessCalendar() {
+ return Stream.of(
+ Arguments.of(QuarkusKogitoBuildContext.builder(), "@Produces"),
+ Arguments.of(SpringBootKogitoBuildContext.builder(), "@Bean"));
+ }
+
+ private static Stream<Arguments> contextBuildersNotDI() {
+ return Stream.of(
+ Arguments.of(JavaKogitoBuildContext.builder()));
+ }
}
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
similarity index 57%
copy from api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
copy to
kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
index 40549bbcd9..dd5e5f18ea 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerQuarkusTemplate.java
@@ -16,26 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.kie.kogito.process;
+package $Package$;
-import org.kie.kogito.KogitoConfig;
-import org.kie.kogito.auth.IdentityProvider;
-import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.signal.SignalManagerHub;
-import org.kie.kogito.uow.UnitOfWorkManager;
+import org.kie.kogito.calendar.BusinessCalendar;
+import org.kie.kogito.calendar.BusinessCalendarImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-public interface ProcessConfig extends KogitoConfig {
- WorkItemHandlerConfig workItemHandlers();
+import jakarta.enterprise.inject.Produces;
- ProcessEventListenerConfig processEventListeners();
+public class BusinessCalendarProducer {
- SignalManagerHub signalManagerHub();
+ private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
- UnitOfWorkManager unitOfWorkManager();
-
- JobsService jobsService();
-
- ProcessVersionResolver versionResolver();
-
- IdentityProvider identityProvider();
-}
+ @Produces
+ public BusinessCalendar createBusinessCalendar() {
+ return new BusinessCalendarImpl();
+ }
+}
\ No newline at end of file
diff --git
a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
similarity index 57%
copy from api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
copy to
kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
index 40549bbcd9..e80c7cb10b 100644
--- a/api/kogito-api/src/main/java/org/kie/kogito/process/ProcessConfig.java
+++
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/class-templates/producer/BusinessCalendarProducerSpringTemplate.java
@@ -16,26 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.kie.kogito.process;
+package $Package$;
-import org.kie.kogito.KogitoConfig;
-import org.kie.kogito.auth.IdentityProvider;
-import org.kie.kogito.jobs.JobsService;
-import org.kie.kogito.signal.SignalManagerHub;
-import org.kie.kogito.uow.UnitOfWorkManager;
+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;
-public interface ProcessConfig extends KogitoConfig {
- WorkItemHandlerConfig workItemHandlers();
+@Configuration
+public class BusinessCalendarProducer {
- ProcessEventListenerConfig processEventListeners();
+ private static final Logger logger =
LoggerFactory.getLogger(BusinessCalendarProducer.class);
- SignalManagerHub signalManagerHub();
-
- UnitOfWorkManager unitOfWorkManager();
-
- JobsService jobsService();
-
- ProcessVersionResolver versionResolver();
-
- IdentityProvider identityProvider();
-}
+ @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]