diff --git a/pom.xml b/pom.xml index ffeef667e8..440704f8eb 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,6 @@ 2.12.2 0.8.8 1.6.0 - 1.3.0 1.8.22 1.9.4 diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index ff9ed4d9c7..79559def3e 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -247,12 +247,6 @@ spring-boot-starter-jersey test --> - - org.mdkt.compiler - InMemoryJavaCompiler - ${InMemoryJavaCompiler.version} - test - jetty-ee10-servlet org.eclipse.jetty.ee10 diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy index c14890632e..fabb3242f7 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy @@ -17,7 +17,6 @@ package org.springframework.cloud.contract.verifier.builder import org.junit.Rule -import org.mdkt.compiler.CompilationException import spock.lang.Issue import spock.lang.Shared import spock.lang.Specification @@ -238,7 +237,7 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements try { SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test) } - catch (CompilationException classFormatError) { + catch (Exception classFormatError) { String output = classFormatError.message assert output.contains('cannot find symbol') assert output.contains('assertThatValueIsANumber') diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy index ceb01b0187..f24d45fdaf 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy @@ -19,6 +19,16 @@ package org.springframework.cloud.contract.verifier.util import java.lang.reflect.Method import javax.inject.Inject +import javax.tools.Diagnostic +import javax.tools.DiagnosticCollector +import javax.tools.FileObject +import javax.tools.ForwardingJavaFileManager +import javax.tools.JavaCompiler +import javax.tools.JavaFileManager +import javax.tools.JavaFileObject +import javax.tools.SimpleJavaFileObject +import javax.tools.StandardJavaFileManager +import javax.tools.ToolProvider import javax.ws.rs.client.Entity import javax.ws.rs.client.WebTarget import javax.ws.rs.core.Response @@ -35,7 +45,6 @@ import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer import org.codehaus.groovy.control.customizers.ImportCustomizer import org.junit.Rule import org.junit.Test -import org.mdkt.compiler.InMemoryJavaCompiler import org.w3c.dom.Document import org.xml.sax.InputSource @@ -46,6 +55,7 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil import org.springframework.util.ReflectionUtils + /** * checking the syntax of produced scripts */ @@ -110,8 +120,7 @@ private void test(String test) { try { if (builderName.toLowerCase().contains("spock")) { tryToCompileGroovy(builderName, test) - } - else { + } else { tryToCompileJava(builderName, test) } } catch (Throwable t) { @@ -125,8 +134,7 @@ private void test(String test) { if (builderName.toLowerCase().contains("spock")) { Script script = tryToCompileGroovy(builderName, test) script.invokeMethod("validate_method()", null) - } - else { + } else { Class clazz = tryToCompileJava(builderName, test) Method method = ReflectionUtils.findMethod(clazz, "validate_method") method.invoke(clazz.newInstance()) @@ -141,8 +149,7 @@ private void test(String test) { static void tryToCompileWithoutCompileStatic(String builderName, String test) { if (builderName.toLowerCase().contains("spock")) { tryToCompileGroovy(builderName, test, false) - } - else { + } else { tryToCompileJava(builderName, test) } } @@ -162,7 +169,7 @@ private void test(String test) { private static String updatedTest(String test, String className) { test.replaceAll("class FooTest", "class " + className) - .replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;") + .replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;") } private static GString getStaticImports(String builderName) { @@ -176,15 +183,43 @@ private void test(String test) { String className = className(test) String fqnClassName = "com.example.${className}" test = test.replaceAll("class FooTest", "class " + className) - .replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;") + .replaceAll("import javax.ws.rs.core.Response", "import javax.ws.rs.core.Response; import javax.ws.rs.client.WebTarget;") return compileJava(fqnClassName, test) } - private static Class compileJava(String fqnClassName, String test) { - return InMemoryJavaCompiler.newInstance() - .ignoreWarnings() - .compile(fqnClassName, test) + @CompileStatic + private static Class compileJava(String fqnClassName, String sourceCode) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler() + DiagnosticCollector diagnostics = new DiagnosticCollector<>() + InMemoryClassLoader classLoader = new InMemoryClassLoader() + StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null) + JavaFileManager fileManager = new InMemoryFileManager(standardFileManager, classLoader) + JavaFileObject javaFile = new InMemorySourceFile(fqnClassName, sourceCode) + JavaCompiler.CompilationTask task = compiler.getTask( + new StringWriter(), + fileManager, + diagnostics, + null, + null, + Collections.singletonList(javaFile) + ) + + boolean success = task.call() + if (!success) { + StringBuilder errorMsg = new StringBuilder("Compilation failed:") + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + errorMsg.append("\nLine ").append(diagnostic.getLineNumber()) + .append(": ").append(diagnostic.getMessage(null)) + } + throw new IllegalStateException(errorMsg.toString()) + } + + try { + return classLoader.loadClass(fqnClassName) + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Failed to load compiled class", e) + } } private static String className(String test) { @@ -210,4 +245,81 @@ private void test(String test) { return true } + @CompileStatic + private static class InMemorySourceFile extends SimpleJavaFileObject { + private final String sourceCode + + InMemorySourceFile(String className, String sourceCode) { + super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), + Kind.SOURCE) + this.sourceCode = sourceCode + } + + @Override + CharSequence getCharContent(boolean ignoreEncodingErrors) { + return sourceCode + } + } + + @CompileStatic + private static class InMemoryClassLoader extends ClassLoader { + private final Map classData = new HashMap<>() + + void addClass(String name, byte[] data) { + classData.put(name, data) + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] data = classData.get(name) + if (data == null) { + throw new ClassNotFoundException(name) + } + return defineClass(name, data, 0, data.length) + } + } + + @CompileStatic + private static class InMemoryFileManager extends ForwardingJavaFileManager { + private final InMemoryClassLoader classLoader + + InMemoryFileManager(StandardJavaFileManager fileManager, InMemoryClassLoader classLoader) { + super(fileManager) + this.classLoader = classLoader + } + + @Override + JavaFileObject getJavaFileForOutput(Location location, + String className, + JavaFileObject.Kind kind, + FileObject sibling) { + return new InMemoryClassFile(className, classLoader) + } + } + + @CompileStatic + private static class InMemoryClassFile extends SimpleJavaFileObject { + private final String className + private final InMemoryClassLoader classLoader + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream() + + InMemoryClassFile(String className, InMemoryClassLoader classLoader) { + super(URI.create("byte:///" + className.replace('.', '/') + Kind.CLASS.extension), + Kind.CLASS) + this.className = className + this.classLoader = classLoader + } + + @Override + OutputStream openOutputStream() { + outputStream.reset() + return new FilterOutputStream(outputStream) { + @Override + void close() throws IOException { + super.close() + classLoader.addClass(className, outputStream.toByteArray()) + } + } + } + } }