summaryrefslogtreecommitdiff
path: root/simulator/odcsim
diff options
context:
space:
mode:
authorGeorgios Andreadis <info@gandreadis.com>2020-06-29 16:04:57 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2020-08-24 16:18:13 +0200
commit46b06fb446e79c390c01953d31d700b8e73da24d (patch)
treeb2329630ebf2c90d297ba0d3046ccd558d12d042 /simulator/odcsim
parentebcacf96fbc1cd16a91523f95dd01db046fb7f90 (diff)
Prepare simulator repository for monorepo
This change prepares the simulator Git repository for the monorepo residing at https://github.com/atlarge-research.com/opendc. To accomodate for this, we move all files into a simulator subdirectory.
Diffstat (limited to 'simulator/odcsim')
-rw-r--r--simulator/odcsim/README.md54
-rw-r--r--simulator/odcsim/build.gradle.kts23
-rw-r--r--simulator/odcsim/odcsim-api/build.gradle.kts40
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Domain.kt54
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationContext.kt63
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt58
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt35
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/EventFlow.kt99
-rw-r--r--simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/StateFlow.kt81
-rw-r--r--simulator/odcsim/odcsim-engine-omega/build.gradle.kts47
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt264
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt37
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt567
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt440
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt74
-rw-r--r--simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider1
16 files changed, 1937 insertions, 0 deletions
diff --git a/simulator/odcsim/README.md b/simulator/odcsim/README.md
new file mode 100644
index 00000000..b930315a
--- /dev/null
+++ b/simulator/odcsim/README.md
@@ -0,0 +1,54 @@
+<h1 align="center">
+ <a href="http://opendc.org/">
+ <img src="../misc/artwork/logo.png" width="100" alt="OpenDC">
+ </a>
+ <br>
+ odcsim
+</h1>
+
+## Introduction
+**odcsim** is a framework for discrete event simulation in Kotlin, used
+by the [OpenDC](https://opendc.org) project.
+Simulations are defined in terms of a hierarchical grouping of actors
+and the interactions between these actors
+([Actor model](https://en.wikipedia.org/wiki/Actor_model)).
+
+## Getting Started
+
+The instructions below are meant for when you would like to use `odcsim` separately from `opendc`. If you simply want to use the simulator, you do not need to follow them, please refer to the [main README](../README.md) for instructions.
+
+### Installation
+Please add the required packages as dependency in your project.
+Releases are available in the [Maven Central Repository](https://search.maven.org/).
+
+The package `odcsim-core` is required to construct a simulation model.
+A `odcsim-engine-*` package is needed for running the simulation
+model.
+
+#### Gradle
+Groovy
+```groovy
+implementation 'com.atlarge.odcsim:odcsim-api:2.0.0'
+runtime 'com.atlarge.odcsim:odcsim-engine-omega:2.0.0'
+```
+Kotlin
+```groovy
+implementation("com.atlarge.odcsim:odcsim-api:2.0.0")
+runtime("com.atlarge.odcsim:odcsim-engine-omega:2.0.0")
+```
+
+#### Maven
+```xml
+<dependency>
+ <groupId>com.atlarge.odcsim</groupId>
+ <artifactId>odcsim-api</artifactId>
+ <version>2.0.0</version>
+</dependency>
+
+<dependency>
+ <groupId>com.atlarge.odcsim</groupId>
+ <artifactId>odcsim-engine-omega</artifactId>
+ <version>2.0.0</version>
+</dependency>
+```
+
diff --git a/simulator/odcsim/build.gradle.kts b/simulator/odcsim/build.gradle.kts
new file mode 100644
index 00000000..cc3f3add
--- /dev/null
+++ b/simulator/odcsim/build.gradle.kts
@@ -0,0 +1,23 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
diff --git a/simulator/odcsim/odcsim-api/build.gradle.kts b/simulator/odcsim/odcsim-api/build.gradle.kts
new file mode 100644
index 00000000..c8c6c90c
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/build.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+description = "Framework for discrete event simulation in Kotlin"
+
+/* Build configuration */
+plugins {
+ `kotlin-library-convention`
+}
+
+dependencies {
+ implementation(kotlin("stdlib"))
+ api("org.slf4j:slf4j-api:${Library.SLF4J}")
+ api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Library.KOTLINX_COROUTINES}")
+
+ testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}")
+ testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}")
+}
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Domain.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Domain.kt
new file mode 100644
index 00000000..c850952c
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Domain.kt
@@ -0,0 +1,54 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim
+
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * An isolated execution unit that runs concurrently in simulation to the other simulation domains. A domain defines a
+ * logical boundary between processes in simulation.
+ */
+public interface Domain : CoroutineScope {
+ /**
+ * The name of this domain.
+ */
+ public val name: String
+
+ /**
+ * The parent domain to which the lifecycle of this domain is bound. In case this is a root domain, this refers to
+ * itself.
+ */
+ public val parent: Domain
+
+ /**
+ * Construct an anonymous simulation sub-domain that is bound to the lifecycle of this domain.
+ */
+ public fun newDomain(): Domain
+
+ /**
+ * Construct a new simulation sub-domain with the specified [name].
+ */
+ public fun newDomain(name: String): Domain
+}
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationContext.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationContext.kt
new file mode 100644
index 00000000..c51d1d8b
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationContext.kt
@@ -0,0 +1,63 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim
+
+import java.time.Clock
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.coroutineContext
+import org.slf4j.Logger
+
+/**
+ * Represents the execution context of a simulation domain.
+ */
+public interface SimulationContext : CoroutineContext.Element {
+ /**
+ * Key for [SimulationContext] instance in the coroutine context.
+ */
+ companion object Key : CoroutineContext.Key<SimulationContext>
+
+ /**
+ * The reference to the current simulation domain.
+ */
+ public val domain: Domain
+
+ /**
+ * The clock tracking the simulation time.
+ */
+ public val clock: Clock
+
+ /**
+ * A logger instance tied to the logical process.
+ */
+ public val log: Logger
+}
+
+/**
+ * The simulation context of the current coroutine.
+ */
+@Suppress("WRONG_MODIFIER_TARGET")
+public suspend inline val simulationContext: SimulationContext
+ @Suppress("ILLEGAL_SUSPEND_PROPERTY_ACCESS")
+ get() = coroutineContext[SimulationContext] ?: throw IllegalStateException("No simulation context available")
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt
new file mode 100644
index 00000000..db05cb1d
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt
@@ -0,0 +1,58 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim
+
+/**
+ * An engine for managing logical processes represented as [Behavior] during simulation.
+ *
+ * An implementation of this interface should be provided by an engine. See for example *odcsim-engine-omega*,
+ * which is the reference implementation of the *odcsim* API.
+ */
+public interface SimulationEngine {
+ /**
+ * The name of this engine instance, used to distinguish between multiple engines running within the same JVM.
+ */
+ public val name: String
+
+ /**
+ * Construct an anonymous root simulation domain.
+ */
+ public fun newDomain(): Domain
+
+ /**
+ * Construct a new root simulation domain with the specified [name].
+ */
+ public fun newDomain(name: String): Domain
+
+ /**
+ * Run the simulation.
+ */
+ public suspend fun run()
+
+ /**
+ * Terminates this engine in an asynchronous fashion.
+ */
+ public suspend fun terminate()
+}
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt
new file mode 100644
index 00000000..a975fa3c
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt
@@ -0,0 +1,35 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim
+
+/**
+ * A factory for [SimulationEngine] instances that allows users to dynamically load engine implementations.
+ */
+public interface SimulationEngineProvider {
+ /**
+ * Construct an [SimulationEngine] with the given [name].
+ */
+ public operator fun invoke(name: String): SimulationEngine
+}
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/EventFlow.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/EventFlow.kt
new file mode 100644
index 00000000..5d9af9ec
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/EventFlow.kt
@@ -0,0 +1,99 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.flow
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.consumeAsFlow
+import java.util.WeakHashMap
+
+/**
+ * A [Flow] that can be used to emit events.
+ */
+public interface EventFlow<T> : Flow<T> {
+ /**
+ * Emit the specified [event].
+ */
+ public fun emit(event: T)
+
+ /**
+ * Close the flow.
+ */
+ public fun close()
+}
+
+/**
+ * Creates a new [EventFlow].
+ */
+@Suppress("FunctionName")
+public fun <T> EventFlow(): EventFlow<T> = EventFlowImpl()
+
+/**
+ * Internal implementation of the [EventFlow] class.
+ */
+@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
+private class EventFlowImpl<T> : EventFlow<T> {
+ private var closed: Boolean = false
+ private val subscribers = WeakHashMap<SendChannel<T>, Unit>()
+
+ override fun emit(event: T) {
+ synchronized(this) {
+ for ((chan, _) in subscribers) {
+ chan.offer(event)
+ }
+ }
+ }
+
+ override fun close() {
+ synchronized(this) {
+ closed = true
+
+ for ((chan, _) in subscribers) {
+ chan.close()
+ }
+ }
+ }
+
+ @InternalCoroutinesApi
+ override suspend fun collect(collector: FlowCollector<T>) {
+ val channel: Channel<T>
+ synchronized(this) {
+ if (closed) {
+ return
+ }
+
+ channel = Channel(Channel.UNLIMITED)
+ subscribers[channel] = Unit
+ }
+ channel.consumeAsFlow().collect(collector)
+ }
+
+ override fun toString(): String = "EventFlow"
+}
diff --git a/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/StateFlow.kt b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/StateFlow.kt
new file mode 100644
index 00000000..50add0ad
--- /dev/null
+++ b/simulator/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/flow/StateFlow.kt
@@ -0,0 +1,81 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.flow
+
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.channels.BroadcastChannel
+import kotlinx.coroutines.channels.ConflatedBroadcastChannel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.asFlow
+
+/**
+ * A [Flow] that contains a single value that changes over time.
+ *
+ * This class exists to implement the DataFlow/StateFlow functionality that will be implemented in `kotlinx-coroutines`
+ * in the future, but is not available yet.
+ * See: https://github.com/Kotlin/kotlinx.coroutines/pull/1354
+ */
+public interface StateFlow<T> : Flow<T> {
+ /**
+ * The current value of this flow.
+ *
+ * Setting a value that is [equal][Any.equals] to the previous one does nothing.
+ */
+ public var value: T
+}
+
+/**
+ * Creates a [StateFlow] with a given initial [value].
+ */
+@Suppress("FunctionName")
+public fun <T> StateFlow(value: T): StateFlow<T> = StateFlowImpl(value)
+
+/**
+ * Internal implementation of the [StateFlow] interface.
+ */
+@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
+private class StateFlowImpl<T>(initialValue: T) : StateFlow<T> {
+ /**
+ * The [BroadcastChannel] to back this flow.
+ */
+ private val chan = ConflatedBroadcastChannel(initialValue)
+
+ /**
+ * The internal [Flow] backing this flow.
+ */
+ private val flow = chan.asFlow()
+
+ public override var value: T = initialValue
+ set(value) {
+ chan.offer(value)
+ field = value
+ }
+
+ @InternalCoroutinesApi
+ override suspend fun collect(collector: FlowCollector<T>) = flow.collect(collector)
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/build.gradle.kts b/simulator/odcsim/odcsim-engine-omega/build.gradle.kts
new file mode 100644
index 00000000..98e2469e
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/build.gradle.kts
@@ -0,0 +1,47 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+description = "Single-threaded reference implementation for the odcsim API"
+
+/* Build configuration */
+plugins {
+ `kotlin-library-convention`
+}
+
+/* Project configuration */
+repositories {
+ jcenter()
+}
+
+dependencies {
+ api(project(":odcsim:odcsim-api"))
+
+ implementation(kotlin("stdlib"))
+ implementation("org.jetbrains:annotations:17.0.0")
+
+ testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}")
+ testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}")
+ testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}")
+ testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}")
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt
new file mode 100644
index 00000000..e675b877
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngine.kt
@@ -0,0 +1,264 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.engine.omega
+
+import com.atlarge.odcsim.Domain
+import com.atlarge.odcsim.SimulationContext
+import com.atlarge.odcsim.SimulationEngine
+import com.atlarge.odcsim.engine.omega.logging.LoggerImpl
+import java.time.Clock
+import java.time.Instant
+import java.time.ZoneId
+import java.util.PriorityQueue
+import java.util.UUID
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.coroutineContext
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.Delay
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.SupervisorJob
+import org.jetbrains.annotations.Async
+import org.slf4j.Logger
+
+/**
+ * The reference implementation of the [SimulationEngine] instance for the OpenDC simulation core.
+ *
+ * This engine implementation is a single-threaded implementation, running logical processes synchronously and
+ * provides a single priority queue for all events (messages, ticks, etc) that occur.
+ *
+ * @param name The name of the engine instance.
+ */
+public class OmegaSimulationEngine(override val name: String) : SimulationEngine {
+ /**
+ * The state of the actor system.
+ */
+ private var state: SimulationEngineState = SimulationEngineState.CREATED
+
+ /**
+ * The clock tracking the simulation time.
+ */
+ private val clock: VirtualClock = VirtualClock(0)
+
+ /**
+ * The event queue to process
+ */
+ private val queue: PriorityQueue<Event> = PriorityQueue(Comparator<Event> { lhs, rhs ->
+ // Note that Comparator gives better performance than Comparable according to
+ // profiling
+ val cmp = lhs.time.compareTo(rhs.time)
+ if (cmp == 0) lhs.id.compareTo(rhs.id) else cmp
+ })
+
+ /**
+ * The active processes in the simulation engine.
+ */
+ private val registry: MutableMap<String, Domain> = HashMap()
+
+ /**
+ * A unique increasing identifier assigned to each event, needed because otherwise two events occurring in sequence
+ * (but at the same time) may be differently ordered in the internal priority queue (queue) since it does not
+ * guarantee insertion order.
+ */
+ private var nextId: Long = 0
+
+ override fun newDomain(): Domain = newDomain(null)
+
+ override fun newDomain(name: String): Domain = newDomain(name, null)
+
+ override suspend fun run() {
+ check(state != SimulationEngineState.TERMINATED) { "The simulation engine is terminated" }
+
+ if (state == SimulationEngineState.CREATED) {
+ state = SimulationEngineState.STARTED
+ }
+
+ val job = coroutineContext[Job]
+
+ while (job?.isActive == true) {
+ val event = queue.peek() ?: break
+ val delivery = event.time
+
+ // A message should never be delivered out of order in this single-threaded implementation. Assert for
+ // sanity
+ assert(delivery >= clock.time) { "Message delivered out of order [expected=$delivery, actual=${clock.time}]" }
+
+ clock.time = delivery
+ queue.poll()
+
+ process(event)
+ }
+ }
+
+ override suspend fun terminate() {
+ state = SimulationEngineState.TERMINATED
+ }
+
+ /**
+ * Schedule the specified event to be processed by the engine.
+ */
+ private fun schedule(@Async.Schedule event: Event) {
+ assert(event.time >= clock.time) { "Message scheduled in the past [received=${event.time}, actual=${clock.time}]" }
+ queue.add(event)
+ }
+
+ /**
+ * Process the delivery of an event.
+ */
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun process(@Async.Execute event: Event) {
+ // This has been inlined into this method for performance
+ when (event) {
+ is Event.Dispatch ->
+ event.block.run()
+ is Event.Resume ->
+ with(event.continuation) { event.dispatcher.resumeUndispatched(Unit) }
+ is Event.Timeout ->
+ if (!event.isCancelled)
+ event.block.run()
+ }
+ }
+
+ /**
+ * Spawn a new simulation domain.
+ */
+ private fun newDomainImpl(name: String, parent: DomainImpl?): Domain {
+ val domain = DomainImpl(name, parent)
+ require(domain.path !in registry) { "Domain name $name not unique" }
+ registry[domain.path] = domain
+ return domain
+ }
+
+ private fun newDomain(parent: DomainImpl?): Domain {
+ val name = "$" + UUID.randomUUID()
+ return newDomainImpl(name, parent)
+ }
+
+ private fun newDomain(name: String, parent: DomainImpl?): Domain {
+ require(name.isNotEmpty()) { "Domain name may not be empty" }
+ require(!name.startsWith("$")) { "Domain name may not start with $-sign" }
+ require(!name.contains("/")) { "Domain name may not contain /" }
+ return newDomainImpl(name, parent)
+ }
+
+ private inner class DomainImpl(override val name: String, parent: DomainImpl?) : SimulationContext, Domain {
+ val job: Job = SupervisorJob(parent?.job)
+ val path: String = (parent?.path ?: "") + "/$name"
+
+ @InternalCoroutinesApi
+ private val dispatcher = object : CoroutineDispatcher(), Delay {
+ // CoroutineDispatcher
+ override fun dispatch(context: CoroutineContext, block: Runnable) {
+ schedule(Event.Dispatch(clock.time, nextId++, block))
+ }
+
+ // Delay
+ override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
+ schedule(Event.Resume(clock.time + timeMillis, nextId++, this, continuation))
+ }
+
+ override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
+ val event = Event.Timeout(clock.time + timeMillis, nextId++, block)
+ schedule(event)
+ return event
+ }
+ }
+
+ private val exceptionHandler = CoroutineExceptionHandler { _, exception ->
+ log.error("Uncaught exception", exception)
+ }
+
+ // SimulationContext
+ override val key: CoroutineContext.Key<*> = SimulationContext.Key
+
+ override val domain: Domain = this
+
+ override val clock: VirtualClock
+ get() = this@OmegaSimulationEngine.clock
+
+ override val log: Logger by lazy(LazyThreadSafetyMode.NONE) { LoggerImpl.invoke(this) }
+
+ override fun newDomain(): Domain = this@OmegaSimulationEngine.newDomain(this)
+
+ override fun newDomain(name: String): Domain = this@OmegaSimulationEngine.newDomain(name, this)
+
+ // Domain
+ override val parent: Domain = parent ?: this
+
+ @InternalCoroutinesApi
+ override val coroutineContext: CoroutineContext = this + CoroutineName(name) + dispatcher + job + exceptionHandler
+
+ override fun toString(): String = path
+ }
+
+ /**
+ * Enumeration to track the state of the actor system.
+ */
+ private enum class SimulationEngineState {
+ CREATED, STARTED, TERMINATED
+ }
+
+ /**
+ * A wrapper around a message that has been scheduled for processing.
+ *
+ * @property time The point in time to deliver the message.
+ */
+ private sealed class Event(val time: Long, val id: Long) {
+ class Dispatch(time: Long, id: Long, val block: Runnable) : Event(time, id) {
+ override fun toString(): String = "Dispatch[$time]"
+ }
+
+ class Resume(time: Long, id: Long, val dispatcher: CoroutineDispatcher, val continuation: CancellableContinuation<Unit>) : Event(time, id) {
+ override fun toString(): String = "Resume[$time]"
+ }
+
+ class Timeout(time: Long, id: Long, val block: Runnable, var isCancelled: Boolean = false) : Event(time, id), DisposableHandle {
+ override fun dispose() {
+ isCancelled = true
+ }
+
+ override fun toString(): String = "Timeout[$time]"
+ }
+ }
+
+ /**
+ * A virtual [Clock] implementation for keeping track of simulation time.
+ */
+ private data class VirtualClock(var time: Long) : Clock() {
+ override fun withZone(zone: ZoneId?): Clock = throw NotImplementedError()
+
+ override fun getZone(): ZoneId = ZoneId.systemDefault()
+
+ override fun instant(): Instant = Instant.ofEpochMilli(time)
+
+ override fun millis(): Long = time
+ }
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt
new file mode 100644
index 00000000..5dba3233
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaSimulationEngineProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2018 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.engine.omega
+
+import com.atlarge.odcsim.SimulationEngine
+import com.atlarge.odcsim.SimulationEngineProvider
+import java.util.ServiceLoader
+
+/**
+ * An [SimulationEngineProvider] for the Omega engine, used by the [ServiceLoader] API to create
+ * [OmegaSimulationEngine] instances.
+ */
+public class OmegaSimulationEngineProvider : SimulationEngineProvider {
+ override operator fun invoke(name: String): SimulationEngine = OmegaSimulationEngine(name)
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt
new file mode 100644
index 00000000..fca4826e
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationAwareLoggerImpl.kt
@@ -0,0 +1,567 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.engine.omega.logging
+
+import com.atlarge.odcsim.SimulationContext
+import org.slf4j.Logger
+import org.slf4j.Marker
+import org.slf4j.helpers.MessageFormatter
+import org.slf4j.spi.LocationAwareLogger
+
+/**
+ * An actor-specific [Logger] implementation that is aware of the calling location.
+ *
+ * @param ctx The owning [SimulationContext] of this logger.
+ * @param delegate The [LocationAwareLogger] to delegate the messages to.
+ */
+internal class LocationAwareLoggerImpl(
+ ctx: SimulationContext,
+ private val delegate: LocationAwareLogger
+) : LoggerImpl(ctx), Logger by delegate {
+ /**
+ * The fully qualified name of this class.
+ */
+ private val fqcn = LocationAwareLoggerImpl::class.java.name
+
+ override fun trace(format: String?, arg: Any?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun trace(format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun trace(format: String?, argArray: Array<Any?>) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun trace(msg: String?, t: Throwable?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t)
+ }
+ }
+
+ override fun trace(marker: Marker?, msg: String?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null)
+ }
+ }
+
+ override fun trace(marker: Marker?, format: String?, arg: Any?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun trace(marker: Marker?, format: String?, argArray: Array<Any?>) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun trace(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!delegate.isTraceEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t)
+ }
+ }
+
+ override fun debug(msg: String?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null)
+ }
+ }
+
+ override fun debug(format: String?, arg: Any?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun debug(format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun debug(format: String?, argArray: Array<Any?>) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val ft = MessageFormatter.arrayFormat(format, argArray)
+ delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable)
+ }
+ }
+
+ override fun debug(msg: String?, t: Throwable?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t)
+ }
+ }
+
+ override fun debug(marker: Marker?, msg: String?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null)
+ }
+ }
+
+ override fun debug(marker: Marker?, format: String?, arg: Any?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val ft = MessageFormatter.format(format, arg)
+ delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable)
+ }
+ }
+
+ override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun debug(marker: Marker?, format: String?, argArray: Array<Any?>) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ val ft = MessageFormatter.arrayFormat(format, argArray)
+ delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, argArray, ft.throwable)
+ }
+ }
+
+ override fun debug(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!delegate.isDebugEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t)
+ }
+ }
+
+ override fun info(msg: String?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null)
+ }
+ }
+
+ override fun info(format: String?, arg: Any?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun info(format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun info(format: String?, argArray: Array<Any?>) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun info(msg: String?, t: Throwable?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, t)
+ }
+ }
+
+ override fun info(marker: Marker?, msg: String?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, null)
+ }
+ }
+
+ override fun info(marker: Marker?, format: String?, arg: Any?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun info(marker: Marker?, format: String?, argArray: Array<Any?>) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun info(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!delegate.isInfoEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, t)
+ }
+ }
+
+ override fun warn(msg: String?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null)
+ }
+ }
+
+ override fun warn(format: String?, arg: Any?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun warn(format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun warn(format: String?, argArray: Array<Any?>) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun warn(msg: String?, t: Throwable?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, t)
+ }
+ }
+
+ override fun warn(marker: Marker?, msg: String?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, null)
+ }
+ }
+
+ override fun warn(marker: Marker?, format: String?, arg: Any?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun warn(marker: Marker?, format: String?, argArray: Array<Any?>) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun warn(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!delegate.isWarnEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, t)
+ }
+ }
+
+ override fun error(msg: String?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null)
+ }
+ }
+
+ override fun error(format: String?, arg: Any?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun error(format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun error(format: String?, argArray: Array<Any?>) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun error(msg: String?, t: Throwable?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t)
+ }
+ }
+
+ override fun error(marker: Marker?, msg: String?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null)
+ }
+ }
+
+ override fun error(marker: Marker?, format: String?, arg: Any?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg).message
+ delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null)
+ }
+ }
+
+ override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.format(format, arg1, arg2).message
+ delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null)
+ }
+ }
+
+ override fun error(marker: Marker?, format: String?, argArray: Array<Any?>) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message
+ delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null)
+ }
+ }
+
+ override fun error(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!delegate.isErrorEnabled) {
+ return
+ }
+
+ withMdc {
+ delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t)
+ }
+ }
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt
new file mode 100644
index 00000000..856cecfa
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LocationIgnorantLoggerImpl.kt
@@ -0,0 +1,440 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.engine.omega.logging
+
+import com.atlarge.odcsim.SimulationContext
+import org.slf4j.Logger
+import org.slf4j.Marker
+
+/**
+ * A [Logger] implementation that is not aware of the calling location.
+ *
+ * @param ctx The owning [SimulationContext] of this logger.
+ * @param delegate The [Logger] to delegate the messages to.
+ */
+internal class LocationIgnorantLoggerImpl(
+ ctx: SimulationContext,
+ private val delegate: Logger
+) : LoggerImpl(ctx), Logger by delegate {
+ override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(marker, format, arg1, arg2) }
+ }
+
+ override fun warn(format: String?, arg1: Any?, arg2: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(format, arg1, arg2) }
+ }
+
+ override fun warn(msg: String?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(msg) }
+ }
+
+ override fun warn(marker: Marker?, format: String?, arg: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(marker, format, arg) }
+ }
+
+ override fun warn(marker: Marker?, format: String?, vararg arguments: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(marker, format, arguments) }
+ }
+
+ override fun warn(format: String?, arg: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(format, arg) }
+ }
+
+ override fun warn(marker: Marker?, msg: String?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(marker, msg) }
+ }
+
+ override fun warn(msg: String?, t: Throwable?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(msg, t) }
+ }
+
+ override fun warn(format: String?, vararg arguments: Any?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(format, *arguments) }
+ }
+
+ override fun warn(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!isWarnEnabled) {
+ return
+ }
+
+ withMdc { delegate.warn(marker, msg, t) }
+ }
+
+ override fun info(marker: Marker?, format: String?, vararg arguments: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(marker, format, *arguments) }
+ }
+
+ override fun info(format: String?, arg: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(format, arg) }
+ }
+
+ override fun info(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(marker, msg, t) }
+ }
+
+ override fun info(msg: String?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(msg) }
+ }
+
+ override fun info(format: String?, vararg arguments: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(format, *arguments) }
+ }
+
+ override fun info(format: String?, arg1: Any?, arg2: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(format, arg1, arg2) }
+ }
+
+ override fun info(marker: Marker?, msg: String?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(marker, msg) }
+ }
+
+ override fun info(marker: Marker?, format: String?, arg: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(marker, format, arg) }
+ }
+
+ override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(marker, format, arg1, arg2) }
+ }
+
+ override fun info(msg: String?, t: Throwable?) {
+ if (!isInfoEnabled) {
+ return
+ }
+
+ withMdc { delegate.info(msg, t) }
+ }
+
+ override fun error(msg: String?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(msg) }
+ }
+
+ override fun error(marker: Marker?, msg: String?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(marker, msg) }
+ }
+
+ override fun error(format: String?, vararg arguments: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(format, *arguments) }
+ }
+
+ override fun error(format: String?, arg: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(format, arg) }
+ }
+
+ override fun error(msg: String?, t: Throwable?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(msg, t) }
+ }
+
+ override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(marker, format, arg1, arg2) }
+ }
+
+ override fun error(marker: Marker?, format: String?, vararg arguments: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(marker, format, *arguments) }
+ }
+
+ override fun error(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(marker, msg, t) }
+ }
+
+ override fun error(format: String?, arg1: Any?, arg2: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(format, arg1, arg2) }
+ }
+
+ override fun error(marker: Marker?, format: String?, arg: Any?) {
+ if (!isErrorEnabled) {
+ return
+ }
+
+ withMdc { delegate.error(marker, format, arg) }
+ }
+
+ override fun debug(format: String?, vararg arguments: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(format, *arguments) }
+ }
+
+ override fun debug(format: String?, arg1: Any?, arg2: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(format, arg1, arg2) }
+ }
+
+ override fun debug(msg: String?, t: Throwable?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(msg, t) }
+ }
+
+ override fun debug(format: String?, arg: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(format, arg) }
+ }
+
+ override fun debug(marker: Marker?, msg: String?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(marker, msg) }
+ }
+
+ override fun debug(msg: String?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(msg) }
+ }
+
+ override fun debug(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(marker, msg, t) }
+ }
+
+ override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(marker, format, arg1, arg2) }
+ }
+
+ override fun debug(marker: Marker?, format: String?, arg: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(marker, format, arg) }
+ }
+
+ override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) {
+ if (!isDebugEnabled) {
+ return
+ }
+
+ withMdc { delegate.debug(marker, format, *arguments) }
+ }
+
+ override fun trace(format: String?, arg: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(format, arg) }
+ }
+
+ override fun trace(marker: Marker?, msg: String?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(marker, msg) }
+ }
+
+ override fun trace(msg: String?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(msg) }
+ }
+
+ override fun trace(msg: String?, t: Throwable?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(msg, t) }
+ }
+
+ override fun trace(format: String?, arg1: Any?, arg2: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(format, arg1, arg2) }
+ }
+
+ override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(marker, format, arg1, arg2) }
+ }
+
+ override fun trace(marker: Marker?, format: String?, arg: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(marker, format, arg) }
+ }
+
+ override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(marker, format, *argArray) }
+ }
+
+ override fun trace(marker: Marker?, msg: String?, t: Throwable?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(marker, msg, t) }
+ }
+
+ override fun trace(format: String?, vararg arguments: Any?) {
+ if (!isTraceEnabled) {
+ return
+ }
+
+ withMdc { delegate.trace(format, *arguments) }
+ }
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt
new file mode 100644
index 00000000..1adcfdc0
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/logging/LoggerImpl.kt
@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.atlarge.odcsim.engine.omega.logging
+
+import com.atlarge.odcsim.SimulationContext
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.slf4j.MDC
+import org.slf4j.spi.LocationAwareLogger
+
+/**
+ * An actor-specific [Logger] implementation.
+ *
+ * @param ctx The owning [SimulationContext] of this logger.
+ */
+internal abstract class LoggerImpl internal constructor(protected val ctx: SimulationContext) : Logger {
+ /**
+ * Configure [MDC] with actor-specific information.
+ */
+ protected inline fun withMdc(block: () -> Unit) {
+ MDC.put(MDC_PROCESS_REF, ctx.domain.name)
+ MDC.put(MDC_PROCESS_TIME, String.format("%d", ctx.clock.millis()))
+ try {
+ block()
+ } finally {
+ MDC.remove(MDC_PROCESS_TIME)
+ MDC.remove(MDC_PROCESS_REF)
+ }
+ }
+
+ /**
+ * Mapped Diagnostic Context (MDC) attribute names.
+ */
+ companion object {
+ val MDC_PROCESS_TIME = "process.time"
+ val MDC_PROCESS_REF = "process.ref"
+
+ /**
+ * Create a [Logger] for the specified [ActorContext].
+ *
+ * @param ctx The actor context to create the logger for.
+ */
+ operator fun invoke(ctx: SimulationContext): Logger {
+ val logger = LoggerFactory.getLogger(ctx.javaClass)
+ return if (logger is LocationAwareLogger) {
+ LocationAwareLoggerImpl(ctx, logger)
+ } else {
+ LocationIgnorantLoggerImpl(ctx, logger)
+ }
+ }
+ }
+}
diff --git a/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider b/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider
new file mode 100644
index 00000000..1131cebd
--- /dev/null
+++ b/simulator/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.SimulationEngineProvider
@@ -0,0 +1 @@
+com.atlarge.odcsim.engine.omega.OmegaSimulationEngineProvider