Injection
To be able to access information about the currently executing workflow external functionalities, FluxFlow provides various injection points.
Injection targets
FluxFlow only supports direct parameter injection. The parameter might be a constructor or method parameter. Other injection mechanisms (like field injection, property/setter injection) are not supported.
In order for such parameters to be able to receive injected values, they must be part of a method or constructor that will be invoked by FluxFlow.
Steps
Every step definition is eligible to receive injected values using their constructor parameters.
Receiving dependencies using a step’s constructor parameters
@Step
class CheckVacationRequestStep(
calendarService: CalendarService,
private val mailService: MailService
) {
// ...
}
All values that need to be injected are resolved and evaluated every time the constructor needs to be invoked by FluxFlow. This is usually the case, whenever the step needs to be activated because it is queried or another workflow action associated with that step is about to be executed.
Be aware that FluxFlow also restores the previous step data state in a similar manner, by either passing the persisted values into matching constructor parameters or calling the relevant properties or setters.
Whenever it is ambiguous whether a constructor parameter refers to a step data value or an external dependency, the step data will take precedence.
To avoid the performance overhead caused by the resolution of conditionally required dependencies, it is usually a good idea to inject them into a more specific injection target (see Workflow elements represented by methods).
Workflow elements represented by methods
Workflow elements that are modeled by declaring a certain method are all valid injection targets. Such workflow objects elements are:
Receiving dependencies using various method parameters
@Step
class CheckVacationRequestStep(
@Data
var startOfVacation: Instant
) {
@Action
fun approve(
mailService: MailService
) {
// ...
}
@DataListener("startOfVacation")
fun onStartOfVacationChanged(
newValue: Instant,
mailService: MailService
) {
// ....
}
}
If the executing method wants to continue the workflow with a new workflow element - which itself requires some dependencies during instantiation - you can declare those transitive dependencies as a regular parameter and pass the value along.
Injection sources
Workflow context
In order to receive information about the currently executing workflow, most injection targets can declare dependencies on the corresponding API objects.
Step
If a workflow element (such as an action, data listener or automation
function) wants to obtain information on the step that itself belongs
to, it may declare a Step parameter.
Requesting the currently executing workflow step
@Step
class CheckVacationRequestStep(
@Data
var startOfVacation: Instant
) {
@Action
fun approve(
currentStep: de.lise.fluxflow.api.step.Step
) {
// ...
}
}
Do not confuse the API model de.lise.fluxflow.api.step.Step with the
@Step annotation (de.lise.fluxflow.stereotyped.step.Step).
If the workflow element is only interested in the step’s identifier
(e.g. for logging), it can also be requested directly by declaring a
StepIdentifier parameter.
Only requesting the step’s identifier
@Step
class CheckVacationRequestStep(
@Data
var startOfVacation: Instant
) {
@Action
fun approve(
currentStepId: StepIdentifier
) {
// ...
}
}
Workflow
Workflow elements can also request the associated workflow, by declaring
a Workflow<> parameter. This injection source is available for all
workflow elements.
Requesting the currently executing workflow
@Step
class CheckVacationRequestStep {
@OnCreated
fun sendNotification(
workflow: Workflow<VacationRequest>, //
) {
//
notificationService.sendNewApprovalRequiredNotification(workflow.model.approvingManager)
}
}
-
This parameter will receive the currently executing workflow.
-
The
notificationServiceis only here to demonstrate how one could use the injected workflow’s model to access further information.
Because of the JVM type erasure, the Workflow<TModel> 's generic type
TModel will not be available at runtime. You need to make sure, that
the requested type indeed matches the workflow’s model. Otherwise, a
ClassCastException might be thrown upon invocation.
This is also why the workflow’s model cannot be injected directly.
If the workflow element is only interested in the workflow’s identifier,
it can also be requested directly be declaring a WorkflowIdentifier
parameter.
Only requesting the workflow’s identifier
@Job
class SendVacationRequestReminderJob {
private val log = LoggerFactory.getLogger(SendVacationRequestReminderJob::class.java)!!
fun sendReminder(
workflowIdentifier: WorkflowIdentifier
) {
log.info("Sending reminder for workflow {}", workflowIdentifier)
}
}
Job
A Job’s payload function is additionally able to request the API object
describing the currently executing job by defining a Job parameter.
Requesting the currently executing job
@Job
class SendVacationRequestReminderJob {
fun sendReminder(
job: de.lise.fluxflow.api.job.Job
) {
// ...
}
}
Be sure not to confuse the correct de.lise.fluxflow.api.job.Job with
any other Job type.
As with all identifier API objects, it’s also possible only to request
the job’s identifier. This is archived by defining a JobIdentifier
parameter.
Only requesting the job’s identifier
@Job
class SendVacationRequestReminderJob {
fun sendReminder(
jobIdentifier: JobIdentifier
) {
// ...
}
}
Configuration values and expressions
If a workflow element needs to access values from the current
configuration or wants to evaluate custom expressions, Spring’s @Value
annotation can be used.
Requesting a configuration value
@Step
class CheckVacationRequestStep {
@OnCreated
fun sendNotification(
workflow: Workflow<VacationRequest>,
@Value("\${notifications.vacationRequest.created:true}")
notificationsEnabled: Boolean,
notificationService: NotificationService,
) {
if(!notificationsEnabled) {
return
}
notificationService.sendNewApprovalRequiredNotification(workflow.model.approvingManager)
}
}
More information regarding the @Value annotation and its features can
be found within the Spring
Documentation.
IoC Container
Last but not least, FluxFlow supports requesting dependencies from the inversion of control container. In effect, all registered beans are available to be injected into workflow elements.
The dependency resolution is currently limited to evaluating the parameter’s formal type. Controlling the dependency resolution using the bean’s name or any other Spring Boot annotation is currently not supported.