From 4d529b964f5ba3a22773a6e1f2177df1deb4971d Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 31 Mar 2023 17:36:13 +0300 Subject: [PATCH 01/14] Add SpringAnalyzerProcess with RD --- .run/Debug All.run.xml | 1 + .run/Debug Spring Analyzer Process.run.xml | 7 + ...Listen for Spring Analyzer Process.run.xml | 15 ++ settings.gradle.kts | 1 + .../kotlin/org/utbot/framework/UtSettings.kt | 30 +++ .../framework/process/OpenModulesContainer.kt | 1 + utbot-intellij/build.gradle.kts | 16 +- .../generator/UtTestsDialogProcessor.kt | 60 ++++- .../plugin/models/GenerateTestsModel.kt | 3 + .../plugin/process/AbstractProcess.kt | 134 +++++++++++ .../intellij/plugin/process/EngineProcess.kt | 149 ++++-------- .../plugin/process/SpringAnalyzerProcess.kt | 78 ++++++ utbot-intellij/src/main/resources/log4j2.xml | 13 +- utbot-rd/build.gradle | 25 ++ .../utbot/rd/models/SpringAnalyzerModel.kt | 23 ++ utbot-spring-analyzer-model/build.gradle.kts | 6 + .../SpringAnalyzerProcessModel.Generated.kt | 225 ++++++++++++++++++ .../generated/SpringAnalyzerRoot.Generated.kt | 58 +++++ utbot-spring-analyzer/build.gradle.kts | 37 ++- .../analyzers/SpringApplicationAnalyzer.kt | 4 +- .../config/TestApplicationConfiguration.kt | 5 +- .../UtBotBeanFactoryPostProcessor.kt | 35 +-- .../process/SpringAnalyzerProcessMain.kt | 54 +++++ 23 files changed, 825 insertions(+), 155 deletions(-) create mode 100644 .run/Debug Spring Analyzer Process.run.xml create mode 100644 .run/Listen for Spring Analyzer Process.run.xml create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt create mode 100644 utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt create mode 100644 utbot-spring-analyzer-model/build.gradle.kts create mode 100644 utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt create mode 100644 utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerRoot.Generated.kt create mode 100644 utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt 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/settings.gradle.kts b/settings.gradle.kts index c80a375ffc..e2643b5b52 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -71,3 +71,4 @@ if (goIde.split(",").contains(ideType)) { } include("utbot-spring-analyzer") +include("utbot-spring-analyzer-model") 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..2124f2084f 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 @@ -296,6 +296,36 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS // endregion +// region spring analyzer process debug + /** + * Path to custom log4j2 configuration file for SpringAnalyzerProcess. + * By default utbot-intellij/src/main/resources/log4j2.xml is used. + * Also default value is used if provided value is not a file. + */ + var springAnalyzerProcessLogConfigFile by getStringProperty("") + + /** + * 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.intellij.plugin.process.EngineProcess + */ + 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. 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-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 5314824aed..507332e96c 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -133,6 +133,11 @@ repositories { maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") } +val fetchSpringAnalyzerJar: Configuration by configurations.creating { + isCanBeResolved = true + isCanBeConsumed = false +} + dependencies { implementation(group ="com.jetbrains.rd", name = "rd-framework", version = rdVersion) implementation(group ="com.jetbrains.rd", name = "rd-core", version = rdVersion) @@ -144,6 +149,7 @@ dependencies { implementation(project(":utbot-framework")) { exclude(group = "org.slf4j", module = "slf4j-api") } implementation(project(":utbot-fuzzers")) + implementation(project(":utbot-spring-analyzer-model")) //api(project(":utbot-analytics")) testImplementation("org.mock-server:mockserver-netty:5.4.1") testApi(project(":utbot-framework")) @@ -168,6 +174,8 @@ dependencies { implementation(project(":utbot-android-studio")) + fetchSpringAnalyzerJar(project(":utbot-spring-analyzer", "springAnalyzerJar")) + testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") @@ -183,4 +191,10 @@ 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 +} + +tasks.processResources { + from(fetchSpringAnalyzerJar) { + into("lib") + } +} 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..aebd5ab6d4 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 @@ -58,6 +60,7 @@ import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.models.packageName import org.utbot.intellij.plugin.process.EngineProcess import org.utbot.intellij.plugin.process.RdTestGenerationResult +import org.utbot.intellij.plugin.process.SpringAnalyzerProcess import org.utbot.intellij.plugin.settings.Settings import org.utbot.intellij.plugin.ui.GenerateTestsDialogWindow import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle @@ -156,7 +159,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.configFqn.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 +194,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 @@ -204,23 +215,39 @@ object UtTestsDialogProcessor { val applicationContext = when (model.projectType) { Spring -> { - val shouldUseImplementors = when (model.typeReplacementApproach) { - TypeReplacementApproach.DoNotReplace -> false - is TypeReplacementApproach.ReplaceIfPossible -> true + val beanQualifiedNames = + if (!model.useSpringAnalyzer) emptyList() + else when (val approach = model.typeReplacementApproach) { + TypeReplacementApproach.DoNotReplace -> emptyList() + is TypeReplacementApproach.ReplaceIfPossible -> { + val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking(Unit) + + springAnalyzerProcess.terminateOnException { _ -> + val beans = springAnalyzerProcess.getBeanQualifiedNames( + classpathList, + approach.configFqn, + emptyList(), + emptyList() + ) + invokeLater { + springAnalyzerProcess.terminate() + } + beans + } + } } SpringApplicationContext( mockFrameworkInstalled, staticMockingConfigured, - // TODO: obtain bean definitions and other info from `utbot-spring-analyzer` - beanQualifiedNames = emptyList(), - shouldUseImplementors = shouldUseImplementors, + beanQualifiedNames, + shouldUseImplementors = beanQualifiedNames.isNotEmpty(), ) } else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) } - val process = EngineProcess.createBlocking(project, classNameToPath) + val process = EngineProcess.createBlocking(EngineProcess.Params(project, classNameToPath)) process.terminateOnException { _ -> process.setupUtContext(buildDirs + classpathList) @@ -409,16 +436,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 +476,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/AbstractProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt new file mode 100644 index 0000000000..81158ba50d --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt @@ -0,0 +1,134 @@ +package org.utbot.intellij.plugin.process + +import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import kotlinx.coroutines.runBlocking +import mu.KLogger +import org.utbot.common.getPid +import org.utbot.common.osSpecificJavaExecutable +import org.utbot.framework.plugin.services.JdkInfoService +import org.utbot.framework.process.OpenModulesContainer +import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.exceptions.InstantProcessDeathException +import org.utbot.rd.generated.synchronizationModel +import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger +import org.utbot.rd.rdPortArgument +import org.utbot.rd.startUtProcessWithRdServer +import org.utbot.rd.terminateOnException +import java.io.File +import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import kotlin.io.path.pathString + +abstract class AbstractProcess protected constructor( + private val rdProcess: ProcessWithRdServer +) : ProcessWithRdServer by rdProcess { + + init { + rdProcess.lifetime.onTermination { + protocol.synchronizationModel.stopProcess.fire(Unit) + } + } + + /** + * @param T additional data needed to instantiate the process + * @param P process type + */ + abstract class Companion( + private val displayName: String, + private val logConfigFileGetter: () -> String, + private val debugPortGetter: () -> Int, + private val runWithDebugGetter: () -> Boolean, + private val suspendExecutionInDebugModeGetter: () -> Boolean, + private val logConfigurationsDirectory: File, + private val logDirectory: File, + logConfigurationFileDeleteKey: String, + private val logAppender: String, + private val currentLogFilename: String, + protected val logger: KLogger, + ) { + private val deleteOpenComment = "" + + private val log4j2ConfigFile: File = run { + logDirectory.mkdirs() + logConfigurationsDirectory.mkdirs() + overrideDefaultRdLoggerFactoryWithKLogger(logger) + + val customFile = File(logConfigFileGetter()) + + if (customFile.exists()) customFile + else { + val log4j2ConfigFile = Files.createTempFile(logConfigurationsDirectory.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=\"$logAppender\"") + .replace("\${env:UTBOT_LOG_DIR}", logDirectory.canonicalPath.trimEnd(File.separatorChar) + File.separatorChar) + Files.copy( + resultConfig.byteInputStream(), + log4j2ConfigFile.toPath(), + StandardCopyOption.REPLACE_EXISTING + ) + } + log4j2ConfigFile + } + } + + private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" + + private val javaExecutablePathString: Path + get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") + + private fun suspendValue(): String = if (suspendExecutionInDebugModeGetter()) "y" else "n" + + private val debugArgument: String? + get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${debugPortGetter()}" + .takeIf { runWithDebugGetter() } + + protected abstract fun obtainProcessSpecificCommandLineArgs(): List + + private fun obtainProcessCommandLineArgs(port: Int) = buildList { + add(javaExecutablePathString.pathString) + val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments + if (javaVersionSpecificArgs.isNotEmpty()) { + addAll(javaVersionSpecificArgs) + } + debugArgument?.let { add(it) } + add(log4j2ConfigSwitch) + addAll(obtainProcessSpecificCommandLineArgs()) + add(rdPortArgument(port)) + } + + protected abstract fun createFromRDProcess(params: T, rdProcess: ProcessWithRdServer) : P + + protected abstract fun getWorkingDirectory(): File + + protected abstract fun createInstantDeathException(): InstantProcessDeathException + + fun createBlocking(params: T): P = runBlocking { invoke(params) } + + suspend operator fun invoke(params: T): P = + LifetimeDefinition().terminateOnException { lifetime -> + val rdProcess = startUtProcessWithRdServer(lifetime) { port -> + val cmd = obtainProcessCommandLineArgs(port) + val process = ProcessBuilder(cmd) + .directory(getWorkingDirectory()) + .start() + + logger.info { "$displayName process started with PID = ${process.getPid}" } + logger.info { "$displayName log directory - ${logDirectory.canonicalPath}" } + logger.info { "$displayName log file - ${logDirectory.resolve(currentLogFilename)}" } + logger.info { "Log4j2 configuration file path - ${log4j2ConfigFile.canonicalPath}" } + + if (!process.isAlive) throw createInstantDeathException() + + process + } + rdProcess.awaitProcessReady() + + return createFromRDProcess(params, rdProcess) + } + } +} 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..5a5928d47a 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 @@ -10,20 +10,27 @@ import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.util.classMembers.MemberInfo import com.jetbrains.rd.util.ConcurrentHashMap -import com.jetbrains.rd.util.lifetime.LifetimeDefinition -import kotlinx.coroutines.runBlocking import mu.KotlinLogging -import org.utbot.common.* +import org.utbot.common.AbstractSettings +import org.utbot.common.utBotTempDirectory import org.utbot.framework.UtSettings -import org.utbot.framework.codegen.domain.* +import org.utbot.framework.codegen.domain.ForceStaticMocking +import org.utbot.framework.codegen.domain.HangingTestsTimeout +import org.utbot.framework.codegen.domain.ParametrizedTestSource +import org.utbot.framework.codegen.domain.ProjectType +import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour +import org.utbot.framework.codegen.domain.StaticsMocking +import org.utbot.framework.codegen.domain.TestFramework import org.utbot.framework.codegen.tree.ututils.UtilClassKind -import org.utbot.framework.plugin.api.* +import org.utbot.framework.plugin.api.ApplicationContext +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.MockFramework +import org.utbot.framework.plugin.api.MockStrategyApi 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.generated.* -import org.utbot.framework.process.generated.MethodDescription import org.utbot.framework.util.Conflict import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.util.KryoHelper @@ -32,77 +39,47 @@ import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener import org.utbot.intellij.plugin.util.assertReadAccessNotAllowed import org.utbot.intellij.plugin.util.methodDescription -import org.utbot.rd.* +import org.utbot.rd.ProcessWithRdServer import org.utbot.rd.exceptions.InstantProcessDeathException import org.utbot.rd.generated.SettingForResult import org.utbot.rd.generated.SettingsModel import org.utbot.rd.generated.settingsModel import org.utbot.rd.generated.synchronizationModel -import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger +import org.utbot.rd.onSchedulerBlocking +import org.utbot.rd.startBlocking import org.utbot.sarif.SourceFindingStrategy import java.io.File -import java.nio.charset.Charset -import java.nio.file.Files import java.nio.file.Path -import java.nio.file.StandardCopyOption 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 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 val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - - private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n" - - private val debugArgument: String? - get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" - .takeIf { UtSettings.runEngineProcessWithDebug } - - private val javaExecutablePathString: Path - get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") - + AbstractProcess(rdProcess) { + + data class Params( + val project: Project, + val classNameToPath: Map + ) + + companion object : AbstractProcess.Companion( + displayName = "Engine", + logConfigFileGetter = { UtSettings.engineProcessLogConfigFile }, + debugPortGetter = { UtSettings.engineProcessDebugPort }, + runWithDebugGetter = { UtSettings.runEngineProcessWithDebug }, + suspendExecutionInDebugModeGetter = { UtSettings.suspendEngineProcessExecutionInDebugMode }, + logConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations"), + logDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs"), + logConfigurationFileDeleteKey = "engine_process_appender_comment_key", + logAppender = "EngineProcessAppender", + currentLogFilename = "utbot-engine-current.log", + logger = KotlinLogging.logger {} + ) { private val pluginClasspath: String get() = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( separator = File.pathSeparator, @@ -112,46 +89,19 @@ class EngineProcess private constructor(val project: Project, private val classN private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt" - 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)) - } - - 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 directory = WorkingDirService.provide().toFile() - val builder = ProcessBuilder(cmd).directory(directory) - val process = builder.start() - - logger.info { "Engine process started with PID = ${process.getPid}" } - logger.info { "Engine process log directory - ${engineProcessLogDirectory.canonicalPath}" } - logger.info { "Engine process log file - ${engineProcessLogDirectory.resolve("utbot-engine-current.log")}" } - logger.info { "Log4j2 configuration file path - ${log4j2ConfigFile.canonicalPath}" } + override fun obtainProcessSpecificCommandLineArgs() = listOf( + "-ea", + "-cp", + pluginClasspath, + startFileName + ) - if (!process.isAlive) { - throw EngineProcessInstantDeathException() - } + override fun getWorkingDirectory() = WorkingDirService.provide().toFile() - process - } - rdProcess.awaitProcessReady() + override fun createFromRDProcess(params: Params, rdProcess: ProcessWithRdServer) = + EngineProcess(params.project, params.classNameToPath, rdProcess) - return EngineProcess(project, classNameToPath, rdProcess) - } + override fun createInstantDeathException() = EngineProcessInstantDeathException() } private val engineModel: EngineProcessModel = onSchedulerBlocking { protocol.engineProcessModel } @@ -394,9 +344,6 @@ class EngineProcess private constructor(val project: Project, private val classN } init { - lifetime.onTermination { - protocol.synchronizationModel.stopProcess.fire(Unit) - } settingsModel.settingFor.set { params -> SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings -> val members: Collection> = diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt new file mode 100644 index 0000000000..a468a72c1b --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt @@ -0,0 +1,78 @@ +package org.utbot.intellij.plugin.process + +import mu.KotlinLogging +import org.apache.commons.io.FileUtils +import org.utbot.common.utBotTempDirectory +import org.utbot.framework.UtSettings +import org.utbot.intellij.plugin.util.assertReadAccessNotAllowed +import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.exceptions.InstantProcessDeathException +import org.utbot.rd.onSchedulerBlocking +import org.utbot.rd.startBlocking +import org.utbot.spring.process.generated.SpringAnalyzerParams +import org.utbot.spring.process.generated.SpringAnalyzerProcessModel +import org.utbot.spring.process.generated.springAnalyzerProcessModel +import java.io.File +import java.nio.file.Files + +class SpringAnalyzerProcessInstantDeathException : + InstantProcessDeathException(UtSettings.springAnalyzerProcessDebugPort, UtSettings.runSpringAnalyzerProcessWithDebug) + +private const val SPRING_ANALYZER_JAR_FILENAME = "utbot-spring-analyzer-shadow.jar" + +class SpringAnalyzerProcess private constructor( + rdProcess: ProcessWithRdServer +) : AbstractProcess(rdProcess) { + + companion object : AbstractProcess.Companion( + displayName = "Spring analyzer", + logConfigFileGetter = { UtSettings.springAnalyzerProcessLogConfigFile }, + debugPortGetter = { UtSettings.springAnalyzerProcessDebugPort }, + runWithDebugGetter = { UtSettings.runSpringAnalyzerProcessWithDebug }, + suspendExecutionInDebugModeGetter = { UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode }, + logConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdSpringAnalyzerProcessLogConfigurations"), + logDirectory = utBotTempDirectory.toFile().resolve("rdSpringAnalyzerProcessLogs"), + logConfigurationFileDeleteKey = "spring_analyzer_process_appender_comment_key", + logAppender = "SpringAnalyzerProcessAppender", + currentLogFilename = "utbot-spring-analyzer-current.log", + logger = KotlinLogging.logger {} + ) { + override fun obtainProcessSpecificCommandLineArgs(): List { + val jarFile = + Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) + .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME) + FileUtils.copyURLToFile( + this::class.java.classLoader.getResource("lib/$SPRING_ANALYZER_JAR_FILENAME"), + jarFile + ) + return listOf("-jar", jarFile.path) + } + + override fun getWorkingDirectory(): File = + Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile() + + override fun createFromRDProcess(params: Unit, rdProcess: ProcessWithRdServer) = + SpringAnalyzerProcess(rdProcess) + + override fun createInstantDeathException() = SpringAnalyzerProcessInstantDeathException() + } + + private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel } + + fun getBeanQualifiedNames( + classpath: List, + configuration: String, + propertyFilesPaths: List, + xmlConfigurationPaths: List + ): List { + assertReadAccessNotAllowed() + val params = SpringAnalyzerParams( + classpath.toTypedArray(), + configuration, + propertyFilesPaths.toTypedArray(), + xmlConfigurationPaths.toTypedArray() + ) + val result = springAnalyzerModel.analyze.startBlocking(params) + return result.beanTypes.toList() + } +} diff --git a/utbot-intellij/src/main/resources/log4j2.xml b/utbot-intellij/src/main/resources/log4j2.xml index 6a9ae540c8..608a235418 100644 --- a/utbot-intellij/src/main/resources/log4j2.xml +++ b/utbot-intellij/src/main/resources/log4j2.xml @@ -10,7 +10,7 @@ - + engine_process_appender_comment_key--> + diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index be70e3e35e..51eb0323e1 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -214,6 +214,31 @@ task generateEngineProcessModels(type: RdGenTask) { } } +task generateSpringAnalyzerModels(type: RdGenTask) { + def currentProjectDir = project.projectDir + def springAnalyzerModelProjectDir = project.rootProject.childProjects["utbot-spring-analyzer-model"].projectDir + def generatedOutputDir = new File(springAnalyzerModelProjectDir, "src/main/kotlin/org/utbot/spring/processs/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.process.generated" + } +} + task generateCommonModels(type: RdGenTask) { def currentProjectDir = project.projectDir def ideaPluginProjectDir = project.rootProject.childProjects["utbot-rd"].projectDir 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..913989efcb --- /dev/null +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt @@ -0,0 +1,23 @@ +@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)) + } + + val springAnalyzerResult = structdef { + field("beanTypes", array(PredefinedType.string)) + } + + init { + call("analyze", springAnalyzerParams, springAnalyzerResult).async + } +} diff --git a/utbot-spring-analyzer-model/build.gradle.kts b/utbot-spring-analyzer-model/build.gradle.kts new file mode 100644 index 0000000000..62902ab16c --- /dev/null +++ b/utbot-spring-analyzer-model/build.gradle.kts @@ -0,0 +1,6 @@ +val rdVersion: String by rootProject + +dependencies { + implementation("com.jetbrains.rd:rd-framework:$rdVersion") + implementation("com.jetbrains.rd:rd-core:$rdVersion") +} diff --git a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt new file mode 100644 index 0000000000..bbd22dc910 --- /dev/null +++ b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt @@ -0,0 +1,225 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") +package org.utbot.spring.process.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 = 1562060411431746104L + + } + 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 +) : 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()} + return SpringAnalyzerParams(classpath, configuration, propertyFilesPaths, xmlConfigurationPaths) + } + + 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) } + } + + + } + //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 + + 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() + 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() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [SpringAnalyzerModel.kt:16] + */ +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-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerRoot.Generated.kt b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerRoot.Generated.kt new file mode 100644 index 0000000000..17b2272973 --- /dev/null +++ b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/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.process.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/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index 2d085bfa62..f06c099654 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -1,6 +1,10 @@ -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 rdVersion: String by rootProject +val log4j2Version: String by rootProject +val kotlinLoggingVersion: String? by rootProject + plugins { id("org.springframework.boot") version "2.7.8" id("io.spring.dependency-management") version "1.1.0" @@ -17,14 +21,22 @@ java { dependencies { implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter-web") + + implementation(project(":utbot-spring-analyzer-model")) + implementation(project(":utbot-rd")) + implementation(project(":utbot-core")) + implementation("com.jetbrains.rd:rd-framework:$rdVersion") + implementation("com.jetbrains.rd:rd-core:$rdVersion") + implementation("org.apache.logging.log4j:log4j-slf4j-impl:$log4j2Version") + implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion") } 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 +47,23 @@ 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) +} + +configurations { + all { + // avoids conflicts with `implementation("org.apache.logging.log4j:log4j-slf4j-impl:$log4j2Version")` + exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + } } 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/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/SpringAnalyzerProcessMain.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt new file mode 100644 index 0000000000..d37b70d839 --- /dev/null +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt @@ -0,0 +1,54 @@ +package org.utbot.spring.process + +import com.jetbrains.rd.framework.IProtocol +import com.jetbrains.rd.util.Logger +import com.jetbrains.rd.util.lifetime.Lifetime +import mu.KotlinLogging +import org.utbot.common.AbstractSettings +import org.utbot.rd.ClientProtocolBuilder +import org.utbot.rd.IdleWatchdog +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.spring.analyzers.SpringApplicationAnalyzer +import org.utbot.spring.process.generated.SpringAnalyzerProcessModel +import org.utbot.spring.process.generated.SpringAnalyzerResult +import org.utbot.spring.process.generated.springAnalyzerProcessModel +import java.io.File +import kotlin.time.Duration.Companion.seconds + +private val messageFromMainTimeoutMillis = 120.seconds +private val logger = KotlinLogging.logger {} + +@Suppress("unused") +object SpringAnalyzerProcessMain + +suspend fun main(args: Array) { + Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) + + logger.info("-----------------------------------------------------------------------") + logger.info("------------------NEW SPRING ANALYZER PROCESS STARTED------------------") + logger.info("-----------------------------------------------------------------------") + // 0 - auto port for server, should not be used here + val port = findRdPort(args) + + + ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { + 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( + SpringApplicationAnalyzer( + params.classpath.toList().map { File(it).toURI().toURL() }, + params.configuration, + params.propertyFilesPaths.toList(), + params.xmlConfigurationPaths.toList() + ).analyze().toTypedArray() + ) + } +} From a884f5c7ae304ff8789f0e69ae4063884c4860ce Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Mon, 3 Apr 2023 19:44:27 +0300 Subject: [PATCH 02/14] Start Spring analyzer process from engine process to use RD logger --- gradle.properties | 1 + .../org/utbot/common/StandardStreamUtil.kt | 21 +++ .../kotlin/org/utbot/framework/UtSettings.kt | 7 - .../framework/process/CommonProcessArgs.kt | 33 ++++ utbot-framework/build.gradle | 2 + .../framework/process/EngineProcessMain.kt | 15 ++ .../process/SpringAnalyzerProcess.kt | 105 ++++++++++++ .../generated/EngineProcessModel.Generated.kt | 90 +++++++++- .../process/InstrumentedProcessMain.kt | 2 +- utbot-intellij/build.gradle.kts | 1 - .../generator/UtTestsDialogProcessor.kt | 59 +++---- .../plugin/process/AbstractProcess.kt | 134 --------------- .../intellij/plugin/process/EngineProcess.kt | 154 ++++++++++++------ .../plugin/process/SpringAnalyzerProcess.kt | 78 --------- utbot-intellij/src/main/resources/log4j2.xml | 13 +- utbot-rd/build.gradle | 3 +- .../rd/loggers/RDApacheCommonsLogFactory.kt | 34 ++++ .../org/utbot/rd/loggers/UtRdConsoleLogger.kt | 2 +- .../org/utbot/rd/loggers/UtRdKLogger.kt | 2 +- .../org/utbot/rd/loggers/UtRdLogUtil.kt | 23 ++- .../org/utbot/rd/models/EngineProcessModel.kt | 5 + .../SpringAnalyzerProcessModel.Generated.kt | 0 .../generated/SpringAnalyzerRoot.Generated.kt | 0 utbot-spring-analyzer/build.gradle.kts | 9 - .../process/SpringAnalyzerProcessMain.kt | 24 ++- 25 files changed, 472 insertions(+), 345 deletions(-) create mode 100644 utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt delete mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt delete mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt rename utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/{processs => process}/generated/SpringAnalyzerProcessModel.Generated.kt (100%) rename utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/{processs => process}/generated/SpringAnalyzerRoot.Generated.kt (100%) diff --git a/gradle.properties b/gradle.properties index 59862181bb..22bbeff4ff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -77,6 +77,7 @@ 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 # configuration for build server # diff --git a/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt new file mode 100644 index 0000000000..c5ac9a8f16 --- /dev/null +++ b/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt @@ -0,0 +1,21 @@ +package org.utbot.common + +import java.io.OutputStream +import java.io.PrintStream + +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() } +} \ No newline at end of file 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 2124f2084f..9702026762 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 @@ -297,13 +297,6 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS // endregion // region spring analyzer process debug - /** - * Path to custom log4j2 configuration file for SpringAnalyzerProcess. - * By default utbot-intellij/src/main/resources/log4j2.xml is used. - * Also default value is used if provided value is not a file. - */ - var springAnalyzerProcessLogConfigFile by getStringProperty("") - /** * The property is useful only for the IntelliJ IDEs. * If the property is set in true the spring analyzer process opens a debug port. diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt new file mode 100644 index 0000000000..835a151f67 --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt @@ -0,0 +1,33 @@ +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 java.nio.file.Path +import kotlin.io.path.pathString + +private val javaExecutablePathString: Path + get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") + +// TODO use it in EngineProcess and InstrumentedProcess +fun withCommonProcessCommandLineArgs( + processSpecificArgs: List, + debugPort: Int, + runWithDebug: Boolean, + suspendExecutionInDebugMode: Boolean, + rdPort: Int +): 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) } + addAll(processSpecificArgs) + add(rdPortArgument(rdPort)) +} \ No newline at end of file diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index 3e0cd02564..d69521acae 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -6,6 +6,8 @@ dependencies { api project(':utbot-framework-api') api project(':utbot-rd') + implementation project(':utbot-spring-analyzer-model') + implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion 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..4189193c5c 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,6 +34,7 @@ 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.summary.summarizeAll @@ -79,6 +80,20 @@ 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() + ).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/SpringAnalyzerProcess.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt new file mode 100644 index 0000000000..ea28231dd1 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt @@ -0,0 +1,105 @@ +package org.utbot.framework.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.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.setupRdLogger +import org.utbot.rd.onSchedulerBlocking +import org.utbot.rd.startBlocking +import org.utbot.rd.startUtProcessWithRdServer +import org.utbot.rd.terminateOnException +import org.utbot.spring.process.generated.SpringAnalyzerParams +import org.utbot.spring.process.generated.SpringAnalyzerProcessModel +import org.utbot.spring.process.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 val logger = KotlinLogging.logger {} +private val rdLogger = UtRdKLogger(logger, "") + +class SpringAnalyzerProcess private constructor( + rdProcess: ProcessWithRdServer +) : ProcessWithRdServer by rdProcess { + + companion object { + private fun obtainProcessSpecificCommandLineArgs(): List { + val jarFile = + Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) + .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME) + FileUtils.copyURLToFile( + this::class.java.classLoader.getResource("lib/$SPRING_ANALYZER_JAR_FILENAME"), + jarFile + ) + return listOf( + "-Dorg.apache.commons.logging.LogFactory=org.utbot.rd.loggers.RDApacheCommonsLogFactory", + "-jar", + jarFile.path + ) + } + + fun createBlocking() = runBlocking { SpringAnalyzerProcess() } + + suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime -> + val rdProcess = startUtProcessWithRdServer(lifetime) { port -> + val cmd = withCommonProcessCommandLineArgs( + obtainProcessSpecificCommandLineArgs(), + debugPort = UtSettings.springAnalyzerProcessDebugPort, + runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, + rdPort = 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) + setupRdLogger(rdProcess, proc.loggerModel, rdLogger) + return proc + } + } + + private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel } + private val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel } + + init { + lifetime.onTermination { + protocol.synchronizationModel.stopProcess.fire(Unit) + } + } + + fun getBeanQualifiedNames( + classpath: List, + configuration: String, + propertyFilesPaths: List, + xmlConfigurationPaths: List + ): List { + val params = SpringAnalyzerParams( + classpath.toTypedArray(), + configuration, + propertyFilesPaths.toTypedArray(), + xmlConfigurationPaths.toTypedArray() + ) + val result = springAnalyzerModel.analyze.startBlocking(params) + return result.beanTypes.toList() + } +} 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..1781b16f22 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 = -5097607716462442558L } 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:103] */ data class FindMethodParamNamesArguments ( val classId: ByteArray, @@ -233,7 +242,7 @@ data class FindMethodParamNamesArguments ( /** - * #### Generated from [EngineProcessModel.kt:103] + * #### Generated from [EngineProcessModel.kt:107] */ data class FindMethodParamNamesResult ( val paramNames: ByteArray @@ -290,7 +299,7 @@ data class FindMethodParamNamesResult ( /** - * #### Generated from [EngineProcessModel.kt:92] + * #### Generated from [EngineProcessModel.kt:96] */ data class FindMethodsInClassMatchingSelectedArguments ( val classId: ByteArray, @@ -353,7 +362,7 @@ data class FindMethodsInClassMatchingSelectedArguments ( /** - * #### Generated from [EngineProcessModel.kt:96] + * #### Generated from [EngineProcessModel.kt:100] */ data class FindMethodsInClassMatchingSelectedResult ( val executableIds: ByteArray @@ -578,7 +587,7 @@ data class GenerateResult ( /** - * #### Generated from [EngineProcessModel.kt:111] + * #### Generated from [EngineProcessModel.kt:115] */ data class GenerateTestReportArgs ( val eventLogMessage: String?, @@ -671,7 +680,7 @@ data class GenerateTestReportArgs ( /** - * #### Generated from [EngineProcessModel.kt:120] + * #### Generated from [EngineProcessModel.kt:124] */ data class GenerateTestReportResult ( val notifyMessage: String, @@ -739,6 +748,69 @@ data class GenerateTestReportResult ( } +/** + * #### Generated from [EngineProcessModel.kt:87] + */ +data class GetSpringBeanQualifiedNamesParams ( + val classpath: Array, + val config: String +) : 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() + return GetSpringBeanQualifiedNamesParams(classpath, config) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GetSpringBeanQualifiedNamesParams) { + buffer.writeArray(value.classpath) { buffer.writeString(it) } + buffer.writeString(value.config) + } + + + } + //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 + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + classpath.contentDeepHashCode() + __r = __r*31 + config.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() + } + printer.print(")") + } + //deepClone + //contexts +} + + /** * #### Generated from [EngineProcessModel.kt:32] */ @@ -803,7 +875,7 @@ data class JdkInfo ( /** - * #### Generated from [EngineProcessModel.kt:87] + * #### Generated from [EngineProcessModel.kt:91] */ data class MethodDescription ( val name: String, @@ -1220,7 +1292,7 @@ data class TestGeneratorParams ( /** - * #### Generated from [EngineProcessModel.kt:106] + * #### Generated from [EngineProcessModel.kt:110] */ data class WriteSarifReportArguments ( val testSetsId: Long, 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..fdc6f2631b 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 @@ -103,7 +103,7 @@ object InstrumentedProcessMain */ fun main(args: Array) = runBlocking { // We don't want user code to litter the standard output, so we redirect it. - closeStandardStreams() + silentlyCloseStandardStreams() if (!args.contains(DISABLE_SANDBOX_OPTION)) { permissions { diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 507332e96c..9b1510891d 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -149,7 +149,6 @@ dependencies { implementation(project(":utbot-framework")) { exclude(group = "org.slf4j", module = "slf4j-api") } implementation(project(":utbot-fuzzers")) - implementation(project(":utbot-spring-analyzer-model")) //api(project(":utbot-analytics")) testImplementation("org.mock-server:mockserver-netty:5.4.1") testApi(project(":utbot-framework")) 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 aebd5ab6d4..21ed28e7bf 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 @@ -55,12 +55,12 @@ import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.plugin.services.WorkingDirService +import org.utbot.framework.process.generated.GetSpringBeanQualifiedNamesParams import org.utbot.intellij.plugin.generator.CodeGenerationController.generateTests import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.models.packageName import org.utbot.intellij.plugin.process.EngineProcess import org.utbot.intellij.plugin.process.RdTestGenerationResult -import org.utbot.intellij.plugin.process.SpringAnalyzerProcess import org.utbot.intellij.plugin.settings.Settings import org.utbot.intellij.plugin.ui.GenerateTestsDialogWindow import org.utbot.intellij.plugin.ui.utils.isBuildWithGradle @@ -213,44 +213,33 @@ object UtTestsDialogProcessor { val mockFrameworkInstalled = model.mockFramework.isInstalled val staticMockingConfigured = model.staticsMocking.isConfigured - val applicationContext = when (model.projectType) { - Spring -> { - val beanQualifiedNames = - if (!model.useSpringAnalyzer) emptyList() - else when (val approach = model.typeReplacementApproach) { - TypeReplacementApproach.DoNotReplace -> emptyList() - is TypeReplacementApproach.ReplaceIfPossible -> { - val springAnalyzerProcess = SpringAnalyzerProcess.createBlocking(Unit) - - springAnalyzerProcess.terminateOnException { _ -> - val beans = springAnalyzerProcess.getBeanQualifiedNames( - classpathList, - approach.configFqn, - emptyList(), - emptyList() + val process = EngineProcess.createBlocking(project, classNameToPath) + + process.terminateOnException { _ -> + process.setupUtContext(buildDirs + classpathList) + val applicationContext = when (model.projectType) { + Spring -> { + val beanQualifiedNames = + if (!model.useSpringAnalyzer) emptyList() + else when (val approach = model.typeReplacementApproach) { + TypeReplacementApproach.DoNotReplace -> emptyList() + is TypeReplacementApproach.ReplaceIfPossible -> process.getSpringBeanQualifiedNames( + GetSpringBeanQualifiedNamesParams( + (buildDirs + classpathList).toTypedArray(), + approach.configFqn ) - invokeLater { - springAnalyzerProcess.terminate() - } - beans - } + ).also { logger.info { "Detected Spring Beans: $it" } } } - } - SpringApplicationContext( - mockFrameworkInstalled, - staticMockingConfigured, - beanQualifiedNames, - shouldUseImplementors = beanQualifiedNames.isNotEmpty(), - ) + SpringApplicationContext( + mockFrameworkInstalled, + staticMockingConfigured, + beanQualifiedNames, + shouldUseImplementors = beanQualifiedNames.isNotEmpty(), + ) + } + else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) } - else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) - } - - val process = EngineProcess.createBlocking(EngineProcess.Params(project, classNameToPath)) - - process.terminateOnException { _ -> - process.setupUtContext(buildDirs + classpathList) process.createTestGenerator( buildDirs, classpath, diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt deleted file mode 100644 index 81158ba50d..0000000000 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/AbstractProcess.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.utbot.intellij.plugin.process - -import com.jetbrains.rd.util.lifetime.LifetimeDefinition -import kotlinx.coroutines.runBlocking -import mu.KLogger -import org.utbot.common.getPid -import org.utbot.common.osSpecificJavaExecutable -import org.utbot.framework.plugin.services.JdkInfoService -import org.utbot.framework.process.OpenModulesContainer -import org.utbot.rd.ProcessWithRdServer -import org.utbot.rd.exceptions.InstantProcessDeathException -import org.utbot.rd.generated.synchronizationModel -import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger -import org.utbot.rd.rdPortArgument -import org.utbot.rd.startUtProcessWithRdServer -import org.utbot.rd.terminateOnException -import java.io.File -import java.nio.charset.Charset -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardCopyOption -import kotlin.io.path.pathString - -abstract class AbstractProcess protected constructor( - private val rdProcess: ProcessWithRdServer -) : ProcessWithRdServer by rdProcess { - - init { - rdProcess.lifetime.onTermination { - protocol.synchronizationModel.stopProcess.fire(Unit) - } - } - - /** - * @param T additional data needed to instantiate the process - * @param P process type - */ - abstract class Companion( - private val displayName: String, - private val logConfigFileGetter: () -> String, - private val debugPortGetter: () -> Int, - private val runWithDebugGetter: () -> Boolean, - private val suspendExecutionInDebugModeGetter: () -> Boolean, - private val logConfigurationsDirectory: File, - private val logDirectory: File, - logConfigurationFileDeleteKey: String, - private val logAppender: String, - private val currentLogFilename: String, - protected val logger: KLogger, - ) { - private val deleteOpenComment = "" - - private val log4j2ConfigFile: File = run { - logDirectory.mkdirs() - logConfigurationsDirectory.mkdirs() - overrideDefaultRdLoggerFactoryWithKLogger(logger) - - val customFile = File(logConfigFileGetter()) - - if (customFile.exists()) customFile - else { - val log4j2ConfigFile = Files.createTempFile(logConfigurationsDirectory.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=\"$logAppender\"") - .replace("\${env:UTBOT_LOG_DIR}", logDirectory.canonicalPath.trimEnd(File.separatorChar) + File.separatorChar) - Files.copy( - resultConfig.byteInputStream(), - log4j2ConfigFile.toPath(), - StandardCopyOption.REPLACE_EXISTING - ) - } - log4j2ConfigFile - } - } - - private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - - private val javaExecutablePathString: Path - get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") - - private fun suspendValue(): String = if (suspendExecutionInDebugModeGetter()) "y" else "n" - - private val debugArgument: String? - get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${debugPortGetter()}" - .takeIf { runWithDebugGetter() } - - protected abstract fun obtainProcessSpecificCommandLineArgs(): List - - private fun obtainProcessCommandLineArgs(port: Int) = buildList { - add(javaExecutablePathString.pathString) - val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments - if (javaVersionSpecificArgs.isNotEmpty()) { - addAll(javaVersionSpecificArgs) - } - debugArgument?.let { add(it) } - add(log4j2ConfigSwitch) - addAll(obtainProcessSpecificCommandLineArgs()) - add(rdPortArgument(port)) - } - - protected abstract fun createFromRDProcess(params: T, rdProcess: ProcessWithRdServer) : P - - protected abstract fun getWorkingDirectory(): File - - protected abstract fun createInstantDeathException(): InstantProcessDeathException - - fun createBlocking(params: T): P = runBlocking { invoke(params) } - - suspend operator fun invoke(params: T): P = - LifetimeDefinition().terminateOnException { lifetime -> - val rdProcess = startUtProcessWithRdServer(lifetime) { port -> - val cmd = obtainProcessCommandLineArgs(port) - val process = ProcessBuilder(cmd) - .directory(getWorkingDirectory()) - .start() - - logger.info { "$displayName process started with PID = ${process.getPid}" } - logger.info { "$displayName log directory - ${logDirectory.canonicalPath}" } - logger.info { "$displayName log file - ${logDirectory.resolve(currentLogFilename)}" } - logger.info { "Log4j2 configuration file path - ${log4j2ConfigFile.canonicalPath}" } - - if (!process.isAlive) throw createInstantDeathException() - - process - } - rdProcess.awaitProcessReady() - - return createFromRDProcess(params, rdProcess) - } - } -} 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 5a5928d47a..8cd28607f4 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 @@ -10,27 +10,20 @@ import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.util.classMembers.MemberInfo import com.jetbrains.rd.util.ConcurrentHashMap +import com.jetbrains.rd.util.lifetime.LifetimeDefinition +import kotlinx.coroutines.runBlocking import mu.KotlinLogging -import org.utbot.common.AbstractSettings -import org.utbot.common.utBotTempDirectory +import org.utbot.common.* import org.utbot.framework.UtSettings -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.ProjectType -import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour -import org.utbot.framework.codegen.domain.StaticsMocking -import org.utbot.framework.codegen.domain.TestFramework +import org.utbot.framework.codegen.domain.* import org.utbot.framework.codegen.tree.ututils.UtilClassKind -import org.utbot.framework.plugin.api.ApplicationContext -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.MockFramework -import org.utbot.framework.plugin.api.MockStrategyApi +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.generated.* +import org.utbot.framework.process.generated.MethodDescription import org.utbot.framework.util.Conflict import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.util.KryoHelper @@ -39,47 +32,77 @@ import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener import org.utbot.intellij.plugin.util.assertReadAccessNotAllowed import org.utbot.intellij.plugin.util.methodDescription -import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.* import org.utbot.rd.exceptions.InstantProcessDeathException import org.utbot.rd.generated.SettingForResult import org.utbot.rd.generated.SettingsModel import org.utbot.rd.generated.settingsModel import org.utbot.rd.generated.synchronizationModel -import org.utbot.rd.onSchedulerBlocking -import org.utbot.rd.startBlocking +import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger import org.utbot.sarif.SourceFindingStrategy import java.io.File +import java.nio.charset.Charset +import java.nio.file.Files import java.nio.file.Path +import java.nio.file.StandardCopyOption 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 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) : - AbstractProcess(rdProcess) { - - data class Params( - val project: Project, - val classNameToPath: Map - ) - - companion object : AbstractProcess.Companion( - displayName = "Engine", - logConfigFileGetter = { UtSettings.engineProcessLogConfigFile }, - debugPortGetter = { UtSettings.engineProcessDebugPort }, - runWithDebugGetter = { UtSettings.runEngineProcessWithDebug }, - suspendExecutionInDebugModeGetter = { UtSettings.suspendEngineProcessExecutionInDebugMode }, - logConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogConfigurations"), - logDirectory = utBotTempDirectory.toFile().resolve("rdEngineProcessLogs"), - logConfigurationFileDeleteKey = "engine_process_appender_comment_key", - logAppender = "EngineProcessAppender", - currentLogFilename = "utbot-engine-current.log", - logger = KotlinLogging.logger {} - ) { + 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 val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" + + private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n" + + private val debugArgument: String? + get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" + .takeIf { UtSettings.runEngineProcessWithDebug } + + private val javaExecutablePathString: Path + get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") + private val pluginClasspath: String get() = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( separator = File.pathSeparator, @@ -89,19 +112,46 @@ class EngineProcess private constructor(val project: Project, private val classN private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt" - override fun obtainProcessSpecificCommandLineArgs() = listOf( - "-ea", - "-cp", - pluginClasspath, - startFileName - ) + 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)) + } + + 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 directory = WorkingDirService.provide().toFile() + val builder = ProcessBuilder(cmd).directory(directory) + val process = builder.start() + + logger.info { "Engine process started with PID = ${process.getPid}" } + logger.info { "Engine process log directory - ${engineProcessLogDirectory.canonicalPath}" } + logger.info { "Engine process log file - ${engineProcessLogDirectory.resolve("utbot-engine-current.log")}" } + logger.info { "Log4j2 configuration file path - ${log4j2ConfigFile.canonicalPath}" } - override fun getWorkingDirectory() = WorkingDirService.provide().toFile() + if (!process.isAlive) { + throw EngineProcessInstantDeathException() + } - override fun createFromRDProcess(params: Params, rdProcess: ProcessWithRdServer) = - EngineProcess(params.project, params.classNameToPath, rdProcess) + process + } + rdProcess.awaitProcessReady() - override fun createInstantDeathException() = EngineProcessInstantDeathException() + return EngineProcess(project, classNameToPath, rdProcess) + } } private val engineModel: EngineProcessModel = onSchedulerBlocking { protocol.engineProcessModel } @@ -117,6 +167,11 @@ class EngineProcess private constructor(val project: Project, private val classN engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader)) } + fun getSpringBeanQualifiedNames(params: GetSpringBeanQualifiedNamesParams): List { + assertReadAccessNotAllowed() + return engineModel.getSpringBeanQualifiedNames.startBlocking(params).toList() + } + private fun computeSourceFileByClass(params: ComputeSourceFileByClassArguments): String = DumbService.getInstance(project).runReadActionInSmartMode { val scope = GlobalSearchScope.allScope(project) @@ -344,6 +399,9 @@ class EngineProcess private constructor(val project: Project, private val classN } init { + lifetime.onTermination { + protocol.synchronizationModel.stopProcess.fire(Unit) + } settingsModel.settingFor.set { params -> SettingForResult(AbstractSettings.allSettings[params.key]?.let { settings: AbstractSettings -> val members: Collection> = diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt deleted file mode 100644 index a468a72c1b..0000000000 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/SpringAnalyzerProcess.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.utbot.intellij.plugin.process - -import mu.KotlinLogging -import org.apache.commons.io.FileUtils -import org.utbot.common.utBotTempDirectory -import org.utbot.framework.UtSettings -import org.utbot.intellij.plugin.util.assertReadAccessNotAllowed -import org.utbot.rd.ProcessWithRdServer -import org.utbot.rd.exceptions.InstantProcessDeathException -import org.utbot.rd.onSchedulerBlocking -import org.utbot.rd.startBlocking -import org.utbot.spring.process.generated.SpringAnalyzerParams -import org.utbot.spring.process.generated.SpringAnalyzerProcessModel -import org.utbot.spring.process.generated.springAnalyzerProcessModel -import java.io.File -import java.nio.file.Files - -class SpringAnalyzerProcessInstantDeathException : - InstantProcessDeathException(UtSettings.springAnalyzerProcessDebugPort, UtSettings.runSpringAnalyzerProcessWithDebug) - -private const val SPRING_ANALYZER_JAR_FILENAME = "utbot-spring-analyzer-shadow.jar" - -class SpringAnalyzerProcess private constructor( - rdProcess: ProcessWithRdServer -) : AbstractProcess(rdProcess) { - - companion object : AbstractProcess.Companion( - displayName = "Spring analyzer", - logConfigFileGetter = { UtSettings.springAnalyzerProcessLogConfigFile }, - debugPortGetter = { UtSettings.springAnalyzerProcessDebugPort }, - runWithDebugGetter = { UtSettings.runSpringAnalyzerProcessWithDebug }, - suspendExecutionInDebugModeGetter = { UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode }, - logConfigurationsDirectory = utBotTempDirectory.toFile().resolve("rdSpringAnalyzerProcessLogConfigurations"), - logDirectory = utBotTempDirectory.toFile().resolve("rdSpringAnalyzerProcessLogs"), - logConfigurationFileDeleteKey = "spring_analyzer_process_appender_comment_key", - logAppender = "SpringAnalyzerProcessAppender", - currentLogFilename = "utbot-spring-analyzer-current.log", - logger = KotlinLogging.logger {} - ) { - override fun obtainProcessSpecificCommandLineArgs(): List { - val jarFile = - Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) - .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME) - FileUtils.copyURLToFile( - this::class.java.classLoader.getResource("lib/$SPRING_ANALYZER_JAR_FILENAME"), - jarFile - ) - return listOf("-jar", jarFile.path) - } - - override fun getWorkingDirectory(): File = - Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile() - - override fun createFromRDProcess(params: Unit, rdProcess: ProcessWithRdServer) = - SpringAnalyzerProcess(rdProcess) - - override fun createInstantDeathException() = SpringAnalyzerProcessInstantDeathException() - } - - private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel } - - fun getBeanQualifiedNames( - classpath: List, - configuration: String, - propertyFilesPaths: List, - xmlConfigurationPaths: List - ): List { - assertReadAccessNotAllowed() - val params = SpringAnalyzerParams( - classpath.toTypedArray(), - configuration, - propertyFilesPaths.toTypedArray(), - xmlConfigurationPaths.toTypedArray() - ) - val result = springAnalyzerModel.analyze.startBlocking(params) - return result.beanTypes.toList() - } -} diff --git a/utbot-intellij/src/main/resources/log4j2.xml b/utbot-intellij/src/main/resources/log4j2.xml index 608a235418..6a9ae540c8 100644 --- a/utbot-intellij/src/main/resources/log4j2.xml +++ b/utbot-intellij/src/main/resources/log4j2.xml @@ -10,7 +10,7 @@ - - + delete_this_comment_key--> diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 51eb0323e1..4e0b26a4bf 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -69,6 +69,7 @@ dependencies { implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion + implementation group: 'commons-logging', name: 'commons-logging', version: commonsLoggingVersion processWithRdServerMockImplementation project(':utbot-rd') @@ -217,7 +218,7 @@ task generateEngineProcessModels(type: RdGenTask) { task generateSpringAnalyzerModels(type: RdGenTask) { def currentProjectDir = project.projectDir def springAnalyzerModelProjectDir = project.rootProject.childProjects["utbot-spring-analyzer-model"].projectDir - def generatedOutputDir = new File(springAnalyzerModelProjectDir, "src/main/kotlin/org/utbot/spring/processs/generated") + def generatedOutputDir = new File(springAnalyzerModelProjectDir, "src/main/kotlin/org/utbot/spring/process/generated") def hashDir = generatedOutputDir def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") def rdParams = extensions.getByName("params") as RdGenExtension diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt new file mode 100644 index 0000000000..26e0ba146f --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt @@ -0,0 +1,34 @@ +package org.utbot.rd.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.rd.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-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..cbd9d304cb 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,6 +4,9 @@ import com.jetbrains.rd.util.* import com.jetbrains.rd.util.lifetime.Lifetime import mu.KLogger import org.utbot.common.LoggerWithLogMethod +import org.utbot.rd.ProcessWithRdServer +import org.utbot.rd.generated.LoggerModel +import org.utbot.rd.generated.synchronizationModel fun Logger.withLevel(logLevel: LogLevel): LoggerWithLogMethod = LoggerWithLogMethod { @@ -24,4 +27,22 @@ fun overrideDefaultRdLoggerFactoryWithKLogger(logger: KLogger) { if (Statics().get() !is UtRdKLoggerFactory) { Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) } -} \ No newline at end of file +} + +// TODO use in instrumented process +fun setupRdLogger(rdProcess: ProcessWithRdServer, loggerModel: LoggerModel, rdLogger: UtRdKLogger) { + // currently we do not specify log level for different categories + // though it is possible with some additional map on categories -> consider performance + loggerModel.getCategoryMinimalLogLevel.set { _ -> + // this logLevel is obtained from KotlinLogger + rdLogger.logLevel.ordinal + } + + loggerModel.log.advise(rdProcess.lifetime) { + val logLevel = UtRdRemoteLogger.logLevelValues[it.logLevelOrdinal] + // assume throwable already in message + rdLogger.log(logLevel, it.message, null) + } + + rdProcess.protocol.synchronizationModel.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..55b350523d 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,10 @@ object EngineProcessModel : Ext(EngineProcessRoot) { val setupContextParams = structdef { field("classpathForUrlsClassloader", immutableList(PredefinedType.string)) } + val getSpringBeanQualifiedNamesParams = structdef { + field("classpath", array(PredefinedType.string)) + field("config", PredefinedType.string) + } val methodDescription = structdef { field("name", PredefinedType.string) field("containingClass", PredefinedType.string.nullable) @@ -124,6 +128,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-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerProcessModel.Generated.kt similarity index 100% rename from utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerProcessModel.Generated.kt rename to utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerProcessModel.Generated.kt diff --git a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerRoot.Generated.kt b/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerRoot.Generated.kt similarity index 100% rename from utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/processs/generated/SpringAnalyzerRoot.Generated.kt rename to utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerRoot.Generated.kt diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index f06c099654..09881cb8fc 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -27,8 +27,6 @@ dependencies { implementation(project(":utbot-core")) implementation("com.jetbrains.rd:rd-framework:$rdVersion") implementation("com.jetbrains.rd:rd-core:$rdVersion") - implementation("org.apache.logging.log4j:log4j-slf4j-impl:$log4j2Version") - implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion") } application { @@ -60,10 +58,3 @@ val springAnalyzerJar: Configuration by configurations.creating { artifacts { add(springAnalyzerJar.name, tasks.shadowJar) } - -configurations { - all { - // avoids conflicts with `implementation("org.apache.logging.log4j:log4j-slf4j-impl:$log4j2Version")` - exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") - } -} 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 index d37b70d839..83371d76a2 100644 --- 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 @@ -2,15 +2,20 @@ 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 mu.KotlinLogging +import com.jetbrains.rd.util.reactive.adviseOnce import org.utbot.common.AbstractSettings +import org.utbot.common.silentlyCloseStandardStreams import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.IdleWatchdog 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.loggers.UtRdKLoggerFactory +import org.utbot.rd.generated.synchronizationModel +import org.utbot.rd.loggers.UtRdRemoteLoggerFactory import org.utbot.spring.analyzers.SpringApplicationAnalyzer import org.utbot.spring.process.generated.SpringAnalyzerProcessModel import org.utbot.spring.process.generated.SpringAnalyzerResult @@ -19,22 +24,25 @@ import java.io.File import kotlin.time.Duration.Companion.seconds private val messageFromMainTimeoutMillis = 120.seconds -private val logger = KotlinLogging.logger {} +private val logger = getLogger() @Suppress("unused") object SpringAnalyzerProcessMain suspend fun main(args: Array) { - Logger.set(Lifetime.Eternal, UtRdKLoggerFactory(logger)) + // We don't want user code to litter the standard output, so we redirect it. + silentlyCloseStandardStreams() - logger.info("-----------------------------------------------------------------------") - logger.info("------------------NEW SPRING ANALYZER PROCESS STARTED------------------") - logger.info("-----------------------------------------------------------------------") - // 0 - auto port for server, should not be used here val port = findRdPort(args) ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { + synchronizationModel.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) } From 8ffdeb41ac9e327133fa666f334f688c72ffde28 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Mon, 3 Apr 2023 22:56:14 +0300 Subject: [PATCH 03/14] Refactor RD processes --- .../framework/process/CommonProcessArgs.kt | 8 +--- utbot-framework/build.gradle | 12 +++++ .../utbot/framework/codegen/domain/Domain.kt | 3 +- .../process/SpringAnalyzerProcess.kt | 12 ++--- .../process/InstrumentedProcessMain.kt | 20 -------- .../process/InstrumentedProcessRunner.kt | 20 +++----- .../instrumentation/rd/InstrumentedProcess.kt | 17 +------ utbot-intellij/build.gradle.kts | 13 ------ .../generator/UtTestsDialogProcessor.kt | 12 ++--- .../intellij/plugin/process/EngineProcess.kt | 46 ++++++++----------- .../org/utbot/rd/loggers/UtRdLogUtil.kt | 1 - .../process/SpringAnalyzerProcessMain.kt | 11 +++-- 12 files changed, 58 insertions(+), 117 deletions(-) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt index 835a151f67..c95e30c633 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt @@ -2,7 +2,6 @@ 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 java.nio.file.Path import kotlin.io.path.pathString @@ -10,13 +9,10 @@ import kotlin.io.path.pathString private val javaExecutablePathString: Path get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") -// TODO use it in EngineProcess and InstrumentedProcess -fun withCommonProcessCommandLineArgs( - processSpecificArgs: List, +fun obtainCommonProcessCommandLineArgs( debugPort: Int, runWithDebug: Boolean, suspendExecutionInDebugMode: Boolean, - rdPort: Int ): List = buildList { val suspendValue = if (suspendExecutionInDebugMode) "y" else "n" val debugArgument = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue},quiet=y,address=$debugPort" @@ -28,6 +24,4 @@ fun withCommonProcessCommandLineArgs( addAll(javaVersionSpecificArgs) } debugArgument?.let { add(it) } - addAll(processSpecificArgs) - add(rdPortArgument(rdPort)) } \ No newline at end of file diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index d69521acae..da9c02323d 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -1,3 +1,7 @@ +configurations { + fetchSpringAnalyzerJar +} + dependencies { api project(':utbot-fuzzers') @@ -36,4 +40,12 @@ dependencies { implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-core', version: ksmtVersion implementation group: 'com.github.UnitTestBot.ksmt', name: 'ksmt-z3', version: ksmtVersion + + 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/SpringAnalyzerProcess.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt index ea28231dd1..b5ad5cec6d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt @@ -15,6 +15,7 @@ import org.utbot.rd.generated.synchronizationModel import org.utbot.rd.loggers.UtRdKLogger import org.utbot.rd.loggers.setupRdLogger import org.utbot.rd.onSchedulerBlocking +import org.utbot.rd.rdPortArgument import org.utbot.rd.startBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException @@ -35,7 +36,7 @@ class SpringAnalyzerProcess private constructor( ) : ProcessWithRdServer by rdProcess { companion object { - private fun obtainProcessSpecificCommandLineArgs(): List { + private fun obtainProcessSpecificCommandLineArgs(port: Int): List { val jarFile = Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME) @@ -46,7 +47,8 @@ class SpringAnalyzerProcess private constructor( return listOf( "-Dorg.apache.commons.logging.LogFactory=org.utbot.rd.loggers.RDApacheCommonsLogFactory", "-jar", - jarFile.path + jarFile.path, + rdPortArgument(port) ) } @@ -54,13 +56,11 @@ class SpringAnalyzerProcess private constructor( suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime -> val rdProcess = startUtProcessWithRdServer(lifetime) { port -> - val cmd = withCommonProcessCommandLineArgs( - obtainProcessSpecificCommandLineArgs(), + val cmd = obtainCommonProcessCommandLineArgs( debugPort = UtSettings.springAnalyzerProcessDebugPort, runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, - rdPort = port - ) + ) + obtainProcessSpecificCommandLineArgs(port) val process = ProcessBuilder(cmd) .directory(Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile()) .start() 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 fdc6f2631b..030b401138 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 @@ -24,9 +24,6 @@ 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 +59,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() } 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 index 8d06d98128..ef1d3c98de 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt @@ -4,10 +4,9 @@ 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.framework.process.obtainCommonProcessCommandLineArgs import org.utbot.instrumentation.agent.DynamicClassTransformer import org.utbot.rd.rdPortArgument import java.io.File @@ -16,14 +15,11 @@ 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") + obtainCommonProcessCommandLineArgs( + debugPort = UtSettings.instrumentedProcessDebugPort, + runWithDebug = UtSettings.runInstrumentedProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendInstrumentedProcessExecutionInDebugMode + ) + listOf("-javaagent:$jarFile", "-ea", "-jar", "$jarFile") } fun start(rdPort: Int): Process { @@ -53,13 +49,9 @@ class InstrumentedProcessRunner { } 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 { 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..eb79ee1464 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 @@ -17,6 +17,7 @@ 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.setupRdLogger import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException @@ -63,21 +64,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) + setupRdLogger(rdProcess, proc.loggerModel, rdLogger) proc.lifetime.onTermination { logger.trace { "process is terminating" } diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 9b1510891d..97cf806fff 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -133,11 +133,6 @@ repositories { maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") } -val fetchSpringAnalyzerJar: Configuration by configurations.creating { - isCanBeResolved = true - isCanBeConsumed = false -} - dependencies { implementation(group ="com.jetbrains.rd", name = "rd-framework", version = rdVersion) implementation(group ="com.jetbrains.rd", name = "rd-core", version = rdVersion) @@ -173,8 +168,6 @@ dependencies { implementation(project(":utbot-android-studio")) - fetchSpringAnalyzerJar(project(":utbot-spring-analyzer", "springAnalyzerJar")) - testImplementation("com.intellij.remoterobot:remote-robot:$remoteRobotVersion") testImplementation("com.intellij.remoterobot:remote-fixtures:$remoteRobotVersion") @@ -191,9 +184,3 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit5Version") testRuntimeOnly("org.junit.vintage:junit-vintage-engine:$junit5Version") } - -tasks.processResources { - from(fetchSpringAnalyzerJar) { - into("lib") - } -} 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 21ed28e7bf..064bcf8f09 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 @@ -55,7 +55,6 @@ import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired import org.utbot.framework.plugin.services.JdkInfoService import org.utbot.framework.plugin.services.WorkingDirService -import org.utbot.framework.process.generated.GetSpringBeanQualifiedNamesParams import org.utbot.intellij.plugin.generator.CodeGenerationController.generateTests import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.models.packageName @@ -162,7 +161,7 @@ object UtTestsDialogProcessor { val springConfigClass = when (val approach = model.typeReplacementApproach) { TypeReplacementApproach.DoNotReplace -> null is TypeReplacementApproach.ReplaceIfPossible -> - approach.configFqn.takeUnless { it.endsWith(".xml") }?.let { + approach.config.takeUnless { it.endsWith(".xml") }?.let { JavaPsiFacade.getInstance(project).findClass(it, GlobalSearchScope.projectScope(project)) ?: error("Can't find configuration class $it") } @@ -223,11 +222,10 @@ object UtTestsDialogProcessor { if (!model.useSpringAnalyzer) emptyList() else when (val approach = model.typeReplacementApproach) { TypeReplacementApproach.DoNotReplace -> emptyList() - is TypeReplacementApproach.ReplaceIfPossible -> process.getSpringBeanQualifiedNames( - GetSpringBeanQualifiedNamesParams( - (buildDirs + classpathList).toTypedArray(), - approach.configFqn - ) + is TypeReplacementApproach.ReplaceIfPossible -> + process.getSpringBeanQualifiedNames( + buildDirs + classpathList, + approach.config ).also { logger.info { "Detected Spring Beans: $it" } } } 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 8cd28607f4..88ba27824e 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,11 +19,10 @@ 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.generated.* import org.utbot.framework.process.generated.MethodDescription +import org.utbot.framework.process.obtainCommonProcessCommandLineArgs import org.utbot.framework.util.Conflict import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.util.KryoHelper @@ -94,15 +93,6 @@ class EngineProcess private constructor(val project: Project, private val classN private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - private fun suspendValue(): String = if (UtSettings.suspendEngineProcessExecutionInDebugMode) "y" else "n" - - private val debugArgument: String? - get() = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=${suspendValue()},quiet=y,address=${UtSettings.engineProcessDebugPort}" - .takeIf { UtSettings.runEngineProcessWithDebug } - - private val javaExecutablePathString: Path - get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") - private val pluginClasspath: String get() = (this.javaClass.classLoader as PluginClassLoader).classPath.baseUrls.joinToString( separator = File.pathSeparator, @@ -112,27 +102,25 @@ class EngineProcess private constructor(val project: Project, private val classN private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt" - 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)) - } + private fun obtainProcessSpecificCommandLineArgs(port: Int) = listOf( + "-ea", + log4j2ConfigSwitch, + "-cp", + pluginClasspath, + startFileName, + rdPortArgument(port) + ) 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 = obtainCommonProcessCommandLineArgs( + debugPort = UtSettings.engineProcessDebugPort, + runWithDebug = UtSettings.runEngineProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, + ) + obtainProcessSpecificCommandLineArgs(port) val directory = WorkingDirService.provide().toFile() val builder = ProcessBuilder(cmd).directory(directory) val process = builder.start() @@ -167,9 +155,11 @@ class EngineProcess private constructor(val project: Project, private val classN engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader)) } - fun getSpringBeanQualifiedNames(params: GetSpringBeanQualifiedNamesParams): List { + fun getSpringBeanQualifiedNames(classpathList: List, config: String): List { assertReadAccessNotAllowed() - return engineModel.getSpringBeanQualifiedNames.startBlocking(params).toList() + return engineModel.getSpringBeanQualifiedNames.startBlocking( + GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config) + ).toList() } private fun computeSourceFileByClass(params: ComputeSourceFileByClassArguments): String = 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 cbd9d304cb..f24bf5b0b9 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 @@ -29,7 +29,6 @@ fun overrideDefaultRdLoggerFactoryWithKLogger(logger: KLogger) { } } -// TODO use in instrumented process fun setupRdLogger(rdProcess: ProcessWithRdServer, loggerModel: LoggerModel, rdLogger: UtRdKLogger) { // currently we do not specify log level for different categories // though it is possible with some additional map on categories -> consider performance 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 index 83371d76a2..ac7abb88f1 100644 --- 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 @@ -17,6 +17,7 @@ import org.utbot.rd.generated.settingsModel import org.utbot.rd.generated.synchronizationModel import org.utbot.rd.loggers.UtRdRemoteLoggerFactory import org.utbot.spring.analyzers.SpringApplicationAnalyzer +import org.utbot.spring.data.ApplicationData import org.utbot.spring.process.generated.SpringAnalyzerProcessModel import org.utbot.spring.process.generated.SpringAnalyzerResult import org.utbot.spring.process.generated.springAnalyzerProcessModel @@ -52,10 +53,12 @@ private fun SpringAnalyzerProcessModel.setup(watchdog: IdleWatchdog, realProtoco watchdog.measureTimeForActiveCall(analyze, "Analyzing Spring Application") { params -> SpringAnalyzerResult( SpringApplicationAnalyzer( - params.classpath.toList().map { File(it).toURI().toURL() }, - params.configuration, - params.propertyFilesPaths.toList(), - params.xmlConfigurationPaths.toList() + ApplicationData( + params.classpath.toList().map { File(it).toURI().toURL() }.toTypedArray(), + params.configuration, + params.propertyFilesPaths.toList(), + params.xmlConfigurationPaths.toList() + ) ).analyze().toTypedArray() ) } From 0a85c5477e589b1bc7fd194f5c02d4e9e6e3a16b Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 5 Apr 2023 12:31:40 +0300 Subject: [PATCH 04/14] Resolve issues with SpringAnalyzerProcess --- .../org/utbot/common/StandardStreamUtil.kt | 21 ------- .../framework/process/CommonProcessArgs.kt | 34 +++++------ .../process/SpringAnalyzerProcess.kt | 57 +++++++++++-------- .../process/InstrumentedProcessMain.kt | 6 +- .../process/InstrumentedProcessRunner.kt | 4 +- .../instrumentation/rd/InstrumentedProcess.kt | 6 +- .../intellij/plugin/process/EngineProcess.kt | 29 +++++----- utbot-rd/build.gradle | 1 - .../kotlin/org/utbot/rd/StandardStreamUtil.kt | 24 ++++++++ .../rd/generated/LoggerModel.Generated.kt | 9 ++- .../SynchronizationModel.Generated.kt | 9 +-- .../org/utbot/rd/loggers/UtRdLogUtil.kt | 11 ++-- .../rdgen/org/utbot/rd/models/LoggerModel.kt | 1 + .../utbot/rd/models/SynchronizationModel.kt | 1 - utbot-spring-analyzer/build.gradle.kts | 4 +- .../loggers/RDApacheCommonsLogFactory.kt | 4 +- .../process/SpringAnalyzerProcessMain.kt | 6 +- 17 files changed, 118 insertions(+), 109 deletions(-) delete mode 100644 utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt create mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt rename {utbot-rd/src/main/kotlin/org/utbot/rd => utbot-spring-analyzer/src/main/kotlin/org/utbot/spring}/loggers/RDApacheCommonsLogFactory.kt (95%) diff --git a/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt deleted file mode 100644 index c5ac9a8f16..0000000000 --- a/utbot-core/src/main/kotlin/org/utbot/common/StandardStreamUtil.kt +++ /dev/null @@ -1,21 +0,0 @@ -package org.utbot.common - -import java.io.OutputStream -import java.io.PrintStream - -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() } -} \ No newline at end of file diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt index c95e30c633..27326400fa 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt @@ -6,22 +6,24 @@ import java.io.File import java.nio.file.Path import kotlin.io.path.pathString -private val javaExecutablePathString: Path - get() = JdkInfoService.jdkInfoProvider.info.path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") +object CommonProcessArgs { + private val javaExecutablePathString: Path + get() = JdkInfoService.provide().path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") -fun obtainCommonProcessCommandLineArgs( - debugPort: Int, - runWithDebug: Boolean, - suspendExecutionInDebugMode: Boolean, -): 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 } + fun obtainCommonProcessCommandLineArgs( + debugPort: Int, + runWithDebug: Boolean, + suspendExecutionInDebugMode: Boolean, + ): 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) + add(javaExecutablePathString.pathString) + val javaVersionSpecificArgs = OpenModulesContainer.javaVersionSpecificArguments + if (javaVersionSpecificArgs.isNotEmpty()) { + addAll(javaVersionSpecificArgs) + } + debugArgument?.let { add(it) } } - debugArgument?.let { add(it) } -} \ No newline at end of file +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt index b5ad5cec6d..88a2860a06 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt @@ -11,9 +11,8 @@ 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.setupRdLogger +import org.utbot.rd.loggers.setup import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.rdPortArgument import org.utbot.rd.startBlocking @@ -28,6 +27,8 @@ 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, "") @@ -36,16 +37,34 @@ class SpringAnalyzerProcess private constructor( ) : ProcessWithRdServer by rdProcess { companion object { - private fun obtainProcessSpecificCommandLineArgs(port: Int): List { - val jarFile = - Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) - .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME) - FileUtils.copyURLToFile( - this::class.java.classLoader.getResource("lib/$SPRING_ANALYZER_JAR_FILENAME"), - jarFile - ) - return listOf( - "-Dorg.apache.commons.logging.LogFactory=org.utbot.rd.loggers.RDApacheCommonsLogFactory", + private val jarFile by lazy { + Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) + .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME).also { jarFile -> + val resource = this::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) + } + } + + private fun obtainSpringAnalyzerProcessCommandLine(port: Int): List { + return CommonProcessArgs.obtainCommonProcessCommandLineArgs( + debugPort = UtSettings.springAnalyzerProcessDebugPort, + runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, + ) + listOf( + "-Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory", "-jar", jarFile.path, rdPortArgument(port) @@ -56,11 +75,7 @@ class SpringAnalyzerProcess private constructor( suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime -> val rdProcess = startUtProcessWithRdServer(lifetime) { port -> - val cmd = obtainCommonProcessCommandLineArgs( - debugPort = UtSettings.springAnalyzerProcessDebugPort, - runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, - suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, - ) + obtainProcessSpecificCommandLineArgs(port) + val cmd = obtainSpringAnalyzerProcessCommandLine(port) val process = ProcessBuilder(cmd) .directory(Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile()) .start() @@ -73,7 +88,7 @@ class SpringAnalyzerProcess private constructor( } rdProcess.awaitProcessReady() val proc = SpringAnalyzerProcess(rdProcess) - setupRdLogger(rdProcess, proc.loggerModel, rdLogger) + proc.loggerModel.setup(rdLogger, proc.lifetime) return proc } } @@ -81,12 +96,6 @@ class SpringAnalyzerProcess private constructor( private val springAnalyzerModel: SpringAnalyzerProcessModel = onSchedulerBlocking { protocol.springAnalyzerProcessModel } private val loggerModel: LoggerModel = onSchedulerBlocking { protocol.loggerModel } - init { - lifetime.onTermination { - protocol.synchronizationModel.stopProcess.fire(Unit) - } - } - fun getBeanQualifiedNames( classpath: List, configuration: String, 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 030b401138..2639b38aff 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 @@ -21,8 +21,8 @@ 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 org.utbot.rd.StandardStreamUtil import java.io.File import java.net.URLClassLoader import java.security.AllPermission @@ -83,7 +83,7 @@ object InstrumentedProcessMain */ fun main(args: Array) = runBlocking { // We don't want user code to litter the standard output, so we redirect it. - silentlyCloseStandardStreams() + StandardStreamUtil.silentlyCloseStandardStreams() if (!args.contains(DISABLE_SANDBOX_OPTION)) { permissions { @@ -97,7 +97,7 @@ fun main(args: Array) = runBlocking { try { ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) { - synchronizationModel.initRemoteLogging.adviseOnce(lifetime) { + 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 index ef1d3c98de..4a2f1e9599 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt @@ -6,7 +6,7 @@ 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.obtainCommonProcessCommandLineArgs +import org.utbot.framework.process.CommonProcessArgs import org.utbot.instrumentation.agent.DynamicClassTransformer import org.utbot.rd.rdPortArgument import java.io.File @@ -15,7 +15,7 @@ private val logger = KotlinLogging.logger {} class InstrumentedProcessRunner { private val cmds: List by lazy { - obtainCommonProcessCommandLineArgs( + CommonProcessArgs.obtainCommonProcessCommandLineArgs( debugPort = UtSettings.instrumentedProcessDebugPort, runWithDebug = UtSettings.runInstrumentedProcessWithDebug, suspendExecutionInDebugMode = UtSettings.suspendInstrumentedProcessExecutionInDebugMode 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 eb79ee1464..9c1d3d5ed4 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 @@ -14,10 +14,8 @@ 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.setupRdLogger +import org.utbot.rd.loggers.setup import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException @@ -64,7 +62,7 @@ class InstrumentedProcess private constructor( logger.trace("rd process started") val proc = InstrumentedProcess(classLoader, rdProcess) - setupRdLogger(rdProcess, proc.loggerModel, rdLogger) + proc.loggerModel.setup(rdLogger, proc.lifetime) proc.lifetime.onTermination { logger.trace { "process is terminating" } 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 88ba27824e..8aa5f138e7 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 @@ -20,9 +20,9 @@ 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.WorkingDirService +import org.utbot.framework.process.CommonProcessArgs import org.utbot.framework.process.generated.* import org.utbot.framework.process.generated.MethodDescription -import org.utbot.framework.process.obtainCommonProcessCommandLineArgs import org.utbot.framework.util.Conflict import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.util.KryoHelper @@ -102,25 +102,26 @@ class EngineProcess private constructor(val project: Project, private val classN private const val startFileName = "org.utbot.framework.process.EngineProcessMainKt" - private fun obtainProcessSpecificCommandLineArgs(port: Int) = listOf( - "-ea", - log4j2ConfigSwitch, - "-cp", - pluginClasspath, - startFileName, - rdPortArgument(port) - ) + private fun obtainEngineProcessCommandLine(port: Int): List = + CommonProcessArgs.obtainCommonProcessCommandLineArgs( + debugPort = UtSettings.engineProcessDebugPort, + runWithDebug = UtSettings.runEngineProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, + ) + listOf( + "-ea", + log4j2ConfigSwitch, + "-cp", + pluginClasspath, + startFileName, + rdPortArgument(port) + ) 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 = obtainCommonProcessCommandLineArgs( - debugPort = UtSettings.engineProcessDebugPort, - runWithDebug = UtSettings.runEngineProcessWithDebug, - suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, - ) + obtainProcessSpecificCommandLineArgs(port) + val cmd = obtainEngineProcessCommandLine(port) val directory = WorkingDirService.provide().toFile() val builder = ProcessBuilder(cmd).directory(directory) val process = builder.start() diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 4e0b26a4bf..85bb42fcf5 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -69,7 +69,6 @@ dependencies { implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlinLoggingVersion - implementation group: 'commons-logging', name: 'commons-logging', version: commonsLoggingVersion processWithRdServerMockImplementation project(':utbot-rd') diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt new file mode 100644 index 0000000000..b96e7eab42 --- /dev/null +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt @@ -0,0 +1,24 @@ +package org.utbot.rd + +import org.utbot.common.silent +import java.io.OutputStream +import java.io.PrintStream + +object StandardStreamUtil { + 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() } + } +} 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/UtRdLogUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/UtRdLogUtil.kt index f24bf5b0b9..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,10 +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.ProcessWithRdServer import org.utbot.rd.generated.LoggerModel -import org.utbot.rd.generated.synchronizationModel - fun Logger.withLevel(logLevel: LogLevel): LoggerWithLogMethod = LoggerWithLogMethod { this.log(logLevel, it) @@ -29,19 +26,19 @@ fun overrideDefaultRdLoggerFactoryWithKLogger(logger: KLogger) { } } -fun setupRdLogger(rdProcess: ProcessWithRdServer, loggerModel: LoggerModel, rdLogger: UtRdKLogger) { +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 - loggerModel.getCategoryMinimalLogLevel.set { _ -> + getCategoryMinimalLogLevel.set { _ -> // this logLevel is obtained from KotlinLogger rdLogger.logLevel.ordinal } - loggerModel.log.advise(rdProcess.lifetime) { + log.advise(processLifetime) { val logLevel = UtRdRemoteLogger.logLevelValues[it.logLevelOrdinal] // assume throwable already in message rdLogger.log(logLevel, it.message, null) } - rdProcess.protocol.synchronizationModel.initRemoteLogging.fire(Unit) + initRemoteLogging.fire(Unit) } 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/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 09881cb8fc..2220aeb586 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -2,8 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer val rdVersion: String by rootProject -val log4j2Version: String by rootProject -val kotlinLoggingVersion: String? by rootProject +val commonsLoggingVersion: String by rootProject plugins { id("org.springframework.boot") version "2.7.8" @@ -27,6 +26,7 @@ dependencies { implementation(project(":utbot-core")) implementation("com.jetbrains.rd:rd-framework:$rdVersion") implementation("com.jetbrains.rd:rd-core:$rdVersion") + implementation("commons-logging:commons-logging:$commonsLoggingVersion") } application { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt similarity index 95% rename from utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt rename to utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt index 26e0ba146f..9fd5f67c9c 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/loggers/RDApacheCommonsLogFactory.kt +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt @@ -1,11 +1,11 @@ -package org.utbot.rd.loggers +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.rd.loggers.RDApacheCommonsLogFactory +@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) 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 index ac7abb88f1..9c4253b4a8 100644 --- 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 @@ -7,7 +7,7 @@ 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.common.silentlyCloseStandardStreams +import org.utbot.rd.StandardStreamUtil import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.IdleWatchdog import org.utbot.rd.RdSettingsContainerFactory @@ -32,13 +32,13 @@ object SpringAnalyzerProcessMain suspend fun main(args: Array) { // We don't want user code to litter the standard output, so we redirect it. - silentlyCloseStandardStreams() + StandardStreamUtil.silentlyCloseStandardStreams() val port = findRdPort(args) ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { - synchronizationModel.initRemoteLogging.adviseOnce(lifetime) { + loggerModel.initRemoteLogging.adviseOnce(lifetime) { Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel)) logger.info { "-----------------------------------------------------------------------" } logger.info { "------------------NEW SPRING ANALYZER PROCESS STARTED------------------" } From 18473ad4b4a0bdeccd2e556c919770a59f467c7b Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Wed, 5 Apr 2023 16:16:30 +0300 Subject: [PATCH 05/14] Move Spring analyzer model to utbot-rd module --- settings.gradle.kts | 1 - utbot-framework/build.gradle | 2 -- .../process/SpringAnalyzerProcess.kt | 6 ++-- utbot-rd/build.gradle | 32 +++++-------------- .../SpringAnalyzerProcessModel.Generated.kt | 2 +- .../generated/SpringAnalyzerRoot.Generated.kt | 2 +- utbot-spring-analyzer-model/build.gradle.kts | 6 ---- utbot-spring-analyzer/build.gradle.kts | 1 - .../process/SpringAnalyzerProcessMain.kt | 7 ++-- 9 files changed, 16 insertions(+), 43 deletions(-) rename {utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process => utbot-rd/src/main/kotlin/org/utbot/rd}/generated/SpringAnalyzerProcessModel.Generated.kt (99%) rename {utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process => utbot-rd/src/main/kotlin/org/utbot/rd}/generated/SpringAnalyzerRoot.Generated.kt (97%) delete mode 100644 utbot-spring-analyzer-model/build.gradle.kts diff --git a/settings.gradle.kts b/settings.gradle.kts index e2643b5b52..c80a375ffc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -71,4 +71,3 @@ if (goIde.split(",").contains(ideType)) { } include("utbot-spring-analyzer") -include("utbot-spring-analyzer-model") diff --git a/utbot-framework/build.gradle b/utbot-framework/build.gradle index da9c02323d..7a0c05b270 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -10,8 +10,6 @@ dependencies { api project(':utbot-framework-api') api project(':utbot-rd') - implementation project(':utbot-spring-analyzer-model') - implementation group: 'com.jetbrains.rd', name: 'rd-framework', version: rdVersion implementation group: 'com.jetbrains.rd', name: 'rd-core', version: rdVersion diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt index 88a2860a06..d8ed343ad3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt @@ -18,9 +18,9 @@ import org.utbot.rd.rdPortArgument import org.utbot.rd.startBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException -import org.utbot.spring.process.generated.SpringAnalyzerParams -import org.utbot.spring.process.generated.SpringAnalyzerProcessModel -import org.utbot.spring.process.generated.springAnalyzerProcessModel +import org.utbot.rd.generated.SpringAnalyzerParams +import org.utbot.rd.generated.SpringAnalyzerProcessModel +import org.utbot.rd.generated.springAnalyzerProcessModel import java.nio.file.Files class SpringAnalyzerProcessInstantDeathException : diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 85bb42fcf5..8ecc00faaf 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -214,10 +214,10 @@ task generateEngineProcessModels(type: RdGenTask) { } } -task generateSpringAnalyzerModels(type: RdGenTask) { +task generateCommonModels(type: RdGenTask) { def currentProjectDir = project.projectDir - def springAnalyzerModelProjectDir = project.rootProject.childProjects["utbot-spring-analyzer-model"].projectDir - def generatedOutputDir = new File(springAnalyzerModelProjectDir, "src/main/kotlin/org/utbot/spring/process/generated") + def ideaPluginProjectDir = project.rootProject.childProjects["utbot-rd"].projectDir + def generatedOutputDir = new File(ideaPluginProjectDir, "src/main/kotlin/org/utbot/rd/generated") def hashDir = generatedOutputDir def sourcesDir = new File(currentProjectDir, "src/main/rdgen/org/utbot/rd/models") def rdParams = extensions.getByName("params") as RdGenExtension @@ -232,32 +232,16 @@ task generateSpringAnalyzerModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.SpringAnalyzerRoot" + root = "org.utbot.rd.models.SynchronizationRoot" directory = generatedOutputDir.canonicalPath - namespace = "org.utbot.spring.process.generated" + namespace = "org.utbot.rd.generated" } -} - -task generateCommonModels(type: RdGenTask) { - def currentProjectDir = project.projectDir - def ideaPluginProjectDir = project.rootProject.childProjects["utbot-rd"].projectDir - def generatedOutputDir = new File(ideaPluginProjectDir, "src/main/kotlin/org/utbot/rd/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.SynchronizationRoot" + root = "org.utbot.rd.models.SettingsRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.rd.generated" @@ -266,7 +250,7 @@ task generateCommonModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.SettingsRoot" + root = "org.utbot.rd.models.LoggerRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.rd.generated" @@ -275,7 +259,7 @@ task generateCommonModels(type: RdGenTask) { rdParams.generator { language = "kotlin" transform = "symmetric" - root = "org.utbot.rd.models.LoggerRoot" + root = "org.utbot.rd.models.SpringAnalyzerRoot" directory = generatedOutputDir.canonicalPath namespace = "org.utbot.rd.generated" diff --git a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt similarity index 99% rename from utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerProcessModel.Generated.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt index bbd22dc910..2227a8faf6 100644 --- a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerProcessModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.spring.process.generated +package org.utbot.rd.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* diff --git a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerRoot.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt similarity index 97% rename from utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerRoot.Generated.kt rename to utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt index 17b2272973..83ec4dc36d 100644 --- a/utbot-spring-analyzer-model/src/main/kotlin/org/utbot/spring/process/generated/SpringAnalyzerRoot.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.spring.process.generated +package org.utbot.rd.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* diff --git a/utbot-spring-analyzer-model/build.gradle.kts b/utbot-spring-analyzer-model/build.gradle.kts deleted file mode 100644 index 62902ab16c..0000000000 --- a/utbot-spring-analyzer-model/build.gradle.kts +++ /dev/null @@ -1,6 +0,0 @@ -val rdVersion: String by rootProject - -dependencies { - implementation("com.jetbrains.rd:rd-framework:$rdVersion") - implementation("com.jetbrains.rd:rd-core:$rdVersion") -} diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index 2220aeb586..c224178deb 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -21,7 +21,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter-web") - implementation(project(":utbot-spring-analyzer-model")) implementation(project(":utbot-rd")) implementation(project(":utbot-core")) implementation("com.jetbrains.rd:rd-framework:$rdVersion") 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 index 9c4253b4a8..fc992ac89d 100644 --- 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 @@ -14,13 +14,12 @@ 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 org.utbot.spring.analyzers.SpringApplicationAnalyzer import org.utbot.spring.data.ApplicationData -import org.utbot.spring.process.generated.SpringAnalyzerProcessModel -import org.utbot.spring.process.generated.SpringAnalyzerResult -import org.utbot.spring.process.generated.springAnalyzerProcessModel +import org.utbot.rd.generated.SpringAnalyzerProcessModel +import org.utbot.rd.generated.SpringAnalyzerResult +import org.utbot.rd.generated.springAnalyzerProcessModel import java.io.File import kotlin.time.Duration.Companion.seconds From f1f36a510e47e61c69c176b300b480862302958c Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 11:14:46 +0300 Subject: [PATCH 06/14] Fix typos in UtSettings docs --- .../src/main/kotlin/org/utbot/framework/UtSettings.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 9702026762..3385f512bf 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) @@ -301,12 +301,12 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS * 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.intellij.plugin.process.EngineProcess + * @see org.utbot.framework.process.SpringAnalyzerProcess */ var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false) /** - * The spring analyzer process JDWP agent's port + * 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) From aeed41dc04214be30b6d213bd97159a24423b960 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 16:04:45 +0300 Subject: [PATCH 07/14] Refactor RD processes --- .../kotlin/org/utbot/framework/UtSettings.kt | 7 +- .../process/AbstractRDProcessCompanion.kt | 37 +++++++ .../framework/process/CommonProcessArgs.kt | 29 ----- .../framework/process/EngineProcessMain.kt | 3 +- .../process/SpringAnalyzerProcess.kt | 78 +++++++------- .../generated/EngineProcessModel.Generated.kt | 28 +++-- .../utbot/instrumentation/ConcreteExecutor.kt | 3 - .../process/InstrumentedProcessRunner.kt | 101 ------------------ .../instrumentation/rd/InstrumentedProcess.kt | 76 ++++++++++++- .../generator/UtTestsDialogProcessor.kt | 8 +- .../intellij/plugin/process/EngineProcess.kt | 97 ++++++++--------- .../SpringAnalyzerProcessModel.Generated.kt | 14 ++- .../org/utbot/rd/models/EngineProcessModel.kt | 1 + .../utbot/rd/models/SpringAnalyzerModel.kt | 1 + .../process/SpringAnalyzerProcessMain.kt | 3 +- 15 files changed, 231 insertions(+), 255 deletions(-) create mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/process/AbstractRDProcessCompanion.kt delete mode 100644 utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt delete mode 100644 utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt 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 3385f512bf..919b5d30a0 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 @@ -303,7 +303,7 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS * @see runInstrumentedProcessWithDebug * @see org.utbot.framework.process.SpringAnalyzerProcess */ - var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false) + var runSpringAnalyzerProcessWithDebug by getBooleanProperty(true) /** * The spring analyzer process JDWP agent's port. @@ -335,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 @@ -343,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/process/AbstractRDProcessCompanion.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/AbstractRDProcessCompanion.kt new file mode 100644 index 0000000000..6dded88686 --- /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 + +private val javaExecutablePathString = + JdkInfoService.provide().path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") + +abstract class AbstractRDProcessCompanion( + private val debugPort: Int, + private val runWithDebug: Boolean, + private val suspendExecutionInDebugMode: Boolean, + private val processSpecificCommandLineArgs: List +) { + 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/CommonProcessArgs.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt deleted file mode 100644 index 27326400fa..0000000000 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/process/CommonProcessArgs.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.utbot.framework.process - -import org.utbot.common.osSpecificJavaExecutable -import org.utbot.framework.plugin.services.JdkInfoService -import java.io.File -import java.nio.file.Path -import kotlin.io.path.pathString - -object CommonProcessArgs { - private val javaExecutablePathString: Path - get() = JdkInfoService.provide().path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") - - fun obtainCommonProcessCommandLineArgs( - debugPort: Int, - runWithDebug: Boolean, - suspendExecutionInDebugMode: Boolean, - ): 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) } - } -} 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 4189193c5c..4fdc3c1c03 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 @@ -88,7 +88,8 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch 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() + xmlConfigurationPaths = emptyList(), + params.useSpringAnalyzer ).toTypedArray() } springAnalyzerProcess.terminate() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt index d8ed343ad3..761b6d99cb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt @@ -14,7 +14,6 @@ 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.rdPortArgument import org.utbot.rd.startBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException @@ -24,7 +23,10 @@ import org.utbot.rd.generated.springAnalyzerProcessModel import java.nio.file.Files class SpringAnalyzerProcessInstantDeathException : - InstantProcessDeathException(UtSettings.springAnalyzerProcessDebugPort, UtSettings.runSpringAnalyzerProcessWithDebug) + 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" @@ -32,50 +34,44 @@ private const val UNKNOWN_MODIFICATION_TIME = 0L private val logger = KotlinLogging.logger {} private val rdLogger = UtRdKLogger(logger, "") +private val jarFile = 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 { - private val jarFile by lazy { - Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) - .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME).also { jarFile -> - val resource = this::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) - } - } - - private fun obtainSpringAnalyzerProcessCommandLine(port: Int): List { - return CommonProcessArgs.obtainCommonProcessCommandLineArgs( - debugPort = UtSettings.springAnalyzerProcessDebugPort, - runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, - suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, - ) + listOf( - "-Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory", - "-jar", - jarFile.path, - rdPortArgument(port) - ) - } - + 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", + jarFile.path + ) + ) { fun createBlocking() = runBlocking { SpringAnalyzerProcess() } suspend operator fun invoke(): SpringAnalyzerProcess = LifetimeDefinition().terminateOnException { lifetime -> val rdProcess = startUtProcessWithRdServer(lifetime) { port -> - val cmd = obtainSpringAnalyzerProcessCommandLine(port) + val cmd = obtainProcessCommandLine(port) val process = ProcessBuilder(cmd) .directory(Files.createTempDirectory(utBotTempDirectory, "spring-analyzer").toFile()) .start() @@ -100,13 +96,15 @@ class SpringAnalyzerProcess private constructor( classpath: List, configuration: String, propertyFilesPaths: List, - xmlConfigurationPaths: List + xmlConfigurationPaths: List, + useSpringAnalyzer: Boolean ): List { val params = SpringAnalyzerParams( classpath.toTypedArray(), configuration, propertyFilesPaths.toTypedArray(), - xmlConfigurationPaths.toTypedArray() + xmlConfigurationPaths.toTypedArray(), + useSpringAnalyzer ) val result = springAnalyzerModel.analyze.startBlocking(params) return result.beanTypes.toList() 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 1781b16f22..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 @@ -72,7 +72,7 @@ class EngineProcessModel private constructor( private val __StringArraySerializer = FrameworkMarshallers.String.array() - const val serializationHash = -5097607716462442558L + const val serializationHash = 1955031277042475752L } override val serializersOwner: ISerializersOwner get() = EngineProcessModel @@ -179,7 +179,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel /** - * #### Generated from [EngineProcessModel.kt:103] + * #### Generated from [EngineProcessModel.kt:104] */ data class FindMethodParamNamesArguments ( val classId: ByteArray, @@ -242,7 +242,7 @@ data class FindMethodParamNamesArguments ( /** - * #### Generated from [EngineProcessModel.kt:107] + * #### Generated from [EngineProcessModel.kt:108] */ data class FindMethodParamNamesResult ( val paramNames: ByteArray @@ -299,7 +299,7 @@ data class FindMethodParamNamesResult ( /** - * #### Generated from [EngineProcessModel.kt:96] + * #### Generated from [EngineProcessModel.kt:97] */ data class FindMethodsInClassMatchingSelectedArguments ( val classId: ByteArray, @@ -362,7 +362,7 @@ data class FindMethodsInClassMatchingSelectedArguments ( /** - * #### Generated from [EngineProcessModel.kt:100] + * #### Generated from [EngineProcessModel.kt:101] */ data class FindMethodsInClassMatchingSelectedResult ( val executableIds: ByteArray @@ -587,7 +587,7 @@ data class GenerateResult ( /** - * #### Generated from [EngineProcessModel.kt:115] + * #### Generated from [EngineProcessModel.kt:116] */ data class GenerateTestReportArgs ( val eventLogMessage: String?, @@ -680,7 +680,7 @@ data class GenerateTestReportArgs ( /** - * #### Generated from [EngineProcessModel.kt:124] + * #### Generated from [EngineProcessModel.kt:125] */ data class GenerateTestReportResult ( val notifyMessage: String, @@ -753,7 +753,8 @@ data class GenerateTestReportResult ( */ data class GetSpringBeanQualifiedNamesParams ( val classpath: Array, - val config: String + val config: String, + val useSpringAnalyzer: Boolean ) : IPrintable { //companion @@ -764,12 +765,14 @@ data class GetSpringBeanQualifiedNamesParams ( override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GetSpringBeanQualifiedNamesParams { val classpath = buffer.readArray {buffer.readString()} val config = buffer.readString() - return GetSpringBeanQualifiedNamesParams(classpath, config) + 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) } @@ -787,6 +790,7 @@ data class GetSpringBeanQualifiedNamesParams ( if (!(classpath contentDeepEquals other.classpath)) return false if (config != other.config) return false + if (useSpringAnalyzer != other.useSpringAnalyzer) return false return true } @@ -795,6 +799,7 @@ data class GetSpringBeanQualifiedNamesParams ( var __r = 0 __r = __r*31 + classpath.contentDeepHashCode() __r = __r*31 + config.hashCode() + __r = __r*31 + useSpringAnalyzer.hashCode() return __r } //pretty print @@ -803,6 +808,7 @@ data class GetSpringBeanQualifiedNamesParams ( printer.indent { print("classpath = "); classpath.print(printer); println() print("config = "); config.print(printer); println() + print("useSpringAnalyzer = "); useSpringAnalyzer.print(printer); println() } printer.print(")") } @@ -875,7 +881,7 @@ data class JdkInfo ( /** - * #### Generated from [EngineProcessModel.kt:91] + * #### Generated from [EngineProcessModel.kt:92] */ data class MethodDescription ( val name: String, @@ -1292,7 +1298,7 @@ data class TestGeneratorParams ( /** - * #### Generated from [EngineProcessModel.kt:110] + * #### Generated from [EngineProcessModel.kt:111] */ data class WriteSarifReportArguments ( val testSetsId: Long, 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/InstrumentedProcessRunner.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt deleted file mode 100644 index 4a2f1e9599..0000000000 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessRunner.kt +++ /dev/null @@ -1,101 +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.UtSettings -import org.utbot.framework.plugin.services.WorkingDirService -import org.utbot.framework.process.CommonProcessArgs -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 { - CommonProcessArgs.obtainCommonProcessCommandLineArgs( - debugPort = UtSettings.instrumentedProcessDebugPort, - runWithDebug = UtSettings.runInstrumentedProcessWithDebug, - suspendExecutionInDebugMode = UtSettings.suspendInstrumentedProcessExecutionInDebugMode - ) + 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 const val UTBOT_INSTRUMENTATION = "utbot-instrumentation" - private const val INSTRUMENTATION_LIB = "lib" - - 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 9c1d3d5ed4..1dba900f43 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 @@ -19,10 +30,44 @@ 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 val jarFile: File = + 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) + + InstrumentedProcess::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.") + InstrumentedProcess::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()) + } + class InstrumentedProcessInstantDeathException : InstantProcessDeathException(UtSettings.instrumentedProcessDebugPort, UtSettings.runInstrumentedProcessWithDebug) @@ -41,18 +86,39 @@ 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:${jarFile.path}") + add("-ea") + add("-jar") + add(jarFile.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() } 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 064bcf8f09..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 @@ -219,14 +219,14 @@ object UtTestsDialogProcessor { val applicationContext = when (model.projectType) { Spring -> { val beanQualifiedNames = - if (!model.useSpringAnalyzer) emptyList() - else when (val approach = model.typeReplacementApproach) { + when (val approach = model.typeReplacementApproach) { TypeReplacementApproach.DoNotReplace -> emptyList() is TypeReplacementApproach.ReplaceIfPossible -> process.getSpringBeanQualifiedNames( buildDirs + classpathList, - approach.config - ).also { logger.info { "Detected Spring Beans: $it" } } + approach.config, + model.useSpringAnalyzer + ).also { logger.info { "Detected Spring Beans: $it" } } } SpringApplicationContext( 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 8aa5f138e7..7fea19b662 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 @@ -20,7 +20,7 @@ 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.WorkingDirService -import org.utbot.framework.process.CommonProcessArgs +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 @@ -56,6 +56,30 @@ private const val configurationFileDeleteKey = "delete_this_comment_key" private const val deleteOpenComment = "" +private val log4j2ConfigFile: File = run { + engineProcessLogDirectory.mkdirs() + engineProcessLogConfigurationsDirectory.mkdirs() + overrideDefaultRdLoggerFactoryWithKLogger(logger) + + val customFile = File(UtSettings.engineProcessLogConfigFile) + + if (customFile.exists()) + customFile + else + Files.createTempFile(engineProcessLogConfigurationsDirectory.toPath(), null, ".xml").toFile() +} + +private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" + +private val pluginClasspath: String + get() = (EngineProcess::class.java.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) class EngineProcessInstantDeathException : @@ -63,65 +87,32 @@ class EngineProcessInstantDeathException : class EngineProcess private constructor(val project: Project, private val classNameToPath: Map, rdProcess: ProcessWithRdServer) : ProcessWithRdServer by rdProcess { - companion object { - private val log4j2ConfigFile: File - + companion object : AbstractRDProcessCompanion( + debugPort = UtSettings.engineProcessDebugPort, + runWithDebug = UtSettings.runEngineProcessWithDebug, + suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, + processSpecificCommandLineArgs = listOf("-ea", log4j2ConfigSwitch, "-cp", pluginClasspath, startFileName) + ) { 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 - ) - } + 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 val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" - - 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" - - private fun obtainEngineProcessCommandLine(port: Int): List = - CommonProcessArgs.obtainCommonProcessCommandLineArgs( - debugPort = UtSettings.engineProcessDebugPort, - runWithDebug = UtSettings.runEngineProcessWithDebug, - suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, - ) + listOf( - "-ea", - log4j2ConfigSwitch, - "-cp", - pluginClasspath, - startFileName, - rdPortArgument(port) - ) - 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() @@ -156,10 +147,10 @@ class EngineProcess private constructor(val project: Project, private val classN engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader)) } - fun getSpringBeanQualifiedNames(classpathList: List, config: String): List { + fun getSpringBeanQualifiedNames(classpathList: List, config: String, useSpringAnalyzer: Boolean): List { assertReadAccessNotAllowed() return engineModel.getSpringBeanQualifiedNames.startBlocking( - GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config) + GetSpringBeanQualifiedNamesParams(classpathList.toTypedArray(), config, useSpringAnalyzer) ).toList() } diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt index 2227a8faf6..c3bc2d9ecf 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt @@ -47,7 +47,7 @@ class SpringAnalyzerProcessModel private constructor( } - const val serializationHash = 1562060411431746104L + const val serializationHash = 528909252282504878L } override val serializersOwner: ISerializersOwner get() = SpringAnalyzerProcessModel @@ -100,7 +100,8 @@ data class SpringAnalyzerParams ( val classpath: Array, val configuration: String, val propertyFilesPaths: Array, - val xmlConfigurationPaths: Array + val xmlConfigurationPaths: Array, + val useSpringAnalyzer: Boolean ) : IPrintable { //companion @@ -113,7 +114,8 @@ data class SpringAnalyzerParams ( val configuration = buffer.readString() val propertyFilesPaths = buffer.readArray {buffer.readString()} val xmlConfigurationPaths = buffer.readArray {buffer.readString()} - return SpringAnalyzerParams(classpath, configuration, propertyFilesPaths, xmlConfigurationPaths) + val useSpringAnalyzer = buffer.readBool() + return SpringAnalyzerParams(classpath, configuration, propertyFilesPaths, xmlConfigurationPaths, useSpringAnalyzer) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: SpringAnalyzerParams) { @@ -121,6 +123,7 @@ data class SpringAnalyzerParams ( buffer.writeString(value.configuration) buffer.writeArray(value.propertyFilesPaths) { buffer.writeString(it) } buffer.writeArray(value.xmlConfigurationPaths) { buffer.writeString(it) } + buffer.writeBool(value.useSpringAnalyzer) } @@ -140,6 +143,7 @@ data class SpringAnalyzerParams ( 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 } @@ -150,6 +154,7 @@ data class SpringAnalyzerParams ( __r = __r*31 + configuration.hashCode() __r = __r*31 + propertyFilesPaths.contentDeepHashCode() __r = __r*31 + xmlConfigurationPaths.contentDeepHashCode() + __r = __r*31 + useSpringAnalyzer.hashCode() return __r } //pretty print @@ -160,6 +165,7 @@ data class SpringAnalyzerParams ( 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(")") } @@ -169,7 +175,7 @@ data class SpringAnalyzerParams ( /** - * #### Generated from [SpringAnalyzerModel.kt:16] + * #### Generated from [SpringAnalyzerModel.kt:17] */ data class SpringAnalyzerResult ( val beanTypes: Array 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 55b350523d..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 @@ -87,6 +87,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) { val getSpringBeanQualifiedNamesParams = structdef { field("classpath", array(PredefinedType.string)) field("config", PredefinedType.string) + field("useSpringAnalyzer", PredefinedType.bool) } val methodDescription = structdef { field("name", PredefinedType.string) 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 index 913989efcb..4a6dc98a64 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/SpringAnalyzerModel.kt @@ -11,6 +11,7 @@ object SpringAnalyzerProcessModel : Ext(SpringAnalyzerRoot) { field("configuration", PredefinedType.string) field("propertyFilesPaths", array(PredefinedType.string)) field("xmlConfigurationPaths", array(PredefinedType.string)) + field("useSpringAnalyzer", PredefinedType.bool) } val springAnalyzerResult = structdef { 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 index fc992ac89d..df38aa683d 100644 --- 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 @@ -51,7 +51,8 @@ suspend fun main(args: Array) { private fun SpringAnalyzerProcessModel.setup(watchdog: IdleWatchdog, realProtocol: IProtocol) { watchdog.measureTimeForActiveCall(analyze, "Analyzing Spring Application") { params -> SpringAnalyzerResult( - SpringApplicationAnalyzer( + if (!params.useSpringAnalyzer) emptyArray() + else SpringApplicationAnalyzer( ApplicationData( params.classpath.toList().map { File(it).toURI().toURL() }.toTypedArray(), params.configuration, From fde68bfe3ccfbc62ed892256052c78f2bf090478 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 16:27:16 +0300 Subject: [PATCH 08/14] Set runSpringAnalyzerProcessWithDebug to false --- .../src/main/kotlin/org/utbot/framework/UtSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 919b5d30a0..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 @@ -303,7 +303,7 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS * @see runInstrumentedProcessWithDebug * @see org.utbot.framework.process.SpringAnalyzerProcess */ - var runSpringAnalyzerProcessWithDebug by getBooleanProperty(true) + var runSpringAnalyzerProcessWithDebug by getBooleanProperty(false) /** * The spring analyzer process JDWP agent's port. From 1bcd3214ee1213d7a3dd9f41ea06f3c9216bc495 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 7 Apr 2023 17:09:31 +0300 Subject: [PATCH 09/14] [utbot-spring] Fixing gradle and rd builds --- build.gradle.kts | 5 ----- utbot-framework/build.gradle | 1 + .../framework/process/EngineProcessMain.kt | 1 + utbot-rd/build.gradle | 18 +++++++++++++++++- utbot-spring-analyzer/build.gradle.kts | 14 ++++++++------ .../SpringAnalyzerProcessModel.Generated.kt | 2 +- .../generated/SpringAnalyzerRoot.Generated.kt | 2 +- .../spring}/process/SpringAnalyzerProcess.kt | 9 +++++---- .../process/SpringAnalyzerProcessMain.kt | 6 +++--- 9 files changed, 37 insertions(+), 21 deletions(-) rename {utbot-rd/src/main/kotlin/org/utbot/rd => utbot-spring-analyzer/src/main/kotlin/org/utbot/spring}/generated/SpringAnalyzerProcessModel.Generated.kt (99%) rename {utbot-rd/src/main/kotlin/org/utbot/rd => utbot-spring-analyzer/src/main/kotlin/org/utbot/spring}/generated/SpringAnalyzerRoot.Generated.kt (97%) rename {utbot-framework/src/main/kotlin/org/utbot/framework => utbot-spring-analyzer/src/main/kotlin/org/utbot/spring}/process/SpringAnalyzerProcess.kt (94%) 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/utbot-framework/build.gradle b/utbot-framework/build.gradle index 7a0c05b270..5ca456d1a1 100644 --- a/utbot-framework/build.gradle +++ b/utbot-framework/build.gradle @@ -38,6 +38,7 @@ 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') } 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 4fdc3c1c03..7270d39c8b 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 @@ -37,6 +37,7 @@ 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 diff --git a/utbot-rd/build.gradle b/utbot-rd/build.gradle index 8ecc00faaf..549a850bbc 100644 --- a/utbot-rd/build.gradle +++ b/utbot-rd/build.gradle @@ -255,6 +255,22 @@ task generateCommonModels(type: RdGenTask) { directory = generatedOutputDir.canonicalPath namespace = "org.utbot.rd.generated" } +} + +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" @@ -262,7 +278,7 @@ task generateCommonModels(type: RdGenTask) { root = "org.utbot.rd.models.SpringAnalyzerRoot" directory = generatedOutputDir.canonicalPath - namespace = "org.utbot.rd.generated" + namespace = "org.utbot.spring.generated" } } diff --git a/utbot-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index c224178deb..0cfc248fb9 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -3,29 +3,31 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTra val rdVersion: String by rootProject val commonsLoggingVersion: String by rootProject +val kotlinLoggingVersion: 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:2.7.8") 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:2.11.0") } application { diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt similarity index 99% rename from utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt rename to utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt index c3bc2d9ecf..1274cfe070 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerProcessModel.Generated.kt +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerProcessModel.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.rd.generated +package org.utbot.spring.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt similarity index 97% rename from utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt rename to utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt index 83ec4dc36d..2ad0d56bac 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/generated/SpringAnalyzerRoot.Generated.kt +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/generated/SpringAnalyzerRoot.Generated.kt @@ -1,5 +1,5 @@ @file:Suppress("EXPERIMENTAL_API_USAGE","EXPERIMENTAL_UNSIGNED_LITERALS","PackageDirectoryMismatch","UnusedImport","unused","LocalVariableName","CanBeVal","PropertyName","EnumEntryName","ClassName","ObjectPropertyName","UnnecessaryVariable","SpellCheckingInspection") -package org.utbot.rd.generated +package org.utbot.spring.generated import com.jetbrains.rd.framework.* import com.jetbrains.rd.framework.base.* diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt similarity index 94% rename from utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt rename to utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt index 761b6d99cb..1487d6734f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/SpringAnalyzerProcess.kt +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcess.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.process +package org.utbot.spring.process import com.jetbrains.rd.util.lifetime.LifetimeDefinition import kotlinx.coroutines.runBlocking @@ -7,6 +7,7 @@ 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 @@ -17,9 +18,9 @@ import org.utbot.rd.onSchedulerBlocking import org.utbot.rd.startBlocking import org.utbot.rd.startUtProcessWithRdServer import org.utbot.rd.terminateOnException -import org.utbot.rd.generated.SpringAnalyzerParams -import org.utbot.rd.generated.SpringAnalyzerProcessModel -import org.utbot.rd.generated.springAnalyzerProcessModel +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 : 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 index df38aa683d..5c67980ea2 100644 --- 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 @@ -17,9 +17,9 @@ 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.rd.generated.SpringAnalyzerProcessModel -import org.utbot.rd.generated.SpringAnalyzerResult -import org.utbot.rd.generated.springAnalyzerProcessModel +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 From 6bcf204f3407d29c8f26043d6f64e05576d18cc2 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 17:50:05 +0300 Subject: [PATCH 10/14] Extract dependency versions to gradle.properties --- gradle.properties | 2 ++ utbot-spring-analyzer/build.gradle.kts | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 22bbeff4ff..485d03a1d9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -78,6 +78,8 @@ 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-spring-analyzer/build.gradle.kts b/utbot-spring-analyzer/build.gradle.kts index 0cfc248fb9..8327393282 100644 --- a/utbot-spring-analyzer/build.gradle.kts +++ b/utbot-spring-analyzer/build.gradle.kts @@ -1,9 +1,11 @@ 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("com.github.johnrengelman.shadow") version "7.1.2" @@ -18,7 +20,7 @@ java { dependencies { // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot - implementation("org.springframework.boot:spring-boot:2.7.8") + implementation("org.springframework.boot:spring-boot:$springBootVersion") implementation(project(":utbot-rd")) implementation(project(":utbot-core")) @@ -27,7 +29,7 @@ dependencies { 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:2.11.0") + implementation("commons-io:commons-io:$commonsIOVersion") } application { From 73e17d15742ebfd1f7286250fd3682132de9ed81 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 17:55:08 +0300 Subject: [PATCH 11/14] Replace InstrumentedProcessRunner with InstrumentedProcess in docs --- .../org/utbot/framework/plugin/services/JdkInfoService.kt | 2 +- .../org/utbot/framework/plugin/services/WorkingDirService.kt | 2 +- utbot-instrumentation-tests/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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-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" } From 7918617e7a2b71b24a879c0f5fa7a81127ed9486 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Fri, 7 Apr 2023 18:26:59 +0300 Subject: [PATCH 12/14] [utbot-spring] Refactoring of ClientProtocolBuilder --- .../org/utbot/framework/process/EngineProcessMain.kt | 7 +------ .../process/InstrumentedProcessMain.kt | 7 +------ .../src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt | 6 +++++- .../utbot/spring/process/SpringAnalyzerProcessMain.kt | 11 ++--------- 4 files changed, 9 insertions(+), 22 deletions(-) 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 7270d39c8b..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 @@ -53,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) 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 2639b38aff..bdd7600af4 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 @@ -82,9 +82,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. - StandardStreamUtil.silentlyCloseStandardStreams() - if (!args.contains(DISABLE_SANDBOX_OPTION)) { permissions { // Enable all permissions for instrumentation. @@ -93,10 +90,8 @@ fun main(args: Array) = runBlocking { } } - val port = findRdPort(args) - try { - ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(port) { + ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeout).start(args) { loggerModel.initRemoteLogging.adviseOnce(lifetime) { Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel)) this.protocol.scheduler.queue { warmupMockito() } 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..b4283c5f2f 100644 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt +++ b/utbot-rd/src/main/kotlin/org/utbot/rd/ClientProcessUtil.kt @@ -146,7 +146,11 @@ 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) { + suspend fun start(args: Array, parent: Lifetime? = null, block: Protocol.(IdleWatchdog) -> Unit) { + StandardStreamUtil.silentlyCloseStandardStreams() + + val port = findRdPort(args) + UtRdCoroutineScope.initialize() val pid = currentProcessPid.toInt() val ldef = parent?.createNested() ?: LifetimeDefinition() 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 index 5c67980ea2..af4146d81a 100644 --- 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 @@ -29,14 +29,8 @@ private val logger = getLogger() @Suppress("unused") object SpringAnalyzerProcessMain -suspend fun main(args: Array) { - // We don't want user code to litter the standard output, so we redirect it. - StandardStreamUtil.silentlyCloseStandardStreams() - - val port = findRdPort(args) - - - ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(port) { +suspend fun main(args: Array) = + ClientProtocolBuilder().withProtocolTimeout(messageFromMainTimeoutMillis).start(args) { loggerModel.initRemoteLogging.adviseOnce(lifetime) { Logger.set(Lifetime.Eternal, UtRdRemoteLoggerFactory(loggerModel)) logger.info { "-----------------------------------------------------------------------" } @@ -46,7 +40,6 @@ suspend fun main(args: Array) { AbstractSettings.setupFactory(RdSettingsContainerFactory(protocol.settingsModel)) springAnalyzerProcessModel.setup(it, protocol) } -} private fun SpringAnalyzerProcessModel.setup(watchdog: IdleWatchdog, realProtocol: IProtocol) { watchdog.measureTimeForActiveCall(analyze, "Analyzing Spring Application") { params -> From c9abeab1f925c5b033f233c399240069807a293d Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Fri, 7 Apr 2023 18:57:07 +0300 Subject: [PATCH 13/14] Do more RD processes refactoring --- .../process/AbstractRDProcessCompanion.kt | 6 +- .../process/InstrumentedProcessMain.kt | 2 - .../instrumentation/rd/InstrumentedProcess.kt | 56 ++++++++++--------- .../intellij/plugin/process/EngineProcess.kt | 48 ++++++++-------- .../kotlin/org/utbot/rd/ClientProcessUtil.kt | 21 ++++++- .../kotlin/org/utbot/rd/StandardStreamUtil.kt | 24 -------- .../spring/process/SpringAnalyzerProcess.kt | 4 +- .../process/SpringAnalyzerProcessMain.kt | 2 - 8 files changed, 78 insertions(+), 85 deletions(-) delete mode 100644 utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt 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 index 6dded88686..40bdc0e880 100644 --- 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 @@ -6,15 +6,15 @@ import org.utbot.rd.rdPortArgument import java.io.File import kotlin.io.path.pathString -private val javaExecutablePathString = - JdkInfoService.provide().path.resolve("bin${File.separatorChar}${osSpecificJavaExecutable()}") - 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) 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 bdd7600af4..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,11 +18,9 @@ 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.loggers.UtRdRemoteLoggerFactory -import org.utbot.rd.StandardStreamUtil import java.io.File import java.net.URLClassLoader import java.security.AllPermission 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 1dba900f43..c324b3e14e 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 @@ -38,30 +38,36 @@ private val rdLogger = UtRdKLogger(logger, "") private const val UTBOT_INSTRUMENTATION = "utbot-instrumentation" private const val INSTRUMENTATION_LIB = "lib" -private val jarFile: File = +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" } ) { - 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) - - InstrumentedProcess::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.") - InstrumentedProcess::class.java.classLoader - .scanForResourcesContaining(DynamicClassTransformer::class.java.nameOfPackage) - .firstOrNull { - it.absolutePath.contains(UTBOT_INSTRUMENTATION) && it.extension == "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. @@ -91,10 +97,10 @@ class InstrumentedProcess private constructor( runWithDebug = UtSettings.runInstrumentedProcessWithDebug, suspendExecutionInDebugMode = UtSettings.suspendInstrumentedProcessExecutionInDebugMode, processSpecificCommandLineArgs = buildList { - add("-javaagent:${jarFile.path}") + add("-javaagent:${instrumentationJarFile.path}") add("-ea") add("-jar") - add(jarFile.path) + add(instrumentationJarFile.path) if (!UtSettings.useSandbox) add(DISABLE_SANDBOX_OPTION) } 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 7fea19b662..6ed76e01db 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 @@ -48,27 +48,37 @@ 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 = "" -private val log4j2ConfigFile: File = run { - engineProcessLogDirectory.mkdirs() - engineProcessLogConfigurationsDirectory.mkdirs() - overrideDefaultRdLoggerFactoryWithKLogger(logger) - +private fun createEngineProcessLog4j2Config(): File { val customFile = File(UtSettings.engineProcessLogConfigFile) - if (customFile.exists()) - customFile - else - Files.createTempFile(engineProcessLogConfigurationsDirectory.toPath(), null, ".xml").toFile() + 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 log4j2ConfigFile: File = createEngineProcessLog4j2Config() + private val log4j2ConfigSwitch = "-Dlog4j2.configurationFile=${log4j2ConfigFile.canonicalPath}" private val pluginClasspath: String @@ -93,20 +103,6 @@ class EngineProcess private constructor(val project: Project, private val classN suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, processSpecificCommandLineArgs = listOf("-ea", log4j2ConfigSwitch, "-cp", pluginClasspath, startFileName) ) { - init { - 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 - ) - } - } - fun createBlocking(project: Project, classNameToPath: Map): EngineProcess = runBlocking { EngineProcess(project, classNameToPath) } suspend operator fun invoke(project: Project, classNameToPath: Map): EngineProcess = 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 b4283c5f2f..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,8 +148,25 @@ class IdleWatchdog(private val ldef: LifetimeDefinition, val timeout: Duration) class ClientProtocolBuilder { private var timeout = Duration.INFINITE + 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) { - StandardStreamUtil.silentlyCloseStandardStreams() + silentlyCloseStandardStreams() val port = findRdPort(args) diff --git a/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt b/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt deleted file mode 100644 index b96e7eab42..0000000000 --- a/utbot-rd/src/main/kotlin/org/utbot/rd/StandardStreamUtil.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.utbot.rd - -import org.utbot.common.silent -import java.io.OutputStream -import java.io.PrintStream - -object StandardStreamUtil { - 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() } - } -} 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 index 1487d6734f..591f02d911 100644 --- 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 @@ -35,7 +35,7 @@ private const val UNKNOWN_MODIFICATION_TIME = 0L private val logger = KotlinLogging.logger {} private val rdLogger = UtRdKLogger(logger, "") -private val jarFile = Files.createDirectories(utBotTempDirectory.toFile().resolve("spring-analyzer").toPath()) +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") @@ -65,7 +65,7 @@ class SpringAnalyzerProcess private constructor( processSpecificCommandLineArgs = listOf( "-Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory", "-jar", - jarFile.path + springAnalyzerJarFile.path ) ) { fun createBlocking() = runBlocking { SpringAnalyzerProcess() } 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 index af4146d81a..8c4eab7b0a 100644 --- 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 @@ -7,11 +7,9 @@ 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.StandardStreamUtil import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.IdleWatchdog 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.loggers.UtRdRemoteLoggerFactory From c1329ac5ddaf65dc73868538251498fe3ff3ef22 Mon Sep 17 00:00:00 2001 From: "Artemii.Kononov" Date: Mon, 10 Apr 2023 10:48:00 +0300 Subject: [PATCH 14/14] [utbot-spring] Making process specific arguments as lambda --- .../process/AbstractRDProcessCompanion.kt | 4 ++-- .../instrumentation/rd/InstrumentedProcess.kt | 20 ++++++++++--------- .../intellij/plugin/process/EngineProcess.kt | 2 +- .../spring/process/SpringAnalyzerProcess.kt | 14 +++++++------ 4 files changed, 22 insertions(+), 18 deletions(-) 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 index 40bdc0e880..41b0499cae 100644 --- 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 @@ -10,14 +10,14 @@ abstract class AbstractRDProcessCompanion( private val debugPort: Int, private val runWithDebug: Boolean, private val suspendExecutionInDebugMode: Boolean, - private val processSpecificCommandLineArgs: List + 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) + addAll(processSpecificCommandLineArgs()) add(rdPortArgument(port)) } 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 c324b3e14e..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 @@ -96,15 +96,17 @@ class InstrumentedProcess private constructor( 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) - } - ) { + 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, instrumentation: TInstrumentation, 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 6ed76e01db..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 @@ -101,7 +101,7 @@ class EngineProcess private constructor(val project: Project, private val classN debugPort = UtSettings.engineProcessDebugPort, runWithDebug = UtSettings.runEngineProcessWithDebug, suspendExecutionInDebugMode = UtSettings.suspendEngineProcessExecutionInDebugMode, - processSpecificCommandLineArgs = listOf("-ea", log4j2ConfigSwitch, "-cp", pluginClasspath, startFileName) + processSpecificCommandLineArgs = { listOf("-ea", log4j2ConfigSwitch, "-cp", pluginClasspath, startFileName) } ) { fun createBlocking(project: Project, classNameToPath: Map): EngineProcess = runBlocking { EngineProcess(project, classNameToPath) } 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 index 591f02d911..e7381e939f 100644 --- 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 @@ -62,12 +62,14 @@ class SpringAnalyzerProcess private constructor( debugPort = UtSettings.springAnalyzerProcessDebugPort, runWithDebug = UtSettings.runSpringAnalyzerProcessWithDebug, suspendExecutionInDebugMode = UtSettings.suspendSpringAnalyzerProcessExecutionInDebugMode, - processSpecificCommandLineArgs = listOf( - "-Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory", - "-jar", - springAnalyzerJarFile.path - ) - ) { + 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 ->