Skip to content

Commit 6a59c72

Browse files
authored
Stop using Spring Boot for pure Spring applications #2156 #2159 (#2172)
1 parent d965a21 commit 6a59c72

14 files changed

+251
-213
lines changed
Lines changed: 24 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,34 @@ 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+
// see more details -- https://github.com/spring-projects/spring-boot/issues/1828
46+
tasks.shadowJar {
47+
configurations = listOf(shadowJarConfiguration)
5348

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
7849
isZip64 = true
7950
// Required for Spring
8051
mergeServiceFiles()
@@ -87,4 +58,14 @@ fun ShadowJar.configureShadowJar(configuration: Configuration) {
8758
})
8859

8960
transform(Log4j2PluginsCacheFileTransformer::class.java)
61+
archiveFileName.set("utbot-spring-analyzer-shadow.jar")
62+
}
63+
64+
val springAnalyzerJar: Configuration by configurations.creating {
65+
isCanBeResolved = false
66+
isCanBeConsumed = true
67+
}
68+
69+
artifacts {
70+
add(springAnalyzerJar.name, tasks.shadowJar)
9071
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.utbot.spring.analyzers
2+
3+
import org.springframework.context.annotation.AnnotationConfigApplicationContext
4+
import org.springframework.core.env.ConfigurableEnvironment
5+
import org.utbot.spring.exception.UtBotSpringShutdownException
6+
7+
class PureSpringApplicationAnalyzer : SpringApplicationAnalyzer {
8+
override fun analyze(sources: Array<Class<*>>, environment: ConfigurableEnvironment): List<String> {
9+
val applicationContext = AnnotationConfigApplicationContext()
10+
applicationContext.register(*sources)
11+
applicationContext.environment = environment
12+
return UtBotSpringShutdownException.catch { applicationContext.refresh() }.beanQualifiedNames
13+
}
14+
15+
override fun canAnalyze() = true
16+
}
Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,8 @@
11
package org.utbot.spring.analyzers
22

3-
import com.jetbrains.rd.util.getLogger
4-
import com.jetbrains.rd.util.info
5-
import org.springframework.boot.builder.SpringApplicationBuilder
6-
import org.springframework.context.ApplicationContextException
7-
import org.utbot.spring.configurators.ApplicationConfigurator
8-
import org.utbot.spring.api.ApplicationData
9-
import org.utbot.spring.postProcessors.UtBotBeanFactoryPostProcessor
3+
import org.springframework.core.env.ConfigurableEnvironment
104

11-
val logger = getLogger<SpringApplicationAnalyzer>()
12-
13-
class SpringApplicationAnalyzer(private val applicationData: ApplicationData) {
14-
15-
fun analyze(): List<String> {
16-
logger.info { "Current Java version is: " + System.getProperty("java.version") }
17-
18-
val applicationBuilder = SpringApplicationBuilder(SpringApplicationAnalyzer::class.java)
19-
val applicationConfigurator = ApplicationConfigurator(applicationBuilder, applicationData)
20-
21-
applicationConfigurator.configureApplication()
22-
23-
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" }
31-
}
32-
return UtBotBeanFactoryPostProcessor.beanQualifiedNames
33-
}
34-
}
5+
interface SpringApplicationAnalyzer {
6+
fun analyze(sources: Array<Class<*>>, environment: ConfigurableEnvironment): List<String>
7+
fun canAnalyze(): Boolean
8+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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.core.SpringVersion
8+
import org.utbot.spring.api.ApplicationData
9+
import org.utbot.spring.utils.EnvironmentFactory
10+
import org.utbot.spring.utils.SourceFinder
11+
12+
private val logger = getLogger<SpringApplicationAnalyzerFacade>()
13+
14+
class SpringApplicationAnalyzerFacade(private val applicationData: ApplicationData) {
15+
16+
fun analyze(): List<String> {
17+
logger.info { "Current Java version is: " + System.getProperty("java.version") }
18+
logger.info { "Current Spring version is: " + runCatching { SpringVersion.getVersion() }.getOrNull() }
19+
logger.info { "Current Spring Boot version is: " + runCatching { SpringBootVersion.getVersion() }.getOrNull() }
20+
21+
val sources = SourceFinder(applicationData).findSources()
22+
val environmentFactory = EnvironmentFactory(applicationData)
23+
24+
for (analyzer in listOf(SpringBootApplicationAnalyzer(), PureSpringApplicationAnalyzer())) {
25+
if (analyzer.canAnalyze()) {
26+
logger.info { "Analyzing with $analyzer" }
27+
try {
28+
return analyzer.analyze(sources, environmentFactory.createEnvironment())
29+
} catch (e: Throwable) {
30+
logger.error("Analyzer $analyzer failed", e)
31+
}
32+
}
33+
}
34+
logger.error { "All Spring analyzers failed, using empty bean list" }
35+
return emptyList()
36+
}
37+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.utbot.spring.analyzers
2+
3+
import org.springframework.boot.builder.SpringApplicationBuilder
4+
import org.springframework.core.env.ConfigurableEnvironment
5+
import org.utbot.spring.exception.UtBotSpringShutdownException
6+
7+
class SpringBootApplicationAnalyzer : SpringApplicationAnalyzer {
8+
override fun analyze(sources: Array<Class<*>>, environment: ConfigurableEnvironment): List<String> {
9+
val app = SpringApplicationBuilder(*sources)
10+
.environment(environment)
11+
.build()
12+
return UtBotSpringShutdownException.catch { app.run() }.beanQualifiedNames
13+
}
14+
15+
override fun canAnalyze(): Boolean = try {
16+
this::class.java.classLoader.loadClass("org.springframework.boot.SpringApplication")
17+
true
18+
} catch (e: ClassNotFoundException) {
19+
false
20+
}
21+
}

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

Lines changed: 0 additions & 67 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.utbot.spring.exception
2+
3+
import com.jetbrains.rd.util.getLogger
4+
import com.jetbrains.rd.util.info
5+
6+
private val logger = getLogger<UtBotSpringShutdownException>()
7+
8+
/**
9+
* Use this exception to shutdown the application
10+
* when all required analysis actions are completed.
11+
*/
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+
// Spring sometimes wraps exceptions in other exceptions, so we go over
23+
// all the causes to determine if UtBotSpringShutdownException was thrown
24+
for(cause in generateSequence(e) { it.cause })
25+
if (cause is UtBotSpringShutdownException) {
26+
logger.info { "UtBotSpringShutdownException has been successfully caught" }
27+
return cause
28+
}
29+
throw e
30+
}
31+
}
32+
}
33+
}

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
}

0 commit comments

Comments
 (0)