Skip to content

Commit 2681213

Browse files
Migrates to jdk's compilation mechanism; fixes gh-2154
1 parent af40ae2 commit 2681213

File tree

4 files changed

+126
-22
lines changed

4 files changed

+126
-22
lines changed

pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
<xerces.version>2.12.2</xerces.version>
6767
<jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
6868
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
69-
<InMemoryJavaCompiler.version>1.3.0</InMemoryJavaCompiler.version>
7069

7170
<contract.kotlin.version>1.8.22</contract.kotlin.version>
7271
<commons-beanutils.version>1.9.4</commons-beanutils.version>

spring-cloud-contract-verifier/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,6 @@
247247
<artifactId>spring-boot-starter-jersey</artifactId>
248248
<scope>test</scope>
249249
</dependency>-->
250-
<dependency>
251-
<groupId>org.mdkt.compiler</groupId>
252-
<artifactId>InMemoryJavaCompiler</artifactId>
253-
<version>${InMemoryJavaCompiler.version}</version>
254-
<scope>test</scope>
255-
</dependency>
256250
<dependency>
257251
<artifactId>jetty-ee10-servlet</artifactId>
258252
<groupId>org.eclipse.jetty.ee10</groupId>

spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package org.springframework.cloud.contract.verifier.builder
1818

1919
import org.junit.Rule
20-
import org.mdkt.compiler.CompilationException
2120
import spock.lang.Issue
2221
import spock.lang.Shared
2322
import spock.lang.Specification
@@ -238,7 +237,7 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
238237
try {
239238
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test)
240239
}
241-
catch (CompilationException classFormatError) {
240+
catch (Exception classFormatError) {
242241
String output = classFormatError.message
243242
assert output.contains('cannot find symbol')
244243
assert output.contains('assertThatValueIsANumber')

spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ package org.springframework.cloud.contract.verifier.util
1919
import java.lang.reflect.Method
2020

2121
import javax.inject.Inject
22+
import javax.tools.Diagnostic
23+
import javax.tools.DiagnosticCollector
24+
import javax.tools.FileObject
25+
import javax.tools.ForwardingJavaFileManager
26+
import javax.tools.JavaCompiler
27+
import javax.tools.JavaFileManager
28+
import javax.tools.JavaFileObject
29+
import javax.tools.SimpleJavaFileObject
30+
import javax.tools.StandardJavaFileManager
31+
import javax.tools.ToolProvider
2232
import javax.ws.rs.client.Entity
2333
import javax.ws.rs.client.WebTarget
2434
import javax.ws.rs.core.Response
@@ -35,7 +45,6 @@ import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
3545
import org.codehaus.groovy.control.customizers.ImportCustomizer
3646
import org.junit.Rule
3747
import org.junit.Test
38-
import org.mdkt.compiler.InMemoryJavaCompiler
3948
import org.w3c.dom.Document
4049
import org.xml.sax.InputSource
4150

@@ -46,6 +55,7 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
4655
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
4756
import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil
4857
import org.springframework.util.ReflectionUtils
58+
4959
/**
5060
* checking the syntax of produced scripts
5161
*/
@@ -110,8 +120,7 @@ private void test(String test) {
110120
try {
111121
if (builderName.toLowerCase().contains("spock")) {
112122
tryToCompileGroovy(builderName, test)
113-
}
114-
else {
123+
} else {
115124
tryToCompileJava(builderName, test)
116125
}
117126
} catch (Throwable t) {
@@ -125,8 +134,7 @@ private void test(String test) {
125134
if (builderName.toLowerCase().contains("spock")) {
126135
Script script = tryToCompileGroovy(builderName, test)
127136
script.invokeMethod("validate_method()", null)
128-
}
129-
else {
137+
} else {
130138
Class clazz = tryToCompileJava(builderName, test)
131139
Method method = ReflectionUtils.findMethod(clazz, "validate_method")
132140
method.invoke(clazz.newInstance())
@@ -141,8 +149,7 @@ private void test(String test) {
141149
static void tryToCompileWithoutCompileStatic(String builderName, String test) {
142150
if (builderName.toLowerCase().contains("spock")) {
143151
tryToCompileGroovy(builderName, test, false)
144-
}
145-
else {
152+
} else {
146153
tryToCompileJava(builderName, test)
147154
}
148155
}
@@ -162,7 +169,7 @@ private void test(String test) {
162169

163170
private static String updatedTest(String test, String className) {
164171
test.replaceAll("class FooTest", "class " + className)
165-
.replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;")
172+
.replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;")
166173
}
167174

168175
private static GString getStaticImports(String builderName) {
@@ -176,15 +183,43 @@ private void test(String test) {
176183
String className = className(test)
177184
String fqnClassName = "com.example.${className}"
178185
test = test.replaceAll("class FooTest", "class " + className)
179-
.replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;")
186+
.replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;")
180187
return compileJava(fqnClassName, test)
181188

182189
}
183190

184-
private static Class<?> compileJava(String fqnClassName, String test) {
185-
return InMemoryJavaCompiler.newInstance()
186-
.ignoreWarnings()
187-
.compile(fqnClassName, test)
191+
@CompileStatic
192+
private static Class<?> compileJava(String fqnClassName, String sourceCode) {
193+
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler()
194+
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>()
195+
InMemoryClassLoader classLoader = new InMemoryClassLoader()
196+
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null)
197+
JavaFileManager fileManager = new InMemoryFileManager(standardFileManager, classLoader)
198+
JavaFileObject javaFile = new InMemorySourceFile(fqnClassName, sourceCode)
199+
JavaCompiler.CompilationTask task = compiler.getTask(
200+
new StringWriter(),
201+
fileManager,
202+
diagnostics,
203+
null,
204+
null,
205+
Collections.singletonList(javaFile)
206+
)
207+
208+
boolean success = task.call()
209+
if (!success) {
210+
StringBuilder errorMsg = new StringBuilder("Compilation failed:")
211+
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
212+
errorMsg.append("\nLine ").append(diagnostic.getLineNumber())
213+
.append(": ").append(diagnostic.getMessage(null))
214+
}
215+
throw new IllegalStateException(errorMsg.toString())
216+
}
217+
218+
try {
219+
return classLoader.loadClass(fqnClassName)
220+
} catch (ClassNotFoundException e) {
221+
throw new IllegalStateException("Failed to load compiled class", e)
222+
}
188223
}
189224

190225
private static String className(String test) {
@@ -210,4 +245,81 @@ private void test(String test) {
210245
return true
211246
}
212247

248+
@CompileStatic
249+
private static class InMemorySourceFile extends SimpleJavaFileObject {
250+
private final String sourceCode
251+
252+
InMemorySourceFile(String className, String sourceCode) {
253+
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension),
254+
Kind.SOURCE)
255+
this.sourceCode = sourceCode
256+
}
257+
258+
@Override
259+
CharSequence getCharContent(boolean ignoreEncodingErrors) {
260+
return sourceCode
261+
}
262+
}
263+
264+
@CompileStatic
265+
private static class InMemoryClassLoader extends ClassLoader {
266+
private final Map<String, byte[]> classData = new HashMap<>()
267+
268+
void addClass(String name, byte[] data) {
269+
classData.put(name, data)
270+
}
271+
272+
@Override
273+
protected Class<?> findClass(String name) throws ClassNotFoundException {
274+
byte[] data = classData.get(name)
275+
if (data == null) {
276+
throw new ClassNotFoundException(name)
277+
}
278+
return defineClass(name, data, 0, data.length)
279+
}
280+
}
281+
282+
@CompileStatic
283+
private static class InMemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
284+
private final InMemoryClassLoader classLoader
285+
286+
InMemoryFileManager(StandardJavaFileManager fileManager, InMemoryClassLoader classLoader) {
287+
super(fileManager)
288+
this.classLoader = classLoader
289+
}
290+
291+
@Override
292+
JavaFileObject getJavaFileForOutput(Location location,
293+
String className,
294+
JavaFileObject.Kind kind,
295+
FileObject sibling) {
296+
return new InMemoryClassFile(className, classLoader)
297+
}
298+
}
299+
300+
@CompileStatic
301+
private static class InMemoryClassFile extends SimpleJavaFileObject {
302+
private final String className
303+
private final InMemoryClassLoader classLoader
304+
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
305+
306+
InMemoryClassFile(String className, InMemoryClassLoader classLoader) {
307+
super(URI.create("byte:///" + className.replace('.', '/') + Kind.CLASS.extension),
308+
Kind.CLASS)
309+
this.className = className
310+
this.classLoader = classLoader
311+
}
312+
313+
@Override
314+
OutputStream openOutputStream() {
315+
outputStream.reset()
316+
return new FilterOutputStream(outputStream) {
317+
@Override
318+
void close() throws IOException {
319+
super.close()
320+
classLoader.addClass(className, outputStream.toByteArray())
321+
}
322+
}
323+
}
324+
}
213325
}

0 commit comments

Comments
 (0)