Skip to content

Commit 7c6c685

Browse files
committed
Set profiles when analyzing pure Spring application
1 parent 5b0e427 commit 7c6c685

10 files changed

Lines changed: 158 additions & 151 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.utbot.spring.analyzers
2+
3+
import com.jetbrains.rd.util.getLogger
4+
import com.jetbrains.rd.util.info
5+
import org.springframework.context.annotation.AnnotationConfigApplicationContext
6+
import org.utbot.spring.exception.UtBotSpringShutdownException
7+
8+
private val logger = getLogger<PureSpringApplicationAnalyzer>()
9+
10+
class PureSpringApplicationAnalyzer : SpringApplicationAnalyzer {
11+
override fun analyze(analysisContext: SpringApplicationAnalysisContext): List<String> {
12+
logger.info { "Analyzing pure Spring application" }
13+
val applicationContext = AnnotationConfigApplicationContext()
14+
applicationContext.register(*analysisContext.sources)
15+
applicationContext.environment = analysisContext.createEnvironment()
16+
return UtBotSpringShutdownException.catch { applicationContext.refresh() }.beanQualifiedNames
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.utbot.spring.analyzers
2+
3+
import org.springframework.core.env.ConfigurableEnvironment
4+
import org.utbot.spring.utils.EnvironmentFactory
5+
6+
class SpringApplicationAnalysisContext(
7+
val sources: Array<Class<*>>,
8+
private val environmentFactory: EnvironmentFactory
9+
) {
10+
fun createEnvironment(): ConfigurableEnvironment = environmentFactory.createEnvironment()
11+
}
Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,5 @@
11
package org.utbot.spring.analyzers
22

3-
import com.jetbrains.rd.util.error
4-
import com.jetbrains.rd.util.getLogger
5-
import com.jetbrains.rd.util.info
6-
import org.springframework.boot.SpringApplication
7-
import org.springframework.boot.SpringBootVersion
8-
import org.springframework.context.ApplicationContextException
9-
import org.springframework.context.annotation.AnnotationConfigApplicationContext
10-
import org.springframework.core.SpringVersion
11-
import org.utbot.common.silent
12-
import org.utbot.spring.utils.SourceFinder
13-
import org.utbot.spring.api.ApplicationData
14-
import org.utbot.spring.exception.UtBotSpringShutdownException
15-
import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
16-
17-
val logger = getLogger<SpringApplicationAnalyzer>()
18-
19-
class SpringApplicationAnalyzer(private val applicationData: ApplicationData) {
20-
21-
fun analyze(): List<String> {
22-
logger.info { "Current Java version is: " + System.getProperty("java.version") }
23-
logger.info {
24-
"Current Spring version is: " + runCatching { SpringVersion.getVersion() }.getOrElse(logger::error)
25-
}
26-
27-
val isSpringBoot = try {
28-
this::class.java.classLoader.loadClass("org.springframework.boot.SpringApplication")
29-
logger.info {
30-
"Current Spring Boot version is: " + runCatching { SpringBootVersion.getVersion() }.getOrElse(logger::error)
31-
}
32-
true
33-
} catch (e: ClassNotFoundException) {
34-
logger.info { "Spring Boot is not detected" }
35-
false
36-
}
37-
38-
logger.info { "Configuration file is: ${applicationData.configurationFile}" }
39-
val sources = SourceFinder(applicationData).findSources()
40-
41-
if (isSpringBoot) analyzeSpringBootApplication(sources)
42-
else analyzePureSpringApplication(sources)
43-
44-
return UtBotBeanFactoryPostProcessor.beanQualifiedNames
45-
}
46-
47-
private fun analyzePureSpringApplication(sources: Array<Class<*>>) {
48-
logger.info { "Analyzing pure Spring application" }
49-
runExpectingUtBotSpringShutdownException {
50-
AnnotationConfigApplicationContext(*sources)
51-
}
52-
}
53-
54-
private fun analyzeSpringBootApplication(sources: Array<Class<*>>) {
55-
logger.info { "Analyzing Spring Boot application" }
56-
try {
57-
runExpectingUtBotSpringShutdownException {
58-
SpringApplication(*sources).run()
59-
}
60-
} catch (e: Throwable) {
61-
logger.error("Failed to analyze Spring Boot application, falling back to using pure Spring", e)
62-
analyzePureSpringApplication(sources)
63-
}
64-
}
65-
66-
private fun runExpectingUtBotSpringShutdownException(block: () -> Unit) {
67-
try {
68-
block()
69-
throw IllegalStateException("Failed to shutdown Spring application with UtBotSpringShutdownException")
70-
} catch (e: Throwable) {
71-
if (generateSequence(e) { it.cause }.any { it is UtBotSpringShutdownException })
72-
logger.info { "Spring application has been successfully shutdown with UtBotSpringShutdownException" }
73-
else
74-
throw e
75-
}
76-
}
77-
}
3+
interface SpringApplicationAnalyzer {
4+
fun analyze(analysisContext: SpringApplicationAnalysisContext): List<String>
5+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.utbot.spring.analyzers
2+
3+
import com.jetbrains.rd.util.error
4+
import com.jetbrains.rd.util.getLogger
5+
import com.jetbrains.rd.util.info
6+
import org.springframework.boot.SpringBootVersion
7+
import org.springframework.boot.builder.SpringApplicationBuilder
8+
import org.springframework.context.annotation.AnnotationConfigApplicationContext
9+
import org.springframework.core.SpringVersion
10+
import org.utbot.spring.utils.SourceFinder
11+
import org.utbot.spring.api.ApplicationData
12+
import org.utbot.spring.exception.UtBotSpringShutdownException
13+
import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
14+
import org.utbot.spring.utils.EnvironmentFactory
15+
16+
private val logger = getLogger<SpringApplicationAnalyzerFacade>()
17+
18+
class SpringApplicationAnalyzerFacade(private val applicationData: ApplicationData) {
19+
20+
fun analyze(): List<String> {
21+
logger.info { "Current Java version is: " + System.getProperty("java.version") }
22+
logger.info {
23+
"Current Spring version is: " + runCatching { SpringVersion.getVersion() }.getOrElse(logger::error)
24+
}
25+
26+
val analyzer = try {
27+
this::class.java.classLoader.loadClass("org.springframework.boot.SpringApplication")
28+
logger.info {
29+
"Current Spring Boot version is: " + runCatching { SpringBootVersion.getVersion() }.getOrElse(logger::error)
30+
}
31+
SpringBootApplicationAnalyzer()
32+
} catch (e: ClassNotFoundException) {
33+
logger.info { "Spring Boot is not detected" }
34+
PureSpringApplicationAnalyzer()
35+
}
36+
37+
logger.info { "Configuration file is: ${applicationData.configurationFile}" }
38+
39+
return analyzer.analyze(
40+
SpringApplicationAnalysisContext(
41+
sources = SourceFinder(applicationData).findSources(),
42+
environmentFactory = EnvironmentFactory(applicationData)
43+
)
44+
)
45+
}
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.utbot.spring.analyzers
2+
3+
import com.jetbrains.rd.util.error
4+
import com.jetbrains.rd.util.getLogger
5+
import com.jetbrains.rd.util.info
6+
import org.springframework.boot.builder.SpringApplicationBuilder
7+
import org.utbot.spring.exception.UtBotSpringShutdownException
8+
9+
private val logger = getLogger<SpringBootApplicationAnalyzer>()
10+
11+
class SpringBootApplicationAnalyzer : SpringApplicationAnalyzer {
12+
override fun analyze(analysisContext: SpringApplicationAnalysisContext): List<String> {
13+
logger.info { "Analyzing Spring Boot application" }
14+
return try {
15+
val app = SpringApplicationBuilder(*analysisContext.sources)
16+
.environment(analysisContext.createEnvironment())
17+
.build()
18+
UtBotSpringShutdownException.catch { app.run() }.beanQualifiedNames
19+
} catch (e: Throwable) {
20+
logger.error("Failed to analyze Spring Boot application, falling back to using pure Spring", e)
21+
PureSpringApplicationAnalyzer().analyze(analysisContext)
22+
}
23+
}
24+
}

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/configurators/ApplicationConfigurator.kt

Lines changed: 0 additions & 67 deletions
This file was deleted.
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
11
package org.utbot.spring.exception
22

3+
import com.jetbrains.rd.util.getLogger
4+
import com.jetbrains.rd.util.info
5+
6+
private val logger = getLogger<UtBotSpringShutdownException>()
7+
38
/**
49
* Use this exception to shutdown the application
510
* when all required analysis actions are completed.
611
*/
7-
class UtBotSpringShutdownException(message: String): RuntimeException(message)
12+
class UtBotSpringShutdownException(
13+
message: String,
14+
val beanQualifiedNames: List<String>
15+
): RuntimeException(message) {
16+
companion object {
17+
fun catch(block: () -> Unit): UtBotSpringShutdownException {
18+
try {
19+
block()
20+
throw IllegalStateException("UtBotSpringShutdownException has not been thrown")
21+
} catch (e: Throwable) {
22+
for(cause in generateSequence(e) { it.cause })
23+
if (cause is UtBotSpringShutdownException) {
24+
logger.info { "UtBotSpringShutdownException has been successfully caught" }
25+
return cause
26+
}
27+
throw e
28+
}
29+
}
30+
}
31+
}

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/postProcessors/UtBotBeanFactoryPostProcessor.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import org.utbot.spring.exception.UtBotSpringShutdownException
1212
val logger = getLogger<UtBotBeanFactoryPostProcessor>()
1313

1414
object UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered {
15-
var beanQualifiedNames: List<String> = emptyList()
16-
private set
17-
1815
/**
1916
* Sets the priority of post processor to highest to avoid side effects from others.
2017
*/
@@ -23,12 +20,12 @@ object UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered
2320
override fun postProcessBeanFactory(beanFactory: ConfigurableListableBeanFactory) {
2421
logger.info { "Started post-processing bean factory in UtBot" }
2522

26-
beanQualifiedNames = findBeanClassNames(beanFactory)
27-
logger.info { "Detected Beans: $beanQualifiedNames" }
23+
val beanQualifiedNames = findBeanClassNames(beanFactory)
24+
logger.info { "Detected ${beanQualifiedNames.size} bean qualified names" }
2825

2926
logger.info { "Finished post-processing bean factory in UtBot" }
3027

31-
throw UtBotSpringShutdownException("Finished post-processing bean factory in UtBot")
28+
throw UtBotSpringShutdownException("Finished post-processing bean factory in UtBot", beanQualifiedNames)
3229
}
3330

3431
private fun findBeanClassNames(beanFactory: ConfigurableListableBeanFactory): List<String> {

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/process/SpringAnalyzerProcessMain.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import org.utbot.rd.RdSettingsContainerFactory
1313
import org.utbot.rd.generated.loggerModel
1414
import org.utbot.rd.generated.settingsModel
1515
import org.utbot.rd.loggers.UtRdRemoteLoggerFactory
16-
import org.utbot.spring.analyzers.SpringApplicationAnalyzer
16+
import org.utbot.spring.analyzers.SpringApplicationAnalyzerFacade
1717
import org.utbot.spring.api.ApplicationData
1818
import org.utbot.spring.generated.SpringAnalyzerProcessModel
1919
import org.utbot.spring.generated.SpringAnalyzerResult
@@ -47,7 +47,7 @@ private fun SpringAnalyzerProcessModel.setup(watchdog: IdleWatchdog, realProtoco
4747
params.profileExpression,
4848
)
4949
SpringAnalyzerResult(
50-
SpringApplicationAnalyzer(applicationData).analyze().toTypedArray()
50+
SpringApplicationAnalyzerFacade(applicationData).analyze().toTypedArray()
5151
)
5252
}
5353
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.utbot.spring.utils
2+
3+
import org.springframework.core.env.ConfigurableEnvironment
4+
import org.springframework.core.env.StandardEnvironment
5+
import org.utbot.spring.api.ApplicationData
6+
7+
class EnvironmentFactory(
8+
private val applicationData: ApplicationData
9+
) {
10+
companion object {
11+
const val DEFAULT_PROFILE_NAME = "default"
12+
}
13+
14+
fun createEnvironment(): ConfigurableEnvironment {
15+
val profilesToActivate = parseProfileExpression(applicationData.profileExpression ?: DEFAULT_PROFILE_NAME)
16+
17+
val environment = StandardEnvironment()
18+
environment.setActiveProfiles(*profilesToActivate)
19+
20+
return environment
21+
}
22+
23+
//TODO: implement this, e.g. 'prod|web' -> listOf(prod, web)
24+
private fun parseProfileExpression(profileExpression: String) : Array<String> = arrayOf(profileExpression)
25+
26+
}

0 commit comments

Comments
 (0)