IJPL-872 fix kernel and rpc services

GitOrigin-RevId: 80c6c0abcf68359e4c55b81560debef9e98ba808
This commit is contained in:
Vladimir Koshelev
2024-03-22 21:48:15 +01:00
committed by intellij-monorepo-bot
parent b7a6cb6d63
commit 82f8055fea
19 changed files with 329 additions and 62 deletions

View File

@@ -1224,6 +1224,9 @@ object CommunityLibraryLicenses {
jetbrainsLibrary("find-classes-model-experimental"),
jetbrainsLibrary("find-file-model"),
jetbrainsLibrary("find-file-model-experimental"),
jetbrainsLibrary("jetbrains.fleet.kernel"),
jetbrainsLibrary("jetbrains.fleet.rpc"),
jetbrainsLibrary("jetbrains.fleet.rpc.server"),
jetbrainsLibrary("git-learning-project"),
jetbrainsLibrary("intellij.remoterobot.remote.fixtures"),
jetbrainsLibrary("intellij.remoterobot.robot.server.core"),

View File

@@ -1,22 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.fleet.rpc
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.client.ClientAppSession
import com.intellij.openapi.client.currentSession
import com.intellij.openapi.components.service
import fleet.rpc.core.FleetTransportFactory
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@ApiStatus.Experimental
interface FleetRpc {
companion object {
fun getInstance(session: ClientAppSession): FleetRpc = session.service()
fun getCurrentInstance(): FleetRpc = ApplicationManager.getApplication().currentSession.service()
}
fun getTransportFactory(): FleetTransportFactory
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.platform.kernel.backend" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="module" module-name="intellij.platform.extensions" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="library" name="kotlinx-collections-immutable" level="project" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
</component>
</module>

View File

@@ -0,0 +1,17 @@
<idea-plugin package="com.intellij.platform.kernel.backend">
<dependencies>
<module name="intellij.platform.kernel"/>
</dependencies>
<extensionPoints>
<extensionPoint qualifiedName="com.intellij.platform.kernel.backend.remoteApiProvider"
interface="com.intellij.platform.kernel.backend.RemoteApiProvider"
dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="com.intellij">
<!--suppress PluginXmlValidity -->
<applicationService serviceInterface="com.intellij.platform.kernel.rpc.RemoteApiProviderService"
serviceImplementation="com.intellij.platform.kernel.backend.RemoteApiRegistry"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,18 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.kernel.backend
import com.intellij.openapi.extensions.ExtensionPointName
import fleet.rpc.RemoteApi
import org.jetbrains.annotations.ApiStatus
import kotlin.reflect.KClass
interface RemoteApiProvider {
data class RemoteApiDescriptor<T : RemoteApi<Unit>>(val klass: KClass<T>, val service: () -> T)
fun getApis(): List<RemoteApiDescriptor<*>>
companion object {
@ApiStatus.Internal
val EP_NAME = ExtensionPointName<RemoteApiProvider>("com.intellij.platform.kernel.backend.remoteApiProvider")
}
}

View File

@@ -0,0 +1,56 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.kernel.backend
import com.intellij.openapi.extensions.ExtensionPointListener
import com.intellij.openapi.extensions.PluginDescriptor
import com.intellij.platform.kernel.backend.RemoteApiProvider.Companion.EP_NAME
import com.intellij.platform.kernel.rpc.RemoteApiProviderService
import com.intellij.util.containers.ContainerUtil
import fleet.rpc.RemoteApi
import fleet.rpc.core.InstanceId
import kotlinx.coroutines.CoroutineScope
import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.set
import kotlin.reflect.KClass
class RemoteApiRegistry(private val coroutineScope: CoroutineScope) : RemoteApiProviderService {
private val remoteApis = ConcurrentHashMap<InstanceId, Pair<KClass<out RemoteApi<Unit>>, RemoteApi<Unit>>>()
private val visitedEPs = ContainerUtil.createConcurrentWeakKeyWeakValueMap<RemoteApiProvider, Unit>()
init {
EP_NAME.addExtensionPointListener(coroutineScope, object : ExtensionPointListener<RemoteApiProvider> {
override fun extensionAdded(extension: RemoteApiProvider, pluginDescriptor: PluginDescriptor) {
if (visitedEPs.putIfAbsent(extension, Unit) == null) {
val apis = extension.getApis()
for (api in apis) {
remoteApis[api.klass.toInstanceId] = api.klass to api.service()
}
}
}
override fun extensionRemoved(extension: RemoteApiProvider, pluginDescriptor: PluginDescriptor) {
visitedEPs.remove(extension)
val apis = extension.getApis()
synchronized(this) {
apis.forEach { api ->
remoteApis.remove(api.klass.toInstanceId)
}
}
}
})
EP_NAME.extensions.filter { visitedEPs.putIfAbsent(it, Unit) == null }.flatMap { it.getApis() }.forEach { api ->
remoteApis[api.klass.toInstanceId] = api.klass to api.service()
}
}
override fun <T : RemoteApi<Unit>> resolve(kclass: KClass<T>): T {
return remoteApis[kclass.toInstanceId]?.second as? T ?: throw IllegalStateException("No remote API found for $kclass")
}
fun resolve(instanceId: InstanceId): Pair<KClass<out RemoteApi<Unit>>, RemoteApi<Unit>> {
return remoteApis[instanceId] ?: throw IllegalStateException("No remote API found for $instanceId")
}
}
private val KClass<out RemoteApi<Unit>>.toInstanceId: InstanceId
get() = InstanceId(this.qualifiedName!!)

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.platform.kernel.monolith" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
</component>
</module>

View File

@@ -0,0 +1,11 @@
<idea-plugin package="com.intellij.platform.kernel.monolith">
<dependencies>
<module name="intellij.platform.kernel"/>
</dependencies>
<extensions defaultExtensionNs="com.intellij">
<!--suppress PluginXmlValidity -->
<applicationService serviceInterface="com.intellij.platform.kernel.KernelService"
serviceImplementation="com.intellij.platform.kernel.monolith.MonolithKernelService"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,35 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.kernel.monolith
import com.intellij.platform.kernel.KernelService
import com.intellij.platform.kernel.util.CommonInstructionSet
import com.intellij.platform.kernel.util.KernelRpcSerialization
import com.intellij.platform.kernel.util.ReadTracker
import com.intellij.platform.kernel.util.withKernel
import fleet.kernel.Kernel
import fleet.kernel.kernel
import fleet.kernel.rebase.LeaderKernelMiddleware
import fleet.kernel.rebase.encoder
import fleet.kernel.rete.Rete
import kotlinx.coroutines.*
internal class MonolithKernelService(coroutineScope: CoroutineScope) : KernelService {
override val kernel: Kernel
override val rete: Rete
init {
val kernelDeferred: CompletableDeferred<Kernel> = CompletableDeferred()
val reteDeferred : CompletableDeferred<Rete> = CompletableDeferred()
coroutineScope.launch(start = CoroutineStart.UNDISPATCHED) {
withKernel(middleware = LeaderKernelMiddleware(KernelRpcSerialization, CommonInstructionSet.encoder())) {
kernelDeferred.complete(kernel())
reteDeferred.complete(currentCoroutineContext()[Rete]!!)
ReadTracker.subscribeForChanges()
}
}
kernel = kernelDeferred.getCompleted()
rete = reteDeferred.getCompleted()
}
}

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="17" />
<stringArg name="apiVersion" arg="1.9" />
<stringArg name="languageVersion" arg="1.9" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>
<arg>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</arg>
<arg>$MAVEN_REPOSITORY$/jetbrains/fleet/rhizomedb-compiler-plugin/1.9.20-0.17/rhizomedb-compiler-plugin-1.9.20-0.17.jar</arg>
</args>
</arrayArg>
<arrayArg name="pluginOptions" />
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/" isTestSource="false" packagePrefix="com.intellij.platform.kernel" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="kotlin-stdlib" level="project" />
<orderEntry type="library" exported="" name="jetbrains.fleet.rpc" level="project" />
<orderEntry type="library" exported="" name="jetbrains.fleet.kernel" level="project" />
<orderEntry type="library" exported="" name="kotlinx-coroutines-core" level="project" />
<orderEntry type="library" exported="" name="io.lacuna:bifurcan" level="project" />
<orderEntry type="library" name="kotlinx-collections-immutable" level="project" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.core" />
</component>
</module>

View File

@@ -0,0 +1 @@
com.intellij.platform.kernel.util.IjLoggerFactory

View File

@@ -0,0 +1,2 @@
<idea-plugin package="com.intellij.platform.kernel">
</idea-plugin>

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.fleet.kernel
package com.intellij.platform.kernel
import com.intellij.openapi.application.ApplicationManager
import fleet.kernel.DbSource
@@ -7,10 +7,8 @@ import fleet.kernel.Kernel
import fleet.kernel.rete.Rete
import fleet.kernel.withCondition
import kotlinx.coroutines.*
import org.jetbrains.annotations.ApiStatus
import kotlin.coroutines.CoroutineContext
@ApiStatus.Internal
@ApiStatus.Experimental
interface KernelService {
val kernel: Kernel
val rete: Rete
@@ -18,9 +16,11 @@ interface KernelService {
val instance: KernelService
get() = ApplicationManager.getApplication().getService(KernelService::class.java)
val kernelCoroutineContext: CoroutineContext
get() = instance.kernel + instance.rete + DbSource(instance.kernel.dbState, instance.kernel.toString())
fun <T> CoroutineScope.saga(condition: () -> Boolean = { true }, block: suspend CoroutineScope.() -> T): Deferred<T> {
val instance = instance
return async(instance.kernel + instance.rete + DbSource(instance.kernel.dbState, instance.kernel.toString())) {
return async(kernelCoroutineContext) {
withCondition(condition, block)
}
}

View File

@@ -0,0 +1,16 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.kernel.rpc
import com.intellij.openapi.application.ApplicationManager
import fleet.rpc.RemoteApi
import kotlin.reflect.KClass
interface RemoteApiProviderService {
fun <T : RemoteApi<Unit>> resolve(klass: KClass<T>): T
companion object {
fun <T : RemoteApi<Unit>> resolve(klass: KClass<T>): T {
return ApplicationManager.getApplication().getService(RemoteApiProviderService::class.java).resolve(klass)
}
}
}

View File

@@ -0,0 +1,79 @@
package com.intellij.platform.kernel.util
import com.intellij.openapi.diagnostic.Logger
import fleet.util.logging.*
import kotlin.reflect.KClass
class IjLogger(private val logger: Logger) : BaseLogger {
override val isTraceEnabled: Boolean
get() = logger.isTraceEnabled
override val isDebugEnabled: Boolean
get() = logger.isDebugEnabled
override val isInfoEnabled: Boolean
get() = true
override val isWarnEnabled: Boolean
get() = true
override val isErrorEnabled: Boolean
get() = true
override fun trace(message: Any?) {
logger.trace(message?.toString() ?: "null")
}
override fun trace(t: Throwable?, message: Any?) {
logger.trace(message?.toString() ?: "null")
if (t != null) {
logger.trace(t)
}
}
override fun debug(message: Any?) {
logger.debug(message?.toString() ?: "null")
}
override fun debug(t: Throwable?, message: Any?) {
logger.debug(message?.toString() ?: "null", t)
}
override fun info(message: Any?) {
logger.info(message?.toString() ?: "null")
}
override fun info(t: Throwable?, message: Any?) {
logger.info(message?.toString() ?: "null", t)
}
override fun warn(message: Any?) {
logger.warn(message?.toString() ?: "null")
}
override fun warn(t: Throwable?, message: Any?) {
logger.warn(message?.toString() ?: "null", t)
}
override fun error(message: Any?) {
logger.error(message.toString())
}
override fun error(t: Throwable?, message: Any?) {
logger.error(message?.toString() ?: "null", t)
}
}
class IjLoggerFactory : KLoggerFactory {
override fun logger(owner: KClass<*>): KLogger {
return KLogger(IjLogger(Logger.getInstance(owner.java)))
}
override fun logger(owner: Class<*>): KLogger {
return KLogger(IjLogger(Logger.getInstance(owner)))
}
override fun logger(owner: Any): KLogger {
return KLogger(IjLogger(Logger.getInstance(owner.toString())))
}
override fun logger(name: String): KLogger {
return KLogger(IjLogger(Logger.getInstance(name)))
}
}

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.kernel
package com.intellij.platform.kernel.util
import com.jetbrains.rhizomedb.Datom
import com.jetbrains.rhizomedb.Pattern
@@ -9,6 +9,7 @@ import it.unimi.dsi.fastutil.ints.IntConsumer
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.LongOpenHashSet
import kotlin.collections.set
internal class ReadTrackingIndex : ReadTrackingContext {
companion object {

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.kernel
package com.intellij.platform.kernel.util
import com.jetbrains.rhizomedb.CascadeDeleteBy
import com.jetbrains.rhizomedb.Unique

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.kernel
package com.intellij.platform.kernel.util
import com.intellij.fleet.kernel.KernelService
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.util.concurrency.annotations.RequiresEdt
@@ -11,7 +10,6 @@ import fleet.kernel.Kernel
import fleet.kernel.KernelMiddleware
import fleet.kernel.kernel
import fleet.kernel.rebase.*
import fleet.kernel.rete.Rete
import fleet.kernel.rete.withRete
import fleet.kernel.subscribe
import fleet.rpc.core.Serialization
@@ -26,7 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger
@ApiStatus.Internal
@ApiStatus.Experimental
suspend fun <T> withKernel(scope: CoroutineScope, middleware: KernelMiddleware, body: suspend () -> T) {
suspend fun <T> withKernel(middleware: KernelMiddleware, body: suspend () -> T) {
val entityClasses = listOf(Kernel::class.java.classLoader).flatMap(::collectEntityClasses)
fleet.kernel.withKernel(entityClasses, middleware = middleware) { currentKernel ->
withRete {
@@ -99,30 +97,6 @@ object ReadTracker {
}
}
val FleetRpcSerialization = Serialization(SerializersModule {
val KernelRpcSerialization = Serialization(SerializersModule {
registerCRUDInstructions()
})
class BaseKernelService(coroutineScope: CoroutineScope) : KernelService {
private val kernelDeferred: CompletableDeferred<Kernel> = CompletableDeferred()
private val reteDeferred : CompletableDeferred<Rete> = CompletableDeferred()
init {
coroutineScope.launch(start = CoroutineStart.ATOMIC) {
withKernel(this, middleware = LeaderKernelMiddleware(FleetRpcSerialization, CommonInstructionSet.encoder())) {
kernelDeferred.complete(kernel())
reteDeferred.complete(currentCoroutineContext()[Rete]!!)
ReadTracker.subscribeForChanges()
}
}
runBlocking {
kernelDeferred.await()
reteDeferred.await()
}
}
override val kernel: Kernel
get() = kernelDeferred.getCompleted()
override val rete: Rete
get() = reteDeferred.getCompleted()
}

View File

@@ -1,7 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.kernel
package com.intellij.platform.kernel.util
import com.intellij.fleet.kernel.KernelService
import com.intellij.platform.kernel.KernelService
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.jetbrains.rhizomedb.EID
import com.jetbrains.rhizomedb.entity
@@ -153,7 +153,6 @@ private class ModelChangeScopeImpl(val sharedChangeScope: SharedChangeScope) : M
val value = serialization.json.encodeToJsonElement(serialization.kSerializer(ktype), value)
val viewModel = lookupOne(ViewModelEntity::modelId, id)
if (viewModel == null) {
println("Aha")
throw IllegalStateException("ViewModelEntity not found for model $id")
}
val alreadyExist = lookupOne(ModelPropertyEntity::id, fqn)