Isolate and deprecate Context plugin mutation logic.

This commit is contained in:
Alexander Nozik 2021-04-06 15:12:40 +03:00
parent 9fbc482549
commit f1f5f7a70c
18 changed files with 165 additions and 105 deletions

View File

@ -4,12 +4,16 @@
### Added ### Added
- LogManager plugin - LogManager plugin
- dataforge-context API dependency on SLF4j - dataforge-context API dependency on SLF4j
- Context `withEnv` and `fetch` methods to manipulate plugins without changing plugins after creation.
### Changed ### Changed
- Kotlin-logging moved from common to JVM and JS. Replaced by console for native. - Kotlin-logging moved from common to JVM and JS. Replaced by console for native.
- Package changed to `space.kscience` - Package changed to `space.kscience`
- Scheme made observable
- Global context is a variable (the singleton is hidden and will be deprecated in future)
### Deprecated ### Deprecated
- Direct use of PluginManager
### Removed ### Removed
- Common dependency on Kotlin-logging - Common dependency on Kotlin-logging

View File

@ -38,6 +38,7 @@ public final class space/kscience/dataforge/context/ClassLoaderPluginKt {
public class space/kscience/dataforge/context/Context : kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/misc/Named, space/kscience/dataforge/provider/Provider { public class space/kscience/dataforge/context/Context : kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/meta/MetaRepr, space/kscience/dataforge/misc/Named, space/kscience/dataforge/provider/Provider {
public static final field Companion Lspace/kscience/dataforge/context/Context$Companion; public static final field Companion Lspace/kscience/dataforge/context/Context$Companion;
public static final field PROPERTY_TARGET Ljava/lang/String; public static final field PROPERTY_TARGET Ljava/lang/String;
public final fun buildContext (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public fun close ()V public fun close ()V
public fun content (Ljava/lang/String;)Ljava/util/Map; public fun content (Ljava/lang/String;)Ljava/util/Map;
public final fun content (Ljava/lang/String;Z)Ljava/util/Map; public final fun content (Ljava/lang/String;Z)Ljava/util/Map;
@ -47,6 +48,7 @@ public class space/kscience/dataforge/context/Context : kotlinx/coroutines/Corou
public final fun getName ()Lspace/kscience/dataforge/names/Name; public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun getParent ()Lspace/kscience/dataforge/context/Context; public final fun getParent ()Lspace/kscience/dataforge/context/Context;
public final fun getPlugins ()Lspace/kscience/dataforge/context/PluginManager; public final fun getPlugins ()Lspace/kscience/dataforge/context/PluginManager;
public final fun getProperties ()Lspace/kscience/dataforge/meta/Laminate;
public fun toMeta ()Lspace/kscience/dataforge/meta/Meta; public fun toMeta ()Lspace/kscience/dataforge/meta/Meta;
} }
@ -58,24 +60,35 @@ public abstract interface class space/kscience/dataforge/context/ContextAware {
} }
public final class space/kscience/dataforge/context/ContextBuilder { public final class space/kscience/dataforge/context/ContextBuilder {
public fun <init> ()V
public fun <init> (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;)V
public synthetic fun <init> (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun build ()Lspace/kscience/dataforge/context/Context; public final fun build ()Lspace/kscience/dataforge/context/Context;
public final fun getName ()Ljava/lang/String; public final fun getName ()Lspace/kscience/dataforge/names/Name;
public final fun name (Ljava/lang/String;)V
public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginFactory;Lspace/kscience/dataforge/meta/Meta;)V
public final fun plugin (Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V public final fun plugin (Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun plugin$default (Lspace/kscience/dataforge/context/ContextBuilder;Lspace/kscience/dataforge/context/PluginTag;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun properties (Lkotlin/jvm/functions/Function1;)V public final fun properties (Lkotlin/jvm/functions/Function1;)V
public final fun setName (Ljava/lang/String;)V public final fun setName (Lspace/kscience/dataforge/names/Name;)V
} }
public final class space/kscience/dataforge/context/ContextKt { public final class space/kscience/dataforge/context/ContextBuilderKt {
public static final fun Context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context; }
public static synthetic fun Context$default (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
public final class space/kscience/dataforge/context/DefaultLogManager : space/kscience/dataforge/context/AbstractPlugin, space/kscience/dataforge/context/LogManager {
public static final field Companion Lspace/kscience/dataforge/context/DefaultLogManager$Companion;
public fun <init> ()V
public fun getTag ()Lspace/kscience/dataforge/context/PluginTag;
public fun log (Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V
}
public final class space/kscience/dataforge/context/DefaultLogManager$Companion : space/kscience/dataforge/context/PluginFactory {
public fun getTag ()Lspace/kscience/dataforge/context/PluginTag;
public fun getType ()Lkotlin/reflect/KClass;
public synthetic fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Ljava/lang/Object;
public fun invoke (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/DefaultLogManager;
} }
public abstract interface class space/kscience/dataforge/context/Factory { public abstract interface class space/kscience/dataforge/context/Factory {
@ -88,12 +101,13 @@ public final class space/kscience/dataforge/context/Factory$DefaultImpls {
public final class space/kscience/dataforge/context/Global : space/kscience/dataforge/context/Context { public final class space/kscience/dataforge/context/Global : space/kscience/dataforge/context/Context {
public static final field INSTANCE Lspace/kscience/dataforge/context/Global; public static final field INSTANCE Lspace/kscience/dataforge/context/Global;
public fun close ()V
public final fun context (Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public static synthetic fun context$default (Lspace/kscience/dataforge/context/Global;Ljava/lang/String;Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
public final fun getContext (Ljava/lang/String;)Lspace/kscience/dataforge/context/Context;
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public final fun getLogger ()Lspace/kscience/dataforge/context/LogManager; public final fun getDefaultLogger ()Lspace/kscience/dataforge/context/LogManager;
}
public final class space/kscience/dataforge/context/GlobalKt {
public static final fun Context (Lkotlin/jvm/functions/Function1;)Lspace/kscience/dataforge/context/Context;
public static synthetic fun Context$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lspace/kscience/dataforge/context/Context;
} }
public abstract interface class space/kscience/dataforge/context/LogManager : space/kscience/dataforge/context/Logable, space/kscience/dataforge/context/Plugin { public abstract interface class space/kscience/dataforge/context/LogManager : space/kscience/dataforge/context/Logable, space/kscience/dataforge/context/Plugin {
@ -128,7 +142,7 @@ public final class space/kscience/dataforge/context/LogManagerKt {
public static final fun error (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V public static final fun error (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V
public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static synthetic fun error$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/LogManager; public static final fun getLogger (Lspace/kscience/dataforge/context/Context;)Lspace/kscience/dataforge/context/Logable;
public static final fun getLogger (Lspace/kscience/dataforge/context/ContextAware;)Lspace/kscience/dataforge/context/Logable; public static final fun getLogger (Lspace/kscience/dataforge/context/ContextAware;)Lspace/kscience/dataforge/context/Logable;
public static final fun info (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V public static final fun info (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;)V
public static synthetic fun info$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V public static synthetic fun info$default (Lspace/kscience/dataforge/context/Logable;Lspace/kscience/dataforge/names/Name;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V

View File

@ -11,6 +11,7 @@ import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.provider.Provider import space.kscience.dataforge.provider.Provider
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.jvm.Synchronized
/** /**
* The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top. * The local environment for anything being done in DataForge framework. Contexts are organized into tree structure with [Global] at the top.
@ -31,12 +32,13 @@ public open class Context internal constructor(
/** /**
* Context properties. Working as substitute for environment variables * Context properties. Working as substitute for environment variables
*/ */
private val properties: Laminate = if (parent == null) { public val properties: Laminate = if (parent == null) {
Laminate(meta) Laminate(meta)
} else { } else {
Laminate(meta, parent.properties) Laminate(meta, parent.properties)
} }
/** /**
* A [PluginManager] for current context * A [PluginManager] for current context
*/ */
@ -68,10 +70,24 @@ public open class Context internal constructor(
} }
} }
private val childrenContexts = HashMap<Name, Context>()
/** /**
* Detach all plugins and terminate context * Build and register a child context
*/
@Synchronized
public fun buildContext(block: ContextBuilder.() -> Unit): Context{
val newContext = ContextBuilder(this).apply(block).build()
childrenContexts[newContext.name] = newContext
return newContext
}
/**
* Detach all plugins, and close child contexts
*/ */
public open fun close() { public open fun close() {
//recursively closed child context
childrenContexts.forEach { it.value.close() }
//detach all plugins //detach all plugins
plugins.forEach { it.detach() } plugins.forEach { it.detach() }
} }
@ -87,9 +103,6 @@ public open class Context internal constructor(
} }
} }
public fun Context(name: String, parent: Context = Global, block: ContextBuilder.() -> Unit = {}): Context =
Global.context(name, parent, block)
/** /**
* The interface for something that encapsulated in context * The interface for something that encapsulated in context
* *

View File

@ -3,8 +3,11 @@ package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaBuilder import space.kscience.dataforge.meta.MetaBuilder
import space.kscience.dataforge.meta.seal import space.kscience.dataforge.meta.seal
import space.kscience.dataforge.meta.toMutableMeta
import space.kscience.dataforge.misc.DFBuilder import space.kscience.dataforge.misc.DFBuilder
import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.toName
import kotlin.collections.component1 import kotlin.collections.component1
import kotlin.collections.component2 import kotlin.collections.component2
@ -14,14 +17,22 @@ import kotlin.collections.set
* A convenience builder for context * A convenience builder for context
*/ */
@DFBuilder @DFBuilder
public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") { public class ContextBuilder internal constructor(
private val factories = HashMap<PluginFactory<*>, Meta>() private val parent: Context,
private var meta = MetaBuilder() public var name: Name? = null,
meta: Meta = Meta.EMPTY,
) {
internal val factories = HashMap<PluginFactory<*>, Meta>()
internal var meta = meta.toMutableMeta()
public fun properties(action: MetaBuilder.() -> Unit) { public fun properties(action: MetaBuilder.() -> Unit) {
meta.action() meta.action()
} }
public fun name(string: String){
this.name = string.toName()
}
@OptIn(DFExperimental::class) @OptIn(DFExperimental::class)
private fun findPluginFactory(tag: PluginTag): PluginFactory<*> = private fun findPluginFactory(tag: PluginTag): PluginFactory<*> =
parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
@ -32,6 +43,10 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
factories[factory] = Meta(metaBuilder) factories[factory] = Meta(metaBuilder)
} }
public fun plugin(factory: PluginFactory<*>, meta: Meta){
factories[factory] = meta
}
public fun plugin(factory: PluginFactory<*>, metaBuilder: MetaBuilder.() -> Unit = {}) { public fun plugin(factory: PluginFactory<*>, metaBuilder: MetaBuilder.() -> Unit = {}) {
factories[factory] = Meta(metaBuilder) factories[factory] = Meta(metaBuilder)
} }
@ -41,10 +56,29 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
} }
public fun build(): Context { public fun build(): Context {
return Context(name.toName(), parent, meta.seal()).apply { val contextName = name ?: "@auto[${hashCode().toUInt().toString(16)}]".toName()
return Context(contextName, parent, meta.seal()).apply {
factories.forEach { (factory, meta) -> factories.forEach { (factory, meta) ->
plugins.load(factory, meta) plugins.load(factory, meta)
} }
} }
} }
} }
/**
* Check if current context contains all plugins required by the builder and return it it does or forks to a new context
* if it does not.
*/
public fun Context.withEnv(block: ContextBuilder.() -> Unit): Context {
fun Context.contains(factory: PluginFactory<*>, meta: Meta): Boolean {
val loaded = plugins[factory.tag] ?: return false
return loaded.meta == meta
}
val builder = ContextBuilder(this, name + "env", properties).apply(block)
val requiresFork = builder.factories.any { (factory, meta) ->
!contains(factory, meta)
} || ((properties as Meta) == builder.meta)
return if (requiresFork) builder.build() else this
}

View File

@ -1,7 +1,7 @@
package space.kscience.dataforge.context package space.kscience.dataforge.context
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.Job
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -13,43 +13,10 @@ internal expect val globalLoggerFactory: PluginFactory<out LogManager>
* A global root context. Closing [Global] terminates the framework. * A global root context. Closing [Global] terminates the framework.
*/ */
@ThreadLocal @ThreadLocal
public object Global : Context("GLOBAL".asName(), null, Meta.EMPTY) { private object GlobalContext : Context("GLOBAL".asName(), null, Meta.EMPTY) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + Job()
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()
/**
* The default logging manager
*/
public val logger: LogManager by lazy { globalLoggerFactory.invoke(context = this).apply { attach(this@Global) } }
/**
* 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>() public val Global: Context get() = GlobalContext
/** public fun Context(block: ContextBuilder.() -> Unit = {}): Context = Global.buildContext(block)
* Get previously built context
*
* @param name
* @return
*/
public fun getContext(name: String): Context? {
return contextRegistry[name]
}
public fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(parent, name).apply(block).build().also {
contextRegistry[name] = it
}
}

View File

@ -1,15 +1,16 @@
package space.kscience.dataforge.context package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.Named import space.kscience.dataforge.misc.Named
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
import kotlin.reflect.KClass
public interface Logable { public fun interface Logable {
public fun log(name: Name, tag: String, body: () -> String) public fun log(name: Name, tag: String, body: () -> String)
} }
public interface LogManager : Plugin, Logable { public interface LogManager : Plugin, Logable {
public companion object { public companion object {
public const val TRACE: String = "TRACE" public const val TRACE: String = "TRACE"
public const val INFO: String = "INFO" public const val INFO: String = "INFO"
@ -41,11 +42,30 @@ public fun Logable.error(throwable: Throwable?, name: Name = Name.EMPTY, body: (
} }
} }
public class DefaultLogManager : AbstractPlugin(), LogManager {
override fun log(name: Name, tag: String, body: () -> String) {
val message: String = body.safe
println("[${context.name}] $name: $message")
}
override val tag: PluginTag get() = Companion.tag
public companion object : PluginFactory<DefaultLogManager> {
override fun invoke(meta: Meta, context: Context): DefaultLogManager = DefaultLogManager()
override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.default")
override val type: KClass<out DefaultLogManager> = DefaultLogManager::class
}
}
/** /**
* Context log manager inherited from parent * Context log manager inherited from parent
*/ */
public val Context.logger: LogManager public val Context.logger: Logable
get() = plugins.find(inherit = true) { it is LogManager } as? LogManager ?: Global.logger get() = plugins.find(inherit = true) { it is LogManager } as? LogManager
?: globalLoggerFactory(context = Global).apply { attach(Global) }
/** /**
* The named proxy logger for a context member * The named proxy logger for a context member

View File

@ -63,5 +63,4 @@ public interface Plugin : Named, ContextAware, Provider, MetaRepr {
public companion object { public companion object {
public const val TARGET: String = "plugin" public const val TARGET: String = "plugin"
} }
} }

View File

@ -13,8 +13,6 @@ import kotlin.reflect.KClass
*/ */
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> { public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
//TODO refactor to read-only container
/** /**
* A set of loaded plugins * A set of loaded plugins
*/ */
@ -85,6 +83,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* @param plugin * @param plugin
* @return * @return
*/ */
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(plugin: T): T { public fun <T : Plugin> load(plugin: T): T {
if (get(plugin::class, plugin.tag, recursive = false) != null) { if (get(plugin::class, plugin.tag, recursive = false) != null) {
error("Plugin with tag ${plugin.tag} already exists in ${context.name}") error("Plugin with tag ${plugin.tag} already exists in ${context.name}")
@ -93,7 +92,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
fetch(factory, meta, true) fetch(factory, meta, true)
} }
Global.logger.info { "Loading plugin ${plugin.name} into ${context.name}" } logger.info { "Loading plugin ${plugin.name} into ${context.name}" }
plugin.attach(context) plugin.attach(context)
plugins.add(plugin) plugins.add(plugin)
return plugin return plugin
@ -103,15 +102,18 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
/** /**
* Load a plugin using its factory * Load a plugin using its factory
*/ */
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T = public fun <T : Plugin> load(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T =
load(factory(meta, context)) load(factory(meta, context))
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T = public fun <T : Plugin> load(factory: PluginFactory<T>, metaBuilder: MetaBuilder.() -> Unit): T =
load(factory, Meta(metaBuilder)) load(factory, Meta(metaBuilder))
/** /**
* Remove a plugin from [PluginManager] * Remove a plugin from [PluginManager]
*/ */
@Deprecated("Use immutable contexts instead")
public fun remove(plugin: Plugin) { public fun remove(plugin: Plugin) {
if (plugins.contains(plugin)) { if (plugins.contains(plugin)) {
Global.logger.info { "Removing plugin ${plugin.name} from ${context.name}" } Global.logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
@ -123,6 +125,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
/** /**
* Get an existing plugin with given meta or load new one using provided factory * Get an existing plugin with given meta or load new one using provided factory
*/ */
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T { public fun <T : Plugin> fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY, recursive: Boolean = true): T {
val loaded = get(factory.type, factory.tag, recursive) val loaded = get(factory.type, factory.tag, recursive)
return when { return when {
@ -132,6 +135,7 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
} }
} }
@Deprecated("Use immutable contexts instead")
public fun <T : Plugin> fetch( public fun <T : Plugin> fetch(
factory: PluginFactory<T>, factory: PluginFactory<T>,
recursive: Boolean = true, recursive: Boolean = true,
@ -141,3 +145,17 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
override fun iterator(): Iterator<Plugin> = plugins.iterator() override fun iterator(): Iterator<Plugin> = plugins.iterator()
} }
/**
* Fetch a plugin with given meta from the context. If the plugin (with given meta) is already registered, it is returned.
* Otherwise, new child context with the plugin is created. In the later case the context could be retrieved from the plugin.
*/
public inline fun <reified T : Plugin> Context.fetch(factory: PluginFactory<T>, meta: Meta = Meta.EMPTY): T {
val existing = plugins[factory]
return if (existing != null && existing.meta == meta) existing
else {
buildContext {
plugin(factory, meta)
}.plugins[factory]!!
}
}

View File

@ -22,7 +22,9 @@ class ContextTest {
@Test @Test
fun testPluginManager() { fun testPluginManager() {
val context = Global.context("test") val context = Global.buildContext{
name("test")
}
context.plugins.load(DummyPlugin()) context.plugins.load(DummyPlugin())
//Global.plugins.load(DummyPlugin()) //Global.plugins.load(DummyPlugin())
val members = context.gather<Name>("test") val members = context.gather<Name>("test")

View File

@ -1,25 +1,4 @@
package space.kscience.dataforge.context package space.kscience.dataforge.context
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name
import kotlin.reflect.KClass
internal actual val globalLoggerFactory: PluginFactory<out LogManager> = DefaultLogManager
public class NativeLogManager : AbstractPlugin(), LogManager {
override fun log(name: Name, tag: String, body: () -> String) {
val message: String = body.safe
println("[${context.name}] $name: $message")
}
override val tag: PluginTag get() = Companion.tag
public companion object : PluginFactory<NativeLogManager> {
override fun invoke(meta: Meta, context: Context): NativeLogManager = NativeLogManager()
override val tag: PluginTag = PluginTag(group = PluginTag.DATAFORGE_GROUP, name = "log.native")
override val type: KClass<out NativeLogManager> = NativeLogManager::class
}
}
internal actual val globalLoggerFactory: PluginFactory<out LogManager> = NativeLogManager

View File

@ -9,6 +9,7 @@ import space.kscience.dataforge.io.MetaFormatFactory.Companion.META_FORMAT_TYPE
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.toName import space.kscience.dataforge.names.toName
import kotlin.native.concurrent.ThreadLocal
import kotlin.reflect.KClass import kotlin.reflect.KClass
public class IOPlugin(meta: Meta) : AbstractPlugin(meta) { public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
@ -62,7 +63,8 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
public companion object : PluginFactory<IOPlugin> { public companion object : PluginFactory<IOPlugin> {
public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat) public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat) public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> =
listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP)
@ -71,4 +73,10 @@ public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
} }
} }
public val Context.io: IOPlugin get() = plugins.fetch(IOPlugin) @ThreadLocal
internal val ioContext = Global.withEnv {
name("IO")
plugin(IOPlugin)
}
public val Context.io: IOPlugin get() = (if (this == Global) ioContext else this).fetch(IOPlugin)

View File

@ -161,7 +161,7 @@ public class TaggedEnvelopeFormat(
} }
} }
private val default by lazy { invoke() } private val default by lazy { invoke(context = ioContext) }
override fun readPartial(input: Input): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) } default.run { readPartial(input) }

View File

@ -197,7 +197,7 @@ public class TaglessEnvelopeFormat(
return TaglessEnvelopeFormat(context.io, meta) return TaglessEnvelopeFormat(context.io, meta)
} }
private val default by lazy { invoke() } private val default by lazy { invoke(context = ioContext) }
override fun readPartial(input: Input): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial(input) } default.run { readPartial(input) }

View File

@ -193,6 +193,7 @@ public abstract interface class space/kscience/dataforge/meta/Meta : space/kscie
public final class space/kscience/dataforge/meta/Meta$Companion { public final class space/kscience/dataforge/meta/Meta$Companion {
public static final field TYPE Ljava/lang/String; public static final field TYPE Ljava/lang/String;
public static final field VALUE_KEY Ljava/lang/String; public static final field VALUE_KEY Ljava/lang/String;
public final fun equals (Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;)Z
public final fun getEMPTY ()Lspace/kscience/dataforge/meta/Meta; public final fun getEMPTY ()Lspace/kscience/dataforge/meta/Meta;
} }

View File

@ -54,6 +54,8 @@ public interface Meta : MetaRepr, ItemProvider {
*/ */
public const val VALUE_KEY: String = "@value" public const val VALUE_KEY: String = "@value"
public fun equals(meta1: Meta, meta2: Meta): Boolean = meta1.items == meta2.items
public val EMPTY: Meta = object : MetaBase() { public val EMPTY: Meta = object : MetaBase() {
override val items: Map<NameToken, MetaItem> = emptyMap() override val items: Map<NameToken, MetaItem> = emptyMap()
} }

View File

@ -32,7 +32,7 @@ public operator fun <M : TypedMeta<M>> M.get(key: NameToken): TypedMetaItem<M>?
public abstract class MetaBase : Meta { public abstract class MetaBase : Meta {
override fun equals(other: Any?): Boolean = if (other is Meta) { override fun equals(other: Any?): Boolean = if (other is Meta) {
this.items == other.items Meta.equals(this, other)
} else { } else {
false false
} }

View File

@ -4,7 +4,6 @@ import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.workspace.WorkspaceBuilder import space.kscience.dataforge.workspace.WorkspaceBuilder
import space.kscience.dataforge.workspace.target import space.kscience.dataforge.workspace.target
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -16,7 +15,7 @@ class BuildersKtTest {
val workspace = WorkspaceBuilder(Global).apply { val workspace = WorkspaceBuilder(Global).apply {
println("I am working") println("I am working")
context("test") context { name("test") }
target("testTarget") { target("testTarget") {
"a" put 12 "a" put 12
@ -29,7 +28,7 @@ class BuildersKtTest {
val script = """ val script = """
println("I am working") println("I am working")
context("test") context{ name("test") }
target("testTarget"){ target("testTarget"){
"a" put 12 "a" put 12

View File

@ -50,8 +50,8 @@ public class WorkspaceBuilder(private val parentContext: Context = Global) : Tas
/** /**
* Define a context for the workspace * Define a context for the workspace
*/ */
public fun context(name: String = "workspace", block: ContextBuilder.() -> Unit = {}) { public fun context(block: ContextBuilder.() -> Unit = {}) {
this.context = ContextBuilder(parentContext, name).apply(block).build() this.context = parentContext.buildContext(block)
} }
/** /**