Add preemptive initialization for plugins

This commit is contained in:
Alexander Nozik 2020-11-10 20:18:54 +03:00
parent d5992c721d
commit 3ab3f2766c
5 changed files with 28 additions and 63 deletions

View File

@ -39,12 +39,11 @@ public final class hep/dataforge/context/ClassLoaderPluginKt {
public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/dataforge/meta/MetaRepr, hep/dataforge/provider/Provider, kotlinx/coroutines/CoroutineScope {
public static final field Companion Lhep/dataforge/context/Context$Companion;
public static final field PROPERTY_TARGET Ljava/lang/String;
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;)V
public final fun activate (Ljava/lang/Object;)V
public fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;)V
public synthetic fun <init> (Lhep/dataforge/names/Name;Lhep/dataforge/context/Context;Lhep/dataforge/meta/Meta;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun close ()V
public fun content (Ljava/lang/String;)Ljava/util/Map;
public final fun content (Ljava/lang/String;Z)Ljava/util/Map;
public final fun deactivate (Ljava/lang/Object;)V
public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext;
public fun getDefaultChainTarget ()Ljava/lang/String;
public fun getDefaultTarget ()Ljava/lang/String;
@ -52,7 +51,6 @@ public class hep/dataforge/context/Context : hep/dataforge/context/Named, hep/da
public final fun getName ()Lhep/dataforge/names/Name;
public final fun getParent ()Lhep/dataforge/context/Context;
public final fun getPlugins ()Lhep/dataforge/context/PluginManager;
public final fun isActive ()Z
public fun toMeta ()Lhep/dataforge/meta/Meta;
}
@ -152,7 +150,7 @@ public final class hep/dataforge/context/PluginFactory$Companion {
}
public final class hep/dataforge/context/PluginManager : hep/dataforge/context/ContextAware, java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker {
public fun <init> (Lhep/dataforge/context/Context;)V
public fun <init> (Lhep/dataforge/context/Context;Ljava/util/Set;)V
public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;)Lhep/dataforge/context/Plugin;
public final fun fetch (Lhep/dataforge/context/PluginFactory;ZLkotlin/jvm/functions/Function1;)Lhep/dataforge/context/Plugin;
public static synthetic fun fetch$default (Lhep/dataforge/context/PluginManager;Lhep/dataforge/context/PluginFactory;ZLhep/dataforge/meta/Meta;ILjava/lang/Object;)Lhep/dataforge/context/Plugin;

View File

@ -28,6 +28,7 @@ public open class Context(
final override val name: Name,
public val parent: Context?,
meta: Meta,
plugins: Set<Plugin> = emptySet(),
) : Named, MetaRepr, Provider, CoroutineScope {
/**
@ -47,32 +48,7 @@ public open class Context(
/**
* A [PluginManager] for current context
*/
public val plugins: PluginManager = PluginManager(this)
@Deprecated("To be removed in favor of immutable plugins")
private val activators = HashSet<Any>()
/**
* Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed
*/
@Deprecated("To be removed in favor of immutable plugins")
public val isActive: Boolean = activators.isNotEmpty()
/**
* Mark context as active and used by [activator]
*/
@Deprecated("To be removed in favor of immutable plugins")
public fun activate(activator: Any) {
activators.add(activator)
}
/**
* Mark context unused by [activator]
*/
@Deprecated("To be removed in favor of immutable plugins")
public fun deactivate(activator: Any) {
activators.remove(activator)
}
public val plugins: PluginManager by lazy { PluginManager(this, plugins)}
override val defaultTarget: String get() = Plugin.TARGET
@ -104,8 +80,6 @@ public open class Context(
* Detach all plugins and terminate context
*/
public open fun close() {
@Suppress("DEPRECATION")
if (isActive) error("Can't close active context")
//detach all plugins
plugins.forEach { it.detach() }
}

View File

@ -8,7 +8,7 @@ import hep.dataforge.names.toName
*/
@DFBuilder
public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") {
private val plugins = ArrayList<Plugin>()
private val plugins = HashSet<Plugin>()
private var meta = MetaBuilder()
public fun properties(action: MetaBuilder.() -> Unit) {
@ -20,9 +20,12 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
}
@OptIn(DFExperimental::class)
public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) {
val factory = parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
private fun findPluginFactory(tag: PluginTag): PluginFactory<*> =
parent.gatherInSequence<PluginFactory<*>>(PluginFactory.TYPE).values
.find { it.tag.matches(tag) } ?: error("Can't resolve plugin factory for $tag")
public fun plugin(tag: PluginTag, metaBuilder: MetaBuilder.() -> Unit = {}) {
val factory = findPluginFactory(tag)
val plugin = factory.invoke(Meta(metaBuilder), parent)
plugins.add(plugin)
}
@ -36,10 +39,6 @@ public class ContextBuilder(private val parent: Context = Global, public var nam
}
public fun build(): Context {
return Context(name.toName(), parent, meta.seal()).apply {
this@ContextBuilder.plugins.forEach {
plugins.load(it)
}
}
return Context(name.toName(), parent, meta.seal(), plugins)
}
}

View File

@ -16,21 +16,24 @@ public interface PluginFactory<T : Plugin> : Factory<T> {
}
}
/**
* The manager for plugin system. Should monitor plugin dependencies and locks.
*
* @property context A context for this plugin manager
* @author Alexander Nozik
*/
public class PluginManager(override val context: Context) : ContextAware, Iterable<Plugin> {
public class PluginManager(override val context: Context, plugins: Set<Plugin>) : ContextAware, Iterable<Plugin> {
//TODO refactor to read-only container
/**
* A set of loaded plugins
*/
private val plugins = HashSet<Plugin>()
private val plugins: HashSet<Plugin> = HashSet(plugins)
init {
plugins.forEach { it.attach(context) }
}
/**
* A [PluginManager] of parent context if it is present
@ -56,7 +59,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
public fun find(inherit: Boolean = true, predicate: (Plugin) -> Boolean): Plugin? =
list(inherit).find(predicate)
/**
* Find a loaded plugin via its tag
*
@ -66,7 +68,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
public operator fun get(tag: PluginTag, inherit: Boolean = true): Plugin? =
find(inherit) { tag.matches(it.tag) }
/**
* Find a loaded plugin via its class. This method does not check if the result is unique and just returns first
* plugin matching the class condition.
@ -96,10 +97,8 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* @return
*/
public fun <T : Plugin> load(plugin: T): T {
if (context.isActive) error("Can't load plugin into active context")
if (get(plugin::class, plugin.tag, recursive = false) != null) {
error("Plugin of type ${plugin::class} already exists in ${context.name}")
error("Plugin with tag ${plugin.tag} already exists in ${context.name}")
} else {
for (tag in plugin.dependsOn()) {
fetch(tag, true)
@ -125,8 +124,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
* Remove a plugin from [PluginManager]
*/
public fun remove(plugin: Plugin) {
if (context.isActive) error("Can't remove plugin from active context")
if (plugins.contains(plugin)) {
logger.info { "Removing plugin ${plugin.name} from ${context.name}" }
plugin.detach()
@ -136,7 +133,6 @@ public class PluginManager(override val context: Context) : ContextAware, Iterab
/**
* Get an existing plugin with given meta or load new one using provided factory
*
*/
public fun <T : Plugin> fetch(factory: PluginFactory<T>, recursive: Boolean = true, meta: Meta = Meta.EMPTY): T {
val loaded = get(factory.type, factory.tag, recursive)

View File

@ -45,25 +45,23 @@ public interface Workspace : ContextAware, Provider {
* Invoke a task in the workspace utilizing caching if possible
*/
public fun <R : Any> run(task: Task<R>, config: Meta): DataNode<R> {
context.activate(this)
try {
val model = task.build(this, config)
task.validate(model)
return task.run(this, model)
} finally {
context.deactivate(this)
}
}
public companion object {
public const val TYPE: String = "workspace"
public operator fun invoke(parent: Context = Global, block: SimpleWorkspaceBuilder.() -> Unit): SimpleWorkspace =
public operator fun invoke(
parent: Context = Global,
block: SimpleWorkspaceBuilder.() -> Unit,
): SimpleWorkspace =
SimpleWorkspaceBuilder(parent).apply(block).build()
}
}
public fun Workspace.run(task: Task<*>, target: String): DataNode<Any> {
val meta = targets[target] ?: error("A target with name $target not found in ${this}")
val meta = targets[target] ?: error("A target with name $target not found in $this")
return run(task, meta)
}