diff --git a/build/build.xml b/build/build.xml
index d23fec2d7..e1ad50423 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -2843,6 +2843,8 @@
org/yawlfoundation/yawl/elements/YAttributeMap.class
org/yawlfoundation/yawl/elements/YVerifiable.class
org/yawlfoundation/yawl/elements/YNetElement.class
+ org/yawlfoundation/yawl/elements/data/YParameter.class
+ org/yawlfoundation/yawl/elements/data/YVariable.class
org/yawlfoundation/yawl/elements/e2wfoj/*
org/yawlfoundation/yawl/engine/YSpecificationID.class
org/yawlfoundation/yawl/engine/YNetData.class
@@ -2861,10 +2863,12 @@
org/yawlfoundation/yawl/exceptions/*
org/yawlfoundation/yawl/logging/YLogDataItem.class
org/yawlfoundation/yawl/logging/YLogDataItemList.class
+ org/yawlfoundation/yawl/resourcing/codelets/*
org/yawlfoundation/yawl/util/DynamicValue.class
org/yawlfoundation/yawl/util/JDOMUtil.class
org/yawlfoundation/yawl/util/YNetElementDocoParser.class
org/yawlfoundation/yawl/util/SaxonErrorListener.class
+ org/yawlfoundation/yawl/util/SaxonUtil.class
org/yawlfoundation/yawl/util/PluginLoaderUtil.class
org/yawlfoundation/yawl/util/YVerificationHandler.class
org/yawlfoundation/yawl/util/YPluginLoader.class
@@ -2878,6 +2882,8 @@
value="${app.version} ${TODAY}"/>
+
diff --git a/src/org/yawlfoundation/yawl/stateless/YStatelessEngine.java b/src/org/yawlfoundation/yawl/stateless/YStatelessEngine.java
index 1e34b5010..02d3ad9fd 100644
--- a/src/org/yawlfoundation/yawl/stateless/YStatelessEngine.java
+++ b/src/org/yawlfoundation/yawl/stateless/YStatelessEngine.java
@@ -28,6 +28,7 @@
*/
public class YStatelessEngine {
+
private final YEngine _engine;
private YCaseMonitor _caseMonitor; // watches for idle cases
@@ -334,9 +335,16 @@ public void resumeCase(YNetRunner runner)
*/
public YWorkItem suspendWorkItem(YWorkItem workItem) throws YStateException {
checkIsLoadedCase(workItem, "suspend work item");
- return _engine.suspendWorkItem(workItem);
+ try {
+ return _engine.suspendWorkItem(workItem);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
+
/**
* Resume a suspended work item
* @param workItem the work item to suspend
@@ -345,19 +353,33 @@ public YWorkItem suspendWorkItem(YWorkItem workItem) throws YStateException {
*/
public YWorkItem unsuspendWorkItem(YWorkItem workItem) throws YStateException {
checkIsLoadedCase(workItem, "unsuspend work item");
- return _engine.unsuspendWorkItem(workItem) ;
+ try {
+ return _engine.unsuspendWorkItem(workItem) ;
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
+
/**
* Roll back a work item from executing to enabled
* @param workItem the work item to roll back
- * @throws YStateException
+ * @throws YStateException if the item cannot be rolled back
*/
- public void rollbackWorkItem(YWorkItem workItem) throws YStateException {
+ public YWorkItem rollbackWorkItem(YWorkItem workItem) throws YStateException {
checkIsLoadedCase(workItem, "rollback work item");
- _engine.rollbackWorkItem(workItem);
+ try {
+ return _engine.rollbackWorkItem(workItem);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
+
/**
* Complete a currently executing work item
@@ -370,13 +392,20 @@ public void rollbackWorkItem(YWorkItem workItem) throws YStateException {
* @throws YQueryException if the data extraction query is malformed
* @throws YDataStateException if the data state cannot be initialised
*/
- public void completeWorkItem(YWorkItem workItem, String data,
+ public YWorkItem completeWorkItem(YWorkItem workItem, String data,
String logPredicate, WorkItemCompletion completionType )
throws YEngineStateException, YStateException, YQueryException, YDataStateException {
checkIsLoadedCase(workItem, "complete work item");
- _engine.completeWorkItem(workItem, data, logPredicate, completionType);
+ try {
+ return _engine.completeWorkItem(workItem, data, logPredicate, completionType);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
+
/**
* Complete a currently executing work item
* @param workItem the work item to complete
@@ -387,11 +416,17 @@ public void completeWorkItem(YWorkItem workItem, String data,
* @throws YQueryException if the data extraction query is malformed
* @throws YDataStateException if the data state cannot be initialised
*/
- public void completeWorkItem(YWorkItem workItem, String data,
+ public YWorkItem completeWorkItem(YWorkItem workItem, String data,
String logPredicate)
throws YEngineStateException, YStateException, YQueryException, YDataStateException {
checkIsLoadedCase(workItem, "complete work item");
- _engine.completeWorkItem(workItem, data, logPredicate, WorkItemCompletion.Normal);
+ try {
+ return _engine.completeWorkItem(workItem, data, logPredicate, WorkItemCompletion.Normal);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
@@ -406,9 +441,16 @@ public void completeWorkItem(YWorkItem workItem, String data,
public YWorkItem startWorkItem(YWorkItem workItem)
throws YEngineStateException, YStateException, YQueryException, YDataStateException {
checkIsLoadedCase(workItem, "start work item");
- return _engine.startWorkItem(workItem);
+ try {
+ return _engine.startWorkItem(workItem);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
+
/**
* Skip an enabled work item (immediately completes)
* @param workItem the work item to skip
@@ -420,7 +462,13 @@ public YWorkItem startWorkItem(YWorkItem workItem)
public YWorkItem skipWorkItem(YWorkItem workItem)
throws YEngineStateException, YStateException, YQueryException, YDataStateException {
checkIsLoadedCase(workItem, "skip work item");
- return _engine.skipWorkItem(workItem);
+ try {
+ return _engine.skipWorkItem(workItem);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
@@ -436,7 +484,13 @@ public YWorkItem skipWorkItem(YWorkItem workItem)
public YWorkItem createNewInstance(YWorkItem workItem, String paramValueForMICreation)
throws YStateException {
checkIsLoadedCase(workItem, "create new work item instance");
- return _engine.createNewInstance(workItem, paramValueForMICreation);
+ try {
+ return _engine.createNewInstance(workItem, paramValueForMICreation);
+ }
+ catch (Exception e) {
+ resumeCaseIdleTimer(workItem);
+ throw e;
+ }
}
@@ -449,7 +503,7 @@ public YWorkItem createNewInstance(YWorkItem workItem, String paramValueForMICre
* or if current number of instances is not less than the maxInstances
* for the task.
*/
- public void checkElegibilityToAddInstances(YWorkItem workItem) throws YStateException {
+ public void checkEligibilityToAddInstances(YWorkItem workItem) throws YStateException {
_engine.checkEligibilityToAddInstances(workItem);
}
@@ -475,7 +529,7 @@ public String unloadCase(YIdentifier caseID) throws YStateException {
throw new YStateException("This engine is not monitoring idle cases");
}
YCase yCase = _caseMonitor.unloadCase(caseID); // notnull guaranteed
- yCase.cancelWorkItemTimers();
+ yCase.removeWorkItemTimers();
String caseXML = yCase.marshal(); // ditto
_engine.getAnnouncer().announceCaseEvent(
new YCaseEvent(YEventType.CASE_UNLOADED, yCase.getRunner()));
@@ -507,6 +561,8 @@ public String marshalCase(YNetRunner runner) throws YStateException {
*/
public YNetRunner restoreCase(String caseXML) throws YSyntaxException, YStateException {
List runners = new YCaseImporter().unmarshal(caseXML, _engine.getAnnouncer());
+
+ // collect events and identify 'top' net runner in the runner hierarchy
List events = new ArrayList<>();
YNetRunner topRunner = null;
for (YNetRunner runner : runners) {
@@ -516,12 +572,73 @@ public YNetRunner restoreCase(String caseXML) throws YSyntaxException, YStateExc
}
}
if (topRunner == null) throw new YStateException("Failed to restore case runner");
- events.add(0, new YCaseEvent(YEventType.CASE_RESTORED, topRunner));
+
+ // add the 'restored' event to the list of generated events
+ YCaseEvent restoredEvent = new YCaseEvent(YEventType.CASE_RESTORED, topRunner);
+ events.add(0, restoredEvent);
+
+ // ensure case is added to the case monitor
+ if (isCaseMonitoringEnabled()) {
+ _caseMonitor.addCase(restoredEvent);
+ }
+
_engine.getAnnouncer().announceEvents(events);
return topRunner;
}
+ /**
+ * Check if the engine is currently executing code for a monitored case of which this
+ * workitem is a member.
+ * @param workItem the workitem to check
+ * @return true if case monitoring is enabled and the case is currently in idle
+ * state (i.e. it has no current executing code associated with it), or false if
+ * case monitoring is enabled and there is code executing for the case.
+ * @throws YStateException when the case is unknown to the engine, or when case
+ * monitoring is disabled for the engine
+ */
+ public boolean isIdleCase(YWorkItem workItem) throws YStateException {
+ return isIdleCase(workItem.getNetRunner().getTopRunner().getCaseID());
+ }
+
+
+ /**
+ * Check if the engine is currently executing code for a monitored case of which
+ * this net runner is a member.
+ * @param runner the net runner to check
+ * @return true if case monitoring is enabled and the case is currently in idle
+ * state (i.e. it has no current executing code associated with it), or false if
+ * case monitoring is enabled and there is code executing for the case.
+ * @throws YStateException when the case is unknown to the engine, or when case
+ * monitoring is disabled for the engine
+ */
+ public boolean isIdleCase(YNetRunner runner) throws YStateException {
+ return isIdleCase(runner.getTopRunner().getCaseID());
+ }
+
+
+ /**
+ * Check if the engine is currently executing code for a monitored case.
+ * @param caseID the id of the case to check
+ * @return true if case monitoring is enabled and the case is currently in idle
+ * state (i.e. it has no current executing code associated with it), or false if
+ * case monitoring is enabled and there is code executing for the case.
+ * @throws YStateException when the case is unknown to the engine, or when case
+ * monitoring is disabled for the engine
+ */
+ public boolean isIdleCase(YIdentifier caseID) throws YStateException {
+ if (isCaseMonitoringEnabled()) {
+ if (_caseMonitor.hasCase(caseID)) {
+ return _caseMonitor.isIdleCase(caseID);
+ }
+ else {
+ throw new YStateException(String.format("Case '%s' is unknown" +
+ " to this engine - perhaps it has been unloaded?", caseID));
+ }
+ }
+ else throw new YStateException("Case monitoring is disabled for this engine");
+ }
+
/**
* Throws a YStateException if cases are being monitored AND the case is unknown to
* this engine
@@ -529,20 +646,37 @@ public YNetRunner restoreCase(String caseXML) throws YSyntaxException, YStateExc
* @param errMsg to be inserted if an exception is thrown
* @throws YStateException if the condition described above evaluates to true
*/
- private void isLoadedCase(YIdentifier caseID, String errMsg) throws YStateException {
+ private boolean isLoadedCase(YIdentifier caseID, String errMsg) throws YStateException {
if (isCaseMonitoringEnabled() && ! _caseMonitor.hasCase(caseID)) {
throw new YStateException(String.format("Unable to %s; case '%s' is unknown" +
" to this engine - perhaps it has been unloaded?", errMsg, caseID));
}
+ return true;
}
private void checkIsLoadedCase(YWorkItem item, String msg) throws YStateException {
- isLoadedCase(item.getNetRunner().getTopRunner().getCaseID(), msg);
+ YIdentifier caseID = item.getNetRunner().getTopRunner().getCaseID();
+ if (isCaseMonitoringEnabled() && isLoadedCase(caseID, msg)) {
+
+ // pause any idle timer while the workitem action is processed
+ _caseMonitor.pauseIdleTimer(caseID);
+ }
}
+
private void checkIsLoadedCase(YNetRunner runner, String msg) throws YStateException {
isLoadedCase(runner.getTopRunner().getCaseID(), msg);
}
+
+ // for all successful workitem method completions above, timer will resume.
+ // this is called from catch blocks above for cases when an exception is thrown.
+ private void resumeCaseIdleTimer(YWorkItem workItem) {
+ if (isCaseMonitoringEnabled()) {
+ YIdentifier caseID = workItem.getNetRunner().getTopRunner().getCaseID();
+ _caseMonitor.resumeIdleTimer(caseID);
+ }
+ }
+
}
diff --git a/src/org/yawlfoundation/yawl/stateless/engine/YEngine.java b/src/org/yawlfoundation/yawl/stateless/engine/YEngine.java
index b4f57bdcc..e3369163e 100644
--- a/src/org/yawlfoundation/yawl/stateless/engine/YEngine.java
+++ b/src/org/yawlfoundation/yawl/stateless/engine/YEngine.java
@@ -435,7 +435,7 @@ private YWorkItem startFiredWorkItem(YNetRunner netRunner, YWorkItem workItem)
* 'force' (forced completion) or 'fail' (forced fail) completion
* @throws YStateException
*/
- public void completeWorkItem(YWorkItem workItem, String data, String logPredicate,
+ public YWorkItem completeWorkItem(YWorkItem workItem, String data, String logPredicate,
WorkItemCompletion completionType)
throws YStateException, YDataStateException, YQueryException, YEngineStateException{
if (_logger.isDebugEnabled()) {
@@ -469,6 +469,8 @@ public void completeWorkItem(YWorkItem workItem, String data, String logPredicat
// }
_logger.debug("<-- completeWorkItem");
+
+ return workItem;
}
@@ -687,7 +689,7 @@ public YWorkItem unsuspendWorkItem(YWorkItem workItem) throws YStateException {
// rolls back a workitem from executing to fired
- public void rollbackWorkItem(YWorkItem workItem) throws YStateException {
+ public YWorkItem rollbackWorkItem(YWorkItem workItem) throws YStateException {
if ((workItem != null) && workItem.getStatus().equals(YWorkItemStatus.statusExecuting)) {
workItem.rollBackStatus();
YNetRunner netRunner = workItem.getTask().getNetRunner().getRunnerWithID(
@@ -699,10 +701,12 @@ public void rollbackWorkItem(YWorkItem workItem) throws YStateException {
}
}
else throw new YStateException("Work Item[" + workItem.getIDString() + "] not found.");
+
+ return workItem;
}
- public void cancelWorkItem(YNetRunner caseRunner, YWorkItem workItem) throws YStateException {
+ public YWorkItem cancelWorkItem(YNetRunner caseRunner, YWorkItem workItem) throws YStateException {
try {
if ((workItem != null) && workItem.getStatus().equals(YWorkItemStatus.statusExecuting)) {
YNetRunner runner = caseRunner.getRunnerWithID(workItem.getCaseID().getParent());
@@ -721,6 +725,8 @@ public void cancelWorkItem(YNetRunner caseRunner, YWorkItem workItem) throws YSt
catch (Exception e) {
throw new YStateException("Failure whilst cancelling workitem: " + e.getMessage());
}
+
+ return workItem;
}
diff --git a/src/org/yawlfoundation/yawl/stateless/engine/YWorkItem.java b/src/org/yawlfoundation/yawl/stateless/engine/YWorkItem.java
index c5292c74b..1c61d38e6 100644
--- a/src/org/yawlfoundation/yawl/stateless/engine/YWorkItem.java
+++ b/src/org/yawlfoundation/yawl/stateless/engine/YWorkItem.java
@@ -99,6 +99,8 @@ public class YWorkItem {
private String _externalLogPredicate; // set by services on checkin
private Element _data;
+ private YWorkItemTimer _timer = null ;
+
private final Logger _log = LogManager.getLogger(YWorkItem.class);
@@ -422,25 +424,24 @@ public void checkStartTimer(YNetData data) {
// if current workitem status equals trigger status, start the timer
if (_timerParameters.triggerMatchesStatus(_status)) {
- YWorkItemTimer timer = null ;
switch (_timerParameters.getTimerType()) {
case Expiry: {
- timer = new YWorkItemTimer(this,
+ _timer = new YWorkItemTimer(this,
_timerParameters.getDate()) ;
break;
}
case Duration: {
- timer = new YWorkItemTimer(this,
+ _timer = new YWorkItemTimer(this,
_timerParameters.getWorkDayDuration());
break;
}
case Interval: {
- timer = new YWorkItemTimer(this,
+ _timer = new YWorkItemTimer(this,
_timerParameters.getTicks(), _timerParameters.getTimeUnit()) ;
}
}
- if (timer != null) {
- _timerExpiry = timer.getEndTime();
+ if (_timer != null) {
+ _timerExpiry = _timer.getEndTime();
setTimerActive();
_timerStarted = true ;
}
@@ -448,6 +449,10 @@ public void checkStartTimer(YNetData data) {
}
}
+ public YWorkItemTimer getTimer() { return _timer; }
+
+ public void setTimer(YWorkItemTimer timer) { _timer = timer; }
+
public void cancelTimer() {
if (hasTimerStarted()) {
@@ -463,6 +468,12 @@ public void cancelTimer() {
}
}
}
+
+ // if suppressing timer cancel events, do it in the parent too
+ YWorkItemTimer parentTimer = parent.getTimer();
+ if (! (parentTimer == null || _timer == null) ) {
+ parentTimer.enableAnnouncements(_timer.announcementsEnabled());
+ }
YTimer.getInstance().cancelTimerTask(parent.getIDString());
}
}
@@ -831,6 +842,7 @@ public String getDocumentation() {
return (_parent != null) ? _parent.getDocumentation() : _documentation;
}
+
public String toXML() {
StringBuilder xml = new StringBuilder(" 0; }
}