Skip to content

Commit 5eb0c6a

Browse files
committed
Stop using Spring Boot for pure Spring applications
1 parent a1ed189 commit 5eb0c6a

File tree

9 files changed

+168
-178
lines changed

9 files changed

+168
-178
lines changed
Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
21
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
32
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
43

@@ -19,62 +18,33 @@ java {
1918
targetCompatibility = JavaVersion.VERSION_1_8
2019
}
2120

22-
val withoutSpringConfiguration by configurations.creating {}
23-
val withSpringConfiguration by configurations.creating {
24-
extendsFrom(withoutSpringConfiguration)
25-
}
26-
configurations.implementation.get().extendsFrom(withSpringConfiguration)
21+
val shadowJarConfiguration: Configuration by configurations.creating {}
22+
configurations.implementation.get().extendsFrom(shadowJarConfiguration)
2723

2824
dependencies {
2925
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot
30-
withSpringConfiguration("org.springframework.boot:spring-boot:$springBootVersion")
31-
26+
implementation("org.springframework.boot:spring-boot:$springBootVersion")
3227
implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
3328

3429
fun ModuleDependency.excludeSlf4jApi() = exclude(group = "org.slf4j", module = "slf4j-api")
3530

36-
withoutSpringConfiguration(project(":utbot-rd")) { excludeSlf4jApi() }
37-
withoutSpringConfiguration(project(":utbot-core")) { excludeSlf4jApi() }
38-
withoutSpringConfiguration(project(":utbot-framework-api")) { excludeSlf4jApi() }
39-
withoutSpringConfiguration("com.jetbrains.rd:rd-framework:$rdVersion") { excludeSlf4jApi() }
40-
withoutSpringConfiguration("com.jetbrains.rd:rd-core:$rdVersion") { excludeSlf4jApi() }
41-
withoutSpringConfiguration("commons-logging:commons-logging:$commonsLoggingVersion") { excludeSlf4jApi() }
42-
withoutSpringConfiguration("commons-io:commons-io:$commonsIOVersion") { excludeSlf4jApi() }
31+
// TODO stop putting dependencies that are only used in SpringAnalyzerProcess into shadow jar
32+
shadowJarConfiguration(project(":utbot-rd")) { excludeSlf4jApi() }
33+
shadowJarConfiguration(project(":utbot-core")) { excludeSlf4jApi() }
34+
shadowJarConfiguration(project(":utbot-framework-api")) { excludeSlf4jApi() }
35+
shadowJarConfiguration("com.jetbrains.rd:rd-framework:$rdVersion") { excludeSlf4jApi() }
36+
shadowJarConfiguration("com.jetbrains.rd:rd-core:$rdVersion") { excludeSlf4jApi() }
37+
shadowJarConfiguration("commons-logging:commons-logging:$commonsLoggingVersion") { excludeSlf4jApi() }
38+
shadowJarConfiguration("commons-io:commons-io:$commonsIOVersion") { excludeSlf4jApi() }
4339
}
4440

4541
application {
4642
mainClass.set("org.utbot.spring.process.SpringAnalyzerProcessMainKt")
4743
}
4844

49-
val shadowWithoutSpring by tasks.register<ShadowJar>("shadowJarWithoutSpring") {
50-
configureShadowJar(withoutSpringConfiguration)
51-
archiveFileName.set("utbot-spring-analyzer-shadow.jar")
52-
}
45+
tasks.shadowJar {
46+
configurations = listOf(shadowJarConfiguration)
5347

54-
val shadowWithSpring by tasks.register<ShadowJar>("shadowJarWithSpring") {
55-
configureShadowJar(withSpringConfiguration)
56-
archiveFileName.set("utbot-spring-analyzer-with-spring-shadow.jar")
57-
}
58-
59-
val springAnalyzerJar: Configuration by configurations.creating {
60-
isCanBeResolved = false
61-
isCanBeConsumed = true
62-
}
63-
64-
artifacts {
65-
add(springAnalyzerJar.name, shadowWithoutSpring)
66-
add(springAnalyzerJar.name, shadowWithSpring)
67-
}
68-
69-
fun ShadowJar.configureShadowJar(configuration: Configuration) {
70-
// see more details -- https://github.com/johnrengelman/shadow/blob/master/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy
71-
group = "shadow"
72-
from(sourceSets.main.get().output)
73-
exclude("META-INF/INDEX.LIST", "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "module-info.class")
74-
75-
configurations = listOf(configuration)
76-
77-
// see more details -- https://github.com/spring-projects/spring-boot/issues/1828
7848
isZip64 = true
7949
// Required for Spring
8050
mergeServiceFiles()
@@ -87,4 +57,14 @@ fun ShadowJar.configureShadowJar(configuration: Configuration) {
8757
})
8858

8959
transform(Log4j2PluginsCacheFileTransformer::class.java)
60+
archiveFileName.set("utbot-spring-analyzer-shadow.jar")
61+
}
62+
63+
val springAnalyzerJar: Configuration by configurations.creating {
64+
isCanBeResolved = false
65+
isCanBeConsumed = true
66+
}
67+
68+
artifacts {
69+
add(springAnalyzerJar.name, tasks.shadowJar)
9070
}
Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package org.utbot.spring.analyzers
22

3+
import com.jetbrains.rd.util.error
34
import com.jetbrains.rd.util.getLogger
45
import com.jetbrains.rd.util.info
5-
import org.springframework.boot.builder.SpringApplicationBuilder
6+
import org.springframework.boot.SpringApplication
7+
import org.springframework.boot.SpringBootVersion
68
import org.springframework.context.ApplicationContextException
7-
import org.utbot.spring.configurators.ApplicationConfigurator
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
813
import org.utbot.spring.api.ApplicationData
14+
import org.utbot.spring.exception.UtBotSpringShutdownException
915
import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
1016

1117
val logger = getLogger<SpringApplicationAnalyzer>()
@@ -14,21 +20,58 @@ class SpringApplicationAnalyzer(private val applicationData: ApplicationData) {
1420

1521
fun analyze(): List<String> {
1622
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+
}
1746

18-
val applicationBuilder = SpringApplicationBuilder(SpringApplicationAnalyzer::class.java)
19-
val applicationConfigurator = ApplicationConfigurator(applicationBuilder, applicationData)
47+
private fun analyzePureSpringApplication(sources: Array<Class<*>>) {
48+
logger.info { "Analyzing pure Spring application" }
49+
runExpectingUtBotSpringShutdownException {
50+
AnnotationConfigApplicationContext(*sources)
51+
}
52+
}
2053

21-
applicationConfigurator.configureApplication()
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+
}
2265

66+
private fun runExpectingUtBotSpringShutdownException(block: () -> Unit) {
2367
try {
24-
applicationBuilder.build()
25-
applicationBuilder.run()
26-
} catch (e: ApplicationContextException) {
27-
// UtBotBeanFactoryPostProcessor destroys bean definitions
28-
// to prevent Spring application from actually starting and
29-
// that causes it to throw ApplicationContextException.
30-
logger.info { "Bean analysis finished successfully" }
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
3175
}
32-
return UtBotBeanFactoryPostProcessor.beanQualifiedNames
3376
}
3477
}

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

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.utbot.spring.exception
2+
3+
/**
4+
* Use this exception to shutdown the application
5+
* when all required analysis actions are completed.
6+
*/
7+
class UtBotSpringShutdownException(message: String): RuntimeException(message)

utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/loggers/RDApacheCommonsLogFactory.kt

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,45 @@ package org.utbot.spring.loggers
22

33
import com.jetbrains.rd.util.LogLevel
44
import com.jetbrains.rd.util.getLogger
5+
import com.jetbrains.rd.util.info
56
import org.apache.commons.logging.Log
67
import org.apache.commons.logging.impl.LogFactoryImpl
8+
import org.utbot.spring.exception.UtBotSpringShutdownException
79

810
@Suppress("unused") // used via -Dorg.apache.commons.logging.LogFactory=org.utbot.spring.loggers.RDApacheCommonsLogFactory
911
class RDApacheCommonsLogFactory : LogFactoryImpl() {
1012
override fun getInstance(name: String): Log {
1113
val logger = getLogger(category = name)
1214
return object : Log {
13-
override fun trace(message: Any?) = logger.log(LogLevel.Trace, message, throwable = null)
14-
override fun trace(message: Any?, t: Throwable?) = logger.log(LogLevel.Trace, message, throwable = t)
15-
override fun debug(message: Any?) = logger.log(LogLevel.Debug, message, throwable = null)
16-
override fun debug(message: Any?, t: Throwable?) = logger.log(LogLevel.Debug, message, throwable = t)
17-
override fun info(message: Any?) = logger.log(LogLevel.Info, message, throwable = null)
18-
override fun info(message: Any?, t: Throwable?) = logger.log(LogLevel.Info, message, throwable = t)
19-
override fun warn(message: Any?) = logger.log(LogLevel.Warn, message, throwable = null)
20-
override fun warn(message: Any?, t: Throwable?) = logger.log(LogLevel.Warn, message, throwable = t)
21-
override fun error(message: Any?) = logger.log(LogLevel.Error, message, throwable = null)
22-
override fun error(message: Any?, t: Throwable?) = logger.log(LogLevel.Error, message, throwable = t)
23-
override fun fatal(message: Any?) = logger.log(LogLevel.Fatal, message, throwable = null)
24-
override fun fatal(message: Any?, t: Throwable?) = logger.log(LogLevel.Fatal, message, throwable = t)
15+
private fun log(level: LogLevel, message: Any?, throwable: Throwable?) {
16+
if (throwable is UtBotSpringShutdownException) {
17+
// avoid polluting logs with stack trace of expected exception
18+
logger.info { message }
19+
logger.info { "${throwable::class.java.name}: ${throwable.message}" }
20+
} else
21+
logger.log(level, message, throwable)
22+
}
23+
private fun isEnabled(level: LogLevel) = logger.isEnabled(level)
2524

26-
override fun isTraceEnabled() = logger.isEnabled(LogLevel.Trace)
27-
override fun isDebugEnabled() = logger.isEnabled(LogLevel.Debug)
28-
override fun isInfoEnabled() = logger.isEnabled(LogLevel.Info)
29-
override fun isErrorEnabled() = logger.isEnabled(LogLevel.Error)
30-
override fun isFatalEnabled() = logger.isEnabled(LogLevel.Fatal)
31-
override fun isWarnEnabled() = logger.isEnabled(LogLevel.Warn)
25+
override fun trace(message: Any?) = log(LogLevel.Trace, message, throwable = null)
26+
override fun trace(message: Any?, t: Throwable?) = log(LogLevel.Trace, message, throwable = t)
27+
override fun debug(message: Any?) = log(LogLevel.Debug, message, throwable = null)
28+
override fun debug(message: Any?, t: Throwable?) = log(LogLevel.Debug, message, throwable = t)
29+
override fun info(message: Any?) = log(LogLevel.Info, message, throwable = null)
30+
override fun info(message: Any?, t: Throwable?) = log(LogLevel.Info, message, throwable = t)
31+
override fun warn(message: Any?) = log(LogLevel.Warn, message, throwable = null)
32+
override fun warn(message: Any?, t: Throwable?) = log(LogLevel.Warn, message, throwable = t)
33+
override fun error(message: Any?) = log(LogLevel.Error, message, throwable = null)
34+
override fun error(message: Any?, t: Throwable?) = log(LogLevel.Error, message, throwable = t)
35+
override fun fatal(message: Any?) = log(LogLevel.Fatal, message, throwable = null)
36+
override fun fatal(message: Any?, t: Throwable?) = log(LogLevel.Fatal, message, throwable = t)
37+
38+
override fun isTraceEnabled() = isEnabled(LogLevel.Trace)
39+
override fun isDebugEnabled() = isEnabled(LogLevel.Debug)
40+
override fun isInfoEnabled() = isEnabled(LogLevel.Info)
41+
override fun isErrorEnabled() = isEnabled(LogLevel.Error)
42+
override fun isFatalEnabled() = isEnabled(LogLevel.Fatal)
43+
override fun isWarnEnabled() = isEnabled(LogLevel.Warn)
3244
}
3345
}
3446
}

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import com.jetbrains.rd.util.getLogger
44
import com.jetbrains.rd.util.info
55
import com.jetbrains.rd.util.warn
66
import org.springframework.beans.factory.BeanCreationException
7-
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
87
import org.springframework.beans.factory.config.BeanFactoryPostProcessor
98
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
10-
import org.springframework.beans.factory.support.BeanDefinitionRegistry
119
import org.springframework.core.PriorityOrdered
10+
import org.utbot.spring.exception.UtBotSpringShutdownException
1211

1312
val logger = getLogger<UtBotBeanFactoryPostProcessor>()
1413

@@ -27,11 +26,9 @@ object UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered
2726
beanQualifiedNames = findBeanClassNames(beanFactory)
2827
logger.info { "Detected Beans: $beanQualifiedNames" }
2928

30-
// After desired post-processing is completed we destroy bean definitions
31-
// to avoid further possible actions with beans that may be unsafe.
32-
destroyBeanDefinitions(beanFactory)
33-
3429
logger.info { "Finished post-processing bean factory in UtBot" }
30+
31+
throw UtBotSpringShutdownException("Finished post-processing bean factory in UtBot")
3532
}
3633

3734
private fun findBeanClassNames(beanFactory: ConfigurableListableBeanFactory): List<String> {
@@ -45,11 +42,4 @@ object UtBotBeanFactoryPostProcessor : BeanFactoryPostProcessor, PriorityOrdered
4542
}.filterNot { it.startsWith("org.utbot.spring") }
4643
.distinct()
4744
}
48-
49-
private fun destroyBeanDefinitions(beanFactory: ConfigurableListableBeanFactory) {
50-
for (beanDefinitionName in beanFactory.beanDefinitionNames) {
51-
val beanRegistry = beanFactory as BeanDefinitionRegistry
52-
beanRegistry.removeBeanDefinition(beanDefinitionName)
53-
}
54-
}
5545
}

0 commit comments

Comments
 (0)