@@ -22,6 +22,8 @@ import org.utbot.spring.generated.SpringAnalyzerParams
22
22
import org.utbot.spring.generated.SpringAnalyzerProcessModel
23
23
import org.utbot.spring.generated.springAnalyzerProcessModel
24
24
import java.io.File
25
+ import java.net.URL
26
+ import java.net.URLClassLoader
25
27
import java.nio.file.Files
26
28
27
29
class SpringAnalyzerProcessInstantDeathException :
@@ -30,30 +32,52 @@ class SpringAnalyzerProcessInstantDeathException :
30
32
UtSettings .runSpringAnalyzerProcessWithDebug
31
33
)
32
34
33
- private const val SPRING_ANALYZER_JAR_FILENAME = " utbot-spring-analyzer-shadow.jar"
34
- private const val SPRING_ANALYZER_JAR_PATH = " lib/$SPRING_ANALYZER_JAR_FILENAME "
35
+ private const val SPRING_ANALYZER_WITHOUT_SPRINGBOOT_JAR_FILENAME = " utbot-spring-analyzer-shadow.jar"
36
+ private const val SPRING_ANALYZER_WITH_SPRINGBOOT_JAR_FILENAME = " utbot-spring-analyzer-with-spring-shadow.jar"
37
+ private const val SPRING_ANALYZER_WITHOUT_SPRINBOOT_JAR_PATH = " lib/$SPRING_ANALYZER_WITHOUT_SPRINGBOOT_JAR_FILENAME "
38
+ private const val SPRING_ANALYZER_WITH_SPRINBOOT_JAR_PATH = " lib/$SPRING_ANALYZER_WITH_SPRINGBOOT_JAR_FILENAME "
39
+
35
40
private const val UNKNOWN_MODIFICATION_TIME = 0L
41
+
36
42
private val logger = KotlinLogging .logger {}
37
43
private val rdLogger = UtRdKLogger (logger, " " )
38
44
39
- private val springAnalyzerJarFile = Files .createDirectories(utBotTempDirectory.toFile().resolve(" spring-analyzer" ).toPath())
40
- .toFile().resolve(SPRING_ANALYZER_JAR_FILENAME ).also { jarFile ->
41
- val resource = SpringAnalyzerProcess ::class .java.classLoader.getResource(SPRING_ANALYZER_JAR_PATH )
42
- ? : error(" Unable to find \" $SPRING_ANALYZER_JAR_PATH \" in resources, make sure it's on the classpath" )
43
- val resourceConnection = resource.openConnection()
44
- val lastResourceModification = try {
45
- resourceConnection.lastModified
46
- } finally {
47
- resourceConnection.getInputStream().close()
45
+ private val springAnalyzerDirectory =
46
+ Files .createDirectories(utBotTempDirectory.toFile().resolve(" spring-analyzer" ).toPath()).toFile()
47
+
48
+ private val springAnalyzerWithoutSpringBootJarFile =
49
+ springAnalyzerDirectory
50
+ .resolve(SPRING_ANALYZER_WITHOUT_SPRINGBOOT_JAR_FILENAME ).also { jarFile ->
51
+ val resource = SpringAnalyzerProcess ::class .java.classLoader.getResource(SPRING_ANALYZER_WITHOUT_SPRINBOOT_JAR_PATH )
52
+ ? : error(" Unable to find \" $SPRING_ANALYZER_WITHOUT_SPRINBOOT_JAR_PATH \" in resources, make sure it's on the classpath" )
53
+ updateJarIfRequired(jarFile, resource)
54
+ }
55
+
56
+ private val springAnalyzerWithSpringBootJarFile =
57
+ springAnalyzerDirectory
58
+ .resolve(SPRING_ANALYZER_WITH_SPRINGBOOT_JAR_FILENAME ).also { jarFile ->
59
+ val resource = SpringAnalyzerProcess ::class .java.classLoader.getResource(SPRING_ANALYZER_WITH_SPRINBOOT_JAR_PATH )
60
+ ? : error(" Unable to find \" $SPRING_ANALYZER_WITH_SPRINBOOT_JAR_PATH \" in resources, make sure it's on the classpath" )
61
+ updateJarIfRequired(jarFile, resource)
48
62
}
49
- if (
50
- ! jarFile.exists() ||
51
- jarFile.lastModified() == UNKNOWN_MODIFICATION_TIME ||
52
- lastResourceModification == UNKNOWN_MODIFICATION_TIME ||
53
- jarFile.lastModified() < lastResourceModification
54
- )
55
- FileUtils .copyURLToFile(resource, jarFile)
63
+
64
+
65
+ private fun updateJarIfRequired (jarFile : File , resource : URL ) {
66
+ val resourceConnection = resource.openConnection()
67
+ val lastResourceModification = try {
68
+ resourceConnection.lastModified
69
+ } finally {
70
+ resourceConnection.getInputStream().close()
71
+ }
72
+ if (
73
+ ! jarFile.exists() ||
74
+ jarFile.lastModified() == UNKNOWN_MODIFICATION_TIME ||
75
+ lastResourceModification == UNKNOWN_MODIFICATION_TIME ||
76
+ jarFile.lastModified() < lastResourceModification
77
+ ) {
78
+ FileUtils .copyURLToFile(resource, jarFile)
56
79
}
80
+ }
57
81
58
82
class SpringAnalyzerProcess private constructor(
59
83
rdProcess : ProcessWithRdServer
@@ -71,31 +95,51 @@ class SpringAnalyzerProcess private constructor(
71
95
72
96
fun createBlocking (classpath : List <String >) = runBlocking { SpringAnalyzerProcess (classpath) }
73
97
74
- suspend operator fun invoke (classpathItems : List <String >): SpringAnalyzerProcess = LifetimeDefinition ().terminateOnException { lifetime ->
75
- val extendedClasspath = listOf (springAnalyzerJarFile.path) + classpathItems
76
- val rdProcess = startUtProcessWithRdServer(lifetime) { port ->
77
- // TODO put right version of the Spring Boot jar on the
78
- // classpath if user uses Spring without Spring Boot
79
- classpathArgs = listOf (
80
- " -cp" ,
81
- " \" ${extendedClasspath.joinToString(File .pathSeparator)} \" " ,
82
- " org.utbot.spring.process.SpringAnalyzerProcessMainKt"
83
- )
84
- val cmd = obtainProcessCommandLine(port)
85
- val process = ProcessBuilder (cmd)
86
- .directory(Files .createTempDirectory(utBotTempDirectory, " spring-analyzer" ).toFile())
87
- .start()
88
-
89
- logger.info { " Spring Analyzer process started with PID = ${process.getPid} " }
90
-
91
- if (! process.isAlive) throw SpringAnalyzerProcessInstantDeathException ()
92
-
93
- process
98
+ suspend operator fun invoke (classpathItems : List <String >): SpringAnalyzerProcess =
99
+ LifetimeDefinition ().terminateOnException { lifetime ->
100
+ val requiredSpringAnalyzerJarPath = findRequiredSpringAnalyzerJarPath(classpathItems)
101
+ val extendedClasspath = listOf (requiredSpringAnalyzerJarPath) + classpathItems
102
+
103
+ val rdProcess = startUtProcessWithRdServer(lifetime) { port ->
104
+ classpathArgs = listOf (
105
+ " -cp" ,
106
+ " \" ${extendedClasspath.joinToString(File .pathSeparator)} \" " ,
107
+ " org.utbot.spring.process.SpringAnalyzerProcessMainKt"
108
+ )
109
+ val cmd = obtainProcessCommandLine(port)
110
+ val process = ProcessBuilder (cmd)
111
+ .directory(Files .createTempDirectory(utBotTempDirectory, " spring-analyzer" ).toFile())
112
+ .start()
113
+
114
+ logger.info { " Spring Analyzer process started with PID = ${process.getPid} " }
115
+
116
+ if (! process.isAlive) throw SpringAnalyzerProcessInstantDeathException ()
117
+
118
+ process
119
+ }
120
+ rdProcess.awaitProcessReady()
121
+ val proc = SpringAnalyzerProcess (rdProcess)
122
+ proc.loggerModel.setup(rdLogger, proc.lifetime)
123
+ return proc
124
+ }
125
+
126
+ /* *
127
+ * Finds the required version of `utbot-spring-analyzer`.
128
+ *
129
+ * If user project type is SpringBootApplication, we use his `spring-boot` version.
130
+ * If it is a "pure Spring" project, we have to add dependencies on `spring-boot`
131
+ * to manage to create our internal SpringBootApplication for bean definitions analysis.
132
+ */
133
+ private fun findRequiredSpringAnalyzerJarPath (classpathItems : List <String >): String {
134
+ val testClassLoader = URLClassLoader (classpathItems.map { File (it).toURI().toURL() }.toTypedArray())
135
+ try {
136
+ testClassLoader.loadClass(" org.springframework.boot.builder.SpringApplicationBuilder" )
137
+ } catch (e: ClassNotFoundException ) {
138
+ return springAnalyzerWithSpringBootJarFile.path
94
139
}
95
- rdProcess.awaitProcessReady()
96
- val proc = SpringAnalyzerProcess (rdProcess)
97
- proc.loggerModel.setup(rdLogger, proc.lifetime)
98
- return proc
140
+
141
+ // TODO: think about using different spring-boot versions depending on spring version in user project
142
+ return springAnalyzerWithoutSpringBootJarFile.path
99
143
}
100
144
}
101
145
0 commit comments