From 7da00b64e90ad5a86fc9a0e04975e2f9d138121e Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Fri, 4 Jul 2025 21:03:26 +0400 Subject: [PATCH 1/5] chore: set sharedLibsRef to match branch feature/read-session-and-test-id-from-cookies --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e5c43d45..2cac3985 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,7 +20,7 @@ apacheHttpClientVersion = 5.2.3 aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 -sharedLibsRef = feature/max-retries-EPMDJ-10975 +sharedLibsRef = feature/read-session-and-test-id-from-cookies sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false From d5ca7f586f358a28546b4f0ce5e5dbc704e3e762 Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Mon, 7 Jul 2025 18:41:40 +0400 Subject: [PATCH 2/5] feat: make sessionId and testId nullable + allow recording coverage even if just one of those is present --- .../kotlin/com/epam/drill/agent/test2code/Test2Code.kt | 4 ++-- .../drill/agent/test2code/coverage/CoverageRecorder.kt | 4 ++-- .../epam/drill/agent/test2code/coverage/CoverageSender.kt | 7 ++++++- .../agent/test2code/coverage/GlobalCoverageRecorder.kt | 4 ++-- .../agent/test2code/coverage/ThreadCoverageRecorder.kt | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) 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 d543f24c..28badfc6 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 @@ -91,7 +91,7 @@ class Test2Code( override fun processServerRequest() { val sessionId = context() val testId = context[DRILL_TEST_ID_HEADER] - if (sessionId == null || testId == null) return + if (sessionId == null && testId == null) return coverageManager.startRecording(sessionId, testId) } @@ -102,7 +102,7 @@ class Test2Code( override fun processServerResponse() { val sessionId = context() val testId = context[DRILL_TEST_ID_HEADER] - if (sessionId == null || testId == null) return + if (sessionId == null && testId == null) return coverageManager.stopRecording(sessionId, testId) } diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageRecorder.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageRecorder.kt index dea485bd..50d54022 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageRecorder.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageRecorder.kt @@ -16,8 +16,8 @@ package com.epam.drill.agent.test2code.coverage interface ICoverageRecorder { - fun startRecording(sessionId: String, testId: String) - fun stopRecording(sessionId: String, testId: String) + fun startRecording(sessionId: String?, testId: String?) + fun stopRecording(sessionId: String?, testId: String?) fun getContext(): ContextCoverage? fun pollRecorded(): Sequence } diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt index eae2fc23..857f2aa8 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt @@ -69,7 +69,12 @@ class IntervalCoverageSender( * @features Coverage data sending */ private fun sendProbes(dataToSend: Sequence) { - dataToSend.map { ClassCoverage(classname = it.name, testId = it.testId, probes = it.probes.values.toBitSet()) } + dataToSend + .map { ClassCoverage( + classname = it.name, + testSessionId = it.sessionId, + testId = it.testId, + probes = it.probes.values.toBitSet()) } .chunked(pageSize) .forEach { sender.send(destination, CoveragePayload(groupId, appId, instanceId, it)) } } diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/GlobalCoverageRecorder.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/GlobalCoverageRecorder.kt index 96128f9d..123d1c01 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/GlobalCoverageRecorder.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/GlobalCoverageRecorder.kt @@ -21,11 +21,11 @@ class GlobalCoverageRecorder: ICoverageRecorder { private val globalExecData: ExecData = ExecData() private val sentGlobalExecData: ExecData = ExecData() - override fun startRecording(sessionId: String, testId: String) { + override fun startRecording(sessionId: String?, testId: String?) { // do nothing } - override fun stopRecording(sessionId: String, testId: String) { + override fun stopRecording(sessionId: String?, testId: String?) { // do nothing } diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ThreadCoverageRecorder.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ThreadCoverageRecorder.kt index 9cd9ecb6..0cc892dd 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ThreadCoverageRecorder.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/ThreadCoverageRecorder.kt @@ -24,7 +24,7 @@ class ThreadCoverageRecorder( private val context: ThreadLocal = ThreadLocal() private val execData: ThreadLocal = ThreadLocal() - override fun startRecording(sessionId: String, testId: String) { + override fun startRecording(sessionId: String?, testId: String?) { val ctx = ContextKey(sessionId, testId) context.set(ctx) execData.set(execDataPool.getOrPut( @@ -34,7 +34,7 @@ class ThreadCoverageRecorder( logger.trace { "Test recording started (sessionId = $sessionId, testId = $testId, threadId = ${Thread.currentThread().id})." } } - override fun stopRecording(sessionId: String, testId: String) { + override fun stopRecording(sessionId: String?, testId: String?) { execDataPool.release(ContextKey(sessionId, testId), execData.get() ?: ExecData()) execData.remove() context.remove() From fe156d2dfa273aa70a18cbbd439e84766820b287 Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Wed, 9 Jul 2025 17:43:43 +0400 Subject: [PATCH 3/5] fix: order of fields in ClassCoverage payload --- .../com/epam/drill/agent/test2code/coverage/CoverageSender.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt index 857f2aa8..f4e5a8c5 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/coverage/CoverageSender.kt @@ -72,8 +72,8 @@ class IntervalCoverageSender( dataToSend .map { ClassCoverage( classname = it.name, - testSessionId = it.sessionId, testId = it.testId, + testSessionId = it.sessionId, probes = it.probes.values.toBitSet()) } .chunked(pageSize) .forEach { sender.send(destination, CoveragePayload(groupId, appId, instanceId, it)) } From f0ecf95cbec44fed7e5ed7180e6a1a6f00989ddf Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 23 Jul 2025 12:49:52 +0200 Subject: [PATCH 4/5] feat: improve parameter validations and descriptions in configuration EPMDJ-11053 --- gradle.properties | 2 +- .../configuration/ParameterDefinitions.kt | 49 ++++--- .../agent/configuration/ValidationBuilder.kt | 37 ----- .../epam/drill/agent/AppArchiveScannerCli.kt | 41 ++++-- .../configuration/AgentMetadataValidator.kt | 128 ------------------ .../agent/transport/JvmModuleMessageSender.kt | 6 +- .../AgentLoggingConfiguration.kt | 4 +- test2code/build.gradle.kts | 1 + .../epam/drill/agent/test2code/Test2Code.kt | 26 ++-- .../configuration/ParametersValidator.kt | 59 -------- ...ns.kt => Test2CodeParameterDefinitions.kt} | 115 ++++++++-------- .../configuration/ValidationBuilder.kt | 28 ---- 12 files changed, 143 insertions(+), 353 deletions(-) delete mode 100644 java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt delete mode 100644 java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentMetadataValidator.kt delete mode 100644 test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParametersValidator.kt rename test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/{ParameterDefinitions.kt => Test2CodeParameterDefinitions.kt} (59%) delete mode 100644 test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ValidationBuilder.kt diff --git a/gradle.properties b/gradle.properties index ab7ebb88..a8e47a79 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ apacheHttpClientVersion = 5.2.3 aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 -sharedLibsRef = feature/max-retries-EPMDJ-10975 +sharedLibsRef = feature/improve-parameter-validations sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false 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 8a7b6179..be139714 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 @@ -16,25 +16,36 @@ package com.epam.drill.agent.configuration import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.NullableAgentParameterDefinition -object ParameterDefinitions { +object ParameterDefinitions: AgentParameterDefinitionCollection() { - val API_URL = AgentParameterDefinition.forString(name = "apiUrl", parser = { if (!it.endsWith("/")) "$it/" else it } ) - val API_KEY = AgentParameterDefinition.forString(name = "apiKey") - val MESSAGE_QUEUE_LIMIT = AgentParameterDefinition.forString(name = "messageQueueLimit", defaultValue = "512Mb") - val MESSAGE_MAX_RETRIES = AgentParameterDefinition.forInt(name = "messageMaxRetries", defaultValue = Int.MAX_VALUE) - val SSL_TRUSTSTORE = AgentParameterDefinition.forString(name = "sslTruststore") - val SSL_TRUSTSTORE_PASSWORD = AgentParameterDefinition.forString(name = "sslTruststorePassword") - val LOG_LEVEL = AgentParameterDefinition.forString(name = "logLevel", defaultValue = "INFO") - val LOG_FILE = AgentParameterDefinition.forString(name = "logFile") - val LOG_LIMIT = AgentParameterDefinition.forInt(name = "logLimit", defaultValue = 512) - val IS_WEB_APP = AgentParameterDefinition.forBoolean(name = "isWebApp") - val IS_KAFKA = AgentParameterDefinition.forBoolean(name = "isKafka") - val IS_CADENCE = AgentParameterDefinition.forBoolean(name = "isCadence") - val IS_TLS_APP = AgentParameterDefinition.forBoolean(name = "isTlsApp") - val IS_ASYNC_APP = AgentParameterDefinition.forBoolean(name = "isAsyncApp") - val IS_COMPATIBILITY_TESTS = AgentParameterDefinition.forBoolean(name = "isCompatibilityTests", defaultValue = false) - val USE_PROTOBUF_SERIALIZER = AgentParameterDefinition.forBoolean(name = "useProtobufSerializer", defaultValue = true) - val USE_GZIP_COMPRESSION = AgentParameterDefinition.forBoolean(name = "useGzipCompression", defaultValue = true) - val IS_WS_MESSAGES = AgentParameterDefinition.forBoolean(name = "isWsMsg", defaultValue = true) + val API_URL = AgentParameterDefinition.forString( + name = "apiUrl", + description = "URL to Drill4J Backend /api endpoint. Example: http://localhost:8090/api", + parser = { if (!it.endsWith("/")) "$it/" else it }, + validator = { validTransportUrl() }).register() + val API_KEY = NullableAgentParameterDefinition.forString( + 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_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() + val SSL_TRUSTSTORE_PASSWORD = NullableAgentParameterDefinition.forString(name = "sslTruststorePassword").register() + val LOG_LEVEL = AgentParameterDefinition.forString(name = "logLevel", defaultValue = "INFO").register() + val LOG_FILE = NullableAgentParameterDefinition.forString(name = "logFile").register() + val LOG_LIMIT = AgentParameterDefinition.forInt(name = "logLimit", defaultValue = 512).register() + val IS_WEB_APP = AgentParameterDefinition.forBoolean(name = "isWebApp", defaultValue = false).register() + val IS_KAFKA = AgentParameterDefinition.forBoolean(name = "isKafka", defaultValue = false).register() + val IS_CADENCE = AgentParameterDefinition.forBoolean(name = "isCadence", defaultValue = false).register() + val IS_TLS_APP = AgentParameterDefinition.forBoolean(name = "isTlsApp", defaultValue = false).register() + val IS_ASYNC_APP = AgentParameterDefinition.forBoolean(name = "isAsyncApp", defaultValue = false).register() + val IS_COMPATIBILITY_TESTS = + AgentParameterDefinition.forBoolean(name = "isCompatibilityTests", defaultValue = false).register() + val USE_PROTOBUF_SERIALIZER = + AgentParameterDefinition.forBoolean(name = "useProtobufSerializer", defaultValue = true).register() + val USE_GZIP_COMPRESSION = AgentParameterDefinition.forBoolean(name = "useGzipCompression", defaultValue = true).register() + val IS_WS_MESSAGES = AgentParameterDefinition.forBoolean(name = "isWsMsg", defaultValue = true).register() } diff --git a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt deleted file mode 100644 index 330cf8d7..00000000 --- a/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt +++ /dev/null @@ -1,37 +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.configuration - -import com.epam.drill.agent.konform.validation.Constraint -import com.epam.drill.agent.konform.validation.ValidationBuilder - -val TRANSPORT_SCHEMES = setOf("http://", "https://") - -fun ValidationBuilder.validTransportUrl() = addConstraint( - "must have a valid URL address, e.g. 'https://localhost:8090', but was '{value}'" -){ TRANSPORT_SCHEMES.any(it::startsWith) } - -fun ValidationBuilder.identifier() = addConstraint( - "must contain only lowercase latin characters", -) { it.matches("^[a-z0-9_-]+\$".toRegex()) } - -fun ValidationBuilder.isValidPackage(): Constraint = addConstraint( - "must have a valid Java package delimited by a forward slash, e.g. 'com/example', but was '{value}'" -) { it.matches("[a-zA-Z_]\\w*(?:/[a-zA-Z_]\\w*)*".toRegex()) } - -fun ValidationBuilder.isValidLogLevel(): Constraint = addConstraint( - "must have a valid logging level for a java package, e.g. 'com.example=INFO', but was '{value}'" -) { it.matches("(([a-zA-Z_]\\w*(\\.[a-zA-Z_]\\w*)*)?=)?(TRACE|DEBUG|INFO|WARN|ERROR)".toRegex()) } 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 index 8416a6dd..cf085a58 100644 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt @@ -16,20 +16,24 @@ package com.epam.drill.agent import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition import com.epam.drill.agent.common.transport.AgentMessage import com.epam.drill.agent.common.transport.AgentMessageDestination -import com.epam.drill.agent.configuration.AgentMetadataValidator +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.ParametersValidator +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) { @@ -46,8 +50,18 @@ fun main(args: Array) { .filterValues { !it.isNullOrEmpty() } .mapKeys { toParameterName(it) } val configuration = DefaultAgentConfiguration(envMap + argsMap) - AgentMetadataValidator.validate(configuration.parameters) - ParametersValidator.validate(configuration.parameters) + 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] @@ -55,18 +69,18 @@ fun main(args: Array) { throw IllegalArgumentException("Either commitSha or buildVersion must be provided") } - configuration.parameters[ParameterDefinitions.LOG_LEVEL].takeIf { it.isNotEmpty() } - ?.let(LoggingConfiguration::setLoggingLevels) - configuration.parameters[ParameterDefinitions.LOG_FILE].takeIf { it.isNotEmpty() } + 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].takeIf(String::isNotEmpty) + apiKey = configuration.parameters[ParameterDefinitions.API_KEY] ?: "", + sslTruststore = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE] ?.let { resolvePath(configuration, it) } ?: "", - sslTruststorePass = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD], + sslTruststorePass = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD] ?: "", gzipCompression = configuration.parameters[ParameterDefinitions.USE_GZIP_COMPRESSION], ) val serializer = JsonAgentMessageSerializer() @@ -96,4 +110,9 @@ internal fun toParameterName(entry: Map.Entry) = entry.key .lowercase() .split("_") .joinToString("") { it.replaceFirstChar(Char::uppercase) } - .replaceFirstChar(Char::lowercase) \ No newline at end of file + .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/AgentMetadataValidator.kt b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentMetadataValidator.kt deleted file mode 100644 index 84a439c6..00000000 --- a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentMetadataValidator.kt +++ /dev/null @@ -1,128 +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.configuration - -import com.epam.drill.agent.common.configuration.AgentParameterDefinition -import com.epam.drill.agent.common.configuration.AgentParameters -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.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 -import mu.KotlinLogging - -object AgentMetadataValidator { - private val logger = KotlinLogging.logger {} - - private class ValidatingParameters(parameters: AgentParameters) { - val appId = parameters[DefaultParameterDefinitions.APP_ID] - val groupId = parameters[DefaultParameterDefinitions.GROUP_ID] - val buildVersion = parameters[DefaultParameterDefinitions.BUILD_VERSION] - val commitSha = parameters[DefaultParameterDefinitions.COMMIT_SHA] - val envId: String? = parameters[DefaultParameterDefinitions.ENV_ID] - val packagePrefixes = parameters[DefaultParameterDefinitions.PACKAGE_PREFIXES] - val apiUrl = parameters[ParameterDefinitions.API_URL] - val apiKey = parameters[ParameterDefinitions.API_KEY] - val logLevel = parameters[ParameterDefinitions.LOG_LEVEL] - val logLevelAsList = logLevel.split(";") - val logLimit = parameters[ParameterDefinitions.LOG_LIMIT] - } - - private val strictValidators = Validation { - ValidatingParameters::groupId required { - identifier() - minLength(3) - } - ValidatingParameters::appId required { - identifier() - minLength(3) - } - ValidatingParameters::apiUrl required { - validTransportUrl() - } - ValidatingParameters::packagePrefixes { - minItems(1) - } - ValidatingParameters::packagePrefixes 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::logLimit ifPresent { - minimum(0) - } - } - - fun validate(parameters: AgentParameters): Map { - val defaultValues: MutableMap = mutableMapOf() - val defaultFor: (AgentParameterDefinition) -> Unit = { - defaultValues[it.name] = it.defaultValue.toString() - } - val isInvalid: (ValidationResult<*>) -> Boolean = { it is Invalid } - strictValidators(ValidatingParameters(parameters)).takeIf(isInvalid)?.let { result -> - val message = "Cannot load the agent because some agent parameters are set incorrectly. " + - convertToMessage(result.errors) - - throw java.lang.IllegalArgumentException(message) - } - softValidators(ValidatingParameters(parameters)).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) - } - } - } - return defaultValues - } - - - - private fun convertToMessage(errors: List) = "Please check the following parameters:\n" + - errors.joinToString("\n") { " - ${convertToField(it)} ${it.message.removeExtraValues()}" } - - private fun convertToField(error: ValidationError) = error.dataPath.removePrefix(".") - .substringBeforeLast("AsList") - .removeSuffix("AsInt") - - //TODO: figure out why Konform adds this prefixes to a message - private fun String.removeExtraValues() = this - .removePrefix("com.epam.drill.agent.shadow.") - .removePrefix("com/epam/drill/agent/shadow/") - -} \ No newline at end of file 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 a6955475..5ca24f7b 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 @@ -48,10 +48,10 @@ actual object JvmModuleMessageSender : AgentMessageSender { private fun messageSender(): QueuedAgentMessageSender { val transport = HttpAgentMessageTransport( serverAddress = Configuration.parameters[ParameterDefinitions.API_URL], - apiKey = Configuration.parameters[ParameterDefinitions.API_KEY], - sslTruststore = Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE].takeIf(String::isNotEmpty) + apiKey = Configuration.parameters[ParameterDefinitions.API_KEY] ?: "", + sslTruststore = Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE] ?.let(::resolvePath) ?: "", - sslTruststorePass = Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD], + sslTruststorePass = Configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD] ?: "", gzipCompression = Configuration.parameters[ParameterDefinitions.USE_GZIP_COMPRESSION], ) val serializer = takeIf { Configuration.parameters[ParameterDefinitions.USE_PROTOBUF_SERIALIZER] }?.let { diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/AgentLoggingConfiguration.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/AgentLoggingConfiguration.kt index a4c15eba..f3a64272 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/AgentLoggingConfiguration.kt +++ b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/AgentLoggingConfiguration.kt @@ -31,7 +31,7 @@ object AgentLoggingConfiguration { fun updateNativeLoggingConfiguration() { val logLevel = Configuration.parameters[ParameterDefinitions.LOG_LEVEL] - val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE].takeIf(String::isNotEmpty) + val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE] val logLimit = Configuration.parameters[ParameterDefinitions.LOG_LIMIT] LoggingConfiguration.setLoggingLevels(logLevel) @@ -50,7 +50,7 @@ object AgentLoggingConfiguration { @OptIn(ExperimentalForeignApi::class) fun updateJvmLoggingConfiguration() { val logLevel = Configuration.parameters[ParameterDefinitions.LOG_LEVEL] - val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE].takeIf(String::isNotEmpty) + val logFile = Configuration.parameters[ParameterDefinitions.LOG_FILE] val logLimit = Configuration.parameters[ParameterDefinitions.LOG_LIMIT] callObjectVoidMethodWithString(LoggingConfiguration::class, "setLoggingLevels", logLevel) diff --git a/test2code/build.gradle.kts b/test2code/build.gradle.kts index f75013d4..8e8cfd87 100644 --- a/test2code/build.gradle.kts +++ b/test2code/build.gradle.kts @@ -41,6 +41,7 @@ dependencies { implementation("io.github.microutils:kotlin-logging-jvm:$microutilsLoggingVersion") implementation(project(":common")) + implementation(project(":agent-config")) implementation(project(":test2code-common")) implementation(project(":test2code-jacoco")) implementation(project(":konform")) 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 e6b7afae..e8785dce 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 @@ -29,13 +29,13 @@ import com.epam.drill.agent.common.transport.AgentMessage import com.epam.drill.agent.common.transport.AgentMessageDestination import com.epam.drill.agent.common.transport.AgentMessageSender import com.epam.drill.agent.common.classloading.EntitySource +import com.epam.drill.agent.configuration.AgentParametersValidator import com.epam.drill.agent.test2code.common.api.AstMethod import com.epam.drill.agent.test2code.common.transport.ClassMetadata import com.epam.drill.agent.test2code.classloading.ClassLoadersScanner import com.epam.drill.agent.test2code.classloading.ClassScanner import com.epam.drill.agent.test2code.classparsing.parseAstClass -import com.epam.drill.agent.test2code.configuration.ParameterDefinitions -import com.epam.drill.agent.test2code.configuration.ParametersValidator +import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions import com.epam.drill.agent.test2code.coverage.* private const val DRILL_TEST_ID_HEADER = "drill-test-id" @@ -57,11 +57,11 @@ class Test2Code( private val coverageManager = DrillCoverageManager private val instrumenter = DrillInstrumenter(coverageManager, coverageManager) private val coverageSender: CoverageSender = IntervalCoverageSender( - groupId= configuration.agentMetadata.groupId, - appId = configuration.agentMetadata.appId, + groupId = configuration.agentMetadata.groupId, + appId = configuration.agentMetadata.appId, instanceId = configuration.agentMetadata.instanceId, - intervalMs = configuration.parameters[ParameterDefinitions.COVERAGE_SEND_INTERVAL], - pageSize = configuration.parameters[ParameterDefinitions.COVERAGE_SEND_PAGE_SIZE], + intervalMs = configuration.parameters[Test2CodeParameterDefinitions.COVERAGE_SEND_INTERVAL], + pageSize = configuration.parameters[Test2CodeParameterDefinitions.COVERAGE_SEND_PAGE_SIZE], sender = sender, collectProbes = { coverageManager.pollRecorded() } ) @@ -74,8 +74,10 @@ class Test2Code( ): ByteArray? = instrumenter.instrument(className, initialBytes) override fun load() { - ParametersValidator.validate(configuration.parameters) - logger.debug { "load: Waiting for transport availability for class metadata scanning" } + AgentParametersValidator(configuration.parameters).validate( + Test2CodeParameterDefinitions.SCAN_CLASS_PATH, + Test2CodeParameterDefinitions.SCAN_CLASS_DELAY + ) thread { scanAndSendMetadataClasses() } @@ -108,9 +110,9 @@ class Test2Code( override fun scanClasses(consumer: (Set) -> Unit) { val packagePrefixes = configuration.agentMetadata.packagesPrefixes - val scanClassPaths = configuration.parameters[ParameterDefinitions.SCAN_CLASS_PATH] - val enableScanClassLoaders = configuration.parameters[ParameterDefinitions.ENABLE_SCAN_CLASS_LOADERS] - val scanClassDelay = configuration.parameters[ParameterDefinitions.SCAN_CLASS_DELAY] + val scanClassPaths = configuration.parameters[Test2CodeParameterDefinitions.SCAN_CLASS_PATH] as List + val enableScanClassLoaders = configuration.parameters[Test2CodeParameterDefinitions.ENABLE_SCAN_CLASS_LOADERS] + val scanClassDelay = configuration.parameters[Test2CodeParameterDefinitions.SCAN_CLASS_DELAY] if (enableScanClassLoaders && scanClassDelay.isPositive()) { logger.debug { "Waiting class scan delay ${scanClassDelay.inWholeMilliseconds} ms..." } runBlocking { delay(scanClassDelay) } @@ -136,7 +138,7 @@ class Test2Code( .also { classCount += it.size } .flatMap { parseAstClass(it.entityName(), it.bytes()) } .also { methodCount += it.size } - .chunked(configuration.parameters[ParameterDefinitions.METHODS_SEND_PAGE_SIZE]) + .chunked(configuration.parameters[Test2CodeParameterDefinitions.METHODS_SEND_PAGE_SIZE]) .forEach(::sendClassMetadata) } logger.info { "Scanned $classCount classes with $methodCount methods" } diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParametersValidator.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParametersValidator.kt deleted file mode 100644 index 23e6a37a..00000000 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParametersValidator.kt +++ /dev/null @@ -1,59 +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.test2code.configuration - -import kotlin.time.Duration -import mu.KotlinLogging -import com.epam.drill.agent.common.configuration.AgentParameters -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 - -object ParametersValidator { - - private class ValidatingParameters(parameters: AgentParameters) { - val scanClassDelay = parameters[ParameterDefinitions.SCAN_CLASS_DELAY] - val scanClassPath = parameters[ParameterDefinitions.SCAN_CLASS_PATH] - } - - private val softValidators = Validation { - ValidatingParameters::scanClassDelay ifPresent { - minimum(Duration.ZERO) - } - ValidatingParameters::scanClassPath onEach { - isNotBlank() - } - } - - private val logger = KotlinLogging.logger {} - - fun validate(parameters: AgentParameters) { - val isInvalid: (ValidationResult<*>) -> Boolean = { it is Invalid } - softValidators(ValidatingParameters(parameters)).takeIf(isInvalid)?.let { result -> - val message = "Some agent parameters were set incorrectly and were replaced with default values. " + - convertToMessage(result.errors) - logger.error { message } - } - } - - 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(".") - -} diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParameterDefinitions.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/Test2CodeParameterDefinitions.kt similarity index 59% rename from test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParameterDefinitions.kt rename to test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/Test2CodeParameterDefinitions.kt index d7b2e7af..43d031db 100644 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ParameterDefinitions.kt +++ b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/Test2CodeParameterDefinitions.kt @@ -1,53 +1,62 @@ -/** - * 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.test2code.configuration - -import kotlin.time.Duration -import kotlin.time.DurationUnit -import kotlin.time.toDuration -import com.epam.drill.agent.common.configuration.AgentParameterDefinition - -object ParameterDefinitions { - - val SCAN_CLASS_PATH = AgentParameterDefinition.forType( - name = "scanClassPath", - defaultValue = emptyList(), - parser = { it.split(";") } - ) - val SCAN_CLASS_DELAY = AgentParameterDefinition.forType( - name = "scanClassDelay", - defaultValue = Duration.ZERO, - parser = { it.toLong().toDuration(DurationUnit.MILLISECONDS) } - ) - val ENABLE_SCAN_CLASS_LOADERS = AgentParameterDefinition.forType( - name = "enableScanClassLoaders", - defaultValue = true, - parser = { it.toBoolean() } - ) - val COVERAGE_SEND_INTERVAL = AgentParameterDefinition.forLong( - name = "coverageSendInterval", - defaultValue = 2000L - ) - val COVERAGE_SEND_PAGE_SIZE = AgentParameterDefinition.forInt( - name = "coverageSendPageSize", - defaultValue = 1000 - ) - val METHODS_SEND_PAGE_SIZE = AgentParameterDefinition.forInt( - name = "methodsSendPageSize", - defaultValue = 1000 - ) - -} +/** + * 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.test2code.configuration + +import kotlin.time.Duration +import com.epam.drill.agent.common.configuration.AgentParameterDefinition +import com.epam.drill.agent.common.configuration.AgentParameterDefinitionCollection +import com.epam.drill.agent.common.configuration.BaseAgentParameterDefinition +import com.epam.drill.agent.common.configuration.ValidationType +import com.epam.drill.agent.configuration.isNotBlank +import com.epam.drill.agent.configuration.minDuration + +object Test2CodeParameterDefinitions: AgentParameterDefinitionCollection() { + + val SCAN_CLASS_PATH = AgentParameterDefinition.forList( + name = "scanClassPath", + description = "Path to JAR/WAR/EAR to scan", + defaultValue = emptyList(), + validation = ValidationType.SOFT, + itemValidator = { + isNotBlank() + } + ).register() + val SCAN_CLASS_DELAY = AgentParameterDefinition.forDuration( + name = "scanClassDelay", + defaultValue = Duration.ZERO, + validation = ValidationType.SOFT, + validator = { + minDuration(Duration.ZERO) + } + ).register() + val ENABLE_SCAN_CLASS_LOADERS = AgentParameterDefinition.forBoolean( + name = "enableScanClassLoaders", + defaultValue = true + ).register() + val COVERAGE_SEND_INTERVAL = AgentParameterDefinition.forLong( + name = "coverageSendInterval", + defaultValue = 2000L + ).register() + val COVERAGE_SEND_PAGE_SIZE = AgentParameterDefinition.forInt( + name = "coverageSendPageSize", + defaultValue = 1000 + ).register() + val METHODS_SEND_PAGE_SIZE = AgentParameterDefinition.forInt( + name = "methodsSendPageSize", + defaultValue = 1000 + ).register() + +} diff --git a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ValidationBuilder.kt b/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ValidationBuilder.kt deleted file mode 100644 index 3dc09944..00000000 --- a/test2code/src/main/kotlin/com/epam/drill/agent/test2code/configuration/ValidationBuilder.kt +++ /dev/null @@ -1,28 +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.test2code.configuration - -import kotlin.time.Duration -import com.epam.drill.agent.konform.validation.Constraint -import com.epam.drill.agent.konform.validation.ValidationBuilder - -fun ValidationBuilder.minimum(minimumInclusive: Duration) = addConstraint( - "must be at least '{0}' ms", minimumInclusive.inWholeMilliseconds.toString() -) { it.inWholeMilliseconds >= minimumInclusive.inWholeMilliseconds } - -fun ValidationBuilder.isNotBlank(): Constraint = addConstraint( - "must be not blank, but was {value}", -) { it.isNotBlank() } From 929310b233b42f765c570413da4c94faea769e8a Mon Sep 17 00:00:00 2001 From: iryabov Date: Thu, 14 Aug 2025 15:00:31 +0200 Subject: [PATCH 5/5] build: update sharedLibsRef to point to main branch --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c12fd6e0..fa6d10fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ apacheHttpClientVersion = 5.2.3 aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 -sharedLibsRef = feature/refactor-configuration-system +sharedLibsRef = main sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false