From 7da00b64e90ad5a86fc9a0e04975e2f9d138121e Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Fri, 4 Jul 2025 21:03:26 +0400 Subject: [PATCH 01/14] 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 02/14] 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 03/14] 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 061ead48db32c650c0ec879d55297b612dec122b Mon Sep 17 00:00:00 2001 From: iryabov Date: Fri, 18 Jul 2025 15:10:23 +0200 Subject: [PATCH 04/14] feat: make AppAgent executable to scan app archives --- java-agent/build.gradle.kts | 21 ++++- .../epam/drill/agent/AppArchiveScannerCli.kt | 79 +++++++++++++++++++ .../epam/drill/agent/test2code/Test2Code.kt | 4 +- 3 files changed, 101 insertions(+), 3 deletions(-) create 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 907c607e..ecd85164 100644 --- a/java-agent/build.gradle.kts +++ b/java-agent/build.gradle.kts @@ -18,6 +18,7 @@ plugins { kotlin("plugin.serialization") id("com.github.johnrengelman.shadow") id("com.github.hierynomus.license") + application } group = "com.epam.drill.agent" @@ -35,6 +36,7 @@ val nativeAgentLibName: String by parent!!.extra val nativeAgentHookEnabled: String by parent!!.extra val macosLd64: String by parent!!.extra val bytebuddyVersion: String by parent!!.extra +val kotlinxCliVersion: String by parent!!.extra repositories { mavenCentral() @@ -95,6 +97,7 @@ kotlin { implementation(project(":common")) implementation(project(":agent-config")) implementation(project(":agent-instrumentation")) + implementation(project(":konform")) } } val commonTest by getting { @@ -109,9 +112,15 @@ kotlin { implementation("com.alibaba:transmittable-thread-local:$transmittableThreadLocalVersion") implementation("io.aesy:datasize:$aesyDatasizeVersion") implementation("net.bytebuddy:byte-buddy:$bytebuddyVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${kotlinxCoroutinesVersion}") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${kotlinxSerializationVersion}") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:${kotlinxSerializationVersion}") + implementation("org.jetbrains.kotlinx:kotlinx-cli:${kotlinxCliVersion}") + implementation(project(":common")) implementation(project(":agent-transport")) implementation(project(":agent-instrumentation")) - runtimeOnly(project(":test2code")) + implementation(project(":test2code")) + implementation(project(":test2code-common")) } } val configureNativeDependencies: KotlinSourceSet.() -> Unit = { @@ -178,7 +187,13 @@ kotlin { "net.bytebuddy", "mu", ) + project.setProperty("mainClassName", "com.epam.drill.agent.AppArchiveScannerCliKt") val runtimeJar by registering(ShadowJar::class) { + manifest { + attributes(mapOf( + "Main-Class" to "com.epam.drill.agent.AppArchiveScannerCliKt" + )) + } mergeServiceFiles() isZip64 = true archiveFileName.set("drill-runtime.jar") @@ -221,6 +236,10 @@ distributions { } } +application { + mainClass.set("com.epam.drill.agent.AppArchiveScannerCliKt") +} + @Suppress("UNUSED_VARIABLE") license { headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") 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 new file mode 100644 index 00000000..89028bb1 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt @@ -0,0 +1,79 @@ +/** + * 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.transport.AgentMessage +import com.epam.drill.agent.common.transport.AgentMessageDestination +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.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.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 configuration = DefaultAgentConfiguration(argsMap) + ParametersValidator.validate(configuration.parameters) + + configuration.parameters[ParameterDefinitions.LOG_LEVEL].takeIf { it.isNotEmpty() } + ?.let(LoggingConfiguration::setLoggingLevels) + configuration.parameters[ParameterDefinitions.LOG_FILE].takeIf { it.isNotEmpty() } + ?.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) + ?.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) + 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 +} \ No newline at end of file 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..e6b7afae 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 @@ -46,7 +46,7 @@ private const val DRILL_TEST_ID_HEADER = "drill-test-id" @Suppress("unused") class Test2Code( id: String, - agentContext: com.epam.drill.agent.common.AgentContext, + agentContext: AgentContext, sender: AgentMessageSender, configuration: AgentConfiguration ) : AgentModule(id, agentContext, sender, configuration), Instrumenter, ClassScanner, RequestProcessor { @@ -128,7 +128,7 @@ class Test2Code( /** * Scan, parse and send metadata classes to the admin side */ - private fun scanAndSendMetadataClasses() { + fun scanAndSendMetadataClasses() { var classCount = 0 var methodCount = 0 scanClasses { classes -> From f43bb3516c7f3a91e3ff40429f44bdd7dd117ff6 Mon Sep 17 00:00:00 2001 From: iryabov Date: Mon, 21 Jul 2025 15:05:21 +0200 Subject: [PATCH 05/14] refactor: move `ValidationBuilder` from a native source set to a common source set --- .../com/epam/drill/agent/configuration/ValidationBuilder.kt | 6 ------ 1 file changed, 6 deletions(-) rename java-agent/src/{nativeMain => commonMain}/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt (88%) diff --git a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt similarity index 88% rename from java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt rename to java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt index 3ee0e39d..330cf8d7 100644 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt +++ b/java-agent/src/commonMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt @@ -15,8 +15,6 @@ */ package com.epam.drill.agent.configuration -import platform.posix.F_OK -import platform.posix.access import com.epam.drill.agent.konform.validation.Constraint import com.epam.drill.agent.konform.validation.ValidationBuilder @@ -30,10 +28,6 @@ fun ValidationBuilder.identifier() = addConstraint( "must contain only lowercase latin characters", ) { it.matches("^[a-z0-9_-]+\$".toRegex()) } -fun ValidationBuilder.pathExists(): Constraint = addConstraint( - "must be an existing filepath, but was {value}", -) { access(it, F_OK) == 0 } - 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()) } From 803abf7100210c2914ddec3072de8a83e1cd20e2 Mon Sep 17 00:00:00 2001 From: iryabov Date: Mon, 21 Jul 2025 15:06:30 +0200 Subject: [PATCH 06/14] feat: add AgentMetadataValidator for validating agent parameters --- .../epam/drill/agent/AppArchiveScannerCli.kt | 2 + .../configuration/AgentMetadataValidator.kt | 128 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentMetadataValidator.kt 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 89028bb1..51d9f063 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 @@ -18,6 +18,7 @@ package com.epam.drill.agent import com.epam.drill.agent.common.configuration.AgentConfiguration 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.DefaultAgentConfiguration import com.epam.drill.agent.configuration.DefaultParameterDefinitions import com.epam.drill.agent.configuration.ParameterDefinitions @@ -41,6 +42,7 @@ fun main(args: Array) { key to value }.filter { it.value.isNotEmpty() } val configuration = DefaultAgentConfiguration(argsMap) + AgentMetadataValidator.validate(configuration.parameters) ParametersValidator.validate(configuration.parameters) configuration.parameters[ParameterDefinitions.LOG_LEVEL].takeIf { it.isNotEmpty() } 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 new file mode 100644 index 00000000..84a439c6 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/configuration/AgentMetadataValidator.kt @@ -0,0 +1,128 @@ +/** + * 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 From 98dd48b9f45a4edce7223c2021793f6845f91176 Mon Sep 17 00:00:00 2001 From: iryabov Date: Mon, 21 Jul 2025 16:28:43 +0200 Subject: [PATCH 07/14] feat: enhance AppArchiveScannerCli to support environment variable parameters --- .../com/epam/drill/agent/AppArchiveScannerCli.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 51d9f063..4be77c74 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 @@ -41,7 +41,11 @@ fun main(args: Array) { val (key, value) = it.removePrefix("--").split("=", limit = 2) key to value }.filter { it.value.isNotEmpty() } - val configuration = DefaultAgentConfiguration(argsMap) + val envMap = System.getenv() + .filterKeys { it.startsWith("DRILL_") } + .filterValues { !it.isNullOrEmpty() } + .mapKeys { toParameterName(it) } + val configuration = DefaultAgentConfiguration(envMap + argsMap) AgentMetadataValidator.validate(configuration.parameters) ParametersValidator.validate(configuration.parameters) @@ -78,4 +82,12 @@ private fun resolvePath(configuration: AgentConfiguration, path: String) = File( val resolved = this.takeIf(File::exists) ?: this.takeUnless(File::isAbsolute)?.let(installationDir::resolve) resolved?.takeUnless(File::isDirectory)?.absolutePath ?: path -} \ No newline at end of file +} + +//TODO: duplicate from ValidatedParametersProvider +internal fun toParameterName(entry: Map.Entry) = entry.key + .removePrefix("DRILL_") + .lowercase() + .split("_") + .joinToString("") { it.replaceFirstChar(Char::uppercase) } + .replaceFirstChar(Char::lowercase) \ No newline at end of file From 679849fb11202eadc8df92329adb2bd59ca8694a Mon Sep 17 00:00:00 2001 From: iryabov Date: Mon, 21 Jul 2025 16:35:17 +0200 Subject: [PATCH 08/14] feat: require commitSha or buildVersion in AppArchiveScannerCli parameters --- .../kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 4be77c74..8416a6dd 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 @@ -49,6 +49,12 @@ fun main(args: Array) { AgentMetadataValidator.validate(configuration.parameters) ParametersValidator.validate(configuration.parameters) + 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].takeIf { it.isNotEmpty() } ?.let(LoggingConfiguration::setLoggingLevels) configuration.parameters[ParameterDefinitions.LOG_FILE].takeIf { it.isNotEmpty() } @@ -78,7 +84,7 @@ fun main(args: Array) { //TODO: duplicate with JvmModuleMessageSender private fun resolvePath(configuration: AgentConfiguration, path: String) = File(path).run { - val installationDir = File(configuration.parameters[DefaultParameterDefinitions.INSTALLATION_DIR]) + 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 From f0ecf95cbec44fed7e5ed7180e6a1a6f00989ddf Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 23 Jul 2025 12:49:52 +0200 Subject: [PATCH 09/14] 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 37fe5eaa7b41194afbde590bddfc0e25dbd66c7a Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 23 Jul 2025 13:07:46 +0200 Subject: [PATCH 10/14] build: remove app-archive-scanner from settings --- app-archive-scanner/LICENSE | 201 ----------------- app-archive-scanner/build.gradle.kts | 151 ------------- .../src/main/kotlin/AppArchiveScannerCli.kt | 213 ------------------ gradle.properties | 2 +- settings.gradle.kts | 1 - 5 files changed, 1 insertion(+), 567 deletions(-) delete mode 100644 app-archive-scanner/LICENSE delete mode 100644 app-archive-scanner/build.gradle.kts delete mode 100644 app-archive-scanner/src/main/kotlin/AppArchiveScannerCli.kt diff --git a/app-archive-scanner/LICENSE b/app-archive-scanner/LICENSE deleted file mode 100644 index 261eeb9e..00000000 --- a/app-archive-scanner/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/app-archive-scanner/build.gradle.kts b/app-archive-scanner/build.gradle.kts deleted file mode 100644 index c35408c8..00000000 --- a/app-archive-scanner/build.gradle.kts +++ /dev/null @@ -1,151 +0,0 @@ -import java.net.URI -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import com.hierynomus.gradle.license.tasks.LicenseCheck -import com.hierynomus.gradle.license.tasks.LicenseFormat - -plugins { - kotlin("jvm") - kotlin("plugin.noarg") - kotlin("plugin.serialization") - id("com.github.hierynomus.license") - application - id("edu.sc.seis.launch4j") version "3.0.6" -} - -group = "com.epam.drill.agent" -version = rootProject.version - -val kotlinxCollectionsVersion: String by parent!!.extra -val kotlinxCoroutinesVersion: String by parent!!.extra -val kotlinxSerializationVersion: String by parent!!.extra -val kotlinxCliVersion: String by parent!!.extra -val jacocoVersion: String by parent!!.extra -val bcelVersion: String by parent!!.extra -val microutilsLoggingVersion: String by parent!!.extra -val logbackVersion: String by parent!!.extra - -repositories { - mavenCentral() -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -dependencies { - - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:$kotlinxCollectionsVersion") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinxSerializationVersion") - implementation("org.jetbrains.kotlinx:kotlinx-cli:$kotlinxCliVersion") - implementation("org.jacoco:org.jacoco.core:$jacocoVersion") - implementation("org.apache.bcel:bcel:$bcelVersion") - implementation("io.github.microutils:kotlin-logging-jvm:$microutilsLoggingVersion") - - implementation(project(":test2code")) - implementation(project(":common")) - implementation(project(":test2code-common")) - implementation(project(":test2code-jacoco")) - implementation(project(":konform")) - implementation(project(":agent-transport")) - - testImplementation(kotlin("test-junit")) -} - -kotlin.sourceSets.all { - languageSettings.optIn("kotlin.Experimental") - languageSettings.optIn("kotlin.ExperimentalStdlibApi") - languageSettings.optIn("kotlin.time.ExperimentalTime") - languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") - languageSettings.optIn("kotlinx.coroutines.FlowPreview") - languageSettings.optIn("kotlinx.coroutines.InternalCoroutinesApi") - languageSettings.optIn("kotlinx.coroutines.ObsoleteCoroutinesApi") - languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") -} - -@Suppress("UNUSED_VARIABLE") -tasks { - withType { - kotlinOptions.jvmTarget = "1.8" - } -} - -noArg { - annotation("kotlinx.serialization.Serializable") -} - -@Suppress("UNUSED_VARIABLE") -license { - headerURI = URI("https://raw.githubusercontent.com/Drill4J/drill4j/develop/COPYRIGHT") - val licenseFormatSources by tasks.registering(LicenseFormat::class) { - source = fileTree("$projectDir/src").also { - include("**/*.kt", "**/*.java", "**/*.groovy") - } - } - val licenseCheckSources by tasks.registering(LicenseCheck::class) { - source = fileTree("$projectDir/src").also { - include("**/*.kt", "**/*.java", "**/*.groovy") - } - } -} - -application { - mainClass.set("com.epam.drill.agent.archive.scanner.AppArchiveScannerCliKt") -} - -tasks.named("jar") { - archiveClassifier.set("fat") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - manifest { - attributes["Main-Class"] = application.mainClass.get() - } - from(sourceSets.main.get().output) - dependsOn(configurations.runtimeClasspath) - from({ - configurations.runtimeClasspath.get() - .filter { it.name.endsWith("jar") } - .map { zipTree(it) } - }) -} - -launch4j { - mainClassName.set(application.mainClass.get()) - bundledJrePath.set(null as String?) -} -tasks.register("packageZipWindows") { - dependsOn("createExe") - from(layout.buildDirectory.dir("launch4j")) - destinationDirectory.set(layout.buildDirectory.dir("dist")) - archiveFileName.set("appArchiveScanner-mingwX64-${rootProject.version}.zip") -} - -val jpackageImageDir = layout.buildDirectory.dir("jpackageImage") - -tasks.register("buildLinuxExecutable") { - val fatJar = tasks.named("jar").flatMap { it.archiveFile } - dependsOn(fatJar) - - doFirst { - jpackageImageDir.get().asFile.deleteRecursively() - } - - commandLine = listOf( - "jpackage", - "--type", "app-image", - "--input", fatJar.get().asFile.parent, - "--main-jar", fatJar.get().asFile.name, - "--main-class", application.mainClass.get(), - "--name", "appArchiveScanner", - "--dest", jpackageImageDir.get().asFile.absolutePath - ) -} - -tasks.register("packageZipLinux") { - dependsOn("buildLinuxExecutable") - val appDir = jpackageImageDir.map { it.dir("appArchiveScanner") } - from(appDir) - destinationDirectory.set(layout.buildDirectory.dir("dist")) - archiveFileName.set("appArchiveScanner-linuxX64-${project.version}.zip") -} diff --git a/app-archive-scanner/src/main/kotlin/AppArchiveScannerCli.kt b/app-archive-scanner/src/main/kotlin/AppArchiveScannerCli.kt deleted file mode 100644 index 6cbfc028..00000000 --- a/app-archive-scanner/src/main/kotlin/AppArchiveScannerCli.kt +++ /dev/null @@ -1,213 +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.archive.scanner - -import com.epam.drill.agent.common.configuration.AgentMetadata -import com.epam.drill.agent.test2code.classloading.ClassLoadersScanner -import com.epam.drill.agent.test2code.classparsing.parseAstClass -import com.epam.drill.agent.test2code.common.api.AstMethod -import com.epam.drill.agent.test2code.common.transport.ClassMetadata -import com.epam.drill.agent.common.transport.AgentMessageDestination -import com.epam.drill.agent.transport.http.HttpAgentMessageTransport -import kotlinx.serialization.json.Json -import kotlinx.cli.* -import java.util.UUID - -fun main(args: Array) { - val parser = ArgParser("Drill4J JVM App Archive Scanner") - - val scanPaths by parser.option( - ArgType.String, - fullName = "scanClassPath", - description = "Path to JAR/WAR/EAR to scan" - ).required() - - val packagePrefixesStr by parser.option( - ArgType.String, - fullName = "packagePrefixes", - description = """ - Packages starting with matching string will be scanned. - It's usually set to the topmost common package of your application. - - Syntax: - 1. Parts of package names are separated with forward slashes / (and not dots .) - 2. Multiple packages can be specified using ; delimiter - 3. To exclude a package use ! before package name. - - Examples: - my/org/some/cool/app; - my/org/some/cool/app;!my/org/some/cool/app/dto - my/org/some/cool/app;my/org/some/dependency - - Documentation: - https://drill4j.github.io/docs/agents/java-agent/#how-to-set-package-prefixes - """.trimIndent() - ).required() - - val apiUrl by parser.option( - ArgType.String, - fullName = "apiUrl", - description = "URL to Drill4J Admin /api endpoint. Example: http://localhost/api" - ).required() - - val groupId by parser.option( - ArgType.String, - fullName = "groupId", - description = "Unique arbitrary string identifying your application group. Example: my-cool-app" - ).required() - - val appId by parser.option( - ArgType.String, - fullName = "appId", - description = "Unique arbitrary string identifying your application. Example: api-service" - ).required() - - val buildVersion by parser.option( - ArgType.String, - fullName = "buildVersion", - description = "Build version of your application. Typically set to version tag. Example: v1.2.3" - ) - - val commitSha by parser.option( - ArgType.String, - fullName = "commitSha", - description = "Full SHA hash of commit from which your application .jar is built. Example: 8d87b0c2379a925f2f5f4d85c731c8e77d9f2b3c" - ) - - val envId by parser.option( - ArgType.String, - fullName = "envId", - description = "Environment identifier in which the application is running. Example: develop" - ) - - val apiKeyFromCmd by parser.option( - ArgType.String, - fullName = "apiKey", - description = "Drill4J API key. It is recommended to set it with DRILL_API_KEY env variable, rather than using command line argument" - ) - - val verbose by parser.option( - ArgType.Boolean, - fullName = "verbose", - description = "Enable verbose output" - ).default(false) - - parser.parse(args) - - if (verbose) println("Verbose mode enabled") - - val packagePrefixes = packagePrefixesStr.split(";") - - if (!apiKeyFromCmd.isNullOrBlank()) { - println("Using API key from command line argument. It is recommended to set it with DRILL_API_KEY env variable instead") - } - - val apiKey = apiKeyFromCmd?.takeIf { !it.isNullOrBlank() } ?: - System.getProperty("DRILL_API_KEY")?.takeIf { !it.isNullOrBlank() } - - if (apiKey.isNullOrBlank()) { - throw IllegalStateException("DRILL_API_KEY environment variable is either not set or set to a blank value") - } - println(""" - scanPaths=${listOf(scanPaths)}, - packagePrefixes=$packagePrefixes, - verbose=$verbose, - apiUrl=$apiUrl, - groupId=$groupId, - appId=$appId, - buildVersion=$buildVersion, - commitSha=$commitSha, - envId=$envId - """.trimIndent()) - - run( - scanPaths = listOf(scanPaths), - packagePrefixes = packagePrefixes, - verbose = verbose, - apiUrl = apiUrl, - apiKey = apiKey, - groupId = groupId, - appId = appId, - buildVersion = buildVersion, - commitSha = commitSha, - envId = envId - ) -} - -fun run( - scanPaths: List, - packagePrefixes: List, - verbose: Boolean = false, - apiUrl: String, - apiKey: String, - groupId: String, - appId: String, - buildVersion: String?, - commitSha: String?, - envId: String? -) { - val methods = mutableListOf() - var classCount = ClassLoadersScanner( - packagePrefixes, - 50, - scanPaths, - false - ) { classes -> - if (verbose) { - classes.forEach { println("Scanning ${it.entityName()}") } - } - methods += classes.flatMap { parseAstClass(it.entityName(), it.bytes()) } - }.scanClasses() - - println("Scan complete. Scanned $classCount classes; ${methods.count()} methods") - - val instanceId = UUID.randomUUID().toString() - - val message = ClassMetadata( - groupId = groupId, - appId = appId, - commitSha = commitSha, - buildVersion = buildVersion, - instanceId = instanceId, - methods = methods - ) - - val agentMetadata = AgentMetadata( - groupId = groupId, - appId = appId, - buildVersion = buildVersion, - commitSha = commitSha, - envId = envId, - instanceId = instanceId, - packagesPrefixes = packagePrefixes - ) - - val transport = HttpAgentMessageTransport(apiUrl, apiKey) - - // TODO support passing sslTruststore & sslTruststorePass - transport.send( - AgentMessageDestination("PUT", "/api/data-ingest/instances"), - Json.encodeToString(AgentMetadata.serializer(), agentMetadata).toByteArray(), - "application/json" - ) - - transport.send( - AgentMessageDestination("PUT", "/api/data-ingest/methods"), - Json.encodeToString(ClassMetadata.serializer(), message).toByteArray(), - "application/json" - ) - println("done!") -} diff --git a/gradle.properties b/gradle.properties index ab7ebb88..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/max-retries-EPMDJ-10975 +sharedLibsRef = main sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false diff --git a/settings.gradle.kts b/settings.gradle.kts index 529b03fb..8d189ccf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,4 +50,3 @@ includeSharedLib("test2code-common") include("test2code-jacoco") include("test2code") include("java-agent") -include("app-archive-scanner") From 2c4e8aa40d29063958bf287a556cede0e9677f8c Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 23 Jul 2025 15:16:49 +0200 Subject: [PATCH 11/14] build: update sharedLibsRef to fix/simple-agent-message-sender --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fa6d10fb..b3f6070d 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 = fix/simple-agent-message-sender sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false From cb558bc2ea979a658101ab2700b75c8662da5749 Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 13 Aug 2025 10:26:32 +0200 Subject: [PATCH 12/14] build: update sharedLibsRef to feature/refactor-configuration-system --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index b3f6070d..c12fd6e0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ apacheHttpClientVersion = 5.2.3 aesyDatasizeVersion = 1.0.0 bytebuddyVersion = 1.14.11 -sharedLibsRef = fix/simple-agent-message-sender +sharedLibsRef = feature/refactor-configuration-system sharedLibsLocalPath = lib-jvm-shared nativeAgentLibName = drill-agent nativeAgentHookEnabled = false From 7d4a009a5244d86086cd50b3bd6f02109bc8d76e Mon Sep 17 00:00:00 2001 From: iryabov Date: Wed, 13 Aug 2025 10:27:24 +0200 Subject: [PATCH 13/14] build: update sharedLibsRef to main --- 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 From 929310b233b42f765c570413da4c94faea769e8a Mon Sep 17 00:00:00 2001 From: iryabov Date: Thu, 14 Aug 2025 15:00:31 +0200 Subject: [PATCH 14/14] 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