Generalized Contexts for multiplatform
This commit is contained in:
parent
83a56fa0cd
commit
ec833dd13c
@ -1,8 +1,8 @@
|
||||
buildscript {
|
||||
extra["kotlinVersion"] = "1.3.11"
|
||||
extra["ioVersion"] = "0.1.2-dev-2"
|
||||
extra["ioVersion"] = "0.1.2"
|
||||
extra["serializationVersion"] = "0.9.1"
|
||||
extra["coroutinesVersion"] = "1.0.1"
|
||||
extra["coroutinesVersion"] = "1.1.0"
|
||||
|
||||
val kotlinVersion: String by extra
|
||||
val ioVersion: String by extra
|
||||
|
@ -9,7 +9,7 @@ repositories {
|
||||
kotlin {
|
||||
targets {
|
||||
fromPreset(presets.jvm, 'jvm')
|
||||
//fromPreset(presets.js, 'js')
|
||||
fromPreset(presets.js, 'js')
|
||||
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
|
||||
// For Linux, preset should be changed to e.g. presets.linuxX64
|
||||
// For MacOS, preset should be changed to e.g. presets.macosX64
|
||||
@ -21,11 +21,19 @@ kotlin {
|
||||
api project(":dataforge-meta")
|
||||
api "org.jetbrains.kotlin:kotlin-reflect"
|
||||
api "io.github.microutils:kotlin-logging-common:1.6.10"
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutinesVersion"
|
||||
}
|
||||
}
|
||||
jvmMain{
|
||||
dependencies{
|
||||
api "io.github.microutils:kotlin-logging:1.6.10"
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
||||
}
|
||||
}
|
||||
jsMain{
|
||||
dependencies{
|
||||
api "io.github.microutils:kotlin-logging-js:1.6.10"
|
||||
api "org.jetbrains.kotlinx:kotlinx-coroutines-core-js:$coroutinesVersion"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package hep.dataforge.context
|
||||
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.Name
|
||||
|
||||
abstract class AbstractPlugin(override val meta: Meta = EmptyMeta) : Plugin {
|
||||
|
||||
private var _context: Context? = null
|
||||
|
||||
override val context: Context
|
||||
get() = _context ?: error("Plugin $tag is not attached")
|
||||
|
||||
override fun attach(context: Context) {
|
||||
this._context = context
|
||||
}
|
||||
|
||||
override fun detach() {
|
||||
this._context = null
|
||||
}
|
||||
|
||||
override fun provideTop(target: String, name: Name): Any? = null
|
||||
|
||||
override fun listTop(target: String): Sequence<Name> = emptySequence()
|
||||
}
|
@ -6,8 +6,11 @@ import hep.dataforge.names.toName
|
||||
import hep.dataforge.provider.Provider
|
||||
import hep.dataforge.provider.provideAll
|
||||
import hep.dataforge.values.Value
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@ -22,12 +25,12 @@ import kotlin.reflect.KClass
|
||||
* Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context.
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
interface Context : Named, MetaRepr, Provider {
|
||||
interface Context : Named, MetaRepr, Provider, CoroutineScope {
|
||||
|
||||
val parent: Context?
|
||||
|
||||
/**
|
||||
* Context properties. Working as substitutes for environment variables
|
||||
* Context properties. Working as substitute for environment variables
|
||||
*/
|
||||
val properties: Meta
|
||||
|
||||
@ -46,11 +49,6 @@ interface Context : Named, MetaRepr, Provider {
|
||||
*/
|
||||
val isActive: Boolean
|
||||
|
||||
/**
|
||||
* Provide services for given type
|
||||
*/
|
||||
fun <T : Any> services(type: KClass<T>): Sequence<T>
|
||||
|
||||
override val defaultTarget: String get() = Plugin.PLUGIN_TARGET
|
||||
|
||||
override fun provideTop(target: String, name: Name): Any? {
|
||||
@ -79,10 +77,19 @@ interface Context : Named, MetaRepr, Provider {
|
||||
*/
|
||||
fun deactivate(activator: Any)
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.Default
|
||||
|
||||
/**
|
||||
* Detach all plugins and terminate context
|
||||
*/
|
||||
fun close()
|
||||
|
||||
override fun toMeta(): Meta = buildMeta {
|
||||
"parent" to parent?.name
|
||||
"properties" to properties.seal()
|
||||
"plugins" to plugins.map { it.toMeta() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,7 +102,11 @@ inline fun <reified T : Any> Context.list(target: String): Sequence<T> {
|
||||
/**
|
||||
* A global root context
|
||||
*/
|
||||
expect object Global : Context
|
||||
expect object Global : Context{
|
||||
fun getContext(name: String): Context
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The interface for something that encapsulated in context
|
||||
|
@ -46,7 +46,7 @@ interface Plugin : Named, Metoid, ContextAware, Provider, MetaRepr {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
fun dependsOn(): List<PluginTag>
|
||||
fun dependsOn(): List<PluginTag> = emptyList()
|
||||
|
||||
/**
|
||||
* Start this plugin and attach registration info to the context. This method
|
||||
|
@ -10,16 +10,17 @@ interface PluginFactory {
|
||||
}
|
||||
|
||||
|
||||
object PluginRepository {
|
||||
expect object PluginRepository {
|
||||
|
||||
/**
|
||||
* List plugins available in the repository
|
||||
*/
|
||||
fun list(): Sequence<PluginFactory> = Global.services(PluginFactory::class)
|
||||
fun list(): Sequence<PluginFactory>
|
||||
|
||||
/**
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch specific plugin and instantiate it with given meta
|
||||
*/
|
||||
fun fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta)
|
||||
fun PluginRepository.fetch(tag: PluginTag, meta: Meta): Plugin = PluginRepository.list().find { it.tag.matches(tag) }?.build(meta)
|
||||
?: error("Plugin with tag $tag not found in the repository")
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package hep.dataforge.context
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
import kotlin.jvm.Synchronized
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
actual object Global: Context, JSContext("GLOBAL", null){
|
||||
/**
|
||||
* Closing all contexts
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
override fun close() {
|
||||
logger.info{"Shutting down GLOBAL"}
|
||||
for (ctx in contextRegistry.values) {
|
||||
ctx.close()
|
||||
}
|
||||
super.close()
|
||||
}
|
||||
|
||||
private val contextRegistry = HashMap<String, Context>()
|
||||
|
||||
/**
|
||||
* Get previously builder context o builder a new one
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
@Synchronized
|
||||
actual fun getContext(name: String): Context {
|
||||
return contextRegistry.getOrPut(name) { JSContext(name) }
|
||||
}
|
||||
}
|
||||
|
||||
open class JSContext(
|
||||
final override val name: String,
|
||||
final override val parent: JSContext? = Global,
|
||||
properties: Meta = EmptyMeta
|
||||
): Context {
|
||||
|
||||
private val _properties = Config().apply { update(properties) }
|
||||
override val properties: Meta
|
||||
get() = if (parent == null) {
|
||||
_properties
|
||||
} else {
|
||||
Laminate(_properties, parent.properties)
|
||||
}
|
||||
|
||||
override val plugins: PluginManager by lazy { PluginManager(this) }
|
||||
override val logger: KLogger = KotlinLogging.logger(name)
|
||||
|
||||
/**
|
||||
* A property showing that dispatch thread is started in the context
|
||||
*/
|
||||
private var started = false
|
||||
|
||||
override fun <T : Any> services(type: KClass<T>): Sequence<T> = TODO("Not implemented")
|
||||
|
||||
/**
|
||||
* Free up resources associated with this context
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
override fun close() {
|
||||
if (isActive) error("Can't close active context")
|
||||
//detach all plugins
|
||||
plugins.forEach { it.detach() }
|
||||
}
|
||||
|
||||
private val activators = HashSet<Any>()
|
||||
|
||||
override val isActive: Boolean = !activators.isEmpty()
|
||||
|
||||
override fun activate(activator: Any) {
|
||||
activators.add(activator)
|
||||
}
|
||||
|
||||
override fun deactivate(activator: Any) {
|
||||
activators.clear()
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package hep.dataforge.context
|
||||
|
||||
actual object PluginRepository {
|
||||
/**
|
||||
* List plugins available in the repository
|
||||
*/
|
||||
actual fun list(): Sequence<PluginFactory> {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
}
|
@ -42,7 +42,6 @@ actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread(
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
override fun close() {
|
||||
logger.info("Shutting down GLOBAL")
|
||||
for (ctx in contextRegistry.values) {
|
||||
@ -60,20 +59,7 @@ actual object Global : Context, JVMContext("GLOBAL", null, Thread.currentThread(
|
||||
* @return
|
||||
*/
|
||||
@Synchronized
|
||||
fun getContext(name: String): Context {
|
||||
actual fun getContext(name: String): Context {
|
||||
return contextRegistry.getOrPut(name) { JVMContext(name) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all contexts and terminate framework
|
||||
*/
|
||||
@JvmStatic
|
||||
fun terminate() {
|
||||
try {
|
||||
close()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Exception while terminating DataForge framework", e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package hep.dataforge.context
|
||||
|
||||
import hep.dataforge.meta.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import mu.KLogger
|
||||
import mu.KotlinLogging
|
||||
import java.lang.ref.WeakReference
|
||||
@ -23,6 +24,7 @@ import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.cast
|
||||
|
||||
@ -72,23 +74,10 @@ open class JVMContext(
|
||||
|
||||
private val serviceCache: MutableMap<Class<*>, ServiceLoader<*>> = HashMap()
|
||||
|
||||
override fun <T : Any> services(type: KClass<T>): Sequence<T> {
|
||||
fun <T : Any> services(type: KClass<T>): Sequence<T> {
|
||||
return serviceCache.getOrPut(type.java) { ServiceLoader.load(type.java, classLoader) }.asSequence().map { type.cast(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get identity for this context
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override fun toMeta(): Meta {
|
||||
return buildMeta {
|
||||
"parent" to parent?.name
|
||||
"properties" to properties.seal()
|
||||
"plugins" to plugins.map { it.toMeta() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free up resources associated with this context
|
||||
*
|
||||
|
@ -0,0 +1,9 @@
|
||||
package hep.dataforge.context
|
||||
|
||||
actual object PluginRepository {
|
||||
/**
|
||||
* List plugins available in the repository
|
||||
*/
|
||||
actual fun list(): Sequence<PluginFactory> = Global.services(PluginFactory::class)
|
||||
|
||||
}
|
@ -9,7 +9,7 @@ repositories {
|
||||
kotlin {
|
||||
targets {
|
||||
fromPreset(presets.jvm, 'jvm')
|
||||
fromPreset(presets.js, 'js')
|
||||
//fromPreset(presets.js, 'js')
|
||||
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
|
||||
// For Linux, preset should be changed to e.g. presets.linuxX64
|
||||
// For MacOS, preset should be changed to e.g. presets.macosX64
|
||||
@ -28,11 +28,5 @@ kotlin {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
jsMain {
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,27 @@
|
||||
package hep.dataforge.io
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.context.ContextAware
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A generic way to render any object in the output.
|
||||
*
|
||||
* An object could be rendered either in append or overlay mode. The mode is decided by the [Output]
|
||||
* based on its configuration and provided meta
|
||||
*
|
||||
*/
|
||||
interface Output<in T : Any> : ContextAware {
|
||||
/**
|
||||
* Render specific object with configuration.
|
||||
*
|
||||
* By convention actual render is called in asynchronous mode, so this method should never
|
||||
* block execution
|
||||
*/
|
||||
interface Output<in T: Any> : ContextAware {
|
||||
fun render(obj: T, meta: Meta = EmptyMeta)
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package hep.dataforge.io
|
||||
|
||||
import hep.dataforge.context.Plugin
|
||||
import hep.dataforge.meta.EmptyMeta
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.names.EmptyName
|
||||
import hep.dataforge.names.Name
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* A manager for outputs
|
||||
*/
|
||||
interface OutputManager : Plugin {
|
||||
/**
|
||||
* Provide an output for given name and stage.
|
||||
*
|
||||
* @param stage represents the node or directory for the output. Empty means root node.
|
||||
* @param name represents the name inside the node.
|
||||
* @param meta configuration for [Output] (not for rendered object)
|
||||
*
|
||||
*/
|
||||
operator fun get(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<Any>
|
||||
|
||||
/**
|
||||
* Get an output specialized for giver ntype
|
||||
*/
|
||||
fun <T : Any> typed(type: KClass<T>, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<T>
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> OutputManager.typed(name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta): Output<T> {
|
||||
return typed(T::class, name, stage, meta)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package hep.dataforge.io
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.meta.Meta
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class TextOutput(override val context: Context, private val output: kotlinx.io.core.Output) : Output<Any> {
|
||||
override fun render(obj: Any, meta: Meta) {
|
||||
context.launch(Dispatchers.IO) {
|
||||
output.append(obj.toString())
|
||||
output.append('\n')
|
||||
}
|
||||
}
|
||||
}
|
@ -102,3 +102,5 @@ operator fun Name.plus(other: Name): Name = Name(this.tokens + other.tokens)
|
||||
operator fun Name.plus(other: String): Name = this + other.toName()
|
||||
|
||||
fun NameToken.toName() = Name(listOf(this))
|
||||
|
||||
val EmptyName = Name(emptyList())
|
Loading…
Reference in New Issue
Block a user