From e1afb1c1be8cfb021f59bab0de7ab6330427a59a Mon Sep 17 00:00:00 2001 From: iryabov Date: Fri, 12 Sep 2025 11:32:24 +0200 Subject: [PATCH 1/9] fix: comment Agent.kt functions --- .../kotlin/com/epam/drill/agent/Agent.kt | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt index 9e8b6304..6e0fe2a3 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt @@ -78,98 +78,98 @@ object Agent { Java Agent (v${agentVersion}) """.trimIndent() - private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") - private val transformers = setOf( - ApplicationClassTransformer, - TomcatHttpServerTransformer, - JettyHttpServerTransformer, - UndertowHttpServerTransformer, - NettyHttpServerTransformer, - JavaHttpClientTransformer, - ApacheHttpClientTransformer, - OkHttp3ClientTransformer, - SpringWebClientTransformer, - KafkaTransformer, - CadenceTransformer, - TTLTransformer, - ReactorTransformer, - SSLEngineTransformer, - JettyWsClientTransformer, - JettyWsServerTransformer, - Jetty9WsMessagesTransformer, - Jetty10WsMessagesTransformer, - Jetty11WsMessagesTransformer, - NettyWsClientTransformer, - NettyWsServerTransformer, - NettyWsMessagesTransformer, - TomcatWsClientTransformer, - TomcatWsServerTransformer, - TomcatWsMessagesTransformer, - UndertowWsClientTransformer, - UndertowWsServerTransformer, - UndertowWsMessagesTransformer, - CompatibilityTestsTransformer, - ) +// private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") +// private val transformers = setOf( +// ApplicationClassTransformer, +// TomcatHttpServerTransformer, +// JettyHttpServerTransformer, +// UndertowHttpServerTransformer, +// NettyHttpServerTransformer, +// JavaHttpClientTransformer, +// ApacheHttpClientTransformer, +// OkHttp3ClientTransformer, +// SpringWebClientTransformer, +// KafkaTransformer, +// CadenceTransformer, +// TTLTransformer, +// ReactorTransformer, +// SSLEngineTransformer, +// JettyWsClientTransformer, +// JettyWsServerTransformer, +// Jetty9WsMessagesTransformer, +// Jetty10WsMessagesTransformer, +// Jetty11WsMessagesTransformer, +// NettyWsClientTransformer, +// NettyWsServerTransformer, +// NettyWsMessagesTransformer, +// TomcatWsClientTransformer, +// TomcatWsServerTransformer, +// TomcatWsMessagesTransformer, +// UndertowWsClientTransformer, +// UndertowWsServerTransformer, +// UndertowWsMessagesTransformer, +// CompatibilityTestsTransformer, +// ) @OptIn(ExperimentalNativeApi::class, ExperimentalForeignApi::class) fun agentOnLoad(options: String): Int { println(logo) - AgentLoggingConfiguration.defaultNativeLoggingConfiguration() - Configuration.initializeNative(options) - AgentLoggingConfiguration.updateNativeLoggingConfiguration() - TransformerRegistrar.initialize(transformers) - - addCapabilities() - setEventCallbacks() - setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) - AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") - - logger.info { "agentOnLoad: Java Agent has been loaded. Pid is: " + getpid() } +// AgentLoggingConfiguration.defaultNativeLoggingConfiguration() +// Configuration.initializeNative(options) +// AgentLoggingConfiguration.updateNativeLoggingConfiguration() +// TransformerRegistrar.initialize(transformers) +// +// addCapabilities() +// setEventCallbacks() +// setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) +// AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") + +// logger.info { "agentOnLoad: Java Agent has been loaded. Pid is: " + getpid() } return JNI_OK } fun agentOnUnload() { - logger.info { "agentOnUnload: Java Agent has been unloaded." } +// logger.info { "agentOnUnload: Java Agent has been unloaded." } } @OptIn(ExperimentalForeignApi::class) fun agentOnVmInit() { - initRuntimeIfNeeded() - SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) - - AgentLoggingConfiguration.defaultJvmLoggingConfiguration() - AgentLoggingConfiguration.updateJvmLoggingConfiguration() - Configuration.initializeJvm() - - - loadJvmModule("com.epam.drill.agent.test2code.Test2Code") - JvmModuleMessageSender.sendAgentMetadata() +// initRuntimeIfNeeded() +// SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) +// +// AgentLoggingConfiguration.defaultJvmLoggingConfiguration() +// AgentLoggingConfiguration.updateJvmLoggingConfiguration() +// Configuration.initializeJvm() +// +// +// loadJvmModule("com.epam.drill.agent.test2code.Test2Code") +// JvmModuleMessageSender.sendAgentMetadata() } fun agentOnVmDeath() { - logger.debug { "agentOnVmDeath" } - } - - @OptIn(ExperimentalForeignApi::class) - private fun addCapabilities() = memScoped { - val jvmtiCapabilities = alloc() - jvmtiCapabilities.can_retransform_classes = 1.toUInt() - jvmtiCapabilities.can_maintain_original_method_order = 1.toUInt() - AddCapabilities(jvmtiCapabilities.ptr) - } - - @OptIn(ExperimentalForeignApi::class) - private fun setEventCallbacks() = memScoped { - val alloc = alloc() - alloc.VMInit = staticCFunction(::vmInitEvent) - alloc.VMDeath = staticCFunction(::vmDeathEvent) - alloc.ClassFileLoadHook = staticCFunction(::classFileLoadHook) - SetEventCallbacks(alloc.ptr, sizeOf().toInt()) - SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) +// logger.debug { "agentOnVmDeath" } } - private fun loadJvmModule(clazz: String) = runCatching { JvmModuleLoader.loadJvmModule(clazz).load() } - .onFailure { logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } } +// @OptIn(ExperimentalForeignApi::class) +// private fun addCapabilities() = memScoped { +// val jvmtiCapabilities = alloc() +// jvmtiCapabilities.can_retransform_classes = 1.toUInt() +// jvmtiCapabilities.can_maintain_original_method_order = 1.toUInt() +// AddCapabilities(jvmtiCapabilities.ptr) +// } + +// @OptIn(ExperimentalForeignApi::class) +// private fun setEventCallbacks() = memScoped { +// val alloc = alloc() +// alloc.VMInit = staticCFunction(::vmInitEvent) +// alloc.VMDeath = staticCFunction(::vmDeathEvent) +// alloc.ClassFileLoadHook = staticCFunction(::classFileLoadHook) +// SetEventCallbacks(alloc.ptr, sizeOf().toInt()) +// SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) +// } + +// private fun loadJvmModule(clazz: String) = runCatching { JvmModuleLoader.loadJvmModule(clazz).load() } +// .onFailure { logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } } } From d8692f24aa05c88c86b1e3b9e8facb5c29ec44cc Mon Sep 17 00:00:00 2001 From: iryabov Date: Fri, 12 Sep 2025 12:49:17 +0200 Subject: [PATCH 2/9] fix: uncomment loadJvmModule --- .../kotlin/com/epam/drill/agent/Agent.kt | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt index 6e0fe2a3..11aede8e 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt @@ -78,7 +78,7 @@ object Agent { Java Agent (v${agentVersion}) """.trimIndent() -// private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") + private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") // private val transformers = setOf( // ApplicationClassTransformer, // TomcatHttpServerTransformer, @@ -115,40 +115,45 @@ object Agent { fun agentOnLoad(options: String): Int { println(logo) // AgentLoggingConfiguration.defaultNativeLoggingConfiguration() -// Configuration.initializeNative(options) + println("!!! Configuration.initializeNative(options)") + Configuration.initializeNative(options) // AgentLoggingConfiguration.updateNativeLoggingConfiguration() // TransformerRegistrar.initialize(transformers) // // addCapabilities() -// setEventCallbacks() + setEventCallbacks() // setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) -// AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") + println("!!! AddToBootstrapClassLoaderSearch(\"${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar\")") + AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") -// logger.info { "agentOnLoad: Java Agent has been loaded. Pid is: " + getpid() } + logger.info { "agentOnLoad: Java Agent has been loaded. Pid is: " + getpid() } return JNI_OK } fun agentOnUnload() { -// logger.info { "agentOnUnload: Java Agent has been unloaded." } + logger.info { "agentOnUnload: Java Agent has been unloaded." } } @OptIn(ExperimentalForeignApi::class) fun agentOnVmInit() { -// initRuntimeIfNeeded() + initRuntimeIfNeeded() // SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) // // AgentLoggingConfiguration.defaultJvmLoggingConfiguration() // AgentLoggingConfiguration.updateJvmLoggingConfiguration() -// Configuration.initializeJvm() + println("!!! Configuration.initializeJvm()") + Configuration.initializeJvm() // // -// loadJvmModule("com.epam.drill.agent.test2code.Test2Code") + println("!!! loadJvmModule(\"com.epam.drill.agent.test2code.Test2Code\")") + loadJvmModule("com.epam.drill.agent.test2code.Test2Code") +// println("!!! JvmModuleMessageSender.sendAgentMetadata()") // JvmModuleMessageSender.sendAgentMetadata() } fun agentOnVmDeath() { -// logger.debug { "agentOnVmDeath" } + logger.debug { "agentOnVmDeath" } } // @OptIn(ExperimentalForeignApi::class) @@ -159,17 +164,20 @@ object Agent { // AddCapabilities(jvmtiCapabilities.ptr) // } -// @OptIn(ExperimentalForeignApi::class) -// private fun setEventCallbacks() = memScoped { -// val alloc = alloc() -// alloc.VMInit = staticCFunction(::vmInitEvent) -// alloc.VMDeath = staticCFunction(::vmDeathEvent) + @OptIn(ExperimentalForeignApi::class) + private fun setEventCallbacks() = memScoped { + val alloc = alloc() + alloc.VMInit = staticCFunction(::vmInitEvent) + alloc.VMDeath = staticCFunction(::vmDeathEvent) // alloc.ClassFileLoadHook = staticCFunction(::classFileLoadHook) -// SetEventCallbacks(alloc.ptr, sizeOf().toInt()) -// SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) -// } + SetEventCallbacks(alloc.ptr, sizeOf().toInt()) + SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) + } -// private fun loadJvmModule(clazz: String) = runCatching { JvmModuleLoader.loadJvmModule(clazz).load() } -// .onFailure { logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } } + private fun loadJvmModule(clazz: String) = runCatching { JvmModuleLoader.loadJvmModule(clazz).load() } + .onFailure { + logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } + println("!!! loadJvmModule: Fatal error: id=${clazz}") + } } From da96c710773bbde8501c4860ad8b4618eec2ac16 Mon Sep 17 00:00:00 2001 From: iryabov Date: Fri, 12 Sep 2025 16:51:46 +0200 Subject: [PATCH 3/9] fix: uncomment ClassFileLoadHook event --- .../kotlin/com/epam/drill/agent/Agent.kt | 30 +++---- .../drill/agent/jvmti/ClassFileLoadHook.kt | 86 +++++++++---------- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt index 11aede8e..5ae1cbf5 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt @@ -120,9 +120,9 @@ object Agent { // AgentLoggingConfiguration.updateNativeLoggingConfiguration() // TransformerRegistrar.initialize(transformers) // -// addCapabilities() + addCapabilities() setEventCallbacks() -// setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) + setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) println("!!! AddToBootstrapClassLoaderSearch(\"${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar\")") AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") @@ -138,38 +138,33 @@ object Agent { @OptIn(ExperimentalForeignApi::class) fun agentOnVmInit() { initRuntimeIfNeeded() -// SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) + SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) // // AgentLoggingConfiguration.defaultJvmLoggingConfiguration() // AgentLoggingConfiguration.updateJvmLoggingConfiguration() - println("!!! Configuration.initializeJvm()") Configuration.initializeJvm() -// -// - println("!!! loadJvmModule(\"com.epam.drill.agent.test2code.Test2Code\")") loadJvmModule("com.epam.drill.agent.test2code.Test2Code") -// println("!!! JvmModuleMessageSender.sendAgentMetadata()") -// JvmModuleMessageSender.sendAgentMetadata() + JvmModuleMessageSender.sendAgentMetadata() } fun agentOnVmDeath() { logger.debug { "agentOnVmDeath" } } -// @OptIn(ExperimentalForeignApi::class) -// private fun addCapabilities() = memScoped { -// val jvmtiCapabilities = alloc() -// jvmtiCapabilities.can_retransform_classes = 1.toUInt() -// jvmtiCapabilities.can_maintain_original_method_order = 1.toUInt() -// AddCapabilities(jvmtiCapabilities.ptr) -// } + @OptIn(ExperimentalForeignApi::class) + private fun addCapabilities() = memScoped { + val jvmtiCapabilities = alloc() + jvmtiCapabilities.can_retransform_classes = 1.toUInt() + jvmtiCapabilities.can_maintain_original_method_order = 1.toUInt() + AddCapabilities(jvmtiCapabilities.ptr) + } @OptIn(ExperimentalForeignApi::class) private fun setEventCallbacks() = memScoped { val alloc = alloc() alloc.VMInit = staticCFunction(::vmInitEvent) alloc.VMDeath = staticCFunction(::vmDeathEvent) -// alloc.ClassFileLoadHook = staticCFunction(::classFileLoadHook) + alloc.ClassFileLoadHook = staticCFunction(::classFileLoadHook) SetEventCallbacks(alloc.ptr, sizeOf().toInt()) SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, null) } @@ -177,7 +172,6 @@ object Agent { private fun loadJvmModule(clazz: String) = runCatching { JvmModuleLoader.loadJvmModule(clazz).load() } .onFailure { logger.error(it) { "loadJvmModule: Fatal error: id=${clazz}" } - println("!!! loadJvmModule: Fatal error: id=${clazz}") } } diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt index affe5f93..968ed895 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt @@ -45,51 +45,51 @@ object ClassFileLoadHook { newClassDataLen: CPointer?, newData: CPointer>?, ) { - initRuntimeIfNeeded() +// initRuntimeIfNeeded() val kClassName = clsName?.toKString() ?: return val kClassData = classData ?: return - - val precheckedTransformers = TransformerRegistrar.enabledTransformers - .filterNot { kClassName.startsWith(DRILL_PACKAGE) } - .filter { it.precheck(kClassName, loader, protectionDomain) } - .takeIf { it.any() } - ?: return - - val (oldClassBytes, reader) = runCatching { - val classBytes = ByteArray(classDataLen).apply { - Memory.of(kClassData, classDataLen).loadByteArray(0, this) - } - classBytes to ClassReader(classBytes) - }.onFailure { - logger.error(it) { "Can't read class: $kClassName" } - }.getOrNull() ?: return - - val permittedTransformers = precheckedTransformers.filter { - it.permit( - kClassName, - reader.superName, - reader.interfaces - ) - } - - val newClassBytes = permittedTransformers.fold(oldClassBytes) { bytes, transformer -> - runCatching { - transformer.transform(kClassName, bytes, loader, protectionDomain) - }.onFailure { - logger.warn(it) { "Can't transform class: $kClassName with ${transformer::class.simpleName}" } - }.getOrNull() - ?.takeIf { it !== bytes } - ?.also { - logger.debug { "$kClassName was transformed by ${transformer::class.simpleName}" } - } ?: bytes - } - - if (newClassBytes !== oldClassBytes) { - totalTransformClass.addAndGet(1).takeIf { it % 100 == 0 }?.let { - logger.debug { "At least $it classes were transformed" } - } - convertToNativePointers(newClassBytes, newData, newClassDataLen) - } + println("!!! ClassFileLoadHook: $kClassName") +// val precheckedTransformers = TransformerRegistrar.enabledTransformers +// .filterNot { kClassName.startsWith(DRILL_PACKAGE) } +// .filter { it.precheck(kClassName, loader, protectionDomain) } +// .takeIf { it.any() } +// ?: return +// +// val (oldClassBytes, reader) = runCatching { +// val classBytes = ByteArray(classDataLen).apply { +// Memory.of(kClassData, classDataLen).loadByteArray(0, this) +// } +// classBytes to ClassReader(classBytes) +// }.onFailure { +// logger.error(it) { "Can't read class: $kClassName" } +// }.getOrNull() ?: return +// +// val permittedTransformers = precheckedTransformers.filter { +// it.permit( +// kClassName, +// reader.superName, +// reader.interfaces +// ) +// } +// +// val newClassBytes = permittedTransformers.fold(oldClassBytes) { bytes, transformer -> +// runCatching { +// transformer.transform(kClassName, bytes, loader, protectionDomain) +// }.onFailure { +// logger.warn(it) { "Can't transform class: $kClassName with ${transformer::class.simpleName}" } +// }.getOrNull() +// ?.takeIf { it !== bytes } +// ?.also { +// logger.debug { "$kClassName was transformed by ${transformer::class.simpleName}" } +// } ?: bytes +// } +// +// if (newClassBytes !== oldClassBytes) { +// totalTransformClass.addAndGet(1).takeIf { it % 100 == 0 }?.let { +// logger.debug { "At least $it classes were transformed" } +// } +// convertToNativePointers(newClassBytes, newData, newClassDataLen) +// } } @OptIn(ExperimentalForeignApi::class) From bbe987cd8be9737eb7d33915f4f0747ca1aeb08e Mon Sep 17 00:00:00 2001 From: iryabov Date: Fri, 12 Sep 2025 18:12:00 +0200 Subject: [PATCH 4/9] feat: add premain function in Agent.kt --- java-agent/build.gradle.kts | 5 +- .../kotlin/com/epam/drill/agent/Agent.kt | 146 ++++++++++++++++++ .../drill/agent/request/HeadersRetriever.kt | 27 +++- .../kotlin/com/epam/drill/agent/Agent.kt | 77 +++++---- .../drill/agent/jvmti/ClassFileLoadHook.kt | 85 +++++----- .../epam/drill/agent/test2code/Test2Code.kt | 1 + 6 files changed, 252 insertions(+), 89 deletions(-) create mode 100644 java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt diff --git a/java-agent/build.gradle.kts b/java-agent/build.gradle.kts index fd0dfaa8..0b1bd59f 100644 --- a/java-agent/build.gradle.kts +++ b/java-agent/build.gradle.kts @@ -190,7 +190,10 @@ kotlin { val runtimeJar by registering(ShadowJar::class) { manifest { attributes(mapOf( - "Main-Class" to "com.epam.drill.agent.AppArchiveScannerCliKt" + "Main-Class" to "com.epam.drill.agent.AppArchiveScannerCliKt", + "Premain-Class" to "com.epam.drill.agent.AgentKt", + "Can-Redefine-Classes" to "true", + "Can-Retransform-Classes" to "true" )) } mergeServiceFiles() diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt new file mode 100644 index 00000000..f872d46b --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -0,0 +1,146 @@ +package com.epam.drill.agent + +import com.epam.drill.agent.common.configuration.AgentMetadata +import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.instrument.ApplicationClassTransformer +import com.epam.drill.agent.instrument.TransformerRegistrar +import com.epam.drill.agent.instrument.clients.ApacheHttpClientTransformer +import com.epam.drill.agent.instrument.clients.JavaHttpClientTransformer +import com.epam.drill.agent.instrument.clients.OkHttp3ClientTransformer +import com.epam.drill.agent.instrument.clients.SpringWebClientTransformer +import com.epam.drill.agent.instrument.jetty.Jetty10WsMessagesTransformer +import com.epam.drill.agent.instrument.jetty.Jetty11WsMessagesTransformer +import com.epam.drill.agent.instrument.jetty.Jetty9WsMessagesTransformer +import com.epam.drill.agent.instrument.jetty.JettyHttpServerTransformer +import com.epam.drill.agent.instrument.jetty.JettyWsClientTransformer +import com.epam.drill.agent.instrument.jetty.JettyWsServerTransformer +import com.epam.drill.agent.instrument.netty.NettyHttpServerTransformer +import com.epam.drill.agent.instrument.netty.NettyWsClientTransformer +import com.epam.drill.agent.instrument.netty.NettyWsMessagesTransformer +import com.epam.drill.agent.instrument.netty.NettyWsServerTransformer +import com.epam.drill.agent.instrument.servers.CadenceTransformer +import com.epam.drill.agent.instrument.servers.CompatibilityTestsTransformer +import com.epam.drill.agent.instrument.servers.KafkaTransformer +import com.epam.drill.agent.instrument.servers.ReactorTransformer +import com.epam.drill.agent.instrument.servers.SSLEngineTransformer +import com.epam.drill.agent.instrument.servers.TTLTransformer +import com.epam.drill.agent.instrument.tomcat.TomcatHttpServerTransformer +import com.epam.drill.agent.instrument.tomcat.TomcatWsClientTransformer +import com.epam.drill.agent.instrument.tomcat.TomcatWsMessagesTransformer +import com.epam.drill.agent.instrument.tomcat.TomcatWsServerTransformer +import com.epam.drill.agent.instrument.undertow.UndertowHttpServerTransformer +import com.epam.drill.agent.instrument.undertow.UndertowWsClientTransformer +import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer +import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer +import com.epam.drill.agent.module.JvmModuleLoader +import com.epam.drill.agent.test2code.Test2Code +import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper +import com.epam.drill.agent.transport.JsonAgentMessageSerializer +import com.epam.drill.agent.transport.JvmModuleMessageSender +import com.epam.drill.agent.transport.SimpleAgentMessageSender +import com.epam.drill.agent.transport.http.HttpAgentMessageTransport +import jdk.internal.org.objectweb.asm.ClassReader +import mu.KotlinLogging +import java.lang.instrument.ClassFileTransformer +import java.lang.instrument.Instrumentation + +private val logo = """ + ____ ____ _ _ _ _ _ + | _"\U | _"\ u ___ |"| |"| | ||"| U |"| u + /| | | |\| |_) |/ |_"_| U | | u U | | u | || |_ _ \| |/ + U| |_| |\| _ < | | \| |/__ \| |/__ |__ _| | |_| |_,-. + |____/ u|_| \_\ U/| |\u |_____| |_____| /|_|\ \___/-(_/ + |||_ // \\_.-,_|___|_,-.// \\ // \\ u_|||_u _// + (__)_) (__) (__)\_)-' '-(_/(_")("_)(_")("_) (__)__) (__) + Java Agent (v${agentVersion}) + """.trimIndent() +private const val DRILL_PACKAGE = "com/epam/drill/agent" + +private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") +private val transformers = setOf( + ApplicationClassTransformer, + TomcatHttpServerTransformer, + JettyHttpServerTransformer, + UndertowHttpServerTransformer, + NettyHttpServerTransformer, + JavaHttpClientTransformer, + ApacheHttpClientTransformer, + OkHttp3ClientTransformer, + SpringWebClientTransformer, + KafkaTransformer, + CadenceTransformer, + TTLTransformer, + ReactorTransformer, +// SSLEngineTransformer, + JettyWsClientTransformer, + JettyWsServerTransformer, + Jetty9WsMessagesTransformer, + Jetty10WsMessagesTransformer, + Jetty11WsMessagesTransformer, + NettyWsClientTransformer, + NettyWsServerTransformer, + NettyWsMessagesTransformer, + TomcatWsClientTransformer, + TomcatWsServerTransformer, + TomcatWsMessagesTransformer, + UndertowWsClientTransformer, + UndertowWsServerTransformer, + UndertowWsMessagesTransformer, + CompatibilityTestsTransformer, + ) + +fun premain(agentArgs: String?, inst: Instrumentation) { + println(logo) + Configuration.initializeJvm(agentArgs ?: "") + TransformerRegistrar.initialize(transformers) + inst.addTransformer(DrillClassLoadTransformer, true) + JvmModuleMessageSender.sendAgentMetadata() + JvmModuleLoader.loadJvmModule(Test2Code::class.java.name) +} + +object DrillClassLoadTransformer : ClassFileTransformer { + override fun transform( + loader: ClassLoader?, + className: String?, + classBeingRedefined: Class<*>?, + protectionDomain: java.security.ProtectionDomain?, + classfileBuffer: ByteArray? + ): ByteArray? { + // Implement transformation logic here if needed + val kClassName = className ?: return null + val kClassBytes = classfileBuffer ?: return null + val precheckedTransformers = TransformerRegistrar.enabledTransformers + .filterNot { kClassName.startsWith(DRILL_PACKAGE) } + .filter { it.precheck(kClassName, loader, protectionDomain) } + .takeIf { it.any() } + ?: return null + val (oldClassBytes, reader) = runCatching { + kClassBytes to ClassReader(kClassBytes) + }.onFailure { + logger.error(it) { "Can't read class: $kClassName" } + }.getOrNull() ?: return null + val permittedTransformers = precheckedTransformers.filter { + it.permit( + kClassName, + reader.superName, + reader.interfaces + ) + } + + val newClassBytes = permittedTransformers.fold(oldClassBytes) { bytes, transformer -> + runCatching { + transformer.transform(kClassName, bytes, loader, protectionDomain) + }.onFailure { + logger.warn(it) { "Can't transform class: $kClassName with ${transformer::class.simpleName}" } + }.getOrNull() + ?.takeIf { it !== bytes } + ?.also { + logger.debug { "$kClassName was transformed by ${transformer::class.simpleName}" } + } ?: bytes + } + + return if (newClassBytes !== oldClassBytes) newClassBytes else null + } +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/request/HeadersRetriever.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/request/HeadersRetriever.kt index 355c7954..874c1fa8 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/request/HeadersRetriever.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/request/HeadersRetriever.kt @@ -16,11 +16,28 @@ package com.epam.drill.agent.request import com.epam.drill.agent.common.request.HeadersRetriever +import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.ParameterDefinitions +import kotlin.text.isNotEmpty actual object HeadersRetriever : HeadersRetriever { - actual external override fun adminAddressHeader(): String - actual external override fun adminAddressValue(): String - actual external override fun sessionHeader(): String - actual external override fun agentIdHeader(): String - actual external override fun agentIdHeaderValue(): String + + private val adminAddress by lazy { Configuration.parameters[ParameterDefinitions.API_URL] } + + private val agentIdHeader by lazy { + Configuration.agentMetadata.groupId.takeIf(String::isNotEmpty) + ?.let { "drill-group-id" to Configuration.agentMetadata.groupId } + ?: let { "drill-agent-id" to Configuration.agentMetadata.appId } + } + + actual override fun adminAddressHeader() = "drill-admin-url" + + actual override fun adminAddressValue() = adminAddress + + actual override fun sessionHeader() = "drill-session-id" + + actual override fun agentIdHeader() = agentIdHeader.first + + actual override fun agentIdHeaderValue() = agentIdHeader.second + } diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt index 5ae1cbf5..02b37c5e 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/Agent.kt @@ -79,51 +79,48 @@ object Agent { """.trimIndent() private val logger = KotlinLogging.logger("com.epam.drill.agent.Agent") -// private val transformers = setOf( -// ApplicationClassTransformer, -// TomcatHttpServerTransformer, -// JettyHttpServerTransformer, -// UndertowHttpServerTransformer, -// NettyHttpServerTransformer, -// JavaHttpClientTransformer, -// ApacheHttpClientTransformer, -// OkHttp3ClientTransformer, -// SpringWebClientTransformer, -// KafkaTransformer, -// CadenceTransformer, -// TTLTransformer, -// ReactorTransformer, -// SSLEngineTransformer, -// JettyWsClientTransformer, -// JettyWsServerTransformer, -// Jetty9WsMessagesTransformer, -// Jetty10WsMessagesTransformer, -// Jetty11WsMessagesTransformer, -// NettyWsClientTransformer, -// NettyWsServerTransformer, -// NettyWsMessagesTransformer, -// TomcatWsClientTransformer, -// TomcatWsServerTransformer, -// TomcatWsMessagesTransformer, -// UndertowWsClientTransformer, -// UndertowWsServerTransformer, -// UndertowWsMessagesTransformer, -// CompatibilityTestsTransformer, -// ) + private val transformers = setOf( + ApplicationClassTransformer, + TomcatHttpServerTransformer, + JettyHttpServerTransformer, + UndertowHttpServerTransformer, + NettyHttpServerTransformer, + JavaHttpClientTransformer, + ApacheHttpClientTransformer, + OkHttp3ClientTransformer, + SpringWebClientTransformer, + KafkaTransformer, + CadenceTransformer, + TTLTransformer, + ReactorTransformer, + SSLEngineTransformer, + JettyWsClientTransformer, + JettyWsServerTransformer, + Jetty9WsMessagesTransformer, + Jetty10WsMessagesTransformer, + Jetty11WsMessagesTransformer, + NettyWsClientTransformer, + NettyWsServerTransformer, + NettyWsMessagesTransformer, + TomcatWsClientTransformer, + TomcatWsServerTransformer, + TomcatWsMessagesTransformer, + UndertowWsClientTransformer, + UndertowWsServerTransformer, + UndertowWsMessagesTransformer, + CompatibilityTestsTransformer, + ) @OptIn(ExperimentalNativeApi::class, ExperimentalForeignApi::class) fun agentOnLoad(options: String): Int { println(logo) -// AgentLoggingConfiguration.defaultNativeLoggingConfiguration() - println("!!! Configuration.initializeNative(options)") + AgentLoggingConfiguration.defaultNativeLoggingConfiguration() Configuration.initializeNative(options) -// AgentLoggingConfiguration.updateNativeLoggingConfiguration() -// TransformerRegistrar.initialize(transformers) -// + AgentLoggingConfiguration.updateNativeLoggingConfiguration() + TransformerRegistrar.initialize(transformers) addCapabilities() setEventCallbacks() setUnhandledExceptionHook({ error: Throwable -> logger.error(error) { "Unhandled event: $error" }}.freeze()) - println("!!! AddToBootstrapClassLoaderSearch(\"${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar\")") AddToBootstrapClassLoaderSearch("${Configuration.parameters[INSTALLATION_DIR]}/drill-runtime.jar") logger.info { "agentOnLoad: Java Agent has been loaded. Pid is: " + getpid() } @@ -139,9 +136,9 @@ object Agent { fun agentOnVmInit() { initRuntimeIfNeeded() SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, null) -// -// AgentLoggingConfiguration.defaultJvmLoggingConfiguration() -// AgentLoggingConfiguration.updateJvmLoggingConfiguration() + + AgentLoggingConfiguration.defaultJvmLoggingConfiguration() + AgentLoggingConfiguration.updateJvmLoggingConfiguration() Configuration.initializeJvm() loadJvmModule("com.epam.drill.agent.test2code.Test2Code") JvmModuleMessageSender.sendAgentMetadata() diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt index 968ed895..3db54803 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/jvmti/ClassFileLoadHook.kt @@ -45,51 +45,50 @@ object ClassFileLoadHook { newClassDataLen: CPointer?, newData: CPointer>?, ) { -// initRuntimeIfNeeded() + initRuntimeIfNeeded() val kClassName = clsName?.toKString() ?: return val kClassData = classData ?: return - println("!!! ClassFileLoadHook: $kClassName") -// val precheckedTransformers = TransformerRegistrar.enabledTransformers -// .filterNot { kClassName.startsWith(DRILL_PACKAGE) } -// .filter { it.precheck(kClassName, loader, protectionDomain) } -// .takeIf { it.any() } -// ?: return -// -// val (oldClassBytes, reader) = runCatching { -// val classBytes = ByteArray(classDataLen).apply { -// Memory.of(kClassData, classDataLen).loadByteArray(0, this) -// } -// classBytes to ClassReader(classBytes) -// }.onFailure { -// logger.error(it) { "Can't read class: $kClassName" } -// }.getOrNull() ?: return -// -// val permittedTransformers = precheckedTransformers.filter { -// it.permit( -// kClassName, -// reader.superName, -// reader.interfaces -// ) -// } -// -// val newClassBytes = permittedTransformers.fold(oldClassBytes) { bytes, transformer -> -// runCatching { -// transformer.transform(kClassName, bytes, loader, protectionDomain) -// }.onFailure { -// logger.warn(it) { "Can't transform class: $kClassName with ${transformer::class.simpleName}" } -// }.getOrNull() -// ?.takeIf { it !== bytes } -// ?.also { -// logger.debug { "$kClassName was transformed by ${transformer::class.simpleName}" } -// } ?: bytes -// } -// -// if (newClassBytes !== oldClassBytes) { -// totalTransformClass.addAndGet(1).takeIf { it % 100 == 0 }?.let { -// logger.debug { "At least $it classes were transformed" } -// } -// convertToNativePointers(newClassBytes, newData, newClassDataLen) -// } + val precheckedTransformers = TransformerRegistrar.enabledTransformers + .filterNot { kClassName.startsWith(DRILL_PACKAGE) } + .filter { it.precheck(kClassName, loader, protectionDomain) } + .takeIf { it.any() } + ?: return + + val (oldClassBytes, reader) = runCatching { + val classBytes = ByteArray(classDataLen).apply { + Memory.of(kClassData, classDataLen).loadByteArray(0, this) + } + classBytes to ClassReader(classBytes) + }.onFailure { + logger.error(it) { "Can't read class: $kClassName" } + }.getOrNull() ?: return + + val permittedTransformers = precheckedTransformers.filter { + it.permit( + kClassName, + reader.superName, + reader.interfaces + ) + } + + val newClassBytes = permittedTransformers.fold(oldClassBytes) { bytes, transformer -> + runCatching { + transformer.transform(kClassName, bytes, loader, protectionDomain) + }.onFailure { + logger.warn(it) { "Can't transform class: $kClassName with ${transformer::class.simpleName}" } + }.getOrNull() + ?.takeIf { it !== bytes } + ?.also { + logger.debug { "$kClassName was transformed by ${transformer::class.simpleName}" } + } ?: bytes + } + + if (newClassBytes !== oldClassBytes) { + totalTransformClass.addAndGet(1).takeIf { it % 100 == 0 }?.let { + logger.debug { "At least $it classes were transformed" } + } + convertToNativePointers(newClassBytes, newData, newClassDataLen) + } } @OptIn(ExperimentalForeignApi::class) diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt index 6cd8c4f2..66a0309b 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt @@ -78,6 +78,7 @@ class Test2Code( } override fun load() { + println("!!! loaded") AgentParametersValidator(configuration.parameters).validate( Test2CodeParameterDefinitions.SCAN_CLASS_PATH, Test2CodeParameterDefinitions.SCAN_CLASS_DELAY From d0f16e17659c766a8329271974725970203d1627 Mon Sep 17 00:00:00 2001 From: iryabov Date: Tue, 16 Sep 2025 11:23:04 +0200 Subject: [PATCH 5/9] feat: enhance logging configuration in premain function --- .../kotlin/com/epam/drill/agent/Agent.kt | 20 ++++++++++++++++++- .../epam/drill/agent/test2code/Test2Code.kt | 1 - 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index f872d46b..0ce9849b 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -34,7 +34,9 @@ import com.epam.drill.agent.instrument.undertow.UndertowHttpServerTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsClientTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer +import com.epam.drill.agent.logging.LoggingConfiguration import com.epam.drill.agent.module.JvmModuleLoader +import com.epam.drill.agent.module.JvmModuleStorage import com.epam.drill.agent.test2code.Test2Code import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper import com.epam.drill.agent.transport.JsonAgentMessageSerializer @@ -93,11 +95,27 @@ private val transformers = setOf( fun premain(agentArgs: String?, inst: Instrumentation) { println(logo) + LoggingConfiguration.readDefaultConfiguration() Configuration.initializeJvm(agentArgs ?: "") + updateJvmLoggingConfiguration() TransformerRegistrar.initialize(transformers) inst.addTransformer(DrillClassLoadTransformer, true) JvmModuleMessageSender.sendAgentMetadata() - JvmModuleLoader.loadJvmModule(Test2Code::class.java.name) + JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() +} + +private fun updateJvmLoggingConfiguration() { + val logLevel = Configuration.parameters[ParameterDefinitions.LOG_LEVEL] + val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE] + val logLimit = Configuration.parameters[ParameterDefinitions.LOG_LIMIT] + + LoggingConfiguration.setLoggingLevels(logLevel) + if (LoggingConfiguration.getLoggingFilename() != logFile) { + LoggingConfiguration.setLoggingFilename(logFile) + } + if (LoggingConfiguration.getLogMessageLimit() != logLimit) { + LoggingConfiguration.setLogMessageLimit(logLimit) + } } object DrillClassLoadTransformer : ClassFileTransformer { diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt index 66a0309b..6cd8c4f2 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/Test2Code.kt @@ -78,7 +78,6 @@ class Test2Code( } override fun load() { - println("!!! loaded") AgentParametersValidator(configuration.parameters).validate( Test2CodeParameterDefinitions.SCAN_CLASS_PATH, Test2CodeParameterDefinitions.SCAN_CLASS_DELAY From 5807f05241af0ba69f07a2702e1d1a137172236a Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Tue, 16 Sep 2025 14:08:08 +0400 Subject: [PATCH 6/9] license: fix --- .../kotlin/com/epam/drill/agent/Agent.kt | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index 0ce9849b..0a206455 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -1,3 +1,18 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.epam.drill.agent import com.epam.drill.agent.common.configuration.AgentMetadata @@ -94,14 +109,19 @@ private val transformers = setOf( ) fun premain(agentArgs: String?, inst: Instrumentation) { - println(logo) - LoggingConfiguration.readDefaultConfiguration() - Configuration.initializeJvm(agentArgs ?: "") - updateJvmLoggingConfiguration() - TransformerRegistrar.initialize(transformers) - inst.addTransformer(DrillClassLoadTransformer, true) - JvmModuleMessageSender.sendAgentMetadata() - JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() + try { + // init + println(logo) + LoggingConfiguration.readDefaultConfiguration() + Configuration.initializeJvm(agentArgs ?: "") + updateJvmLoggingConfiguration() + TransformerRegistrar.initialize(transformers) + inst.addTransformer(DrillClassLoadTransformer, true) + JvmModuleMessageSender.sendAgentMetadata() + JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() + } catch (e: Throwable) { + e.printStackTrace(); + } } private fun updateJvmLoggingConfiguration() { From 24ae0787bc5d5f313b35bb72f54615f91390d372 Mon Sep 17 00:00:00 2001 From: iryabov Date: Tue, 16 Sep 2025 15:10:17 +0200 Subject: [PATCH 7/9] feat: implement runtime and environment parameters provider in JVM --- gradle.properties | 2 +- .../ParameterValidationException.kt | 0 .../ValidatedParametersProvider.kt | 298 +++++++++--------- .../kotlin/com/epam/drill/agent/Agent.kt | 4 +- .../agent/configuration/Configuration.kt | 43 ++- .../RuntimeParametersProvider.kt | 28 ++ 6 files changed, 222 insertions(+), 153 deletions(-) rename java-agent/src/{nativeMain => commonMain}/kotlin/com/epam/drill/agent/configuration/ParameterValidationException.kt (100%) rename java-agent/src/{nativeMain => commonMain}/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt (97%) create mode 100644 java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/RuntimeParametersProvider.kt diff --git a/gradle.properties b/gradle.properties index 9be62a1f..5653d889 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ apacheHttpClientVersion = 5.2.3 aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 -sharedLibsRef = main +sharedLibsRef = feature/premain-java-agent sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ParameterValidationException.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterValidationException.kt similarity index 100% rename from java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ParameterValidationException.kt rename to java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterValidationException.kt diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt similarity index 97% rename from java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt rename to java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt index 2fe47dff..21af8347 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidatedParametersProvider.kt @@ -1,149 +1,149 @@ -/** - * Copyright 2020 - 2022 EPAM Systems - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.epam.drill.agent.configuration - -import kotlin.reflect.KProperty -import mu.KotlinLogging -import com.epam.drill.agent.common.configuration.AgentParameterDefinition -import com.epam.drill.agent.konform.validation.Invalid -import com.epam.drill.agent.konform.validation.Validation -import com.epam.drill.agent.konform.validation.ValidationError -import com.epam.drill.agent.konform.validation.ValidationErrors -import com.epam.drill.agent.konform.validation.ValidationResult -import com.epam.drill.agent.konform.validation.jsonschema.minItems -import com.epam.drill.agent.konform.validation.jsonschema.minLength -import com.epam.drill.agent.konform.validation.jsonschema.minimum -import com.epam.drill.agent.konform.validation.jsonschema.pattern - -class ValidatedParametersProvider( - private val configurationProviders: Set, - override val priority: Int = Int.MAX_VALUE -) : AgentConfigurationProvider { - - private class ValidatingParameters(provider: ValidatedParametersProvider) { - val appId by provider - val groupId by provider - val buildVersion by provider - val commitSha by provider - val envId by provider - val packagePrefixes by provider - val packagePrefixesAsList = packagePrefixes?.split(";") ?: emptyList() - val drillInstallationDir by provider - val apiUrl by provider - val apiKey by provider - val logLevel by provider - val logLevelAsList = logLevel?.split(";") ?: emptyList() - val logLimit by provider - val logLimitAsInt = logLimit?.toIntOrNull() - } - - private val strictValidators = Validation { - ValidatingParameters::drillInstallationDir required { - minLength(1) - } - ValidatingParameters::groupId required { - identifier() - minLength(3) - } - ValidatingParameters::appId required { - identifier() - minLength(3) - } - ValidatingParameters::apiUrl required { - validTransportUrl() - } - ValidatingParameters::packagePrefixesAsList { - minItems(1) - } - ValidatingParameters::packagePrefixesAsList onEach { - isValidPackage() - } - } - - private val softValidators = Validation { - ValidatingParameters::buildVersion ifPresent { - pattern("^\\S*$") hint "must not contain whitespaces" - } - ValidatingParameters::commitSha ifPresent { - pattern("^[a-f0-9]{40}\$") hint "must be a valid full commit SHA" - } - ValidatingParameters::envId ifPresent { - minLength(1) - } - ValidatingParameters::apiKey ifPresent { - minLength(1) - } - ValidatingParameters::logLevelAsList onEach { - isValidLogLevel() - } - ValidatingParameters::logLimitAsInt ifPresent { - minimum(0) - } - } - - private val logger = KotlinLogging.logger("com.epam.drill.agent.configuration.ValidatedParametersProvider") - - private val validatingConfiguration = validatingConfiguration() - - override val configuration - get() = validateConfiguration() - - fun validate(): List { - val strictValidationErrors = strictValidators(ValidatingParameters(this)).takeIf { it is Invalid }?.errors?.toList() ?: emptyList() - val softValidationErrors = softValidators(ValidatingParameters(this)).takeIf { it is Invalid }?.errors?.toList() ?: emptyList() - return strictValidationErrors + softValidationErrors - } - - internal fun validatingConfiguration() = configurationProviders - .sortedBy(AgentConfigurationProvider::priority) - .map(AgentConfigurationProvider::configuration) - .reduce { acc, map -> acc + map } - - private fun validateConfiguration() = mutableMapOf().also { defaultValues -> - val defaultFor: (AgentParameterDefinition) -> Unit = { - defaultValues[it.name] = it.defaultValue.toString() - } - val isInvalid: (ValidationResult<*>) -> Boolean = { it is Invalid } - strictValidators(ValidatingParameters(this)).takeIf(isInvalid)?.let { result -> - val message = "Cannot load the agent because some agent parameters are set incorrectly. " + - convertToMessage(result.errors) - logger.error { message } - throw ParameterValidationException(message) - } - softValidators(ValidatingParameters(this)).takeIf(isInvalid)?.let { result -> - val message = "Some agent parameters were set incorrectly and were replaced with default values. " + - convertToMessage(result.errors) - logger.error { message } - result.errors.forEach { error -> - when (convertToField(error)) { - ValidatingParameters::logLevel.name -> defaultFor(ParameterDefinitions.LOG_LEVEL) - ValidatingParameters::logLimit.name -> defaultFor(ParameterDefinitions.LOG_LIMIT) - } - } - } - } - - private fun convertToMessage(errors: ValidationErrors) = "Please check the following parameters:\n" + - errors.joinToString("\n") { " - ${convertToField(it)} ${it.message}" } - - private fun convertToField(error: ValidationError) = error.dataPath.removePrefix(".") - .substringBeforeLast("AsList") - .removeSuffix("AsInt") - - private operator fun getValue(thisRef: Any, property: KProperty<*>) = - validatingConfiguration[property.name] - -} +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +import kotlin.reflect.KProperty +import mu.KotlinLogging +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.konform.validation.Invalid +import com.epam.drill.agent.konform.validation.Validation +import com.epam.drill.agent.konform.validation.ValidationError +import com.epam.drill.agent.konform.validation.ValidationErrors +import com.epam.drill.agent.konform.validation.ValidationResult +import com.epam.drill.agent.konform.validation.jsonschema.minItems +import com.epam.drill.agent.konform.validation.jsonschema.minLength +import com.epam.drill.agent.konform.validation.jsonschema.minimum +import com.epam.drill.agent.konform.validation.jsonschema.pattern + +class ValidatedParametersProvider( + private val configurationProviders: Set, + override val priority: Int = Int.MAX_VALUE +) : AgentConfigurationProvider { + + private class ValidatingParameters(provider: ValidatedParametersProvider) { + val appId by provider + val groupId by provider + val buildVersion by provider + val commitSha by provider + val envId by provider + val packagePrefixes by provider + val packagePrefixesAsList = packagePrefixes?.split(";") ?: emptyList() + val drillInstallationDir by provider + val apiUrl by provider + val apiKey by provider + val logLevel by provider + val logLevelAsList = logLevel?.split(";") ?: emptyList() + val logLimit by provider + val logLimitAsInt = logLimit?.toIntOrNull() + } + + private val strictValidators = Validation { + ValidatingParameters::drillInstallationDir required { + minLength(1) + } + ValidatingParameters::groupId required { + identifier() + minLength(3) + } + ValidatingParameters::appId required { + identifier() + minLength(3) + } + ValidatingParameters::apiUrl required { + validTransportUrl() + } + ValidatingParameters::packagePrefixesAsList { + minItems(1) + } + ValidatingParameters::packagePrefixesAsList onEach { + isValidPackage() + } + } + + private val softValidators = Validation { + ValidatingParameters::buildVersion ifPresent { + pattern("^\\S*$") hint "must not contain whitespaces" + } + ValidatingParameters::commitSha ifPresent { + pattern("^[a-f0-9]{40}\$") hint "must be a valid full commit SHA" + } + ValidatingParameters::envId ifPresent { + minLength(1) + } + ValidatingParameters::apiKey ifPresent { + minLength(1) + } + ValidatingParameters::logLevelAsList onEach { + isValidLogLevel() + } + ValidatingParameters::logLimitAsInt ifPresent { + minimum(0) + } + } + + private val logger = KotlinLogging.logger("com.epam.drill.agent.configuration.ValidatedParametersProvider") + + private val validatingConfiguration = validatingConfiguration() + + override val configuration + get() = validateConfiguration() + + fun validate(): List { + val strictValidationErrors = strictValidators(ValidatingParameters(this)).takeIf { it is Invalid }?.errors?.toList() ?: emptyList() + val softValidationErrors = softValidators(ValidatingParameters(this)).takeIf { it is Invalid }?.errors?.toList() ?: emptyList() + return strictValidationErrors + softValidationErrors + } + + internal fun validatingConfiguration() = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .map(AgentConfigurationProvider::configuration) + .reduce { acc, map -> acc + map } + + private fun validateConfiguration() = mutableMapOf().also { defaultValues -> + val defaultFor: (AgentParameterDefinition) -> Unit = { + defaultValues[it.name] = it.defaultValue.toString() + } + val isInvalid: (ValidationResult<*>) -> Boolean = { it is Invalid } + strictValidators(ValidatingParameters(this)).takeIf(isInvalid)?.let { result -> + val message = "Cannot load the agent because some agent parameters are set incorrectly. " + + convertToMessage(result.errors) + logger.error { message } + throw ParameterValidationException(message) + } + softValidators(ValidatingParameters(this)).takeIf(isInvalid)?.let { result -> + val message = "Some agent parameters were set incorrectly and were replaced with default values. " + + convertToMessage(result.errors) + logger.error { message } + result.errors.forEach { error -> + when (convertToField(error)) { + ValidatingParameters::logLevel.name -> defaultFor(ParameterDefinitions.LOG_LEVEL) + ValidatingParameters::logLimit.name -> defaultFor(ParameterDefinitions.LOG_LIMIT) + } + } + } + } + + private fun convertToMessage(errors: ValidationErrors) = "Please check the following parameters:\n" + + errors.joinToString("\n") { " - ${convertToField(it)} ${it.message}" } + + private fun convertToField(error: ValidationError) = error.dataPath.removePrefix(".") + .substringBeforeLast("AsList") + .removeSuffix("AsInt") + + private operator fun getValue(thisRef: Any, property: KProperty<*>) = + validatingConfiguration[property.name] + +} diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index 0a206455..4f6f4052 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -90,7 +90,7 @@ private val transformers = setOf( CadenceTransformer, TTLTransformer, ReactorTransformer, -// SSLEngineTransformer, +// SSLEngineTransformer, TODO does not work in JVM due to too early initialization of HeadersRetriever JettyWsClientTransformer, JettyWsServerTransformer, Jetty9WsMessagesTransformer, @@ -113,7 +113,7 @@ fun premain(agentArgs: String?, inst: Instrumentation) { // init println(logo) LoggingConfiguration.readDefaultConfiguration() - Configuration.initializeJvm(agentArgs ?: "") + Configuration.initializeNative(agentArgs ?: "") updateJvmLoggingConfiguration() TransformerRegistrar.initialize(transformers) inst.addTransformer(DrillClassLoadTransformer, true) diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt index 945ba7f8..6ce213e2 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt @@ -19,19 +19,60 @@ import mu.KotlinLogging import com.epam.drill.agent.common.configuration.AgentConfiguration import com.epam.drill.agent.common.configuration.AgentMetadata import com.epam.drill.agent.common.configuration.AgentParameters +import com.epam.drill.agent.configuration.provider.AgentOptionsProvider +import com.epam.drill.agent.configuration.provider.EnvironmentVariablesProvider actual object Configuration : AgentConfiguration { private val logger = KotlinLogging.logger {} private lateinit var configuration: DefaultAgentConfiguration + private fun inputParameters(configurationProviders: Set) = configurationProviders + .sortedBy(AgentConfigurationProvider::priority) + .map(AgentConfigurationProvider::configuration) + .reduce { acc, map -> acc + map } + + private fun defineDefaults(agentParameters: AgentParameters) { + agentParameters.define( + DefaultParameterDefinitions.APP_ID, + DefaultParameterDefinitions.INSTANCE_ID, + DefaultParameterDefinitions.BUILD_VERSION, + DefaultParameterDefinitions.GROUP_ID, + DefaultParameterDefinitions.COMMIT_SHA, + DefaultParameterDefinitions.ENV_ID, + DefaultParameterDefinitions.INSTALLATION_DIR, + DefaultParameterDefinitions.CONFIG_PATH + ) + agentParameters.define(DefaultParameterDefinitions.PACKAGE_PREFIXES) + } + actual override val agentMetadata: AgentMetadata get() = configuration.agentMetadata actual override val parameters: AgentParameters get() = configuration.parameters - actual fun initializeNative(agentOptions: String): Unit = throw NotImplementedError() + actual fun initializeNative(agentOptions: String) { + val environmentVariablesProvider = EnvironmentVariablesProvider() + logger.debug { "initializeNative: Found environment variables: ${environmentVariablesProvider.configuration}" } + val agentOptionsProvider = AgentOptionsProvider(agentOptions) + logger.debug { "initializeNative: Found agent options: ${agentOptionsProvider.configuration}" } + val validatedParametersProvider = ValidatedParametersProvider(setOf( + environmentVariablesProvider, + agentOptionsProvider + )) + val runtimeParametersProvider = RuntimeParametersProvider() + + val inputParameters = inputParameters(setOf( + validatedParametersProvider, + environmentVariablesProvider, + agentOptionsProvider, + runtimeParametersProvider + )) + configuration = DefaultAgentConfiguration(inputParameters) + defineDefaults(configuration.parameters) + logger.debug { "initializeNative: Final input parameters: ${configuration.inputParameters}" } + } actual fun initializeJvm(inputParameters: String) { val parameters = inputParameters.split(",") diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/RuntimeParametersProvider.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/RuntimeParametersProvider.kt new file mode 100644 index 00000000..aacc933d --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/RuntimeParametersProvider.kt @@ -0,0 +1,28 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent.configuration + +class RuntimeParametersProvider( + override val priority: Int = 100 +) : AgentConfigurationProvider { + + override val configuration = configuration() + + private fun configuration() = mapOf( + Pair(DefaultParameterDefinitions.INSTANCE_ID.name, java.util.UUID.randomUUID().toString()) + ) + +} From 10ce3087dda24d0d1b461464e0c656c304bd0eba Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 17 Sep 2025 17:09:56 +0200 Subject: [PATCH 8/9] refactor: combine premain and main methods into one Agent.kt file --- java-agent/build.gradle.kts | 4 +- .../kotlin/com/epam/drill/agent/Agent.kt | 61 +++++++-- .../epam/drill/agent/AppArchiveScannerCli.kt | 118 ------------------ .../agent/configuration/Configuration.kt | 6 - .../agent/transport/JvmModuleMessageSender.kt | 4 + 5 files changed, 59 insertions(+), 134 deletions(-) delete mode 100644 java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt diff --git a/java-agent/build.gradle.kts b/java-agent/build.gradle.kts index 0b1bd59f..8c41231e 100644 --- a/java-agent/build.gradle.kts +++ b/java-agent/build.gradle.kts @@ -186,11 +186,11 @@ kotlin { "net.bytebuddy", "mu", ) - project.setProperty("mainClassName", "com.epam.drill.agent.AppArchiveScannerCliKt") + project.setProperty("mainClassName", "com.epam.drill.agent.AgentKt") val runtimeJar by registering(ShadowJar::class) { manifest { attributes(mapOf( - "Main-Class" to "com.epam.drill.agent.AppArchiveScannerCliKt", + "Main-Class" to "com.epam.drill.agent.AgentKt", "Premain-Class" to "com.epam.drill.agent.AgentKt", "Can-Redefine-Classes" to "true", "Can-Retransform-Classes" to "true" diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index 4f6f4052..54f805d8 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -17,7 +17,11 @@ package com.epam.drill.agent import com.epam.drill.agent.common.configuration.AgentMetadata import com.epam.drill.agent.common.transport.AgentMessageDestination +import com.epam.drill.agent.configuration.AgentParameterValidationError +import com.epam.drill.agent.configuration.AgentParametersValidator import com.epam.drill.agent.configuration.Configuration +import com.epam.drill.agent.configuration.DefaultAgentConfiguration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions import com.epam.drill.agent.instrument.ApplicationClassTransformer import com.epam.drill.agent.instrument.TransformerRegistrar @@ -39,7 +43,6 @@ import com.epam.drill.agent.instrument.servers.CadenceTransformer import com.epam.drill.agent.instrument.servers.CompatibilityTestsTransformer import com.epam.drill.agent.instrument.servers.KafkaTransformer import com.epam.drill.agent.instrument.servers.ReactorTransformer -import com.epam.drill.agent.instrument.servers.SSLEngineTransformer import com.epam.drill.agent.instrument.servers.TTLTransformer import com.epam.drill.agent.instrument.tomcat.TomcatHttpServerTransformer import com.epam.drill.agent.instrument.tomcat.TomcatWsClientTransformer @@ -51,8 +54,8 @@ import com.epam.drill.agent.instrument.undertow.UndertowWsMessagesTransformer import com.epam.drill.agent.instrument.undertow.UndertowWsServerTransformer import com.epam.drill.agent.logging.LoggingConfiguration import com.epam.drill.agent.module.JvmModuleLoader -import com.epam.drill.agent.module.JvmModuleStorage import com.epam.drill.agent.test2code.Test2Code +import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper import com.epam.drill.agent.transport.JsonAgentMessageSerializer import com.epam.drill.agent.transport.JvmModuleMessageSender @@ -62,6 +65,7 @@ import jdk.internal.org.objectweb.asm.ClassReader import mu.KotlinLogging import java.lang.instrument.ClassFileTransformer import java.lang.instrument.Instrumentation +import kotlin.system.exitProcess private val logo = """ ____ ____ _ _ _ _ _ @@ -106,24 +110,66 @@ private val transformers = setOf( UndertowWsServerTransformer, UndertowWsMessagesTransformer, CompatibilityTestsTransformer, - ) +) fun premain(agentArgs: String?, inst: Instrumentation) { try { - // init println(logo) LoggingConfiguration.readDefaultConfiguration() Configuration.initializeNative(agentArgs ?: "") updateJvmLoggingConfiguration() + validateConfiguration() TransformerRegistrar.initialize(transformers) - inst.addTransformer(DrillClassLoadTransformer, true) + inst.addTransformer(DrillClassFileTransformer, true) JvmModuleMessageSender.sendAgentMetadata() JvmModuleLoader.loadJvmModule(Test2Code::class.java.name).load() } catch (e: Throwable) { - e.printStackTrace(); + println("Drill4J Initialization Error:\n${e.message ?: e::class.java.name}") } } +fun main(args: Array) { + try { + println(logo) + LoggingConfiguration.readDefaultConfiguration() + Configuration.initializeNative(args.convertToAgentArgs()) + updateJvmLoggingConfiguration() + validateConfiguration() + + val commitSha = Configuration.parameters[DefaultParameterDefinitions.COMMIT_SHA] + val buildVersion = Configuration.parameters[DefaultParameterDefinitions.BUILD_VERSION] + if (commitSha == null && buildVersion == null) + throw AgentParameterValidationError("Either commitSha or buildVersion must be provided") + + JvmModuleMessageSender.sendBuildMetadata() + val test2Code = JvmModuleLoader.loadJvmModule(Test2Code::class.java.name) as Test2Code + test2Code.scanAndSendMetadataClasses() + Runtime.getRuntime().addShutdownHook(Thread { JvmModuleMessageSender.shutdown() }) + exitProcess(0) + } catch (e: Throwable) { + println("Drill4J Initialization Error:\n${e.message ?: e::class.java.name}") + exitProcess(1) + } +} + +private fun validateConfiguration() { + val validator = AgentParametersValidator(Configuration.parameters) + validator.validate( + DefaultParameterDefinitions, + ParameterDefinitions, + Test2CodeParameterDefinitions + ) +} + +private fun Array.convertToAgentArgs(): String = this + .filter { it.startsWith("--") && it.contains("=") } + .associate { + val (key, value) = it.removePrefix("--").split("=", limit = 2) + key to value + }.filter { it.value.isNotEmpty() } + .map { "${it.key}=${it.value}" } + .joinToString(",") + private fun updateJvmLoggingConfiguration() { val logLevel = Configuration.parameters[ParameterDefinitions.LOG_LEVEL] val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE] @@ -138,7 +184,7 @@ private fun updateJvmLoggingConfiguration() { } } -object DrillClassLoadTransformer : ClassFileTransformer { +object DrillClassFileTransformer : ClassFileTransformer { override fun transform( loader: ClassLoader?, className: String?, @@ -146,7 +192,6 @@ object DrillClassLoadTransformer : ClassFileTransformer { protectionDomain: java.security.ProtectionDomain?, classfileBuffer: ByteArray? ): ByteArray? { - // Implement transformation logic here if needed val kClassName = className ?: return null val kClassBytes = classfileBuffer ?: return null val precheckedTransformers = TransformerRegistrar.enabledTransformers diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt deleted file mode 100644 index 4cacdf49..00000000 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2020 - 2022 EPAM Systems - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.epam.drill.agent - -import com.epam.drill.agent.common.configuration.AgentConfiguration -import com.epam.drill.agent.common.configuration.AgentMetadata -import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection -import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition -import com.epam.drill.agent.common.transport.AgentMessageDestination -import com.epam.drill.agent.configuration.AgentParameterValidationError -import com.epam.drill.agent.configuration.AgentParametersValidator -import com.epam.drill.agent.configuration.DefaultAgentConfiguration -import com.epam.drill.agent.configuration.DefaultParameterDefinitions -import com.epam.drill.agent.configuration.ParameterDefinitions -import com.epam.drill.agent.logging.LoggingConfiguration -import com.epam.drill.agent.test2code.Test2Code -import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions -import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper -import com.epam.drill.agent.transport.JsonAgentMessageSerializer -import com.epam.drill.agent.transport.SimpleAgentMessageSender -import com.epam.drill.agent.transport.http.HttpAgentMessageTransport -import java.io.File -import kotlin.system.exitProcess -import kotlin.takeIf - -fun main(args: Array) { - LoggingConfiguration.readDefaultConfiguration() - - val argsMap: Map = args - .filter { it.startsWith("--") && it.contains("=") } - .associate { - val (key, value) = it.removePrefix("--").split("=", limit = 2) - key to value - }.filter { it.value.isNotEmpty() } - val envMap = System.getenv() - .filterKeys { it.startsWith("DRILL_") } - .filterValues { !it.isNullOrEmpty() } - .mapKeys { toParameterName(it) } - val configuration = DefaultAgentConfiguration(envMap + argsMap) - val definitions = collectAgentParameterDefinitions( - DefaultParameterDefinitions, - ParameterDefinitions, - Test2CodeParameterDefinitions - ) - val validator = AgentParametersValidator(configuration.parameters) - try { - validator.validate(*definitions.toTypedArray()) } - catch (e: AgentParameterValidationError) { - println(e.message) - exitProcess(1) - } - - val commitSha = configuration.parameters[DefaultParameterDefinitions.COMMIT_SHA] - val buildVersion = configuration.parameters[DefaultParameterDefinitions.BUILD_VERSION] - if (commitSha == null && buildVersion == null) { - throw IllegalArgumentException("Either commitSha or buildVersion must be provided") - } - - configuration.parameters[ParameterDefinitions.LOG_LEVEL] - .let(LoggingConfiguration::setLoggingLevels) - configuration.parameters[ParameterDefinitions.LOG_FILE] - ?.let(LoggingConfiguration::setLoggingFilename) - configuration.parameters[ParameterDefinitions.LOG_LIMIT].let(LoggingConfiguration::setLogMessageLimit) - - val transport = HttpAgentMessageTransport( - serverAddress = configuration.parameters[ParameterDefinitions.API_URL], - apiKey = configuration.parameters[ParameterDefinitions.API_KEY] ?: "", - sslTruststore = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE] - ?.let { resolvePath(configuration, it) } ?: "", - sslTruststorePass = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD] ?: "", - gzipCompression = configuration.parameters[ParameterDefinitions.USE_GZIP_COMPRESSION], - ) - val serializer = JsonAgentMessageSerializer() - val mapper = HttpAgentMessageDestinationMapper() - val sender = SimpleAgentMessageSender(transport, serializer, mapper) - val test2Code = Test2Code( - id = "test2Code", - agentContext = RequestAgentContext, - sender = sender, - configuration = configuration - ) - sender.send(AgentMessageDestination("PUT", "builds"), configuration.agentMetadata, AgentMetadata.serializer()) - test2Code.scanAndSendMetadataClasses() -} - -//TODO: duplicate with JvmModuleMessageSender -private fun resolvePath(configuration: AgentConfiguration, path: String) = File(path).run { - val installationDir = File(configuration.parameters[DefaultParameterDefinitions.INSTALLATION_DIR] ?: "") - val resolved = this.takeIf(File::exists) - ?: this.takeUnless(File::isAbsolute)?.let(installationDir::resolve) - resolved?.takeUnless(File::isDirectory)?.absolutePath ?: path -} - -//TODO: duplicate from ValidatedParametersProvider -internal fun toParameterName(entry: Map.Entry) = entry.key - .removePrefix("DRILL_") - .lowercase() - .split("_") - .joinToString("") { it.replaceFirstChar(Char::uppercase) } - .replaceFirstChar(Char::lowercase) - -private fun collectAgentParameterDefinitions(vararg collections: AgentParameterDefinitionCollection): List> { - return collections.flatMap { it.getAll() } - -} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt index 6ce213e2..6569cdad 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/Configuration.kt @@ -57,14 +57,8 @@ actual object Configuration : AgentConfiguration { logger.debug { "initializeNative: Found environment variables: ${environmentVariablesProvider.configuration}" } val agentOptionsProvider = AgentOptionsProvider(agentOptions) logger.debug { "initializeNative: Found agent options: ${agentOptionsProvider.configuration}" } - val validatedParametersProvider = ValidatedParametersProvider(setOf( - environmentVariablesProvider, - agentOptionsProvider - )) val runtimeParametersProvider = RuntimeParametersProvider() - val inputParameters = inputParameters(setOf( - validatedParametersProvider, environmentVariablesProvider, agentOptionsProvider, runtimeParametersProvider diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt index 4b0790e9..5bdeb0e6 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt @@ -44,6 +44,10 @@ actual object JvmModuleMessageSender : AgentMessageSender { messageSender.send(AgentMessageDestination("PUT", "instances"), Configuration.agentMetadata, AgentMetadata.serializer()) } + fun sendBuildMetadata() { + messageSender.send(AgentMessageDestination("PUT", "builds"), Configuration.agentMetadata, AgentMetadata.serializer()) + } + override fun shutdown() { messageSender.shutdown() } From 31718b6a3b48216d3998237b3ea6e1d282a2bd10 Mon Sep 17 00:00:00 2001 From: iryabov Date: Thu, 18 Sep 2025 13:28:05 +0200 Subject: [PATCH 9/9] feat: add message sending mode parameter --- .../configuration/ParameterDefinitions.kt | 4 ++ .../kotlin/com/epam/drill/agent/Agent.kt | 7 ---- .../agent/transport/JvmModuleMessageSender.kt | 39 +++++++++++++++---- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt index 8472b1aa..058faaea 100644 --- a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ParameterDefinitions.kt @@ -30,6 +30,10 @@ object ParameterDefinitions: AgentParameterDefinitionCollection() { name = "apiKey", description = "Drill4J API key. It is recommended to set it with DRILL_API_KEY env variable, rather than using command line argument" ).register() + val MESSAGE_SENDING_MODE = AgentParameterDefinition.forString( + name = "messageSendingMode", + description = "Message sending mode. Possible values: DIRECT, QUEUED", + defaultValue = "QUEUED").register() val MESSAGE_QUEUE_LIMIT = AgentParameterDefinition.forString(name = "messageQueueLimit", defaultValue = "512Mb").register() val MESSAGE_MAX_RETRIES = AgentParameterDefinition.forInt(name = "messageMaxRetries", defaultValue = Int.MAX_VALUE).register() val SSL_TRUSTSTORE = NullableAgentParameterDefinition.forString(name = "sslTruststore").register() diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt index 54f805d8..597cef82 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/Agent.kt @@ -15,12 +15,9 @@ */ package com.epam.drill.agent -import com.epam.drill.agent.common.configuration.AgentMetadata -import com.epam.drill.agent.common.transport.AgentMessageDestination import com.epam.drill.agent.configuration.AgentParameterValidationError import com.epam.drill.agent.configuration.AgentParametersValidator import com.epam.drill.agent.configuration.Configuration -import com.epam.drill.agent.configuration.DefaultAgentConfiguration import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions import com.epam.drill.agent.instrument.ApplicationClassTransformer @@ -56,11 +53,7 @@ import com.epam.drill.agent.logging.LoggingConfiguration import com.epam.drill.agent.module.JvmModuleLoader import com.epam.drill.agent.test2code.Test2Code import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions -import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper -import com.epam.drill.agent.transport.JsonAgentMessageSerializer import com.epam.drill.agent.transport.JvmModuleMessageSender -import com.epam.drill.agent.transport.SimpleAgentMessageSender -import com.epam.drill.agent.transport.http.HttpAgentMessageTransport import jdk.internal.org.objectweb.asm.ClassReader import mu.KotlinLogging import java.lang.instrument.ClassFileTransformer diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt index 5bdeb0e6..62b4c39f 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/transport/JvmModuleMessageSender.kt @@ -35,17 +35,25 @@ actual object JvmModuleMessageSender : AgentMessageSender { private const val QUEUE_DEFAULT_SIZE: Long = 512L * 1024 * 1024 private val logger = KotlinLogging.logger {} - private val messageSender = messageSender() + private val messageSender by lazy { messageSender() } override fun send(destination: AgentMessageDestination, message: T, serializer: KSerializer) = messageSender.send(destination, message, serializer) actual fun sendAgentMetadata() { - messageSender.send(AgentMessageDestination("PUT", "instances"), Configuration.agentMetadata, AgentMetadata.serializer()) + messageSender.send( + AgentMessageDestination("PUT", "instances"), + Configuration.agentMetadata, + AgentMetadata.serializer() + ) } fun sendBuildMetadata() { - messageSender.send(AgentMessageDestination("PUT", "builds"), Configuration.agentMetadata, AgentMetadata.serializer()) + messageSender.send( + AgentMessageDestination("PUT", "builds"), + Configuration.agentMetadata, + AgentMetadata.serializer() + ) } override fun shutdown() { @@ -53,7 +61,7 @@ actual object JvmModuleMessageSender : AgentMessageSender { } @OptIn(InternalSerializationApi::class) - private fun messageSender(): QueuedAgentMessageSender { + private fun messageSender(): AgentMessageSender { val transport = HttpAgentMessageTransport( serverAddress = Configuration.parameters[ParameterDefinitions.API_URL], apiKey = Configuration.parameters[ParameterDefinitions.API_KEY] ?: "", @@ -69,10 +77,25 @@ actual object JvmModuleMessageSender : AgentMessageSender { val queue = InMemoryAgentMessageQueue( capacity = Configuration.parameters[ParameterDefinitions.MESSAGE_QUEUE_LIMIT].let(::parseBytes), ) - return QueuedAgentMessageSender( - transport, serializer, mapper, queue, - maxRetries = Configuration.parameters[ParameterDefinitions.MESSAGE_MAX_RETRIES] - ) + return when (Configuration.parameters[ParameterDefinitions.MESSAGE_SENDING_MODE].uppercase()) { + "DIRECT" -> SimpleAgentMessageSender(transport, serializer, mapper).also { + logger.info { "Using DIRECT message sending mode." } + } + "QUEUED" -> QueuedAgentMessageSender( + transport, serializer, mapper, queue, + maxRetries = Configuration.parameters[ParameterDefinitions.MESSAGE_MAX_RETRIES] + ).also { + logger.info { "Using QUEUED message sending mode." } + } + + else -> SimpleAgentMessageSender(transport, serializer, mapper).also { + logger.warn { + "Unknown message sending mode: ${Configuration.parameters[ParameterDefinitions.MESSAGE_SENDING_MODE]}. " + + "Falling back to DIRECT mode." + + } + } + } } private fun resolvePath(path: String) = File(path).run {