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