Initial commit
commit
e4b0e720fa
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.gradle
|
||||||
|
build
|
@ -0,0 +1,31 @@
|
|||||||
|
# Servitor
|
||||||
|
|
||||||
|
> A workflow engine
|
||||||
|
|
||||||
|
# Components
|
||||||
|
|
||||||
|
This project is split is multiple components which _should_ allow for effective clustering of relevant parts.
|
||||||
|
|
||||||
|
## [Engine](/engine)
|
||||||
|
|
||||||
|
The main component of Servitor, this part executes all logic noted in the workflow files
|
||||||
|
|
||||||
|
## [Relay](/relay)
|
||||||
|
|
||||||
|
The relay component does all the talking to remote endpoints, waits for their answers and puts the result back in the queue for the engine to process
|
||||||
|
|
||||||
|
## [Gateway](/gateway)
|
||||||
|
|
||||||
|
The gateway component observes and records all actions happening on the relay and in the engine.
|
||||||
|
It also serves the API (and When Time Comes the web frontend).
|
||||||
|
|
||||||
|
## [Monolith](/monolith)
|
||||||
|
|
||||||
|
Monolith is a special version of Servitor, which combines all tech necessary to run Servitor in a single jar.
|
||||||
|
This will be most likely the first thing you'll want to use if you want to play around with Servitor or want to deploy it in a small setup.
|
||||||
|
|
||||||
|
Monolith will not require -any- outside dependencies, it will use a bundled software for the message queues (ActiveMQ), key value store (???), and relation database (SQLite).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
[servitor]
|
||||||
|
|
||||||
|
[web]
|
||||||
|
bind = ":8888"
|
||||||
|
|
||||||
|
[queue]
|
||||||
|
uri = "tcp://localhost:61616"
|
||||||
|
username = "artemis"
|
||||||
|
password = "simetraehcapa"
|
||||||
|
|
@ -0,0 +1,79 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
|
||||||
|
id 'java'
|
||||||
|
id 'idea'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
// Hack to allow defining global dependencies
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||||
|
|
||||||
|
group = 'wf.servitor'
|
||||||
|
version = '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
def versions = project.ext.versions = [
|
||||||
|
kotlin : '1.3.61',
|
||||||
|
jackson : '2.9.10',
|
||||||
|
joda_time : '2.10.5',
|
||||||
|
koin : '2.0.1',
|
||||||
|
kotlinx_coroutines: '1.3.2',
|
||||||
|
jexl : '3.1',
|
||||||
|
junit : '5.5.2',
|
||||||
|
activemq : '2.11.0',
|
||||||
|
]
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Kotlin
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
|
||||||
|
|
||||||
|
// Kotlin tests
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-test:${versions.kotlin}"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-test-junit5:${versions.kotlin}"
|
||||||
|
|
||||||
|
// Kotlin Coroutines
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinx_coroutines}"
|
||||||
|
|
||||||
|
// Koin
|
||||||
|
implementation "org.koin:koin-core:${versions.koin}"
|
||||||
|
|
||||||
|
// ActiveMQ
|
||||||
|
implementation "org.apache.activemq:artemis-core-client:${versions.activemq}"
|
||||||
|
|
||||||
|
// JUnit
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter:${versions.junit}"
|
||||||
|
|
||||||
|
if (project.name != 'common') {
|
||||||
|
implementation project(':common')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "12"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "12"
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceCompatibility = 12
|
||||||
|
targetCompatibility = 12
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
def versions = project.ext.versions;
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Jackson
|
||||||
|
implementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}"
|
||||||
|
implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
|
||||||
|
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:${versions.jackson}"
|
||||||
|
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson}"
|
||||||
|
|
||||||
|
// Joda Time
|
||||||
|
implementation "joda-time:joda-time:${versions.joda_time}"
|
||||||
|
|
||||||
|
// Konf
|
||||||
|
implementation 'com.uchuhimo:konf-core:0.22.1'
|
||||||
|
implementation 'com.uchuhimo:konf-toml:0.22.1'
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package wf.servitor.common
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Workflow
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
sealed class Event : Serializable {
|
||||||
|
data class Task(
|
||||||
|
override val workflow: Workflow,
|
||||||
|
val context: Map<String, Any?> = mapOf(),
|
||||||
|
override val flow: String = "entry",
|
||||||
|
override val path: List<Int> = listOf(0),
|
||||||
|
val dispatchedValues: List<Any?> = listOf(),
|
||||||
|
override val id: UUID = UUID.randomUUID()
|
||||||
|
) : Event(), TaskLike
|
||||||
|
|
||||||
|
|
||||||
|
data class Relay(
|
||||||
|
val service: String,
|
||||||
|
val method: String,
|
||||||
|
val arguments: List<Any?>,
|
||||||
|
val task: Task,
|
||||||
|
val id: UUID = UUID.randomUUID()
|
||||||
|
) : Event()
|
||||||
|
|
||||||
|
data class TaskUpdate(
|
||||||
|
val task: TaskLike,
|
||||||
|
val status: String
|
||||||
|
) : Event()
|
||||||
|
|
||||||
|
|
||||||
|
interface TaskLike : Serializable {
|
||||||
|
val id: UUID
|
||||||
|
val workflow: Workflow
|
||||||
|
val flow: String
|
||||||
|
val path: List<Int>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
package wf.servitor.common.cli
|
||||||
|
|
@ -0,0 +1,43 @@
|
|||||||
|
package wf.servitor.common
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSession
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ServerLocator
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.core.scope.Scope
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import wf.servitor.common.config.Config
|
||||||
|
import wf.servitor.common.config.createConfig
|
||||||
|
import wf.servitor.common.event.Session
|
||||||
|
|
||||||
|
fun Scope.config(): Config = get()
|
||||||
|
|
||||||
|
val commonModule = module {
|
||||||
|
single { createConfig(getOrNull(named("argument.config"))) }
|
||||||
|
|
||||||
|
single { ActiveMQClient.createServerLocator(config().queue.url) }
|
||||||
|
|
||||||
|
single { get<ServerLocator>().createSessionFactory() }
|
||||||
|
|
||||||
|
single {
|
||||||
|
val serverLocator: ServerLocator = get()
|
||||||
|
val config = config().queue
|
||||||
|
|
||||||
|
get<ClientSessionFactory>().createSession(
|
||||||
|
config.username,
|
||||||
|
config.password,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
serverLocator.ackBatchSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
single { get<ClientSession>().createProducer() }
|
||||||
|
|
||||||
|
single(named("queue.task")) {
|
||||||
|
Session(get(), config().queue.names.taskQueue)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package wf.servitor.common.config
|
||||||
|
|
||||||
|
class Config(val queue: Queue) {
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package wf.servitor.common.config
|
||||||
|
|
||||||
|
class Queue(
|
||||||
|
val url: String,
|
||||||
|
val username: String?,
|
||||||
|
val password: String?,
|
||||||
|
val names: Names
|
||||||
|
) {
|
||||||
|
|
||||||
|
class Names(
|
||||||
|
val prefix: String,
|
||||||
|
val task: String,
|
||||||
|
val relay: String,
|
||||||
|
val observer: String
|
||||||
|
) {
|
||||||
|
val taskAddress
|
||||||
|
get() = "$prefix.$task"
|
||||||
|
|
||||||
|
val taskQueue
|
||||||
|
get() = "$prefix.$task.queue"
|
||||||
|
|
||||||
|
val relayAddress
|
||||||
|
get() = "$prefix.$relay"
|
||||||
|
|
||||||
|
val relayQueue
|
||||||
|
get() = "$prefix.$relay.queue"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package wf.servitor.common.config
|
||||||
|
|
||||||
|
import com.uchuhimo.konf.ConfigSpec
|
||||||
|
|
||||||
|
object QueueSpec : ConfigSpec() {
|
||||||
|
val url by optional("tcp://localhost:61616", description = "url of ActiveMQ server")
|
||||||
|
val username by optional<String?>(null, description = "username for ActiveMQ server")
|
||||||
|
val password by optional<String?>(null, description = "password for ActiveMQ server")
|
||||||
|
|
||||||
|
object Names : ConfigSpec() {
|
||||||
|
val namePrefix by optional(
|
||||||
|
"servitor",
|
||||||
|
name = "prefix",
|
||||||
|
description = "the prefix of all queues and addresses used in ActiveMQ"
|
||||||
|
)
|
||||||
|
|
||||||
|
val task by optional(
|
||||||
|
"task",
|
||||||
|
description = "the name of the task address, the queue will always appear at [prefix].[task].queue"
|
||||||
|
)
|
||||||
|
|
||||||
|
val observation by optional(
|
||||||
|
"observe",
|
||||||
|
description = "the name of the observation queue, which will listen to all messages send to [prefix].#"
|
||||||
|
)
|
||||||
|
|
||||||
|
val relay by optional(
|
||||||
|
"relay",
|
||||||
|
description = "the name of the relay address, the queue will appear at [prefix].[relay].queue"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
package wf.servitor.common.config
|
||||||
|
|
||||||
|
import com.uchuhimo.konf.source.toml
|
||||||
|
import com.uchuhimo.konf.Config.Companion as KonfConfig
|
||||||
|
|
||||||
|
fun createConfig(file: String? = null): Config {
|
||||||
|
val config = run {
|
||||||
|
var config = KonfConfig {
|
||||||
|
addSpec(QueueSpec)
|
||||||
|
}
|
||||||
|
.from.toml.file("/etc/servitor/config.toml", optional = true)
|
||||||
|
.from.json.file("/etc/servitor/config.json", optional = true)
|
||||||
|
|
||||||
|
if (file !== null) {
|
||||||
|
config = if (file.endsWith("json")) {
|
||||||
|
config.from.json.file(file)
|
||||||
|
} else {
|
||||||
|
config.from.toml.file(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
return Config(
|
||||||
|
Queue(
|
||||||
|
config[QueueSpec.url],
|
||||||
|
config[QueueSpec.username],
|
||||||
|
config[QueueSpec.password],
|
||||||
|
Queue.Names(
|
||||||
|
config[QueueSpec.Names.namePrefix],
|
||||||
|
config[QueueSpec.Names.task],
|
||||||
|
config[QueueSpec.Names.relay],
|
||||||
|
config[QueueSpec.Names.observation]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package wf.servitor.common.event
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.api.core.Message
|
||||||
|
import org.apache.activemq.artemis.api.core.RoutingType
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientMessage
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSession
|
||||||
|
import wf.servitor.common.Event
|
||||||
|
import java.io.*
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
|
class Session(private val session: ClientSession, private val queue: String) {
|
||||||
|
private val consumer by lazy {
|
||||||
|
lock.withLock {
|
||||||
|
session.start()
|
||||||
|
}
|
||||||
|
session.createConsumer(queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val lock = ReentrantLock()
|
||||||
|
|
||||||
|
private val producer by lazy { session.createProducer() }
|
||||||
|
|
||||||
|
private fun makeMessage(routingType: RoutingType, obj: Event, group: String? = null): Message {
|
||||||
|
return session.createMessage(true).also {
|
||||||
|
it.routingType = routingType
|
||||||
|
|
||||||
|
group?.let(it::setGroupID)
|
||||||
|
val bbos = ByteArrayOutputStream()
|
||||||
|
val oos = ObjectOutputStream(bbos)
|
||||||
|
oos.writeObject(obj)
|
||||||
|
it.bodyBuffer.writeBytes(bbos.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendUpdate(update: Event.TaskUpdate) = lock.withLock {
|
||||||
|
println("Sending update")
|
||||||
|
println(update)
|
||||||
|
producer.send(
|
||||||
|
"servitor.observer",
|
||||||
|
makeMessage(RoutingType.ANYCAST, update, "servitor.task.${update.task.id}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queueTask(task: Event.Task) = lock.withLock {
|
||||||
|
println("Sending task")
|
||||||
|
println(task)
|
||||||
|
|
||||||
|
producer.send(
|
||||||
|
"servitor.task",
|
||||||
|
makeMessage(RoutingType.ANYCAST, task, "servitor.task.${task.id}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queueRelay(relay: Event.Relay) = lock.withLock {
|
||||||
|
producer.send(
|
||||||
|
"servitor.relay",
|
||||||
|
makeMessage(RoutingType.ANYCAST, relay, "servitor.task.${relay.task.id}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onMessage(block: (ClientMessage) -> Unit) {
|
||||||
|
lock.withLock {
|
||||||
|
consumer.setMessageHandler(block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun close() {
|
||||||
|
session.commit()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Serializable> extract(message: ClientMessage): T {
|
||||||
|
message.acknowledge()
|
||||||
|
|
||||||
|
val byteArray = ByteArray(message.bodySize)
|
||||||
|
message.bodyBuffer.readBytes(byteArray)
|
||||||
|
val obj = ObjectInputStream(ByteArrayInputStream(byteArray)).readObject()
|
||||||
|
|
||||||
|
if (obj !is T) {
|
||||||
|
throw RuntimeException("Queue is FUCKED")
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package wf.servitor.common.utils
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import wf.servitor.common.workflow.Flow
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
object JacksonModule : SimpleModule() {
|
||||||
|
val listStepType = object : TypeReference<List<Step>>() {}
|
||||||
|
|
||||||
|
init {
|
||||||
|
addDeserializer(Flow::class.java, object : JsonDeserializer<Flow>() {
|
||||||
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Flow {
|
||||||
|
return Flow(p.readValueAs<List<Step>>(listStepType).toMutableList())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addSerializer(DateTime::class.java, object : JsonSerializer<DateTime>() {
|
||||||
|
override fun serialize(value: DateTime, gen: JsonGenerator, serializers: SerializerProvider) {
|
||||||
|
gen.writeString(value.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
|
||||||
|
class Document(
|
||||||
|
val name: String = "",
|
||||||
|
val workflow: Workflow
|
||||||
|
) : Serializable
|
@ -0,0 +1,17 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
data class Flow(val steps: List<Step> = listOf()) : Serializable {
|
||||||
|
fun getStep(path: List<Int>): Step? {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
.drop(1)
|
||||||
|
.fold(steps.getOrNull(path.first())) { s, index ->
|
||||||
|
s?.getChild(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.databind.PropertyNamingStrategy
|
||||||
|
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||||
|
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
|
||||||
|
import wf.servitor.common.utils.JacksonModule
|
||||||
|
|
||||||
|
object Jackson {
|
||||||
|
val yaml = ObjectMapper(YAMLFactory()).apply {
|
||||||
|
registerKotlinModule()
|
||||||
|
registerModule(JacksonModule)
|
||||||
|
disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||||
|
propertyNamingStrategy = PropertyNamingStrategy.LOWER_CAMEL_CASE
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = ObjectMapper().apply {
|
||||||
|
registerKotlinModule()
|
||||||
|
registerModule(JacksonModule)
|
||||||
|
propertyNamingStrategy = PropertyNamingStrategy.LOWER_CAMEL_CASE
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder
|
||||||
|
import wf.servitor.common.workflow.step.*
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
@JsonDeserialize(builder = Step.Builder::class)
|
||||||
|
interface Step : Serializable {
|
||||||
|
val name: String
|
||||||
|
|
||||||
|
fun getChild(index: Int): Step? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun children() = 0
|
||||||
|
fun getScript(): String? = null
|
||||||
|
fun next(result: Any?): StepContinuation = StepContinuation.Continue
|
||||||
|
|
||||||
|
@JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with")
|
||||||
|
class Builder {
|
||||||
|
var name: String = ""
|
||||||
|
var `if`: String? = null
|
||||||
|
var `do`: String? = null
|
||||||
|
var then: String? = null
|
||||||
|
var `else`: String? = null
|
||||||
|
var options: List<OptionsStep.Option>? = null
|
||||||
|
var collect: List<Step>? = null
|
||||||
|
|
||||||
|
fun withName(value: String) {
|
||||||
|
this.name = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withIf(value: String) {
|
||||||
|
this.`if` = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withDo(value: String) {
|
||||||
|
this.`do` = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withThen(value: String) {
|
||||||
|
this.then = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withElse(value: String) {
|
||||||
|
this.`else` = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withOptions(value: List<OptionsStep.Option>) {
|
||||||
|
this.options = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withCollect(value: List<Step>) {
|
||||||
|
this.collect = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): Step {
|
||||||
|
val `if` = this.`if`
|
||||||
|
val `do` = this.`do`
|
||||||
|
|
||||||
|
if (`if` != null) {
|
||||||
|
if (`do` != null) {
|
||||||
|
return IfActionStep(`if`, `do`, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val then = then
|
||||||
|
val `else` = `else`
|
||||||
|
|
||||||
|
if (then != null || `else` != null) {
|
||||||
|
return IfJumpStep(`if`, then, `else`, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
error("Step missing 'then', 'else' or 'do'")
|
||||||
|
}
|
||||||
|
|
||||||
|
val options = options
|
||||||
|
if (options != null) {
|
||||||
|
return OptionsStep(options, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val collect = collect
|
||||||
|
if (collect != null) {
|
||||||
|
return CollectStep(collect, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (`do` != null) {
|
||||||
|
return ActionStep(`do`, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
error("No matching type of step could be found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
sealed class StepContinuation {
|
||||||
|
object Continue : StepContinuation()
|
||||||
|
object End : StepContinuation()
|
||||||
|
|
||||||
|
data class Flow(val name: String) : StepContinuation()
|
||||||
|
data class Step(val name: String) : StepContinuation()
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package wf.servitor.common.workflow
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
class Workflow(
|
||||||
|
val entry: Flow = Flow(),
|
||||||
|
val flows: MutableMap<String, Flow> = mutableMapOf(),
|
||||||
|
val services: MutableMap<String, Map<String, Any?>> = mutableMapOf()
|
||||||
|
) : Serializable {
|
||||||
|
fun getStep(flow: String, path: List<Int>): Step? = (if (flow == "entry") entry else flows.get(flow))?.getStep(path)
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package wf.servitor.common.workflow.step
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
class ActionStep(val `do`: String, override val name: String) : Step {
|
||||||
|
override fun getScript() = `do`
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package wf.servitor.common.workflow.step
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
class CollectStep(val collect: List<Step>, override val name: String) : Step
|
@ -0,0 +1,5 @@
|
|||||||
|
package wf.servitor.common.workflow.step
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
class IfActionStep(val `if`: String, val `do`: String, override val name: String = `if`) : Step
|
@ -0,0 +1,10 @@
|
|||||||
|
package wf.servitor.common.workflow.step
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
class IfJumpStep(val `if`: String, val then: String? = null, val `else`: String? = null, override val name: String = `if`) :
|
||||||
|
Step {
|
||||||
|
override fun getScript(): String? {
|
||||||
|
return `if`
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package wf.servitor.common.workflow.step
|
||||||
|
|
||||||
|
import wf.servitor.common.workflow.Step
|
||||||
|
|
||||||
|
class OptionsStep(val options: List<Option>, override val name: String) : Step {
|
||||||
|
class Option(val `when`: String, val `do`: String)
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
def versions = project.ext.versions;
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.apache.commons:commons-jexl3:${versions.jexl}"
|
||||||
|
}
|
@ -0,0 +1,189 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package wf.servitor.engine
|
||||||
|
|
||||||
|
import org.apache.commons.jexl3.JexlBuilder
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val engineModule = module {
|
||||||
|
single { JexlBuilder().create() }
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package wf.servitor.engine
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import wf.servitor.common.commonModule
|
||||||
|
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
startKoin {
|
||||||
|
modules(commonModule)
|
||||||
|
modules(engineModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
val engine = Engine()
|
||||||
|
runBlocking {
|
||||||
|
engine.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
|||||||
|
package wf.servitor.engine.dispatcher
|
||||||
|
|
||||||
|
import org.apache.commons.jexl3.JexlContext
|
||||||
|
import org.apache.commons.jexl3.MapContext
|
||||||
|
import wf.servitor.common.workflow.Workflow
|
||||||
|
|
||||||
|
|
||||||
|
class NamespaceAwareMapContext(
|
||||||
|
private val workflow: Workflow,
|
||||||
|
map: Map<String, Any?>,
|
||||||
|
private val dispatchedValues: MutableList<Any?>
|
||||||
|
) : MapContext(map), JexlContext.NamespaceResolver {
|
||||||
|
override fun resolveNamespace(name: String): Any? {
|
||||||
|
workflow.services.get(name) ?: return null
|
||||||
|
return NamespaceFaker(name, dispatchedValues)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package wf.servitor.engine.dispatcher
|
||||||
|
|
||||||
|
import org.apache.commons.jexl3.JexlContext
|
||||||
|
import org.apache.commons.jexl3.introspection.JexlMethod
|
||||||
|
import wf.servitor.engine.exception.UnresolvedRemoteCallException
|
||||||
|
|
||||||
|
class NamespaceFaker(
|
||||||
|
private val namespace: String,
|
||||||
|
private val dispatchedValues: MutableList<Any?>,
|
||||||
|
val nextValueIndex: Int = dispatchedValues.size
|
||||||
|
) : JexlContext {
|
||||||
|
override fun has(name: String) = true
|
||||||
|
override fun get(name: String): Any {
|
||||||
|
return CallExecutor {
|
||||||
|
if (dispatchedValues.isNotEmpty()) {
|
||||||
|
val first = dispatchedValues.first()
|
||||||
|
dispatchedValues.removeAt(0)
|
||||||
|
first
|
||||||
|
} else {
|
||||||
|
throw UnresolvedRemoteCallException(namespace, name, it.toList(), nextValueIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(name: String, value: Any?) {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallExecutor(val dispatchedValue: (Array<out Any?>) -> Any?) : JexlMethod {
|
||||||
|
override fun tryInvoke(name: String?, obj: Any?, vararg params: Any?): Any {
|
||||||
|
TODO("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCacheable() = false;
|
||||||
|
override fun getReturnType(): Class<*> = TODO()
|
||||||
|
override fun tryFailed(rval: Any?) = true
|
||||||
|
|
||||||
|
override fun invoke(obj: Any?, vararg params: Any?): Any? {
|
||||||
|
return dispatchedValue(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package wf.servitor.engine.exception
|
||||||
|
|
||||||
|
class UnresolvedRemoteCallException(val service: String, val method: String, val arguments: List<Any?>, val index: Int) :
|
||||||
|
RuntimeException("Halted execution for external call", null, true, false) {
|
||||||
|
// Don't create a stack trace for this exception.
|
||||||
|
override fun fillInStackTrace(): Throwable {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package wf.servitor.engine
|
||||||
|
|
||||||
|
class EngineInABox {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,123 @@
|
|||||||
|
package wf.servitor.engine
|
||||||
|
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration
|
||||||
|
import org.apache.activemq.artemis.api.core.QueueAttributes
|
||||||
|
import org.apache.activemq.artemis.api.core.RoutingType
|
||||||
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSession
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ServerLocator
|
||||||
|
import org.koin.core.context.startKoin
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.dsl.module
|
||||||
|
import wf.servitor.common.Event
|
||||||
|
import wf.servitor.common.event.Session
|
||||||
|
import wf.servitor.common.workflow.Flow
|
||||||
|
import wf.servitor.common.workflow.Workflow
|
||||||
|
import wf.servitor.common.workflow.step.ActionStep
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
|
||||||
|
class EngineTests {
|
||||||
|
fun workflow(): Workflow {
|
||||||
|
return Workflow(
|
||||||
|
Flow(
|
||||||
|
listOf(
|
||||||
|
ActionStep("sum = 1 + 1", "sum"),
|
||||||
|
ActionStep("sum += 4", "more-sum"),
|
||||||
|
ActionStep("sum += nice:plus(sum, 4)", "remote-sum")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
services = mutableMapOf(
|
||||||
|
"nice" to mapOf()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOk() {
|
||||||
|
val app = startKoin {
|
||||||
|
modules(module {
|
||||||
|
single { ActiveMQClient.createServerLocator("tcp://localhost:61616") }
|
||||||
|
single { get<ServerLocator>().createSessionFactory() }
|
||||||
|
single {
|
||||||
|
val serverLocator: ServerLocator = get()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get<ClientSessionFactory>().createSession(
|
||||||
|
"artemis",
|
||||||
|
"simetraehcapa",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
serverLocator.isPreAcknowledge,
|
||||||
|
serverLocator.ackBatchSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
single { get<ClientSession>().createProducer() }
|
||||||
|
single(named("queue.task")) {
|
||||||
|
val session: ClientSession = get()
|
||||||
|
println("Creating address")
|
||||||
|
session.createAddress(SimpleString("servitor.task"), RoutingType.ANYCAST, true)
|
||||||
|
|
||||||
|
println("Checking task queue")
|
||||||
|
if (!session.queueQuery(SimpleString("servitor.task.queue")).isExists) {
|
||||||
|
println("Creating task queue")
|
||||||
|
session.createQueue(
|
||||||
|
SimpleString("servitor.task"),
|
||||||
|
SimpleString("servitor.task.queue"),
|
||||||
|
true,
|
||||||
|
QueueAttributes().apply {
|
||||||
|
routingType = RoutingType.ANYCAST
|
||||||
|
durable = true
|
||||||
|
purgeOnNoConsumers = false
|
||||||
|
maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Checking observation queue")
|
||||||
|
if (!session.queueQuery(SimpleString("servitor.observe")).isExists) {
|
||||||
|
println("Creating observation queue")
|
||||||
|
session.createQueue(
|
||||||
|
SimpleString("servitor.#"),
|
||||||
|
SimpleString("servitor.observe"),
|
||||||
|
true,
|
||||||
|
QueueAttributes().apply {
|
||||||
|
routingType = RoutingType.ANYCAST
|
||||||
|
durable = true
|
||||||
|
purgeOnNoConsumers = false
|
||||||
|
maxConsumers = ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Session(get(), "servitor.task.queue")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
modules(engineModule)
|
||||||
|
}
|
||||||
|
|
||||||
|
val session: Session = app.koin.get(named("queue.task"))
|
||||||
|
val engine = Engine()
|
||||||
|
val engineJob = GlobalScope.launch {
|
||||||
|
engine.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
println("Queueing task")
|
||||||
|
session.queueTask(Event.Task(workflow()))
|
||||||
|
println("Send task")
|
||||||
|
delay(5000)
|
||||||
|
engine.stop()
|
||||||
|
engineJob.join()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
kotlin.code.style=official
|
Binary file not shown.
@ -0,0 +1,6 @@
|
|||||||
|
#Tue Jan 14 22:21:44 CET 2020
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
@ -0,0 +1,12 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package wf.servitor.relay
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = 'servitor'
|
||||||
|
include 'common', 'engine', 'gateway', 'relay', 'monolith'
|
Loading…
Reference in New Issue