@@ -19,6 +19,16 @@ package org.springframework.cloud.contract.verifier.util
1919import java.lang.reflect.Method
2020
2121import 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
2232import javax.ws.rs.client.Entity
2333import javax.ws.rs.client.WebTarget
2434import javax.ws.rs.core.Response
@@ -35,7 +45,6 @@ import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
3545import org.codehaus.groovy.control.customizers.ImportCustomizer
3646import org.junit.Rule
3747import org.junit.Test
38- import org.mdkt.compiler.InMemoryJavaCompiler
3948import org.w3c.dom.Document
4049import org.xml.sax.InputSource
4150
@@ -46,6 +55,7 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
4655import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
4756import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil
4857import 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(" \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+ }
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