Case reactivation
Target audience: Modelers
Overview
A case instance moved to the history (archive) when finished, cannot be reopened by default. Until Flowable version 3.10.0, if you needed to do stuff in the same case instance again, you had to keep it open.
This is not a big problem. However, you might end up having tons of such "sleeping" case instances in the runtime where there might be something happening again at a later state, maybe not. Although limited, these sleeping instances have an impact on performance when working with the active case instances.
With the case reactivation functionality, you have the ability to regularly finish a case and move it away from the runtime to the history and still be able to reactivate it at any later time, should you want to reopen it again and continue with its execution.
In this how-to, we are explaining all the details around case reactivation and we will look at some examples how to model and use case reactivation.
Reactivate Event Listener
By default, a case instance cannot be reactivated once finished and moved to the history. Adding a reactivation listener to a case model allows a finished (archived) case instance to be reactivated (reopened). What reactivation actually means is fully up to the modeler as there might be different possibilities what should happen when a case instance is reactivated.
Let's imagine at a sample case with three stages; data collection, review for approval and execution. Reactivating a finished case instance might be done in several ways: maybe you only want to redo the execution part of the case again, maybe you want to go back further and even redo the review and approval phase or even restart the case from the data collection stage.
As you can see, there are a lot of options and that's why reactivation needs further information in the case model to actually work, there is no general "just reactivate my case again". That's why we need to add some modeling information around case reactivation for it to work as required.
It all starts with the reactivate listener, without it, you can't reopen a case instance. Adding a reactivate listener kind of marks the case model to support case reactivation.
You find the reactivate listener in the listener section in Design and by simply drag and drop it into the case model, you mark it to support case reactivation:
If you simply add a case reactivate listener to a case model and you don't do anything else, the case behaves exactly the same when reactivated as if it was newly started with the only difference that it obviously keeps all of its data, especially variables which might have an impact on case execution like activating sentries or conditions leading to plan items being executed or not. Most likely you don't want this default behavior and need a more fine-grained way of controlling what has to be re-executed when you reopen the case.
In the next chapters we are going into the details how case reactivation actually works and how you can define exactly what happens when you reactivate a case instance.
Reactivation Modelling Modes
Case reactivation can be modeled in two ways: inclusive or exclusive. Inclusive means by default, all plan items are going to be reactivated, and you would have to explicitly mark them to be ignored, if they should not become active again. Exclusive is the opposite and means by default, none of the plan items are reactivated, and you would have to explicitly mark them to be activated, should they become active again.
Depending on your reactivation use-case one or the other is easier to model, is most of the case being reactivated, use the inclusive mode, is only a small part to be reactivated, use the exclusive mode.
You can define the default reactivation behavior for all plan item in a case model in the reactivation listeners Default plan item reactivation
section. As you can see,
it contains exactly the same reactivation rules and expression possibilities as on each plan item too. If a plan item does not explicitly define reactivation
rules, then the ones defined in the reactivation listener are used instead.
If there are none defined on the listener too, the basic default behavior is inclusive, which means the case is reactivated exactly the same way as if it was
newly started, but of course keeps its data.
You can even make it dynamic, by using expressions for the default rules defined in the reactivation event listener. They mean exactly the same as on the plan items and are coming into play as soon as there are none explicitly defined on a plan item.
How Case Reactivation Works
Case reactivation happens in two phases, once you trigger the reactivate listener on an archived case. How you can trigger it will be looked at later in this how-to guide. Let's look at the two phases first.
Phase One
First, the case engine activates and executes all directly associated plan items of the reactivate listener. This can be achieved when using the complete event of the reactivate listener in combination with an entry sentry on a plan item. You might want to reset some data or initialize variables BEFORE the actual case reactivation takes place using a variable initialize task or even using a service you invoke during phase one.
Phase Two
Once those primary plan items have been activated and executed, the case engine reactivates all plan items in the case according their reactivation rules. It is basically the very same mechanism as if you would start a new case instance from scratch with the only difference that you can have an impact on how plan items get treated during reactivation using the reactivation rules.
Reactivation Rules
Every plan item, including stages, support reactivation rules where you can specify how the case engine should treat them once the case gets reactivated in phase two.
In a reactivated case, you often want to treat plan items different from how they are designed to execute when starting a new case instance. Maybe you only want to re-execute a certain part of the case model, maybe it needs to be dynamic, depending on what to achieve with case reactivation, some stages or others need to be reactivated, etc.
You can achieve all that using the plan items reactivation rules, which support expressions to make it dynamic according to how you want to reactivate a particular case instance.
There are three types of reactivation rules, and they are evaluated in a fixed order.
Direct Activation Condition
This is the first condition to be evaluated during case reactivation. If set to true or if there is an expression evaluating to true, that plan item immediately gets activated, regardless of any entry sentry or condition which might be necessary to restart at a certain state of the case (e.g. a stage).
Using this condition helps you to directly reactivate certain stages or plan items for case reactivation regardless of their entry sentries and further conditions.
Ignore Condition
If set to true or evaluating to true, the plan item having this rule is ignored when the case is reactivated and hence cannot be part of the execution again. Even if it has an entry sentry which would later activate it (e.g. when a linked stage is finished), it will NOT be activated again.
This condition helps you to prevent any plan item or full stage to be re-executed in a reactivated case, no matter their entry sentries or conditions.
Default Condition
This is the last of the three conditions and only gets evaluated, if the previous ones are either not set or evaluated to false. If you set this one to true or it is an expression evaluating to true, that plan item behaves like a regular one as if the case instance was started from scratch. This means that any necessary entry sentry is considered and evaluated and the engine proceeds as normal.
Evaluating the Reactivation Rules at Case Reactivation
It is important to understand how the case engine evaluates and treats the reactivation conditions at runtime when a case is reactivated in phase two.
Specially, if you combine rules and expressions, it is essential to understand in which order they are evaluated.
To recap, the evaluation of those rules and conditions take place in the following order:
- First, the direct activation condition is evaluated and if true, the plan item immediately gets activated, and the other rules are ignored. This also happens even if the plan item has entry sentries or conditions.
- If the direct activation is not set at all or was evaluating to false, the ignore condition is evaluated and if true, the plan item is ignored and will not be taken into account again.
- If either both rules (direct activation and ignore) are not set or evaluated to false, the default condition gets evaluated and if true, the plan item gets initialized the very same way as it was when the case was originally started. If evaluating to false, it has the same effect as ignore. If it is not set at all (not false, not true, no expression), then the default rules as being specified on the reactivate listener apply in the exact same way as listed here.
You can specify default reactivation rule conditions on the reactivate listener itself instead of modeling each plan item individually. This might be useful when you want most of the plan items to behave the same way as if the case was freshly started and only have a couple of exceptions or the opposite: when you only want to selectively reactivate a few plan items on case reactivation.
Additionally, when triggering a case reactivation, you can optionally pass in an arbitrary set of variables, allowing you to control precisely, how the case instance needs to behave during reactivation by using that data in the reactivation condition expression for instance.
It is very important to fully understand how the engine determines how to treat a plan item according its reactivation rules. You should think the very same way when modeling reactivation rules:
- Does this plan item needs to immediately be activated? Regardless its entry sentries and conditions? If yes, set the
Direct activation
condition to true or an expression evaluating to true at runtime in the desired condition. - Does this plan item needs to be ignored when reactivating the case? Yes? Then set the
Ignore condition
to true or an expression evaluating to true and make sure theDirect activation condition
is NOT evaluating to true the same time! - Does this plan item needs to behave exactly the same way as if the case was newly started? Which means all entry sentries and conditions are taken into account as well. If yes, set the
Default condition
to true or an expression evaluating to true and make sure the ignore and direct activation conditions are NOT evaluating to true the same time! - Does this plan item needs the exact behavior as defined in the default reactivation section on the reactivate listener? If yes, make sure that the ignore and direct activation conditions are either not set at all or evaluate to false and the default condition is NOT set and hence does not evaluate to true or false.
Reactivate a Historic Case Instance Programmatically
The case reactivation builder is used to create all the necessary and optional information for an archived / finished case to be reactivated.
Here is a sample on how you can reactivate a case instance:
CaseInstance reactivatedCase = cmmnHistoryService.createCaseReactivationBuilder("historicCaseId")
.transientVariable("fullReactivation", true)
.variable("foo", "bar")
.reactivate();
Of course you can also use the case reactivation action in the Flowable Work UI to trigger case reactivation, optionally with a start form, if you dynamically want to influence the reactivation.
Sample case models
In this section we want to have a look at some case models in regard to case reactivation to see different variations on how to use the reactivation rules and conditions to achieve exactly what you need when reactivating a case.
Example 1: Basics
Let's get started with a very simple case model to explain the basic concepts and possibilities around case reactivation.
We have two stages: Setup data
and Review and approve
. When we simply add the reactivate listener to the case model and don't define anything else like reactivation rules and conditions, the case behaves exactly the same as if it was newly created when you reactivate it, with the only difference, that any data you added before will have been preserved.
In other words: when the case is reactivated, both tasks in the setup stage are becoming active and once completed, the review and approve stage is activated with the review task.
As said; the only difference to a new case is the fact that all existing data is preserved.
But maybe you don't want that, maybe you want to (partially) remove or reset data to be entered new when reactivating the case.
Maybe you asked yourself how the case can terminate if there is the reactivate listener? As we need to be compliant with the CMMN specs, yes, it is a regular listener with some custom information. It is marked automatically as to be ignored for parent completion, that's why you don't have to do anything more concerning case completion.
Case model: Initialize Additional Data
This brings us to variation b, where we initialize / reset or remove any previously entered data before we reactivate the case.
As an addition, we added an initialization plan item to the reactivate listener which gets triggered in phase one of the reactivation where we can reset or reinitialize the data as needed.
Everything which is triggered on case reactivation only and not as part of the regular case needs to be ignored for parent completion. Otherwise the case will be prevented from completing. That's why we need to mark the initialization task to be ignored for parent completion.
Case model: Ignore a Plan Item
Maybe we want to reset the approval chain and redo the approval stage based on the new chain, but not change any of the case data as in the Setup approval data
task.
In terms of the model, everything should work the same as in variation b, but the setup data task should not be activated again. This is very simple as we only need to check the ignore condition on the reactivation ruleset of that task, and it will be ignored during reactivation.
Case model: Reactivate a Particular Stage
What if we only want to re-execute the Review and approve
stage and leave all the data and approval chain as it was defined before?
In this case we need to ignore the first stage completely but what happens with the second one? Is the default behavior sufficient for it? No, because it has an entry sentry, and the default behavior would actually set the stage up in available state and wait for the first stage to complete. As we will ignore the first stage, this would never happen and hence the review stage would never become active. This would leave the case in a sort of "limbo" state where you can't do anything anymore except terminate it.
As you can see, it is very important to be clear about how you treat the plan items for reactivation to prevent situations where you can't do anything in a case anymore.
What we need is to mark the second stage to directly become active when the case is reactivated to circumvent the entry sentry and its trigger.
So for this variation to work, check the first stage ignore condition
in the reactivation section and check the direct activation condition
in the second stage.
This way, when the case is reactivated, it immediately starts the Review and approve
stage and you can redo the approvals, everything else remains the same as it was executed before during the runtime of the case.
Case model: Using Expressions for Dynamic Behavior
What if we want to leave it up to the person reactivating the case? They should be able to choose between the following options:
- Redo everything, optionally with resetting the data
- Redefine the approval chain and redo the approvals
- Only redo the approvals, based on all the data we already have
In this case, we create a start form for the reactivate listener which might look like this:
The chosen option is stored in a variable named reactivationOption
and we have the option values as redoAndResetData
, redoAndKeepData
, redefineApprovalsAndApprove
and redoApprovals
.
How would we now have to define the various reactivation rule conditions to make it work dynamically according what the user has chosen?
Let's start with the data initialization step. Depending on the option chosen, we need to reset various data. As we have several options, best might be to create a small reactivation process where we can add gateways with conditions for the various variable initialization tasks.
The condition of the first sequence flow might be something like ${reactivationOption == 'redoAndResetData'}
to only execute if we have chosen option one for reactivation.
The condition of the last sequence flow might be something like ${reactivationOption == 'redoAndResetData' || reactivationOption == 'redefineApprovalsAndApprove'}
to execute if option 1 OR option 3 are chosen.
We need a default sequence to make sure the process is not suck at the gateway for option 2 and 4 where we don't need to reset anything.
Don't add user tasks to the phase one of the reactivation unless you have initialized all necessary data from phase two before. Phase one ends when there is nothing more to execute as directly dependant plan items connected to the reactivate listener. If there is a user task, execution stops and then phase two is executed, so anything you need from the user having an impact on case reactivation, get it within the start form and not in a user task during phase one.
Next step; think about the reactivation rule conditions for the various plan items. The best way to achieve it is to go through all the stages first and define their behavior for reactivation. Later on, if necessary, go through the plan items within stages to see, if you need additional rules.
When do we need to reactivate the Setup data
stage? Yes, for all options except option 4 (redoApprovals
). As that stage does not have any entry sentries or special conditions, the default reactivation is ok, we only need to ignore it for option 4.
So these are the reactivation rule conditions for the stage:
We need to ignore reactivation for option 4 only that's why we set that condition to ${reactivationOption == 'redoApprovals'}
.
The second stage is a bit more complex. When we have option 1, 2 or 3, the stage will eventually be activated once the first one is done, so for reactivation, it needs to have the default condition for those options. For option 4, it is different as we don't have the first stage in that case and directly go into the second stage. So for option 4, we need to directly activate the second stage as otherwise it would never be active as the first stage is not going to trigger it.
For the second stage we need both, the default and the direct activation rule condition.
We set the condition to directly activate the stage when we have option 1: ${reactivationOption == 'redoApprovals'}
. And in all other cases, we only need the default
reactivation: ${reactivationOption != 'redoApprovals'}
.
We are done with the reactivation rule conditions for the two stages, do we need any plan item inside a stage to behave differently than default? Yes, for the option redefineApprovalsAndApprove
we don't need the first task named Setup approval data
and so we set the ignore condition to ${reactivationOption == 'redefineApprovalsAndApprove}
.
Wait a second, we also don't need it for option 4, right? Correct, but as we already set the ignore option for the complete stage, we don't need to also set it on all child plan items as they will be ignored automatically, if their parent doesn't get activated at all.
Recap and Good Practice
As you could see, there are a lot of options available for case reactivation, but it also makes it rather a complex topic, and you might need some experience and testing in order to fully master it.
Here are some tips and good practices:
- Start with the basic question: what should be my default? Ignore and explicitly mark the plan items needed for reactivation? Or the opposite? Reactivate by default and only mark the ones that should be ignored?
- According to your answer, set the the
Default reactivation
conditions in the reactivate event listener accordingly to eitherIgnore
orDefault
. - If necessary add a service task or even small reactivation process task and connect it directly with the reactivate event listener through a entry sentry. In here change data as needed and prepare the case instance before it actually get reactivated.
- Mark all those directly connected plan items as
Impact on parent completion: Ignore
to make sure they don't have an impact on the regular case model and are just used for case reactivation. - Do NOT use waiting states (e.g. user tasks) inside the plan items executed in phase 1 of the reactivation!
- Don't connect regular stages and other plan items directly with the reactivate event listener, unless they are explicitly and exclusively meant for reactivation only and should be executed in phase 1 of case reactivation to prepare the case instance for reactivation.
- Once this initial reactivation modeling is done, go through all your stages and plan items and ask yourself, if they need to be reactivated or ignored and define their reactivation rules and conditions accordingly.