diff --git a/gradle.properties b/gradle.properties index e5c43d45..fa6d10fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,7 @@ publishVersion = 1.1.0 kotlinxCollectionsVersion = 0.3.5 kotlinxCoroutinesVersion = 1.5.2-native-mt kotlinxSerializationVersion = 1.3.1 +kotlinxCliVersion = 0.3.6 atomicfuVersion = 0.23.0 grgitVersion = 4.1.1 ktorVersion = 1.6.8 @@ -20,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/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/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/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..cf085a58 --- /dev/null +++ b/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/AppArchiveScannerCli.kt @@ -0,0 +1,118 @@ +/** + * Copyright 2020 - 2022 EPAM Systems + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.epam.drill.agent + +import com.epam.drill.agent.common.configuration.AgentConfiguration +import com.epam.drill.agent.common.configuration.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.AgentParameterValidationError +import com.epam.drill.agent.configuration.AgentParametersValidator +import com.epam.drill.agent.configuration.DefaultAgentConfiguration +import com.epam.drill.agent.configuration.DefaultParameterDefinitions +import com.epam.drill.agent.configuration.ParameterDefinitions +import com.epam.drill.agent.logging.LoggingConfiguration +import com.epam.drill.agent.test2code.Test2Code +import com.epam.drill.agent.test2code.configuration.Test2CodeParameterDefinitions +import com.epam.drill.agent.transport.HttpAgentMessageDestinationMapper +import com.epam.drill.agent.transport.JsonAgentMessageSerializer +import com.epam.drill.agent.transport.SimpleAgentMessageSender +import com.epam.drill.agent.transport.http.HttpAgentMessageTransport +import java.io.File +import kotlin.system.exitProcess +import kotlin.takeIf + +fun main(args: Array) { + LoggingConfiguration.readDefaultConfiguration() + + val argsMap: Map = args + .filter { it.startsWith("--") && it.contains("=") } + .associate { + val (key, value) = it.removePrefix("--").split("=", limit = 2) + key to value + }.filter { it.value.isNotEmpty() } + val envMap = System.getenv() + .filterKeys { it.startsWith("DRILL_") } + .filterValues { !it.isNullOrEmpty() } + .mapKeys { toParameterName(it) } + val configuration = DefaultAgentConfiguration(envMap + argsMap) + val definitions = collectAgentParameterDefinitions( + DefaultParameterDefinitions, + ParameterDefinitions, + Test2CodeParameterDefinitions + ) + val validator = AgentParametersValidator(configuration.parameters) + try { + validator.validate(*definitions.toTypedArray()) } + catch (e: AgentParameterValidationError) { + println(e.message) + exitProcess(1) + } + + val commitSha = configuration.parameters[DefaultParameterDefinitions.COMMIT_SHA] + val buildVersion = configuration.parameters[DefaultParameterDefinitions.BUILD_VERSION] + if (commitSha == null && buildVersion == null) { + throw IllegalArgumentException("Either commitSha or buildVersion must be provided") + } + + configuration.parameters[ParameterDefinitions.LOG_LEVEL] + .let(LoggingConfiguration::setLoggingLevels) + configuration.parameters[ParameterDefinitions.LOG_FILE] + ?.let(LoggingConfiguration::setLoggingFilename) + configuration.parameters[ParameterDefinitions.LOG_LIMIT].let(LoggingConfiguration::setLogMessageLimit) + + val transport = HttpAgentMessageTransport( + serverAddress = configuration.parameters[ParameterDefinitions.API_URL], + apiKey = configuration.parameters[ParameterDefinitions.API_KEY] ?: "", + sslTruststore = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE] + ?.let { resolvePath(configuration, it) } ?: "", + sslTruststorePass = configuration.parameters[ParameterDefinitions.SSL_TRUSTSTORE_PASSWORD] ?: "", + gzipCompression = configuration.parameters[ParameterDefinitions.USE_GZIP_COMPRESSION], + ) + val serializer = JsonAgentMessageSerializer() + val mapper = HttpAgentMessageDestinationMapper() + val sender = SimpleAgentMessageSender(transport, serializer, mapper) + val test2Code = Test2Code( + id = "test2Code", + agentContext = RequestAgentContext, + sender = sender, + configuration = configuration + ) + sender.send(AgentMessageDestination("PUT", "builds"), configuration.agentMetadata) + test2Code.scanAndSendMetadataClasses() +} + +//TODO: duplicate with JvmModuleMessageSender +private fun resolvePath(configuration: AgentConfiguration, path: String) = File(path).run { + val installationDir = File(configuration.parameters[DefaultParameterDefinitions.INSTALLATION_DIR] ?: "") + val resolved = this.takeIf(File::exists) + ?: this.takeUnless(File::isAbsolute)?.let(installationDir::resolve) + resolved?.takeUnless(File::isDirectory)?.absolutePath ?: path +} + +//TODO: duplicate from ValidatedParametersProvider +internal fun toParameterName(entry: Map.Entry) = entry.key + .removePrefix("DRILL_") + .lowercase() + .split("_") + .joinToString("") { it.replaceFirstChar(Char::uppercase) } + .replaceFirstChar(Char::lowercase) + +private fun collectAgentParameterDefinitions(vararg collections: AgentParameterDefinitionCollection): List> { + return collections.flatMap { it.getAll() } + +} \ No newline at end of file diff --git a/java-agent/src/jvmMain/kotlin/com/epam/drill/agent/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/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt b/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt deleted file mode 100644 index 3ee0e39d..00000000 --- a/java-agent/src/nativeMain/kotlin/com/epam/drill/agent/configuration/ValidationBuilder.kt +++ /dev/null @@ -1,43 +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 platform.posix.F_OK -import platform.posix.access -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.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()) } - -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/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 d543f24c..bc19e215 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" @@ -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 { @@ -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() } @@ -91,7 +93,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,15 +104,15 @@ 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) } 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) } @@ -128,7 +130,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 -> @@ -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() } 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..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 @@ -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, + testId = it.testId, + testSessionId = it.sessionId, + 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()