Skip to content

Commit 450f1c6

Browse files
Introduce utbot-spring-commons-api module #2239 (#2251)
Separate utbot-spring-commons-api logic in a separate module
1 parent 3475da0 commit 450f1c6

File tree

17 files changed

+117
-57
lines changed

17 files changed

+117
-57
lines changed

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,4 @@ if (goIde.split(",").contains(ideType)) {
7272

7373
include("utbot-spring-analyzer")
7474
include("utbot-spring-commons")
75+
include("utbot-spring-commons-api")

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ class SpringCodeGenerator(
6161
logger.info { "Code generation phase started at ${now()}" }
6262
val astConstructor = when (springTestsType) {
6363
SpringTestsType.UNIT_TESTS -> CgSpringUnitTestClassConstructor(context)
64-
SpringTestsType.INTEGRATION_TESTS -> CgSpringIntegrationTestClassConstructor(context)
64+
// TODO replace with CgSpringIntegrationTestClassConstructor
65+
SpringTestsType.INTEGRATION_TESTS -> CgSpringUnitTestClassConstructor(context)
6566
}
6667
val testClassFile = astConstructor.construct(testClassModel)
6768
logger.info { "Code generation phase finished at ${now()}" }

utbot-instrumentation/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ val fetchSpringCommonsJar: Configuration by configurations.creating {
4242
dependencies {
4343
implementation(project(":utbot-framework-api"))
4444
implementation(project(":utbot-rd"))
45-
implementation(project(":utbot-spring-commons"))
45+
implementation(project(":utbot-spring-commons-api"))
46+
4647

4748
implementation("org.ow2.asm:asm:$asmVersion")
4849
implementation("org.ow2.asm:asm-commons:$asmVersion")

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SpringUtExecutionInstrumentation.kt

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import org.utbot.instrumentation.instrumentation.Instrumentation
1414
import org.utbot.instrumentation.instrumentation.execution.mock.SpringInstrumentationContext
1515
import org.utbot.instrumentation.instrumentation.execution.phases.ModelConstructionPhase
1616
import org.utbot.instrumentation.process.HandlerClassesLoader
17+
import org.utbot.spring.api.context.ContextWrapper
18+
import org.utbot.spring.api.instantiator.InstantiationSettings
19+
import org.utbot.spring.api.instantiator.ApplicationInstantiatorFacade
1720
import org.utbot.spring.api.repositoryWrapper.RepositoryInteraction
1821
import java.security.ProtectionDomain
19-
import kotlin.random.Random
2022

2123
/**
2224
* UtExecutionInstrumentation wrapper that is aware of Spring config and initialises Spring context
@@ -25,7 +27,7 @@ class SpringUtExecutionInstrumentation(
2527
private val instrumentation: UtExecutionInstrumentation,
2628
private val springConfig: String
2729
) : Instrumentation<UtConcreteExecutionResult> by instrumentation {
28-
private lateinit var springContext: Any
30+
private lateinit var springContext: ContextWrapper
2931

3032
companion object {
3133
private val logger = getLogger<SpringUtExecutionInstrumentation>()
@@ -52,22 +54,23 @@ class SpringUtExecutionInstrumentation(
5254
val classLoader = utContext.classLoader
5355
Thread.currentThread().contextClassLoader = classLoader
5456

55-
val primarySources = arrayOf(
56-
classLoader.loadClass(springConfig),
57-
classLoader.loadClass("org.utbot.spring.repositoryWrapper.RepositoryWrapperConfiguration")
57+
val instantiationSettings = InstantiationSettings(
58+
configurationClasses = arrayOf(
59+
classLoader.loadClass(springConfig),
60+
classLoader.loadClass("org.utbot.spring.repositoryWrapper.RepositoryWrapperConfiguration")
61+
),
62+
profileExpression = null,
5863
)
5964

60-
// Setting server.port value to 0 means given Spring to select any appropriate port itself.
61-
// See https://stackoverflow.com/questions/21083170/how-to-configure-port-for-a-spring-boot-application
62-
val args = arrayOf("--server.port=0")
63-
64-
// TODO if we don't have SpringBoot just create ApplicationContext here, reuse code from utbot-spring-analyzer
65-
// TODO recreate context/app every time whenever we change method under test
66-
val springAppClass =
67-
classLoader.loadClass("org.springframework.boot.SpringApplication")
68-
springContext = springAppClass
69-
.getMethod("run", primarySources::class.java, args::class.java)
70-
.invoke(null, primarySources, args)
65+
val springFacadeInstance = classLoader
66+
.loadClass("org.utbot.spring.instantiator.SpringApplicationInstantiatorFacade")
67+
.getConstructor()
68+
.newInstance()
69+
springFacadeInstance as ApplicationInstantiatorFacade
70+
71+
// TODO: recreate context/app every time whenever we change method under test
72+
springContext = springFacadeInstance.instantiate(instantiationSettings)
73+
?: error("Failed to initialize Spring context")
7174
}
7275

7376
override fun invoke(
@@ -79,20 +82,11 @@ class SpringUtExecutionInstrumentation(
7982
RepositoryInteraction.recordedInteractions.clear()
8083
// TODO properly detect which beans need to be reset, right now "orderRepository" and "orderService" are hardcoded
8184
val beanNamesToReset = listOf("orderRepository", "orderService")
85+
8286
beanNamesToReset.forEach { beanNameToReset ->
83-
val beanDefToReset = springContext::class.java
84-
.getMethod("getBeanDefinition", String::class.java)
85-
.invoke(springContext, beanNameToReset)
86-
springContext::class.java
87-
.getMethod("removeBeanDefinition", String::class.java)
88-
.invoke(springContext, beanNameToReset)
89-
springContext::class.java
90-
.getMethod(
91-
"registerBeanDefinition",
92-
String::class.java,
93-
utContext.classLoader.loadClass("org.springframework.beans.factory.config.BeanDefinition")
94-
)
95-
.invoke(springContext, beanNameToReset, beanDefToReset)
87+
val beanDefToReset = springContext.getBeanDefinition(beanNameToReset)
88+
springContext.removeBeanDefinition(beanNameToReset)
89+
springContext.registerBeanDefinition(beanNameToReset, beanDefToReset)
9690
}
9791

9892
val jdbcTemplate = getBean("jdbcTemplate")
@@ -124,10 +118,7 @@ class SpringUtExecutionInstrumentation(
124118
}
125119
}
126120

127-
fun getBean(beanName: String): Any =
128-
springContext::class.java
129-
.getMethod("getBean", String::class.java)
130-
.invoke(springContext, beanName)
121+
fun getBean(beanName: String): Any = springContext.getBean(beanName)
131122

132123
fun saveToRepository(repository: Any, entity: Any) {
133124
// ignore repository interactions done during repository fill up

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ internal object HandlerClassesLoader : URLClassLoader(emptyArray()) {
4646
* - we want org.utbot.spring to be loaded by [HandlerClassesLoader] so it can use Spring directly
4747
*/
4848
override fun loadClass(name: String, resolve: Boolean): Class<*> {
49-
// TODO extract `utbot-spring-commons-api` into a separate module to not mess around with class loader
50-
if (name.startsWith("org.slf4j") ||
51-
(name.startsWith("org.utbot.spring") && !name.startsWith("org.utbot.spring.api"))) {
49+
if (name.startsWith("org.slf4j")) {
5250
return (findLoadedClass(name) ?: findClass(name)).apply {
5351
if (resolve) resolveClass(this)
5452
}

utbot-spring-analyzer/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ dependencies {
3131
implementation(project(":utbot-rd")) { excludeSlf4jApi() }
3232
implementation(project(":utbot-core")) { excludeSlf4jApi() }
3333
implementation(project(":utbot-framework-api")) { excludeSlf4jApi() }
34-
implementation(project(":utbot-spring-commons")) { excludeSlf4jApi() }
34+
35+
runtimeOnly(project(":utbot-spring-commons")) { excludeSlf4jApi() }
36+
implementation(project(":utbot-spring-commons-api")) { excludeSlf4jApi() }
37+
3538
implementation("com.jetbrains.rd:rd-framework:$rdVersion") { excludeSlf4jApi() }
3639
implementation("com.jetbrains.rd:rd-core:$rdVersion") { excludeSlf4jApi() }
3740
implementation("commons-logging:commons-logging:$commonsLoggingVersion") { excludeSlf4jApi() }

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzer/SpringApplicationAnalyzer.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package org.utbot.spring.analyzer
22

3-
import org.utbot.spring.context.InstantiationContext
4-
import org.utbot.spring.instantiator.SpringApplicationInstantiatorFacade
3+
import org.utbot.framework.plugin.api.util.utContext
4+
import org.utbot.spring.api.instantiator.InstantiationSettings
55
import org.utbot.spring.api.ApplicationData
6+
import org.utbot.spring.api.instantiator.ApplicationInstantiatorFacade
67
import org.utbot.spring.exception.UtBotSpringShutdownException
78
import org.utbot.spring.generated.BeanDefinitionData
89
import org.utbot.spring.utils.SourceFinder
@@ -11,13 +12,19 @@ class SpringApplicationAnalyzer {
1112

1213
fun getBeanDefinitions(applicationData: ApplicationData): Array<BeanDefinitionData> {
1314
val configurationClasses = SourceFinder(applicationData).findSources()
14-
val instantiationContext = InstantiationContext(
15+
val instantiationSettings = InstantiationSettings(
1516
configurationClasses,
1617
applicationData.profileExpression,
1718
)
1819

20+
val springFacadeInstance = this::class.java.classLoader
21+
.loadClass("org.utbot.spring.instantiator.SpringApplicationInstantiatorFacade")
22+
.getConstructor()
23+
.newInstance()
24+
springFacadeInstance as ApplicationInstantiatorFacade
25+
1926
return UtBotSpringShutdownException
20-
.catch { SpringApplicationInstantiatorFacade(instantiationContext).instantiate() }
27+
.catch { springFacadeInstance.instantiate(instantiationSettings) }
2128
.beanDefinitions
2229
.toTypedArray()
2330
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
id("java")
3+
}
4+
5+
java {
6+
sourceCompatibility = JavaVersion.VERSION_1_8
7+
targetCompatibility = JavaVersion.VERSION_1_8
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.utbot.spring.api.context
2+
3+
interface ContextWrapper {
4+
val context: Any?
5+
6+
fun getBeanDefinition(beanName: String): Any
7+
8+
fun removeBeanDefinition(beanName: String)
9+
10+
fun registerBeanDefinition(beanName: String, beanDefinition: Any)
11+
12+
fun getBean(beanName: String): Any
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.utbot.spring.api.instantiator
2+
3+
import org.utbot.spring.api.context.ContextWrapper
4+
5+
6+
interface ApplicationInstantiatorFacade {
7+
fun instantiate(instantiationSettings: InstantiationSettings): ContextWrapper?
8+
}

0 commit comments

Comments
 (0)