You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
6.0 KiB
Kotlin
190 lines
6.0 KiB
Kotlin
package wf.servitor.engine
|
|
|
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
import kotlinx.coroutines.channels.Channel
|
|
import kotlinx.coroutines.channels.sendBlocking
|
|
import kotlinx.coroutines.selects.whileSelect
|
|
import org.apache.activemq.artemis.api.core.client.ClientMessage
|
|
import org.apache.commons.jexl3.JexlEngine
|
|
import org.apache.commons.jexl3.JexlException
|
|
import org.koin.core.KoinComponent
|
|
import org.koin.core.inject
|
|
import org.koin.core.qualifier.named
|
|
import wf.servitor.common.Event.*
|
|
import wf.servitor.common.event.Session
|
|
import wf.servitor.common.workflow.Step
|
|
import wf.servitor.common.workflow.StepContinuation
|
|
import wf.servitor.engine.dispatcher.NamespaceAwareMapContext
|
|
import wf.servitor.engine.exception.UnresolvedRemoteCallException
|
|
|
|
class Engine : KoinComponent {
|
|
private val jexl by inject<JexlEngine>()
|
|
private val session by inject<Session>(named("queue.task"))
|
|
private val channel = Channel<ClientMessage>(2)
|
|
private var cancel = Channel<Unit>()
|
|
|
|
suspend fun run() {
|
|
session.onMessage {
|
|
channel.sendBlocking(it)
|
|
}
|
|
|
|
whileSelect {
|
|
channel.onReceive {
|
|
val task: Task = session.extract(it)
|
|
|
|
println("Got task!")
|
|
println(task)
|
|
|
|
// TODO check tombstone store
|
|
|
|
executeTask(task)
|
|
|
|
true
|
|
}
|
|
|
|
cancel.onReceive {
|
|
false
|
|
}
|
|
}
|
|
|
|
|
|
session.close()
|
|
}
|
|
|
|
suspend fun stop() {
|
|
this.cancel.send(Unit)
|
|
}
|
|
|
|
private fun executeTask(task: Task) {
|
|
session.sendUpdate(TaskUpdate(task, "running"))
|
|
|
|
val workflow = task.workflow
|
|
val step = workflow.getStep(task.flow, task.path)
|
|
|
|
if (step === null) {
|
|
endTask(task)
|
|
return
|
|
}
|
|
|
|
val script = step.getScript()
|
|
|
|
if (script === null) {
|
|
session.sendUpdate(TaskUpdate(task, "done"))
|
|
nextTask(step, task)
|
|
return
|
|
}
|
|
|
|
try {
|
|
val (res, context) = this.execute(script, task)
|
|
session.sendUpdate(TaskUpdate(task.copy(context = context), "done"))
|
|
nextTask(step, task, context, res)
|
|
} catch (e: UnresolvedRemoteCallException) {
|
|
session.queueRelay(Relay(e.service, e.method, e.arguments, task))
|
|
return
|
|
} catch (e: Throwable) {
|
|
session.sendUpdate(TaskUpdate(task, "error"))
|
|
return
|
|
}
|
|
}
|
|
|
|
private fun nextTask(
|
|
step: Step,
|
|
task: Task,
|
|
context: Map<String, Any?> = task.context,
|
|
result: Any? = null
|
|
) {
|
|
when (val continuation = step.next(result)) {
|
|
StepContinuation.End -> {
|
|
val endedTask = task.copy(context = context, dispatchedValues = listOf())
|
|
endTask(endedTask)
|
|
}
|
|
|
|
StepContinuation.Continue -> {
|
|
val next: List<Int>? = run select@{
|
|
val newList = task.path.toMutableList()
|
|
if (newList.count() > 1) {
|
|
val tree = newList.indices.drop(1).map {
|
|
newList.subList(0, it) to task.workflow.getStep(task.flow, newList.subList(0, it - 1))
|
|
}.reversed()
|
|
|
|
for ((path, parent) in tree) {
|
|
if (parent == null) {
|
|
continue
|
|
}
|
|
|
|
path[path.lastIndex]++
|
|
if (parent.children() < path[path.lastIndex] && null != parent.getChild(path[path.lastIndex])) {
|
|
return@select path
|
|
}
|
|
}
|
|
}
|
|
|
|
val lastNext = listOf(newList.first() + 1)
|
|
|
|
if (task.workflow.getStep(task.flow, lastNext) !== null) {
|
|
lastNext
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
|
|
if (next === null) {
|
|
endTask(task, context)
|
|
} else {
|
|
session.queueTask(task.copy(context = context, path = next, dispatchedValues = listOf()))
|
|
}
|
|
}
|
|
|
|
is StepContinuation.Flow -> {
|
|
session.queueTask(Task(task.workflow, task.context, continuation.name))
|
|
}
|
|
|
|
is StepContinuation.Step -> {
|
|
val stepList: List<Step> = task.workflow.flows[task.flow]?.steps ?: listOf()
|
|
var found = false
|
|
for (item in 0..stepList.count()) {
|
|
if (stepList[item].name == continuation.name) {
|
|
session.queueTask(
|
|
task.copy(
|
|
context = context,
|
|
path = listOf(item),
|
|
dispatchedValues = listOf()
|
|
)
|
|
)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
session.sendUpdate(TaskUpdate(task, "error"))
|
|
TODO("Do correct error handling")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun endTask(
|
|
task: Task,
|
|
context: Map<String, Any?> = task.context
|
|
) {
|
|
session.sendUpdate(TaskUpdate(task.copy(dispatchedValues = listOf(), context = context), "ended"))
|
|
}
|
|
|
|
private fun execute(input: String, task: Task): Pair<Any?, Map<String, Any?>> {
|
|
try {
|
|
val mutableContext = task.context.toMutableMap()
|
|
val jc = NamespaceAwareMapContext(task.workflow, mutableContext, task.dispatchedValues.toMutableList())
|
|
val script = jexl.createScript(input)
|
|
return script.execute(jc) to mutableContext
|
|
} catch (j: JexlException) {
|
|
val cause = j.cause
|
|
if (cause is UnresolvedRemoteCallException) {
|
|
throw cause
|
|
}
|
|
|
|
throw j
|
|
}
|
|
}
|
|
}
|