diff --git a/.run/Debug All.run.xml b/.run/Debug All.run.xml
index 4e66ecc45a..a0cf8bd64d 100644
--- a/.run/Debug All.run.xml
+++ b/.run/Debug All.run.xml
@@ -3,6 +3,7 @@
+
\ No newline at end of file
diff --git a/.run/Debug Spring Analyzer Process.run.xml b/.run/Debug Spring Analyzer Process.run.xml
new file mode 100644
index 0000000000..91c80ebf73
--- /dev/null
+++ b/.run/Debug Spring Analyzer Process.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Listen for Spring Analyzer Process.run.xml b/.run/Listen for Spring Analyzer Process.run.xml
new file mode 100644
index 0000000000..80a615201a
--- /dev/null
+++ b/.run/Listen for Spring Analyzer Process.run.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 259ec1b8e2..19e94c53a0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -19,11 +19,6 @@ plugins {
`maven-publish`
}
-configure {
- sourceCompatibility = VERSION_11
- targetCompatibility = VERSION_17
-}
-
allprojects {
apply {
diff --git a/gradle.properties b/gradle.properties
index 59862181bb..485d03a1d9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -77,6 +77,9 @@ pytorchNativeVersion=1.9.1
shadowJarVersion=7.1.2
openblasVersion=0.3.10-1.5.4
arpackNgVersion=3.7.0-1.5.4
+commonsLoggingVersion=1.2
+commonsIOVersion=2.11.0
+springBootVersion=2.7.8
# configuration for build server
#
diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
index cdff5e110a..983e806661 100644
--- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
@@ -283,7 +283,7 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
var runEngineProcessWithDebug by getBooleanProperty(false)
/**
- * The engine process JDWP agent's port of the instrumented process.
+ * The engine process JDWP agent's port of the engine process.
* A debugger attaches to the port in order to debug the process.
*/
var engineProcessDebugPort by getIntProperty(5005)
@@ -296,6 +296,29 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
// endregion
+// region spring analyzer process debug
+ /**
+ * The property is useful only for the IntelliJ IDEs.
+ * If the property is set in true the spring analyzer process opens a debug port.
+ * @see runInstrumentedProcessWithDebug
+ * @see org.utbot.framework.process.SpringAnalyzerProcess
+ */
+ var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false)
+
+ /**
+ * The spring analyzer process JDWP agent's port.
+ * A debugger attaches to the port in order to debug the process.
+ */
+ var springAnalyzerProcessDebugPort by getIntProperty(5007)
+
+ /**
+ * Value of the suspend mode for the JDWP agent of the spring analyzer process.
+ * If the value is true, the spring analyzer process will suspend until a debugger attaches to it.
+ */
+ var suspendSpringAnalyzerProcessExecutionInDebugMode by getBooleanProperty(true)
+
+// endregion
+
// region instrumented process debug
/**
* The instrumented process JDWP agent's port of the instrumented process.
@@ -312,7 +335,8 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
/**
* If true, runs the instrumented process with the ability to attach a debugger.
*
- * To debug the instrumented process, set the breakpoint in the instrumentedProcessRunner.start() line
+ * To debug the instrumented process, set the breakpoint in the
+ * [org.utbot.instrumentation.rd.InstrumentedProcess.Companion.invoke]
* and in the instrumented process's main function and run the main process.
* Then run the remote JVM debug configuration in IDEA.
* If you see the message in console about successful connection, then
@@ -320,7 +344,7 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
* Now you can put the breakpoints in the instrumented process and debug
* both processes simultaneously.
*
- * @see [org.utbot.instrumentation.process.InstrumentedProcessRunner.cmds]
+ * @see [org.utbot.instrumentation.rd.InstrumentedProcess.Companion.invoke]
*/
var runInstrumentedProcessWithDebug by getBooleanProperty(false)
// endregion
diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt
index 62ed3560c2..08d18c1034 100644
--- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/JdkInfoService.kt
@@ -11,7 +11,7 @@ data class JdkInfo(
/**
* Singleton to enable abstract access to path to JDK.
- * Used in [org.utbot.instrumentation.process.InstrumentedProcessRunner].
+ * Used in [org.utbot.framework.process.AbstractRDProcessCompanion].
* The purpose is to use the same JDK in [org.utbot.instrumentation.ConcreteExecutor] and in the test runs.
* This is necessary because the engine can be run from the various starting points, like IDEA plugin, CLI, etc.
*/
diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt
index d7ce174bdd..e448a21a76 100644
--- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/services/WorkingDirService.kt
@@ -6,7 +6,7 @@ import java.nio.file.Paths
/**
* Singleton to enable abstract access to the working directory.
*
- * Used in [org.utbot.instrumentation.process.InstrumentedProcessRunner].
+ * Used in [org.utbot.instrumentation.rd.InstrumentedProcess].
* The purpose is to use the same working directory in [org.utbot.instrumentation.ConcreteExecutor]
* and in the test runs.
*/
diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/AbstractRDProcessCompanion.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/AbstractRDProcessCompanion.kt
new file mode 100644
index 0000000000..41b0499cae
--- /dev/null
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/AbstractRDProcessCompanion.kt
@@ -0,0 +1,37 @@
+package org.utbot.framework.process
+
+import org.utbot.common.osSpecificJavaExecutable
+import org.utbot.framework.plugin.services.JdkInfoService
+import org.utbot.rd.rdPortArgument
+import java.io.File
+import kotlin.io.path.pathString
+
+abstract class AbstractRDProcessCompanion(
+ private val debugPort: Int,
+ private val runWithDebug: Boolean,
+ private val suspendExecutionInDebugMode: Boolean,
+ private val processSpecificCommandLineArgs: () -> List
+) {
+ private val javaExecutablePathString get() =
+ JdkInfoService.provide().path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}")
+
+ protected fun obtainProcessCommandLine(port: Int): List = buildList {
+ addAll(obtainCommonProcessCommandLineArgs())
+ addAll(processSpecificCommandLineArgs())
+ add(rdPortArgument(port))
+ }
+
+ private fun obtainCommonProcessCommandLineArgs(): List = buildList {
+ val suspendValue = if (suspendExecutionInDebugMode) "y" else "n"
+ val debugArgument =
+ "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue},quiet=y,address=$debugPort"
+ .takeIf { runWithDebug }
+
+ add(javaExecutablePathString.pathString)
+ val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments
+ if (javaVersionSpecificArgs.isNotEmpty()) {
+ addAll(javaVersionSpecificArgs)
+ }
+ debugArgument?.let { add(it) }
+ }
+}
\ No newline at end of file
diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt
index b5616b4838..2cc37032d4 100644
--- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/OpenModulesContainer.kt
@@ -11,6 +11,7 @@ object OpenModulesContainer {
init {
modulesContainer = buildList {
openPackage("java.base", "sun.security.util")
+ openPackage("java.base", "sun.reflect.annotation")
openPackage("java.base", "java.text")
openPackage("java.base", "java.lang.invoke")
openPackage("java.base", "jdk.internal.misc")
diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle
index 3e0cd02564..5ca456d1a1 100644
--- a/utbot-framework/build.gradle
+++ b/utbot-framework/build.gradle
@@ -1,3 +1,7 @@
+configurations {
+ fetchSpringAnalyzerJar
+}
+
dependencies {
api project(':utbot-fuzzers')
@@ -34,4 +38,13 @@ dependencies {
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-core', version: ksmtVersion
implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-z3', version: ksmtVersion
+ implementation project(':utbot-spring-analyzer')
+
+ fetchSpringAnalyzerJar project(path: ':utbot-spring-analyzer', configuration: 'springAnalyzerJar')
+}
+
+processResources {
+ from(configurations.fetchSpringAnalyzerJar) {
+ into "lib"
+ }
}
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt
index 8a3b48e9dc..248bd160db 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt
@@ -3,7 +3,6 @@ package org.utbot.framework.codegen.domain
import org.utbot.framework.DEFAULT_EXECUTION_TIMEOUT_IN_INSTRUMENTED_PROCESS_MS
import org.utbot.framework.codegen.domain.builtin.mockitoClassId
import org.utbot.framework.codegen.domain.builtin.ongoingStubbingClassId
-import org.utbot.framework.codegen.domain.context.CgContext
import org.utbot.framework.codegen.domain.models.CgClassId
import org.utbot.framework.codegen.tree.argumentsClassId
import org.utbot.framework.plugin.api.BuiltinClassId
@@ -751,7 +750,7 @@ sealed class TypeReplacementApproach {
*
* Currently used in Spring applications only.
*/
- class ReplaceIfPossible(val configFqn: String) : TypeReplacementApproach()
+ class ReplaceIfPossible(val config: String) : TypeReplacementApproach()
}
abstract class DependencyInjectionFramework(
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
index f0ac645d73..ca1d6cb96b 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt
@@ -34,8 +34,10 @@ import org.utbot.rd.RdSettingsContainerFactory
import org.utbot.rd.findRdPort
import org.utbot.rd.generated.settingsModel
import org.utbot.rd.loggers.UtRdKLoggerFactory
+import org.utbot.rd.terminateOnException
import org.utbot.sarif.RdSourceFindingStrategyFacade
import org.utbot.sarif.SarifReport
+import org.utbot.spring.process.SpringAnalyzerProcess
import org.utbot.summary.summarizeAll
import java.io.File
import java.net.URLClassLoader
@@ -51,16 +53,11 @@ object EngineProcessMain
// use log4j2.configurationFile property to set log4j configuration
suspend fun main(args: Array) = runBlocking {
- Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
-
logger.info("-----------------------------------------------------------------------")
logger.info("-------------------NEW ENGINE PROCESS STARTED--------------------------")
logger.info("-----------------------------------------------------------------------")
- // 0 - auto port for server, should not be used here
- val port = findRdPort(args)
-
- ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) {
+ ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(args) {
AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel))
val kryoHelper = KryoHelper(lifetime)
engineProcessModel.setup(kryoHelper, it, protocol)
@@ -79,6 +76,21 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
File(it).toURI().toURL()
}.toTypedArray())))
}
+ watchdog.measureTimeForActiveCall(getSpringBeanQualifiedNames, "Getting Spring bean definitions") { params ->
+ val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking()
+ val beans = springAnalyzerProcess.terminateOnException { _ ->
+ springAnalyzerProcess.getBeanQualifiedNames(
+ params.classpath.toList(),
+ params.config,
+ // TODO remove once spring-analyzer learns to find resources on its own, temporarily leaving it here for testing with hardcoded absolute paths
+ propertyFilesPaths = emptyList(),
+ xmlConfigurationPaths = emptyList(),
+ params.useSpringAnalyzer
+ ).toTypedArray()
+ }
+ springAnalyzerProcess.terminate()
+ beans
+ }
watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params ->
AnalyticsConfigureUtil.configureML()
Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter)
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
index c4029c1862..47d86d3837 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
@@ -19,6 +19,7 @@ import kotlin.jvm.JvmStatic
*/
class EngineProcessModel private constructor(
private val _setupUtContext: RdCall,
+ private val _getSpringBeanQualifiedNames: RdCall>,
private val _createTestGenerator: RdCall,
private val _isCancelled: RdCall,
private val _generate: RdCall,
@@ -41,6 +42,7 @@ class EngineProcessModel private constructor(
serializers.register(RenderParams)
serializers.register(RenderResult)
serializers.register(SetupContextParams)
+ serializers.register(GetSpringBeanQualifiedNamesParams)
serializers.register(MethodDescription)
serializers.register(FindMethodsInClassMatchingSelectedArguments)
serializers.register(FindMethodsInClassMatchingSelectedResult)
@@ -68,8 +70,9 @@ class EngineProcessModel private constructor(
return EngineProcessModel()
}
+ private val __StringArraySerializer = FrameworkMarshallers.String.array()
- const val serializationHash = -2087034443345538396L
+ const val serializationHash = 1955031277042475752L
}
override val serializersOwner: ISerializersOwner get() = EngineProcessModel
@@ -77,6 +80,7 @@ class EngineProcessModel private constructor(
//fields
val setupUtContext: RdCall get() = _setupUtContext
+ val getSpringBeanQualifiedNames: RdCall> get() = _getSpringBeanQualifiedNames
val createTestGenerator: RdCall get() = _createTestGenerator
val isCancelled: RdCall get() = _isCancelled
val generate: RdCall get() = _generate
@@ -90,6 +94,7 @@ class EngineProcessModel private constructor(
//initializer
init {
_setupUtContext.async = true
+ _getSpringBeanQualifiedNames.async = true
_createTestGenerator.async = true
_isCancelled.async = true
_generate.async = true
@@ -103,6 +108,7 @@ class EngineProcessModel private constructor(
init {
bindableChildren.add("setupUtContext" to _setupUtContext)
+ bindableChildren.add("getSpringBeanQualifiedNames" to _getSpringBeanQualifiedNames)
bindableChildren.add("createTestGenerator" to _createTestGenerator)
bindableChildren.add("isCancelled" to _isCancelled)
bindableChildren.add("generate" to _generate)
@@ -118,6 +124,7 @@ class EngineProcessModel private constructor(
private constructor(
) : this(
RdCall(SetupContextParams, FrameworkMarshallers.Void),
+ RdCall>(GetSpringBeanQualifiedNamesParams, __StringArraySerializer),
RdCall(TestGeneratorParams, FrameworkMarshallers.Void),
RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Bool),
RdCall(GenerateParams, GenerateResult),
@@ -136,6 +143,7 @@ class EngineProcessModel private constructor(
printer.println("EngineProcessModel (")
printer.indent {
print("setupUtContext = "); _setupUtContext.print(printer); println()
+ print("getSpringBeanQualifiedNames = "); _getSpringBeanQualifiedNames.print(printer); println()
print("createTestGenerator = "); _createTestGenerator.print(printer); println()
print("isCancelled = "); _isCancelled.print(printer); println()
print("generate = "); _generate.print(printer); println()
@@ -152,6 +160,7 @@ class EngineProcessModel private constructor(
override fun deepClone(): EngineProcessModel {
return EngineProcessModel(
_setupUtContext.deepClonePolymorphic(),
+ _getSpringBeanQualifiedNames.deepClonePolymorphic(),
_createTestGenerator.deepClonePolymorphic(),
_isCancelled.deepClonePolymorphic(),
_generate.deepClonePolymorphic(),
@@ -170,7 +179,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel
/**
- * #### Generated from [EngineProcessModel.kt:99]
+ * #### Generated from [EngineProcessModel.kt:104]
*/
data class FindMethodParamNamesArguments (
val classId: ByteArray,
@@ -233,7 +242,7 @@ data class FindMethodParamNamesArguments (
/**
- * #### Generated from [EngineProcessModel.kt:103]
+ * #### Generated from [EngineProcessModel.kt:108]
*/
data class FindMethodParamNamesResult (
val paramNames: ByteArray
@@ -290,7 +299,7 @@ data class FindMethodParamNamesResult (
/**
- * #### Generated from [EngineProcessModel.kt:92]
+ * #### Generated from [EngineProcessModel.kt:97]
*/
data class FindMethodsInClassMatchingSelectedArguments (
val classId: ByteArray,
@@ -353,7 +362,7 @@ data class FindMethodsInClassMatchingSelectedArguments (
/**
- * #### Generated from [EngineProcessModel.kt:96]
+ * #### Generated from [EngineProcessModel.kt:101]
*/
data class FindMethodsInClassMatchingSelectedResult (
val executableIds: ByteArray
@@ -578,7 +587,7 @@ data class GenerateResult (
/**
- * #### Generated from [EngineProcessModel.kt:111]
+ * #### Generated from [EngineProcessModel.kt:116]
*/
data class GenerateTestReportArgs (
val eventLogMessage: String?,
@@ -671,7 +680,7 @@ data class GenerateTestReportArgs (
/**
- * #### Generated from [EngineProcessModel.kt:120]
+ * #### Generated from [EngineProcessModel.kt:125]
*/
data class GenerateTestReportResult (
val notifyMessage: String,
@@ -739,6 +748,75 @@ data class GenerateTestReportResult (
}
+/**
+ * #### Generated from [EngineProcessModel.kt:87]
+ */
+data class GetSpringBeanQualifiedNamesParams (
+ val classpath: Array,
+ val config: String,
+ val useSpringAnalyzer: Boolean
+) : IPrintable {
+ //companion
+
+ companion object : IMarshaller {
+ override val _type: KClass = GetSpringBeanQualifiedNamesParams::class
+
+ @Suppress("UNCHECKED_CAST")
+ override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GetSpringBeanQualifiedNamesParams {
+ val classpath = buffer.readArray {buffer.readString()}
+ val config = buffer.readString()
+ val useSpringAnalyzer = buffer.readBool()
+ return GetSpringBeanQualifiedNamesParams(classpath, config, useSpringAnalyzer)
+ }
+
+ override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GetSpringBeanQualifiedNamesParams) {
+ buffer.writeArray(value.classpath) { buffer.writeString(it) }
+ buffer.writeString(value.config)
+ buffer.writeBool(value.useSpringAnalyzer)
+ }
+
+
+ }
+ //fields
+ //methods
+ //initializer
+ //secondary constructor
+ //equals trait
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other::class != this::class) return false
+
+ other as GetSpringBeanQualifiedNamesParams
+
+ if (!(classpath contentDeepEquals other.classpath)) return false
+ if (config != other.config) return false
+ if (useSpringAnalyzer != other.useSpringAnalyzer) return false
+
+ return true
+ }
+ //hash code trait
+ override fun hashCode(): Int {
+ var __r = 0
+ __r = __r*31 + classpath.contentDeepHashCode()
+ __r = __r*31 + config.hashCode()
+ __r = __r*31 + useSpringAnalyzer.hashCode()
+ return __r
+ }
+ //pretty print
+ override fun print(printer: PrettyPrinter) {
+ printer.println("GetSpringBeanQualifiedNamesParams (")
+ printer.indent {
+ print("classpath = "); classpath.print(printer); println()
+ print("config = "); config.print(printer); println()
+ print("useSpringAnalyzer = "); useSpringAnalyzer.print(printer); println()
+ }
+ printer.print(")")
+ }
+ //deepClone
+ //contexts
+}
+
+
/**
* #### Generated from [EngineProcessModel.kt:32]
*/
@@ -803,7 +881,7 @@ data class JdkInfo (
/**
- * #### Generated from [EngineProcessModel.kt:87]
+ * #### Generated from [EngineProcessModel.kt:92]
*/
data class MethodDescription (
val name: String,
@@ -1220,7 +1298,7 @@ data class TestGeneratorParams (
/**
- * #### Generated from [EngineProcessModel.kt:106]
+ * #### Generated from [EngineProcessModel.kt:111]
*/
data class WriteSarifReportArguments (
val testSetsId: Long,
diff --git a/utbot-instrumentation-tests/build.gradle b/utbot-instrumentation-tests/build.gradle
index a6df1408c9..aefc5967a8 100644
--- a/utbot-instrumentation-tests/build.gradle
+++ b/utbot-instrumentation-tests/build.gradle
@@ -31,7 +31,7 @@ dependencies {
}
processResources {
- // We will extract this jar in `InstrumentedProcessRunner` class.
+ // We will extract this jar in `InstrumentedProcess` class.
from(configurations.fetchInstrumentationJar) {
into "instrumentation-lib"
}
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
index 07abbdb850..dc34abe14d 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt
@@ -27,7 +27,6 @@ import org.utbot.framework.plugin.api.FieldId
import org.utbot.framework.plugin.api.util.UtContext
import org.utbot.framework.plugin.api.util.signature
import org.utbot.instrumentation.instrumentation.Instrumentation
-import org.utbot.instrumentation.process.InstrumentedProcessRunner
import org.utbot.instrumentation.process.generated.ComputeStaticFieldParams
import org.utbot.instrumentation.process.generated.InvokeMethodCommandParams
import org.utbot.instrumentation.rd.InstrumentedProcess
@@ -112,7 +111,6 @@ class ConcreteExecutor> p
internal val pathsToUserClasses: String
) : Closeable, Executor {
private val ldef: LifetimeDefinition = LifetimeDefinition()
- private val instrumentedProcessRunner: InstrumentedProcessRunner = InstrumentedProcessRunner()
companion object {
@@ -161,7 +159,6 @@ class ConcreteExecutor> p
if (proc == null || !proc.lifetime.isAlive) {
proc = InstrumentedProcess(
ldef,
- instrumentedProcessRunner,
instrumentation,
pathsToUserClasses,
classLoader
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
index 0e1af9c98c..486e282537 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt
@@ -18,15 +18,10 @@ import org.utbot.instrumentation.util.KryoHelper
import org.utbot.rd.IdleWatchdog
import org.utbot.rd.ClientProtocolBuilder
import org.utbot.rd.RdSettingsContainerFactory
-import org.utbot.rd.findRdPort
import org.utbot.rd.generated.loggerModel
import org.utbot.rd.generated.settingsModel
-import org.utbot.rd.generated.synchronizationModel
import org.utbot.rd.loggers.UtRdRemoteLoggerFactory
import java.io.File
-import java.io.IOException
-import java.io.OutputStream
-import java.io.PrintStream
import java.net.URLClassLoader
import java.security.AllPermission
import kotlin.time.Duration
@@ -62,23 +57,6 @@ const val DISABLE_SANDBOX_OPTION = "--disable-sandbox"
private val logger = getLogger()
private val messageFromMainTimeout: Duration = 120.seconds
-private fun closeStandardStreams() {
- // we should change out/err streams as not to spend time on user output
- // and also because rd default logging system writes some initial values to stdout, polluting it as well
- val tmpStream = PrintStream(object : OutputStream() {
- override fun write(b: Int) {}
- })
- val prevOut = System.out
- val prevError = System.err
- System.setOut(tmpStream)
- System.setErr(tmpStream)
- // stdin/stderr should be closed as not to leave hanging descriptors
- // and we cannot log any exceptions here as rd remote logging is still not configured
- // so we pass any exceptions
- silent { prevOut.close() }
- silent { prevError.close() }
-}
-
interface DummyForMockitoWarmup {
fun method1()
}
@@ -102,9 +80,6 @@ object InstrumentedProcessMain
* It should be compiled into separate jar file (instrumented_process.jar) and be run with an agent (agent.jar) option.
*/
fun main(args: Array) = runBlocking {
- // We don't want user code to litter the standard output, so we redirect it.
- closeStandardStreams()
-
if (!args.contains(DISABLE_SANDBOX_OPTION)) {
permissions {
// Enable all permissions for instrumentation.
@@ -113,11 +88,9 @@ fun main(args: Array) = runBlocking {
}
}
- val port = findRdPort(args)
-
try {
- ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) {
- synchronizationModel.initRemoteLogging.adviseOnce(lifetime) {
+ ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(args) {
+ loggerModel.initRemoteLogging.adviseOnce(lifetime) {
Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel))
this.protocol.scheduler.queue { warmupMockito() }
}
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt
deleted file mode 100644
index 8d06d98128..0000000000
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-package org.utbot.instrumentation.process
-
-import mu.KotlinLogging
-import org.utbot.common.*
-import org.utbot.common.scanForResourcesContaining
-import org.utbot.common.utBotTempDirectory
-import org.utbot.framework.plugin.services.JdkInfoService
-import org.utbot.framework.UtSettings
-import org.utbot.framework.plugin.services.WorkingDirService
-import org.utbot.framework.process.OpenModulesContainer
-import org.utbot.instrumentation.agent.DynamicClassTransformer
-import org.utbot.rd.rdPortArgument
-import java.io.File
-
-private val logger = KotlinLogging.logger {}
-
-class InstrumentedProcessRunner {
- private val cmds: List by lazy {
- val debugCmd = listOfNotNull(DEBUG_RUN_CMD.takeIf { UtSettings.runInstrumentedProcessWithDebug })
- val javaVersionSpecificArguments = OpenModulesContainer.javaVersionSpecificArguments
- val pathToJava = JdkInfoService.provide().path
-
- listOf(pathToJava.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}").toString()) +
- debugCmd +
- javaVersionSpecificArguments +
- listOf("-javaagent:$jarFile", "-ea", "-jar", "$jarFile")
- }
-
- fun start(rdPort: Int): Process {
- val portArgument = rdPortArgument(rdPort)
-
- logger.debug { "Starting instrumented process: ${cmds.joinToString(" ")} $portArgument" }
-
- val directory = WorkingDirService.provide().toFile()
- val commandsWithOptions = buildList {
- addAll(cmds)
- if (!UtSettings.useSandbox) {
- add(DISABLE_SANDBOX_OPTION)
- }
- add(portArgument)
- }
-
- val processBuilder = ProcessBuilder(commandsWithOptions)
- .directory(directory)
-
- return processBuilder.start().also {
- logger.info {
- "------------------------------------------------------------------\n" +
- "--------Instrumented process started with PID=${it.getPid}--------\n" +
- "------------------------------------------------------------------"
- }
- }
- }
-
- companion object {
- private fun suspendValue(): String = if (UtSettings.suspendInstrumentedProcessExecutionInDebugMode) "y" else "n"
-
- private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation"
- private const val INSTRUMENTATION_LIB = "lib"
-
- private val DEBUG_RUN_CMD = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.instrumentedProcessDebugPort}"
-
- private val NULL_FILE_PATH: String = if (System.getProperty("os.name").startsWith("Windows")) {
- "NUL"
- } else {
- "/dev/null"
- }
-
- private val NULL_FILE = File(NULL_FILE_PATH)
-
- /**
- * * Firstly, searches for utbot-instrumentation jar in the classpath.
- *
- * * In case of failure, searches for utbot-instrumentation jar in the resources and extracts it to the
- * temporary directory. This jar file must be placed to the resources by `processResources` gradle task
- * in the gradle configuration of the project which depends on utbot-instrumentation module.
- */
- private val jarFile: File by lazy {
- logger.debug().measureTime({ "Finding $UTBOT_INSTRUMENTATION jar" } ) {
- run {
- logger.debug("Trying to find jar in the resources.")
- val tempDir = utBotTempDirectory.toFile()
- val unzippedJarName = "$UTBOT_INSTRUMENTATION-${currentProcessPid}.jar"
- val instrumentationJarFile = File(tempDir, unzippedJarName)
-
- InstrumentedProcessRunner::class.java.classLoader
- .firstOrNullResourceIS(INSTRUMENTATION_LIB) { resourcePath ->
- resourcePath.contains(UTBOT_INSTRUMENTATION) && resourcePath.endsWith(".jar")
- }
- ?.use { input ->
- instrumentationJarFile.writeBytes(input.readBytes())
- }
- ?: return@run null
- instrumentationJarFile
- } ?: run {
- logger.debug("Failed to find jar in the resources. Trying to find it in the classpath.")
- InstrumentedProcessRunner::class.java.classLoader
- .scanForResourcesContaining(DynamicClassTransformer::class.java.nameOfPackage)
- .firstOrNull {
- it.absolutePath.contains(UTBOT_INSTRUMENTATION) && it.extension == "jar"
- }
- } ?: error("""
- Can't find file: $UTBOT_INSTRUMENTATION-.jar.
- Make sure you added $UTBOT_INSTRUMENTATION-.jar to the resources folder from gradle.
- """.trimIndent())
- }
- }
- }
-}
\ No newline at end of file
diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
index 973c79a597..03d1ccb4ec 100644
--- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
+++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt
@@ -2,9 +2,20 @@ package org.utbot.instrumentation.rd
import com.jetbrains.rd.util.lifetime.Lifetime
import mu.KotlinLogging
+import org.utbot.common.currentProcessPid
+import org.utbot.common.debug
+import org.utbot.common.firstOrNullResourceIS
+import org.utbot.common.getPid
+import org.utbot.common.measureTime
+import org.utbot.common.nameOfPackage
+import org.utbot.common.scanForResourcesContaining
+import org.utbot.common.utBotTempDirectory
import org.utbot.framework.UtSettings
+import org.utbot.framework.plugin.services.WorkingDirService
+import org.utbot.framework.process.AbstractRDProcessCompanion
+import org.utbot.instrumentation.agent.DynamicClassTransformer
import org.utbot.instrumentation.instrumentation.Instrumentation
-import org.utbot.instrumentation.process.InstrumentedProcessRunner
+import org.utbot.instrumentation.process.DISABLE_SANDBOX_OPTION
import org.utbot.instrumentation.process.generated.AddPathsParams
import org.utbot.instrumentation.process.generated.InstrumentedProcessModel
import org.utbot.instrumentation.process.generated.SetInstrumentationParams
@@ -14,16 +25,55 @@ import org.utbot.rd.ProcessWithRdServer
import org.utbot.rd.exceptions.InstantProcessDeathException
import org.utbot.rd.generated.LoggerModel
import org.utbot.rd.generated.loggerModel
-import org.utbot.rd.generated.synchronizationModel
import org.utbot.rd.loggers.UtRdKLogger
-import org.utbot.rd.loggers.UtRdRemoteLogger
+import org.utbot.rd.loggers.setup
import org.utbot.rd.onSchedulerBlocking
import org.utbot.rd.startUtProcessWithRdServer
import org.utbot.rd.terminateOnException
+import java.io.File
private val logger = KotlinLogging.logger { }
private val rdLogger = UtRdKLogger(logger, "")
+private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation"
+private const val INSTRUMENTATION_LIB = "lib"
+
+private fun tryFindInstrumentationJarInResources(): File? {
+ logger.debug("Trying to find jar in the resources.")
+ val tempDir = utBotTempDirectory.toFile()
+ val unzippedJarName = "$UTBOT_INSTRUMENTATION-${currentProcessPid}.jar"
+ val instrumentationJarFile = File(tempDir, unzippedJarName)
+
+ InstrumentedProcess::class.java.classLoader
+ .firstOrNullResourceIS(INSTRUMENTATION_LIB) { resourcePath ->
+ resourcePath.contains(UTBOT_INSTRUMENTATION) && resourcePath.endsWith(".jar")
+ }
+ ?.use { input ->
+ instrumentationJarFile.writeBytes(input.readBytes())
+ } ?: return null
+ return instrumentationJarFile
+}
+
+private fun tryFindInstrumentationJarOnClasspath(): File? {
+ logger.debug("Trying to find it in the classpath.")
+ return InstrumentedProcess::class.java.classLoader
+ .scanForResourcesContaining(DynamicClassTransformer::class.java.nameOfPackage)
+ .firstOrNull {
+ it.absolutePath.contains(UTBOT_INSTRUMENTATION) && it.extension == "jar"
+ }
+}
+
+private val instrumentationJarFile: File =
+ logger.debug().measureTime({ "Finding $UTBOT_INSTRUMENTATION jar" } ) {
+ tryFindInstrumentationJarInResources() ?: run {
+ logger.debug("Failed to find jar in the resources.")
+ tryFindInstrumentationJarOnClasspath()
+ } ?: error("""
+ Can't find file: $UTBOT_INSTRUMENTATION-.jar.
+ Make sure you added $UTBOT_INSTRUMENTATION-.jar to the resources folder from gradle.
+ """.trimIndent())
+ }
+
class InstrumentedProcessInstantDeathException :
InstantProcessDeathException(UtSettings.instrumentedProcessDebugPort, UtSettings.runInstrumentedProcessWithDebug)
@@ -42,18 +92,41 @@ class InstrumentedProcess private constructor(
val instrumentedProcessModel: InstrumentedProcessModel = onSchedulerBlocking { protocol.instrumentedProcessModel }
val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel }
- companion object {
+ companion object : AbstractRDProcessCompanion(
+ debugPort = UtSettings.instrumentedProcessDebugPort,
+ runWithDebug = UtSettings.runInstrumentedProcessWithDebug,
+ suspendExecutionInDebugMode = UtSettings.suspendInstrumentedProcessExecutionInDebugMode,
+ processSpecificCommandLineArgs = {
+ buildList {
+ add("-javaagent:${instrumentationJarFile.path}")
+ add("-ea")
+ add("-jar")
+ add(instrumentationJarFile.path)
+ if (!UtSettings.useSandbox)
+ add(DISABLE_SANDBOX_OPTION)
+ }
+ }) {
+
suspend operator fun > invoke(
parent: Lifetime,
- instrumentedProcessRunner: InstrumentedProcessRunner,
instrumentation: TInstrumentation,
pathsToUserClasses: String,
classLoader: ClassLoader?
): InstrumentedProcess = parent.createNested().terminateOnException { lifetime ->
val rdProcess: ProcessWithRdServer = startUtProcessWithRdServer(
lifetime = lifetime
- ) {
- val process = instrumentedProcessRunner.start(it)
+ ) { port ->
+ val cmd = obtainProcessCommandLine(port)
+ logger.debug { "Starting instrumented process: $cmd" }
+ val directory = WorkingDirService.provide().toFile()
+ val processBuilder = ProcessBuilder(cmd)
+ .directory(directory)
+ val process = processBuilder.start()
+ logger.info {
+ "------------------------------------------------------------------\n" +
+ "--------Instrumented process started with PID=${process.getPid}--------\n" +
+ "------------------------------------------------------------------"
+ }
if (!process.isAlive) {
throw InstrumentedProcessInstantDeathException()
}
@@ -63,21 +136,7 @@ class InstrumentedProcess private constructor(
logger.trace("rd process started")
val proc = InstrumentedProcess(classLoader, rdProcess)
-
- // currently we do not specify log level for different categories in instrumented process
- // though it is possible with some additional map on categories -> consider performance
- proc.loggerModel.getCategoryMinimalLogLevel.set { _ ->
- // this logLevel is obtained from KotlinLogger
- rdLogger.logLevel.ordinal
- }
-
- proc.loggerModel.log.advise(proc.lifetime) {
- val logLevel = UtRdRemoteLogger.logLevelValues[it.logLevelOrdinal]
- // assume throwable already in message
- rdLogger.log(logLevel, it.message, null)
- }
-
- rdProcess.protocol.synchronizationModel.initRemoteLogging.fire(Unit)
+ proc.loggerModel.setup(rdLogger, proc.lifetime)
proc.lifetime.onTermination {
logger.trace { "process is terminating" }
diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts
index 5314824aed..97cf806fff 100644
--- a/utbot-intellij/build.gradle.kts
+++ b/utbot-intellij/build.gradle.kts
@@ -183,4 +183,4 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$junit4PlatformVersion")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit5Version")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:$junit5Version")
-}
\ No newline at end of file
+}
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt
index cd1958d82f..cfd5cb6a77 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt
@@ -13,9 +13,11 @@ import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.JavaPsiFacade
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMethod
+import com.intellij.psi.search.GlobalSearchScope
import com.intellij.refactoring.util.classMembers.MemberInfo
import com.intellij.task.ProjectTask
import com.intellij.task.ProjectTaskManager
@@ -38,7 +40,7 @@ import kotlin.io.path.pathString
import mu.KotlinLogging
import org.jetbrains.concurrency.Promise
import org.jetbrains.idea.maven.project.MavenProjectsManager
-import org.jetbrains.kotlin.idea.util.module
+import org.jetbrains.kotlin.idea.base.util.module
import org.utbot.framework.CancellationStrategyType.CANCEL_EVERYTHING
import org.utbot.framework.CancellationStrategyType.NONE
import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS
@@ -156,7 +158,15 @@ object UtTestsDialogProcessor {
}
private fun createTests(project: Project, model: GenerateTestsModel) {
- val promise = compile(project, model.srcClasses.map { it.containingFile.virtualFile }.toTypedArray())
+ val springConfigClass = when (val approach = model.typeReplacementApproach) {
+ TypeReplacementApproach.DoNotReplace -> null
+ is TypeReplacementApproach.ReplaceIfPossible ->
+ approach.config.takeUnless { it.endsWith(".xml") }?.let {
+ JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.projectScope(project)) ?:
+ error("Can't find configuration class $it")
+ }
+ }
+ val promise = compile(project, (model.srcClasses + listOfNotNull(springConfigClass)).map { it.containingFile.virtualFile }.toTypedArray())
promise.onSuccess {
if (it.hasErrors() || it.isAborted)
return@onSuccess
@@ -183,7 +193,7 @@ object UtTestsDialogProcessor {
updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0)
val buildPaths = ReadAction
- .nonBlocking { findPaths(model.srcClasses) }
+ .nonBlocking { findPaths(model.srcClasses, springConfigClass) }
.executeSynchronously()
?: return
@@ -202,28 +212,32 @@ object UtTestsDialogProcessor {
val mockFrameworkInstalled = model.mockFramework.isInstalled
val staticMockingConfigured = model.staticsMocking.isConfigured
- val applicationContext = when (model.projectType) {
- Spring -> {
- val shouldUseImplementors = when (model.typeReplacementApproach) {
- TypeReplacementApproach.DoNotReplace -> false
- is TypeReplacementApproach.ReplaceIfPossible -> true
- }
-
- SpringApplicationContext(
- mockFrameworkInstalled,
- staticMockingConfigured,
- // TODO: obtain bean definitions and other info from `utbot-spring-analyzer`
- beanQualifiedNames = emptyList(),
- shouldUseImplementors = shouldUseImplementors,
- )
- }
- else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured)
- }
-
val process = EngineProcess.createBlocking(project, classNameToPath)
process.terminateOnException { _ ->
process.setupUtContext(buildDirs + classpathList)
+ val applicationContext = when (model.projectType) {
+ Spring -> {
+ val beanQualifiedNames =
+ when (val approach = model.typeReplacementApproach) {
+ TypeReplacementApproach.DoNotReplace -> emptyList()
+ is TypeReplacementApproach.ReplaceIfPossible ->
+ process.getSpringBeanQualifiedNames(
+ buildDirs + classpathList,
+ approach.config,
+ model.useSpringAnalyzer
+ ).also { logger.info { "Detected Spring Beans: $it" } }
+ }
+
+ SpringApplicationContext(
+ mockFrameworkInstalled,
+ staticMockingConfigured,
+ beanQualifiedNames,
+ shouldUseImplementors = beanQualifiedNames.isNotEmpty(),
+ )
+ }
+ else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured)
+ }
process.createTestGenerator(
buildDirs,
classpath,
@@ -409,16 +423,23 @@ object UtTestsDialogProcessor {
}
}
- private fun findPaths(srcClasses: Set): BuildPaths? {
+ private fun findPaths(srcClasses: Set, springConfigPsiClass: PsiClass?): BuildPaths? {
val srcModule = findSrcModule(srcClasses)
+ val springConfigModule = springConfigPsiClass?.let { it.module ?: error("Module for spring configuration class not found") }
- val buildDirs = CompilerPaths.getOutputPaths(arrayOf(srcModule))
+ val buildDirs = CompilerPaths.getOutputPaths(setOfNotNull(
+ srcModule, springConfigModule
+ ).toTypedArray())
.toList()
.filter { Paths.get(it).exists() }
.nullize() ?: return null
val pathsList = OrderEnumerator.orderEntries(srcModule).recursively().pathsList
+ springConfigModule?.takeIf { it != srcModule }?.let { module ->
+ pathsList.addAll(OrderEnumerator.orderEntries(module).recursively().pathsList.pathList)
+ }
+
val (classpath, classpathList) = if (IntelliJApiHelper.isAndroidStudio()) {
// Filter out manifests from classpath.
val filterPredicate = { it: String ->
@@ -442,6 +463,6 @@ object UtTestsDialogProcessor {
val classpath: String,
val classpathList: List,
val pluginJarsPath: List
- // ^ TODO: Now we collect ALL dependent libs and pass them to the instrumented process. Most of them are redundant.
+ // ^ TODO: Now we collect ALL dependent libs and pass them to the instrumented and spring analyzer processes. Most of them are redundant.
)
}
\ No newline at end of file
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt
index ca30e87370..211c7174a8 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt
@@ -56,6 +56,9 @@ class GenerateTestsModel(
lateinit var typeReplacementApproach: TypeReplacementApproach
+ // TODO remove once issue with spring-analyzer expecting .properties and .xml file paths is resolved
+ var useSpringAnalyzer = false
+
val conflictTriggers: ConflictTriggers = ConflictTriggers()
var runGeneratedTestsWithCoverage : Boolean = false
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
index 6ce0c11c32..ca27e4bd85 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
@@ -19,9 +19,8 @@ import org.utbot.framework.codegen.domain.*
import org.utbot.framework.codegen.tree.ututils.UtilClassKind
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.services.JdkInfo
-import org.utbot.framework.plugin.services.JdkInfoService
import org.utbot.framework.plugin.services.WorkingDirService
-import org.utbot.framework.process.OpenModulesContainer
+import org.utbot.framework.process.AbstractRDProcessCompanion
import org.utbot.framework.process.generated.*
import org.utbot.framework.process.generated.MethodDescription
import org.utbot.framework.util.Conflict
@@ -49,90 +48,67 @@ import kotlin.io.path.pathString
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
-private val engineProcessLogConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations")
-private val logger = KotlinLogging.logger {}
-private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs")
+private val engineProcessLogConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations").also { it.mkdirs() }
+private val logger = KotlinLogging.logger {}.also { overrideDefaultRdLoggerFactoryWithKLogger(it) }
+private val engineProcessLogDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs").also { it.mkdirs() }
private const val configurationFileDeleteKey = "delete_this_comment_key"
private const val deleteOpenComment = ""
-data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long)
-
-class EngineProcessInstantDeathException :
- InstantProcessDeathException(UtSettings.engineProcessDebugPort, UtSettings.runEngineProcessWithDebug)
-
-class EngineProcess private constructor(val project: Project, private val classNameToPath: Map, rdProcess: ProcessWithRdServer) :
- ProcessWithRdServer by rdProcess {
- companion object {
- private val log4j2ConfigFile: File
-
- init {
- engineProcessLogDirectory.mkdirs()
- engineProcessLogConfigurationsDirectory.mkdirs()
- overrideDefaultRdLoggerFactoryWithKLogger(logger)
-
- val customFile = File(UtSettings.engineProcessLogConfigFile)
-
- if (customFile.exists()) {
- log4j2ConfigFile = customFile
- } else {
- log4j2ConfigFile = Files.createTempFile(engineProcessLogConfigurationsDirectory.toPath(), null, ".xml").toFile()
- this.javaClass.classLoader.getResourceAsStream("log4j2.xml")?.use { logConfig ->
- val resultConfig = logConfig.readBytes().toString(Charset.defaultCharset())
- .replace(Regex("$deleteOpenComment|$deleteCloseComment"), "")
- .replace("ref=\"IdeaAppender\"", "ref=\"EngineProcessAppender\"")
- .replace("\${env:UTBOT_LOG_DIR}", engineProcessLogDirectory.canonicalPath.trimEnd(File.separatorChar) + File.separatorChar)
- Files.copy(
- resultConfig.byteInputStream(),
- log4j2ConfigFile.toPath(),
- StandardCopyOption.REPLACE_EXISTING
- )
- }
- }
- }
+private fun createEngineProcessLog4j2Config(): File {
+ val customFile = File(UtSettings.engineProcessLogConfigFile)
+
+ val log4j2ConfigFile =
+ if (customFile.exists()) customFile
+ else Files.createTempFile(engineProcessLogConfigurationsDirectory.toPath(), null, ".xml").toFile()
+
+ EngineProcess::class.java.classLoader.getResourceAsStream("log4j2.xml")?.use { logConfig ->
+ val resultConfig = logConfig.readBytes().toString(Charset.defaultCharset())
+ .replace(Regex("$deleteOpenComment|$deleteCloseComment"), "")
+ .replace("ref=\"IdeaAppender\"", "ref=\"EngineProcessAppender\"")
+ .replace("\${env:UTBOT_LOG_DIR}", engineProcessLogDirectory.canonicalPath.trimEnd(File.separatorChar) + File.separatorChar)
+ Files.copy(
+ resultConfig.byteInputStream(),
+ log4j2ConfigFile.toPath(),
+ StandardCopyOption.REPLACE_EXISTING
+ )
+ }
+ return log4j2ConfigFile
+}
- private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}"
+private val log4j2ConfigFile: File = createEngineProcessLog4j2Config()
- private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n"
+private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}"
- private val debugArgument: String?
- get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}"
- .takeIf { UtSettings.runEngineProcessWithDebug }
+private val pluginClasspath: String
+ get() = (EngineProcess::class.java.classLoader as PluginClassLoader).classPath.baseUrls.joinToString(
+ separator = File.pathSeparator,
+ prefix = "\"",
+ postfix = "\""
+ )
- private val javaExecutablePathString: Path
- get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}")
+private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt"
- private val pluginClasspath: String
- get() = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString(
- separator = File.pathSeparator,
- prefix = "\"",
- postfix = "\""
- )
-
- private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt"
+data class RdTestGenerationResult(val notEmptyCases: Int, val testSetsId: Long)
- private fun obtainEngineProcessCommandLine(port: Int) = buildList {
- add(javaExecutablePathString.pathString)
- add("-ea")
- val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments
- if (javaVersionSpecificArgs.isNotEmpty()) {
- addAll(javaVersionSpecificArgs)
- }
- debugArgument?.let { add(it) }
- add(log4j2ConfigSwitch)
- add("-cp")
- add(pluginClasspath)
- add(startFileName)
- add(rdPortArgument(port))
- }
+class EngineProcessInstantDeathException :
+ InstantProcessDeathException(UtSettings.engineProcessDebugPort, UtSettings.runEngineProcessWithDebug)
+class EngineProcess private constructor(val project: Project, private val classNameToPath: Map, rdProcess: ProcessWithRdServer) :
+ ProcessWithRdServer by rdProcess {
+ companion object : AbstractRDProcessCompanion(
+ debugPort = UtSettings.engineProcessDebugPort,
+ runWithDebug = UtSettings.runEngineProcessWithDebug,
+ suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode,
+ processSpecificCommandLineArgs = { listOf("-ea", log4j2ConfigSwitch, "-cp", pluginClasspath, startFileName) }
+ ) {
fun createBlocking(project: Project, classNameToPath: Map): EngineProcess = runBlocking { EngineProcess(project, classNameToPath) }
suspend operator fun invoke(project: Project, classNameToPath: Map): EngineProcess =
LifetimeDefinition().terminateOnException { lifetime ->
val rdProcess = startUtProcessWithRdServer(lifetime) { port ->
- val cmd = obtainEngineProcessCommandLine(port)
+ val cmd = obtainProcessCommandLine(port)
val directory = WorkingDirService.provide().toFile()
val builder = ProcessBuilder(cmd).directory(directory)
val process = builder.start()
@@ -167,6 +143,13 @@ class EngineProcess private constructor(val project: Project, private val classN
engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader))
}
+ fun getSpringBeanQualifiedNames(classpathList: List, config: String, useSpringAnalyzer: Boolean): List {
+ assertReadAccessNotAllowed()
+ return engineModel.getSpringBeanQualifiedNames.startBlocking(
+ GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config, useSpringAnalyzer)
+ ).toList()
+ }
+
private fun computeSourceFileByClass(params: ComputeSourceFileByClassArguments): String =
DumbService.getInstance(project).runReadActionInSmartMode {
val scope = GlobalSearchScope.allScope(project)
diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle
index be70e3e35e..549a850bbc 100644
--- a/utbot-rd/build.gradle
+++ b/utbot-rd/build.gradle
@@ -257,6 +257,31 @@ task generateCommonModels(type: RdGenTask) {
}
}
+task generateSpringModels(type: RdGenTask) {
+ def currentProjectDir = project.projectDir
+ def ideaPluginProjectDir = project.rootProject.childProjects["utbot-spring-analyzer"].projectDir
+ def generatedOutputDir = new File(ideaPluginProjectDir, "src/main/kotlin/org/utbot/spring/generated")
+ def hashDir = generatedOutputDir
+ def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models")
+ def rdParams = extensions.getByName("params") as RdGenExtension
+
+ group = "rdgen"
+ rdParams.verbose = true
+ rdParams.sources(sourcesDir)
+ rdParams.hashFolder = hashDir.canonicalPath
+ // where to search roots
+ rdParams.packages = "org.utbot.rd.models"
+
+ rdParams.generator {
+ language = "kotlin"
+ transform = "symmetric"
+ root = "org.utbot.rd.models.SpringAnalyzerRoot"
+
+ directory = generatedOutputDir.canonicalPath
+ namespace = "org.utbot.spring.generated"
+ }
+}
+
task generateCSharpModels(type: RdGenTask) {
def currentProjectDir = project.projectDir
def riderPluginProjectDir = project.rootProject.projectDir.toPath().resolve("utbot-rider").toFile()
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
index a9b3d23bc0..133437c11c 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt
@@ -21,6 +21,8 @@ import org.utbot.common.*
import org.utbot.rd.generated.synchronizationModel
import org.utbot.rd.loggers.withLevel
import java.io.File
+import java.io.OutputStream
+import java.io.PrintStream
import kotlin.time.Duration
const val rdProcessDirName = "rdProcessSync"
@@ -146,7 +148,28 @@ class IdleWatchdog(private val ldef: LifetimeDefinition, val timeout: Duration)
class ClientProtocolBuilder {
private var timeout = Duration.INFINITE
- suspend fun start(port: Int, parent: Lifetime? = null, block: Protocol.(IdleWatchdog) -> Unit) {
+ private fun silentlyCloseStandardStreams() {
+ // we should change out/err streams as not to spend time on user output
+ // and also because rd default logging system writes some initial values to stdout, polluting it as well
+ val tmpStream = PrintStream(object : OutputStream() {
+ override fun write(b: Int) {}
+ })
+ val prevOut = System.out
+ val prevError = System.err
+ System.setOut(tmpStream)
+ System.setErr(tmpStream)
+ // stdin/stderr should be closed as not to leave hanging descriptors
+ // and we cannot log any exceptions here as rd remote logging is still not configured
+ // so we pass any exceptions
+ silent { prevOut.close() }
+ silent { prevError.close() }
+ }
+
+ suspend fun start(args: Array, parent: Lifetime? = null, block: Protocol.(IdleWatchdog) -> Unit) {
+ silentlyCloseStandardStreams()
+
+ val port = findRdPort(args)
+
UtRdCoroutineScope.initialize()
val pid = currentProcessPid.toInt()
val ldef = parent?.createNested() ?: LifetimeDefinition()
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt
index 11d81e7768..f5a45841b3 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/LoggerModel.Generated.kt
@@ -18,6 +18,7 @@ import kotlin.jvm.JvmStatic
* #### Generated from [LoggerModel.kt:8]
*/
class LoggerModel private constructor(
+ private val _initRemoteLogging: RdSignal,
private val _log: RdSignal,
private val _getCategoryMinimalLogLevel: RdCall
) : RdExtBase() {
@@ -47,13 +48,14 @@ class LoggerModel private constructor(
}
- const val serializationHash = -6259198217478203203L
+ const val serializationHash = 1686273842005935878L
}
override val serializersOwner: ISerializersOwner get() = LoggerModel
override val serializationHash: Long get() = LoggerModel.serializationHash
//fields
+ val initRemoteLogging: IAsyncSignal get() = _initRemoteLogging
val log: IAsyncSignal get() = _log
/**
@@ -64,11 +66,13 @@ class LoggerModel private constructor(
//methods
//initializer
init {
+ _initRemoteLogging.async = true
_log.async = true
_getCategoryMinimalLogLevel.async = true
}
init {
+ bindableChildren.add("initRemoteLogging" to _initRemoteLogging)
bindableChildren.add("log" to _log)
bindableChildren.add("getCategoryMinimalLogLevel" to _getCategoryMinimalLogLevel)
}
@@ -76,6 +80,7 @@ class LoggerModel private constructor(
//secondary constructor
private constructor(
) : this(
+ RdSignal(FrameworkMarshallers.Void),
RdSignal(LogArguments),
RdCall(FrameworkMarshallers.String, FrameworkMarshallers.Int)
)
@@ -86,6 +91,7 @@ class LoggerModel private constructor(
override fun print(printer: PrettyPrinter) {
printer.println("LoggerModel (")
printer.indent {
+ print("initRemoteLogging = "); _initRemoteLogging.print(printer); println()
print("log = "); _log.print(printer); println()
print("getCategoryMinimalLogLevel = "); _getCategoryMinimalLogLevel.print(printer); println()
}
@@ -94,6 +100,7 @@ class LoggerModel private constructor(
//deepClone
override fun deepClone(): LoggerModel {
return LoggerModel(
+ _initRemoteLogging.deepClonePolymorphic(),
_log.deepClonePolymorphic(),
_getCategoryMinimalLogLevel.deepClonePolymorphic()
)
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
index 503f6dd889..06c65bcc62 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SynchronizationModel.Generated.kt
@@ -19,7 +19,6 @@ import kotlin.jvm.JvmStatic
*/
class SynchronizationModel private constructor(
private val _suspendTimeoutTimer: RdCall,
- private val _initRemoteLogging: RdSignal,
private val _synchronizationSignal: RdSignal,
private val _stopProcess: RdSignal
) : RdExtBase() {
@@ -48,7 +47,7 @@ class SynchronizationModel private constructor(
}
- const val serializationHash = 5881306106692642003L
+ const val serializationHash = -8851121542976813112L
}
override val serializersOwner: ISerializersOwner get() = SynchronizationModel
@@ -56,7 +55,6 @@ class SynchronizationModel private constructor(
//fields
val suspendTimeoutTimer: RdCall get() = _suspendTimeoutTimer
- val initRemoteLogging: IAsyncSignal get() = _initRemoteLogging
val synchronizationSignal: IAsyncSignal get() = _synchronizationSignal
/**
@@ -67,14 +65,12 @@ class SynchronizationModel private constructor(
//initializer
init {
_suspendTimeoutTimer.async = true
- _initRemoteLogging.async = true
_synchronizationSignal.async = true
_stopProcess.async = true
}
init {
bindableChildren.add("suspendTimeoutTimer" to _suspendTimeoutTimer)
- bindableChildren.add("initRemoteLogging" to _initRemoteLogging)
bindableChildren.add("synchronizationSignal" to _synchronizationSignal)
bindableChildren.add("stopProcess" to _stopProcess)
}
@@ -83,7 +79,6 @@ class SynchronizationModel private constructor(
private constructor(
) : this(
RdCall(FrameworkMarshallers.Bool, FrameworkMarshallers.Void),
- RdSignal(FrameworkMarshallers.Void),
RdSignal(FrameworkMarshallers.String),
RdSignal(FrameworkMarshallers.Void)
)
@@ -95,7 +90,6 @@ class SynchronizationModel private constructor(
printer.println("SynchronizationModel (")
printer.indent {
print("suspendTimeoutTimer = "); _suspendTimeoutTimer.print(printer); println()
- print("initRemoteLogging = "); _initRemoteLogging.print(printer); println()
print("synchronizationSignal = "); _synchronizationSignal.print(printer); println()
print("stopProcess = "); _stopProcess.print(printer); println()
}
@@ -105,7 +99,6 @@ class SynchronizationModel private constructor(
override fun deepClone(): SynchronizationModel {
return SynchronizationModel(
_suspendTimeoutTimer.deepClonePolymorphic(),
- _initRemoteLogging.deepClonePolymorphic(),
_synchronizationSignal.deepClonePolymorphic(),
_stopProcess.deepClonePolymorphic()
)
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
index b696b5f55c..b1e5dfd01e 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdConsoleLogger.kt
@@ -15,7 +15,7 @@ class UtRdConsoleLogger(
}
private fun format(category: String, level: LogLevel, message: Any?, throwable: Throwable?) : String {
- val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception() //to print stacktrace
+ val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception("No exception was actually thrown, this exception is used purely to log trace")
val rdCategory = if (category.isNotEmpty()) "RdCategory: ${category.substringAfterLast('.').padEnd(25)} | " else ""
return "${LocalDateTime.now().format(timeFormatter)} | ${level.toString().uppercase().padEnd(5)} | $rdCategory${message?.toString()?:""} ${throwableToPrint?.getThrowableText()?.let { "| $it" }?:""}"
}
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
index b706757690..f1bd3f9349 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdKLogger.kt
@@ -24,7 +24,7 @@ class UtRdKLogger(private val realLogger: KLogger, val category: String) : Logge
}
private fun format(level: LogLevel, message: Any?, throwable: Throwable?): String {
- val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception() //to print stacktrace
+ val throwableToPrint = if (level < LogLevel.Error) throwable else throwable ?: Exception("No exception was actually thrown, this exception is used purely to log trace")
val rdCategory = if (category.isNotEmpty()) "RdCategory: ${category.substringAfterLast('.').padEnd(25)} | " else ""
return "$rdCategory${message?.toString() ?: ""} ${throwableToPrint?.getThrowableText()?.let { "| $it" } ?: ""}"
}
diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt
index 9aae485486..4ed40d3f2f 100644
--- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt
+++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt
@@ -4,7 +4,7 @@ import com.jetbrains.rd.util.*
import com.jetbrains.rd.util.lifetime.Lifetime
import mu.KLogger
import org.utbot.common.LoggerWithLogMethod
-
+import org.utbot.rd.generated.LoggerModel
fun Logger.withLevel(logLevel: LogLevel): LoggerWithLogMethod = LoggerWithLogMethod {
this.log(logLevel, it)
@@ -24,4 +24,21 @@ fun overrideDefaultRdLoggerFactoryWithKLogger(logger: KLogger) {
if (Statics().get() !is UtRdKLoggerFactory) {
Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger))
}
-}
\ No newline at end of file
+}
+
+fun LoggerModel.setup(rdLogger: UtRdKLogger, processLifetime: Lifetime) {
+ // currently we do not specify log level for different categories
+ // though it is possible with some additional map on categories -> consider performance
+ getCategoryMinimalLogLevel.set { _ ->
+ // this logLevel is obtained from KotlinLogger
+ rdLogger.logLevel.ordinal
+ }
+
+ log.advise(processLifetime) {
+ val logLevel = UtRdRemoteLogger.logLevelValues[it.logLevelOrdinal]
+ // assume throwable already in message
+ rdLogger.log(logLevel, it.message, null)
+ }
+
+ initRemoteLogging.fire(Unit)
+}
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
index bbfe08a25a..b94b75d16c 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
@@ -84,6 +84,11 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
val setupContextParams = structdef {
field("classpathForUrlsClassloader", immutableList(PredefinedType.string))
}
+ val getSpringBeanQualifiedNamesParams = structdef {
+ field("classpath", array(PredefinedType.string))
+ field("config", PredefinedType.string)
+ field("useSpringAnalyzer", PredefinedType.bool)
+ }
val methodDescription = structdef {
field("name", PredefinedType.string)
field("containingClass", PredefinedType.string.nullable)
@@ -124,6 +129,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) {
}
init {
call("setupUtContext", setupContextParams, PredefinedType.void).async
+ call("getSpringBeanQualifiedNames", getSpringBeanQualifiedNamesParams, array(PredefinedType.string)).async
call("createTestGenerator", testGeneratorParams, PredefinedType.void).async
call("isCancelled", PredefinedType.void, PredefinedType.bool).async
call("generate", generateParams, generateResult).async
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt
index 7ca4d8e6b7..c3f2e93e4f 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/LoggerModel.kt
@@ -13,6 +13,7 @@ object LoggerModel : Ext(LoggerRoot) {
}
init {
+ signal("initRemoteLogging", PredefinedType.void).async
signal("log", logArguments).async
call(
"getCategoryMinimalLogLevel",
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt
new file mode 100644
index 0000000000..4a6dc98a64
--- /dev/null
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt
@@ -0,0 +1,24 @@
+@file:Suppress("unused")
+package org.utbot.rd.models
+
+import com.jetbrains.rd.generator.nova.*
+
+object SpringAnalyzerRoot : Root()
+
+object SpringAnalyzerProcessModel : Ext(SpringAnalyzerRoot) {
+ val springAnalyzerParams = structdef {
+ field("classpath", array(PredefinedType.string))
+ field("configuration", PredefinedType.string)
+ field("propertyFilesPaths", array(PredefinedType.string))
+ field("xmlConfigurationPaths", array(PredefinedType.string))
+ field("useSpringAnalyzer", PredefinedType.bool)
+ }
+
+ val springAnalyzerResult = structdef {
+ field("beanTypes", array(PredefinedType.string))
+ }
+
+ init {
+ call("analyze", springAnalyzerParams, springAnalyzerResult).async
+ }
+}
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
index 077173ba23..f0d86d23fa 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SynchronizationModel.kt
@@ -8,7 +8,6 @@ object SynchronizationRoot: Root()
object SynchronizationModel: Ext(SynchronizationRoot) {
init {
call("suspendTimeoutTimer", PredefinedType.bool, PredefinedType.void).async
- signal("initRemoteLogging", PredefinedType.void).async
signal("synchronizationSignal", PredefinedType.string).async
signal("StopProcess", PredefinedType.void).apply {
async
diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts
index 2d085bfa62..8327393282 100644
--- a/utbot-spring-analyzer/build.gradle.kts
+++ b/utbot-spring-analyzer/build.gradle.kts
@@ -1,30 +1,43 @@
-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
+import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
+val springBootVersion: String by rootProject
+val rdVersion: String by rootProject
+val commonsLoggingVersion: String by rootProject
+val kotlinLoggingVersion: String by rootProject
+val commonsIOVersion: String by rootProject
+
plugins {
- id("org.springframework.boot") version "2.7.8"
- id("io.spring.dependency-management") version "1.1.0"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("java")
application
}
java {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_17
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
- implementation("org.springframework.boot:spring-boot-starter")
- implementation("org.springframework.boot:spring-boot-starter-web")
+ // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot
+ implementation("org.springframework.boot:spring-boot:$springBootVersion")
+
+ implementation(project(":utbot-rd"))
+ implementation(project(":utbot-core"))
+ implementation(project(":utbot-framework-api"))
+ implementation("com.jetbrains.rd:rd-framework:$rdVersion")
+ implementation("com.jetbrains.rd:rd-core:$rdVersion")
+ implementation("commons-logging:commons-logging:$commonsLoggingVersion")
+ implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
+ implementation("commons-io:commons-io:$commonsIOVersion")
}
application {
- mainClass.set("org.utbot.spring.ApplicationRunnerKt")
+ mainClass.set("org.utbot.spring.process.SpringAnalyzerProcessMainKt")
}
// see more details about this task -- https://github.com/spring-projects/spring-boot/issues/1828
-tasks.withType(ShadowJar::class.java) {
+tasks.shadowJar {
isZip64 = true
// Required for Spring
mergeServiceFiles()
@@ -35,4 +48,16 @@ tasks.withType(ShadowJar::class.java) {
paths = listOf("META-INF/spring.factories")
mergeStrategy = "append"
})
+
+ transform(Log4j2PluginsCacheFileTransformer::class.java)
+ archiveFileName.set("utbot-spring-analyzer-shadow.jar")
+}
+
+val springAnalyzerJar: Configuration by configurations.creating {
+ isCanBeResolved = false
+ isCanBeConsumed = true
+}
+
+artifacts {
+ add(springAnalyzerJar.name, tasks.shadowJar)
}
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzers/SpringApplicationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzers/SpringApplicationAnalyzer.kt
index fd75d46171..1ee6143d3f 100644
--- a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzers/SpringApplicationAnalyzer.kt
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzers/SpringApplicationAnalyzer.kt
@@ -8,13 +8,14 @@ import org.utbot.spring.configurators.ApplicationConfigurationType.XmlConfigurat
import org.utbot.spring.configurators.ApplicationConfigurator
import org.utbot.spring.data.ApplicationData
import org.utbot.spring.utils.FakeFileManager
+import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
import java.io.File
class SpringApplicationAnalyzer(
private val applicationData: ApplicationData
) {
- fun analyze() {
+ fun analyze(): List {
val fakeFileManager =
FakeFileManager(applicationData.propertyFilesPaths + applicationData.xmlConfigurationPaths)
fakeFileManager.createTempFiles()
@@ -38,6 +39,7 @@ class SpringApplicationAnalyzer(
} finally {
fakeFileManager.deleteTempFiles()
}
+ return UtBotBeanFactoryPostProcessor.beanQualifiedNames
}
private fun findConfigurationType(applicationData: ApplicationData): ApplicationConfigurationType {
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/config/TestApplicationConfiguration.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/config/TestApplicationConfiguration.kt
index 3b4d255553..b783fdc3b0 100644
--- a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/config/TestApplicationConfiguration.kt
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/config/TestApplicationConfiguration.kt
@@ -11,7 +11,6 @@ import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
open class TestApplicationConfiguration {
@Bean
- open fun utBotBeanFactoryPostProcessor(): BeanFactoryPostProcessor {
- return UtBotBeanFactoryPostProcessor()
- }
+ open fun utBotBeanFactoryPostProcessor(): BeanFactoryPostProcessor =
+ UtBotBeanFactoryPostProcessor
}
\ No newline at end of file
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt
new file mode 100644
index 0000000000..1274cfe070
--- /dev/null
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt
@@ -0,0 +1,231 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection")
+package org.utbot.spring.generated
+
+import com.jetbrains.rd.framework.*
+import com.jetbrains.rd.framework.base.*
+import com.jetbrains.rd.framework.impl.*
+
+import com.jetbrains.rd.util.lifetime.*
+import com.jetbrains.rd.util.reactive.*
+import com.jetbrains.rd.util.string.*
+import com.jetbrains.rd.util.*
+import kotlin.reflect.KClass
+import kotlin.jvm.JvmStatic
+
+
+
+/**
+ * #### Generated from [SpringAnalyzerModel.kt:8]
+ */
+class SpringAnalyzerProcessModel private constructor(
+ private val _analyze: RdCall
+) : RdExtBase() {
+ //companion
+
+ companion object : ISerializersOwner {
+
+ override fun registerSerializersCore(serializers: ISerializers) {
+ serializers.register(SpringAnalyzerParams)
+ serializers.register(SpringAnalyzerResult)
+ }
+
+
+ @JvmStatic
+ @JvmName("internalCreateModel")
+ @Deprecated("Use create instead", ReplaceWith("create(lifetime, protocol)"))
+ internal fun createModel(lifetime: Lifetime, protocol: IProtocol): SpringAnalyzerProcessModel {
+ @Suppress("DEPRECATION")
+ return create(lifetime, protocol)
+ }
+
+ @JvmStatic
+ @Deprecated("Use protocol.springAnalyzerProcessModel or revise the extension scope instead", ReplaceWith("protocol.springAnalyzerProcessModel"))
+ fun create(lifetime: Lifetime, protocol: IProtocol): SpringAnalyzerProcessModel {
+ SpringAnalyzerRoot.register(protocol.serializers)
+
+ return SpringAnalyzerProcessModel()
+ }
+
+
+ const val serializationHash = 528909252282504878L
+
+ }
+ override val serializersOwner: ISerializersOwner get() = SpringAnalyzerProcessModel
+ override val serializationHash: Long get() = SpringAnalyzerProcessModel.serializationHash
+
+ //fields
+ val analyze: RdCall get() = _analyze
+ //methods
+ //initializer
+ init {
+ _analyze.async = true
+ }
+
+ init {
+ bindableChildren.add("analyze" to _analyze)
+ }
+
+ //secondary constructor
+ private constructor(
+ ) : this(
+ RdCall(SpringAnalyzerParams, SpringAnalyzerResult)
+ )
+
+ //equals trait
+ //hash code trait
+ //pretty print
+ override fun print(printer: PrettyPrinter) {
+ printer.println("SpringAnalyzerProcessModel (")
+ printer.indent {
+ print("analyze = "); _analyze.print(printer); println()
+ }
+ printer.print(")")
+ }
+ //deepClone
+ override fun deepClone(): SpringAnalyzerProcessModel {
+ return SpringAnalyzerProcessModel(
+ _analyze.deepClonePolymorphic()
+ )
+ }
+ //contexts
+}
+val IProtocol.springAnalyzerProcessModel get() = getOrCreateExtension(SpringAnalyzerProcessModel::class) { @Suppress("DEPRECATION") SpringAnalyzerProcessModel.create(lifetime, this) }
+
+
+
+/**
+ * #### Generated from [SpringAnalyzerModel.kt:9]
+ */
+data class SpringAnalyzerParams (
+ val classpath: Array,
+ val configuration: String,
+ val propertyFilesPaths: Array,
+ val xmlConfigurationPaths: Array,
+ val useSpringAnalyzer: Boolean
+) : IPrintable {
+ //companion
+
+ companion object : IMarshaller {
+ override val _type: KClass = SpringAnalyzerParams::class
+
+ @Suppress("UNCHECKED_CAST")
+ override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SpringAnalyzerParams {
+ val classpath = buffer.readArray {buffer.readString()}
+ val configuration = buffer.readString()
+ val propertyFilesPaths = buffer.readArray {buffer.readString()}
+ val xmlConfigurationPaths = buffer.readArray {buffer.readString()}
+ val useSpringAnalyzer = buffer.readBool()
+ return SpringAnalyzerParams(classpath, configuration, propertyFilesPaths, xmlConfigurationPaths, useSpringAnalyzer)
+ }
+
+ override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SpringAnalyzerParams) {
+ buffer.writeArray(value.classpath) { buffer.writeString(it) }
+ buffer.writeString(value.configuration)
+ buffer.writeArray(value.propertyFilesPaths) { buffer.writeString(it) }
+ buffer.writeArray(value.xmlConfigurationPaths) { buffer.writeString(it) }
+ buffer.writeBool(value.useSpringAnalyzer)
+ }
+
+
+ }
+ //fields
+ //methods
+ //initializer
+ //secondary constructor
+ //equals trait
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other::class != this::class) return false
+
+ other as SpringAnalyzerParams
+
+ if (!(classpath contentDeepEquals other.classpath)) return false
+ if (configuration != other.configuration) return false
+ if (!(propertyFilesPaths contentDeepEquals other.propertyFilesPaths)) return false
+ if (!(xmlConfigurationPaths contentDeepEquals other.xmlConfigurationPaths)) return false
+ if (useSpringAnalyzer != other.useSpringAnalyzer) return false
+
+ return true
+ }
+ //hash code trait
+ override fun hashCode(): Int {
+ var __r = 0
+ __r = __r*31 + classpath.contentDeepHashCode()
+ __r = __r*31 + configuration.hashCode()
+ __r = __r*31 + propertyFilesPaths.contentDeepHashCode()
+ __r = __r*31 + xmlConfigurationPaths.contentDeepHashCode()
+ __r = __r*31 + useSpringAnalyzer.hashCode()
+ return __r
+ }
+ //pretty print
+ override fun print(printer: PrettyPrinter) {
+ printer.println("SpringAnalyzerParams (")
+ printer.indent {
+ print("classpath = "); classpath.print(printer); println()
+ print("configuration = "); configuration.print(printer); println()
+ print("propertyFilesPaths = "); propertyFilesPaths.print(printer); println()
+ print("xmlConfigurationPaths = "); xmlConfigurationPaths.print(printer); println()
+ print("useSpringAnalyzer = "); useSpringAnalyzer.print(printer); println()
+ }
+ printer.print(")")
+ }
+ //deepClone
+ //contexts
+}
+
+
+/**
+ * #### Generated from [SpringAnalyzerModel.kt:17]
+ */
+data class SpringAnalyzerResult (
+ val beanTypes: Array
+) : IPrintable {
+ //companion
+
+ companion object : IMarshaller {
+ override val _type: KClass = SpringAnalyzerResult::class
+
+ @Suppress("UNCHECKED_CAST")
+ override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): SpringAnalyzerResult {
+ val beanTypes = buffer.readArray {buffer.readString()}
+ return SpringAnalyzerResult(beanTypes)
+ }
+
+ override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SpringAnalyzerResult) {
+ buffer.writeArray(value.beanTypes) { buffer.writeString(it) }
+ }
+
+
+ }
+ //fields
+ //methods
+ //initializer
+ //secondary constructor
+ //equals trait
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other::class != this::class) return false
+
+ other as SpringAnalyzerResult
+
+ if (!(beanTypes contentDeepEquals other.beanTypes)) return false
+
+ return true
+ }
+ //hash code trait
+ override fun hashCode(): Int {
+ var __r = 0
+ __r = __r*31 + beanTypes.contentDeepHashCode()
+ return __r
+ }
+ //pretty print
+ override fun print(printer: PrettyPrinter) {
+ printer.println("SpringAnalyzerResult (")
+ printer.indent {
+ print("beanTypes = "); beanTypes.print(printer); println()
+ }
+ printer.print(")")
+ }
+ //deepClone
+ //contexts
+}
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt
new file mode 100644
index 0000000000..2ad0d56bac
--- /dev/null
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt
@@ -0,0 +1,58 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection")
+package org.utbot.spring.generated
+
+import com.jetbrains.rd.framework.*
+import com.jetbrains.rd.framework.base.*
+import com.jetbrains.rd.framework.impl.*
+
+import com.jetbrains.rd.util.lifetime.*
+import com.jetbrains.rd.util.reactive.*
+import com.jetbrains.rd.util.string.*
+import com.jetbrains.rd.util.*
+import kotlin.reflect.KClass
+import kotlin.jvm.JvmStatic
+
+
+
+/**
+ * #### Generated from [SpringAnalyzerModel.kt:6]
+ */
+class SpringAnalyzerRoot private constructor(
+) : RdExtBase() {
+ //companion
+
+ companion object : ISerializersOwner {
+
+ override fun registerSerializersCore(serializers: ISerializers) {
+ SpringAnalyzerRoot.register(serializers)
+ SpringAnalyzerProcessModel.register(serializers)
+ }
+
+
+
+
+
+ const val serializationHash = -4315357569975275049L
+
+ }
+ override val serializersOwner: ISerializersOwner get() = SpringAnalyzerRoot
+ override val serializationHash: Long get() = SpringAnalyzerRoot.serializationHash
+
+ //fields
+ //methods
+ //initializer
+ //secondary constructor
+ //equals trait
+ //hash code trait
+ //pretty print
+ override fun print(printer: PrettyPrinter) {
+ printer.println("SpringAnalyzerRoot (")
+ printer.print(")")
+ }
+ //deepClone
+ override fun deepClone(): SpringAnalyzerRoot {
+ return SpringAnalyzerRoot(
+ )
+ }
+ //contexts
+}
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt
new file mode 100644
index 0000000000..9fd5f67c9c
--- /dev/null
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt
@@ -0,0 +1,34 @@
+package org.utbot.spring.loggers
+
+import com.jetbrains.rd.util.LogLevel
+import com.jetbrains.rd.util.getLogger
+import org.apache.commons.logging.Log
+import org.apache.commons.logging.impl.LogFactoryImpl
+
+@Suppress("unused") // used via -Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory
+class RDApacheCommonsLogFactory : LogFactoryImpl() {
+ override fun getInstance(name: String): Log {
+ val logger = getLogger(category = name)
+ return object : Log {
+ override fun trace(message: Any?) = logger.log(LogLevel.Trace, message, throwable = null)
+ override fun trace(message: Any?, t: Throwable?) = logger.log(LogLevel.Trace, message, throwable = t)
+ override fun debug(message: Any?) = logger.log(LogLevel.Debug, message, throwable = null)
+ override fun debug(message: Any?, t: Throwable?) = logger.log(LogLevel.Debug, message, throwable = t)
+ override fun info(message: Any?) = logger.log(LogLevel.Info, message, throwable = null)
+ override fun info(message: Any?, t: Throwable?) = logger.log(LogLevel.Info, message, throwable = t)
+ override fun warn(message: Any?) = logger.log(LogLevel.Warn, message, throwable = null)
+ override fun warn(message: Any?, t: Throwable?) = logger.log(LogLevel.Warn, message, throwable = t)
+ override fun error(message: Any?) = logger.log(LogLevel.Error, message, throwable = null)
+ override fun error(message: Any?, t: Throwable?) = logger.log(LogLevel.Error, message, throwable = t)
+ override fun fatal(message: Any?) = logger.log(LogLevel.Fatal, message, throwable = null)
+ override fun fatal(message: Any?, t: Throwable?) = logger.log(LogLevel.Fatal, message, throwable = t)
+
+ override fun isTraceEnabled() = logger.isEnabled(LogLevel.Trace)
+ override fun isDebugEnabled() = logger.isEnabled(LogLevel.Debug)
+ override fun isInfoEnabled() = logger.isEnabled(LogLevel.Info)
+ override fun isErrorEnabled() = logger.isEnabled(LogLevel.Error)
+ override fun isFatalEnabled() = logger.isEnabled(LogLevel.Fatal)
+ override fun isWarnEnabled() = logger.isEnabled(LogLevel.Warn)
+ }
+ }
+}
\ No newline at end of file
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/postProcessors/UtBotBeanFactoryPostProcessor.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/postProcessors/UtBotBeanFactoryPostProcessor.kt
index 98ffca5f39..b0276dbac2 100644
--- a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/postProcessors/UtBotBeanFactoryPostProcessor.kt
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/postProcessors/UtBotBeanFactoryPostProcessor.kt
@@ -6,11 +6,9 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.factory.support.BeanDefinitionRegistry
import org.springframework.core.PriorityOrdered
-import java.io.File
-import java.io.FileWriter
-import java.util.Arrays
-
-class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered {
+object UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered {
+ var beanQualifiedNames: List = emptyList()
+ private set
/**
* Sets the priority of post processor to highest to avoid side effects from others.
@@ -21,8 +19,7 @@ class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered
println("Started post-processing bean factory in UtBot")
val beanClassNames = findBeanClassNames(beanFactory)
- //TODO: will be replaced with more appropriate IPC approach.
- writeToFile(beanClassNames)
+ beanQualifiedNames = beanClassNames.distinct()
// After desired post-processing is completed we destroy bean definitions
// to avoid further possible actions with beans that may be unsafe.
@@ -59,28 +56,4 @@ class UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered
beanRegistry.removeBeanDefinition(beanDefinitionName)
}
}
-
- private fun writeToFile(beanClassNames: ArrayList) {
- try {
- val springBeansFile = File("SpringBeans.txt")
- val fileWriter = FileWriter(springBeansFile)
-
- val distinctClassNames = beanClassNames.stream()
- .distinct()
- .toArray()
- Arrays.sort(distinctClassNames)
-
- for (beanClassName in distinctClassNames) {
- fileWriter.append(beanClassName.toString())
- fileWriter.append("\n")
- }
-
- fileWriter.flush()
- fileWriter.close()
-
- println("Storing bean information completed successfully")
- } catch (e: Throwable) {
- println("Storing bean information failed with exception $e")
- }
- }
}
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt
new file mode 100644
index 0000000000..e7381e939f
--- /dev/null
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt
@@ -0,0 +1,115 @@
+package org.utbot.spring.process
+
+import com.jetbrains.rd.util.lifetime.LifetimeDefinition
+import kotlinx.coroutines.runBlocking
+import mu.KotlinLogging
+import org.apache.commons.io.FileUtils
+import org.utbot.common.getPid
+import org.utbot.common.utBotTempDirectory
+import org.utbot.framework.UtSettings
+import org.utbot.framework.process.AbstractRDProcessCompanion
+import org.utbot.rd.ProcessWithRdServer
+import org.utbot.rd.exceptions.InstantProcessDeathException
+import org.utbot.rd.generated.LoggerModel
+import org.utbot.rd.generated.loggerModel
+import org.utbot.rd.loggers.UtRdKLogger
+import org.utbot.rd.loggers.setup
+import org.utbot.rd.onSchedulerBlocking
+import org.utbot.rd.startBlocking
+import org.utbot.rd.startUtProcessWithRdServer
+import org.utbot.rd.terminateOnException
+import org.utbot.spring.generated.SpringAnalyzerParams
+import org.utbot.spring.generated.SpringAnalyzerProcessModel
+import org.utbot.spring.generated.springAnalyzerProcessModel
+import java.nio.file.Files
+
+class SpringAnalyzerProcessInstantDeathException :
+ InstantProcessDeathException(
+ UtSettings.springAnalyzerProcessDebugPort,
+ UtSettings.runSpringAnalyzerProcessWithDebug
+ )
+
+private const val SPRING_ANALYZER_JAR_FILENAME = "utbot-spring-analyzer-shadow.jar"
+private const val SPRING_ANALYZER_JAR_PATH = "lib/$SPRING_ANALYZER_JAR_FILENAME"
+private const val UNKNOWN_MODIFICATION_TIME = 0L
+private val logger = KotlinLogging.logger {}
+private val rdLogger = UtRdKLogger(logger, "")
+
+private val springAnalyzerJarFile = Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath())
+ .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME).also { jarFile ->
+ val resource = SpringAnalyzerProcess::class.java.classLoader.getResource(SPRING_ANALYZER_JAR_PATH)
+ ?: error("Unable to find \"$SPRING_ANALYZER_JAR_PATH\" in resources, make sure it's on the classpath")
+ val resourceConnection = resource.openConnection()
+ val lastResourceModification = try {
+ resourceConnection.lastModified
+ } finally {
+ resourceConnection.getInputStream().close()
+ }
+ if (
+ !jarFile.exists() ||
+ jarFile.lastModified() == UNKNOWN_MODIFICATION_TIME ||
+ lastResourceModification == UNKNOWN_MODIFICATION_TIME ||
+ jarFile.lastModified() < lastResourceModification
+ )
+ FileUtils.copyURLToFile(resource, jarFile)
+ }
+
+class SpringAnalyzerProcess private constructor(
+ rdProcess: ProcessWithRdServer
+) : ProcessWithRdServer by rdProcess {
+
+ companion object : AbstractRDProcessCompanion(
+ debugPort = UtSettings.springAnalyzerProcessDebugPort,
+ runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug,
+ suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode,
+ processSpecificCommandLineArgs = {
+ listOf(
+ "-Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory",
+ "-jar",
+ springAnalyzerJarFile.path
+ )
+ }) {
+
+ fun createBlocking() = runBlocking { SpringAnalyzerProcess() }
+
+ suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime ->
+ val rdProcess = startUtProcessWithRdServer(lifetime) { port ->
+ val cmd = obtainProcessCommandLine(port)
+ val process = ProcessBuilder(cmd)
+ .directory(Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile())
+ .start()
+
+ logger.info { "Spring Analyzer process started with PID = ${process.getPid}" }
+
+ if (!process.isAlive) throw SpringAnalyzerProcessInstantDeathException()
+
+ process
+ }
+ rdProcess.awaitProcessReady()
+ val proc = SpringAnalyzerProcess(rdProcess)
+ proc.loggerModel.setup(rdLogger, proc.lifetime)
+ return proc
+ }
+ }
+
+ private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel }
+ private val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel }
+
+ fun getBeanQualifiedNames(
+ classpath: List,
+ configuration: String,
+ propertyFilesPaths: List,
+ xmlConfigurationPaths: List,
+ useSpringAnalyzer: Boolean
+ ): List {
+ val params = SpringAnalyzerParams(
+ classpath.toTypedArray(),
+ configuration,
+ propertyFilesPaths.toTypedArray(),
+ xmlConfigurationPaths.toTypedArray(),
+ useSpringAnalyzer
+ )
+ val result = springAnalyzerModel.analyze.startBlocking(params)
+ return result.beanTypes.toList()
+ }
+}
diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt
new file mode 100644
index 0000000000..8c4eab7b0a
--- /dev/null
+++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt
@@ -0,0 +1,56 @@
+package org.utbot.spring.process
+
+import com.jetbrains.rd.framework.IProtocol
+import com.jetbrains.rd.util.Logger
+import com.jetbrains.rd.util.getLogger
+import com.jetbrains.rd.util.info
+import com.jetbrains.rd.util.lifetime.Lifetime
+import com.jetbrains.rd.util.reactive.adviseOnce
+import org.utbot.common.AbstractSettings
+import org.utbot.rd.ClientProtocolBuilder
+import org.utbot.rd.IdleWatchdog
+import org.utbot.rd.RdSettingsContainerFactory
+import org.utbot.rd.generated.loggerModel
+import org.utbot.rd.generated.settingsModel
+import org.utbot.rd.loggers.UtRdRemoteLoggerFactory
+import org.utbot.spring.analyzers.SpringApplicationAnalyzer
+import org.utbot.spring.data.ApplicationData
+import org.utbot.spring.generated.SpringAnalyzerProcessModel
+import org.utbot.spring.generated.SpringAnalyzerResult
+import org.utbot.spring.generated.springAnalyzerProcessModel
+import java.io.File
+import kotlin.time.Duration.Companion.seconds
+
+private val messageFromMainTimeoutMillis = 120.seconds
+private val logger = getLogger()
+
+@Suppress("unused")
+object SpringAnalyzerProcessMain
+
+suspend fun main(args: Array) =
+ ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(args) {
+ loggerModel.initRemoteLogging.adviseOnce(lifetime) {
+ Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel))
+ logger.info { "-----------------------------------------------------------------------" }
+ logger.info { "------------------NEW SPRING ANALYZER PROCESS STARTED------------------" }
+ logger.info { "-----------------------------------------------------------------------" }
+ }
+ AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel))
+ springAnalyzerProcessModel.setup(it, protocol)
+ }
+
+private fun SpringAnalyzerProcessModel.setup(watchdog: IdleWatchdog, realProtocol: IProtocol) {
+ watchdog.measureTimeForActiveCall(analyze, "Analyzing Spring Application") { params ->
+ SpringAnalyzerResult(
+ if (!params.useSpringAnalyzer) emptyArray()
+ else SpringApplicationAnalyzer(
+ ApplicationData(
+ params.classpath.toList().map { File(it).toURI().toURL() }.toTypedArray(),
+ params.configuration,
+ params.propertyFilesPaths.toList(),
+ params.xmlConfigurationPaths.toList()
+ )
+ ).analyze().toTypedArray()
+ )
+ }
+}