@@ -19,6 +19,16 @@ package org.springframework.cloud.contract.verifier.util
19
19
import java.lang.reflect.Method
20
20
21
21
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
22
32
import javax.ws.rs.client.Entity
23
33
import javax.ws.rs.client.WebTarget
24
34
import javax.ws.rs.core.Response
@@ -35,7 +45,6 @@ import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
35
45
import org.codehaus.groovy.control.customizers.ImportCustomizer
36
46
import org.junit.Rule
37
47
import org.junit.Test
38
- import org.mdkt.compiler.InMemoryJavaCompiler
39
48
import org.w3c.dom.Document
40
49
import org.xml.sax.InputSource
41
50
@@ -46,6 +55,7 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
46
55
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
47
56
import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil
48
57
import org.springframework.util.ReflectionUtils
58
+
49
59
/**
50
60
* checking the syntax of produced scripts
51
61
*/
@@ -110,8 +120,7 @@ private void test(String test) {
110
120
try {
111
121
if (builderName. toLowerCase(). contains(" spock" )) {
112
122
tryToCompileGroovy(builderName, test)
113
- }
114
- else {
123
+ } else {
115
124
tryToCompileJava(builderName, test)
116
125
}
117
126
} catch (Throwable t) {
@@ -125,8 +134,7 @@ private void test(String test) {
125
134
if (builderName. toLowerCase(). contains(" spock" )) {
126
135
Script script = tryToCompileGroovy(builderName, test)
127
136
script. invokeMethod(" validate_method()" , null )
128
- }
129
- else {
137
+ } else {
130
138
Class clazz = tryToCompileJava(builderName, test)
131
139
Method method = ReflectionUtils . findMethod(clazz, " validate_method" )
132
140
method. invoke(clazz. newInstance())
@@ -141,8 +149,7 @@ private void test(String test) {
141
149
static void tryToCompileWithoutCompileStatic (String builderName , String test ) {
142
150
if (builderName. toLowerCase(). contains(" spock" )) {
143
151
tryToCompileGroovy(builderName, test, false )
144
- }
145
- else {
152
+ } else {
146
153
tryToCompileJava(builderName, test)
147
154
}
148
155
}
@@ -162,7 +169,7 @@ private void test(String test) {
162
169
163
170
private static String updatedTest (String test , String className ) {
164
171
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;" )
166
173
}
167
174
168
175
private static GString getStaticImports (String builderName ) {
@@ -176,15 +183,43 @@ private void test(String test) {
176
183
String className = className(test)
177
184
String fqnClassName = " com.example.${ className} "
178
185
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;" )
180
187
return compileJava(fqnClassName, test)
181
188
182
189
}
183
190
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(" \n Line " ). 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
+ }
188
223
}
189
224
190
225
private static String className (String test ) {
@@ -210,4 +245,81 @@ private void test(String test) {
210
245
return true
211
246
}
212
247
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
+ }
213
325
}
0 commit comments