From 3f0fd7a004fe1d88f8a4348b05bb17e6eae12901 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 21 Nov 2022 18:32:32 +0100 Subject: [PATCH] Fixes java contract runtime compilation; fixes gh-1849 --- docs/pom.xml | 2 +- samples/standalone/dsl/http-server/pom.xml | 43 ++++++++++++++++ .../util/ContractVerifierDslConverter.java | 51 ++++++++++++++++--- 3 files changed, 88 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index cacfde52de..969d2b0d46 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -21,7 +21,7 @@ stubrunner.*|wiremock.*| deploy - 4.0.0 + 4.0.6 diff --git a/samples/standalone/dsl/http-server/pom.xml b/samples/standalone/dsl/http-server/pom.xml index 2f9d221416..d07b9d3d8c 100644 --- a/samples/standalone/dsl/http-server/pom.xml +++ b/samples/standalone/dsl/http-server/pom.xml @@ -138,6 +138,49 @@ true + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + process-test-resources + + + + + + + + + + + + + + + + + + + + + + + + + run + + + + + + ant-contrib + ant-contrib + 20020829 + + + diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/ContractVerifierDslConverter.java b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/ContractVerifierDslConverter.java index f3a5db19b6..856f03b931 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/ContractVerifierDslConverter.java +++ b/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/util/ContractVerifierDslConverter.java @@ -26,10 +26,13 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -52,6 +55,7 @@ import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.spec.ContractConverter; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Converts a String or a Groovy or Java file into a {@link Contract}. @@ -140,7 +144,7 @@ public class ContractVerifierDslConverter implements ContractConverter constructor = classConstructor(dsl); + private static Object parseJavaFile(File rootFolder, File dsl) throws IllegalAccessException, + InvocationTargetException, InstantiationException, IOException, NoSuchMethodException { + Constructor constructor = classConstructor(rootFolder, dsl); Object newInstance = constructor.newInstance(); if (!(newInstance instanceof Supplier)) { if (LOG.isDebugEnabled()) { @@ -167,7 +171,7 @@ public class ContractVerifierDslConverter implements ContractConverter classConstructor(File dsl) + private static Constructor classConstructor(File rootFolder, File dsl) throws IllegalAccessException, IOException, NoSuchMethodException { try (StandardJavaFileManager fileManager = COMPILER.getStandardFileManager(null, null, null)) { try (Stream lines = Files.lines(Paths.get(dsl.getAbsolutePath()))) { @@ -175,6 +179,15 @@ public class ContractVerifierDslConverter implements ContractConverter classpathLocations = new HashSet<>(); + classpathLocations.add(rootFolder); + appendUrlsFromAllClassLoaders(classpathLocations); + String classPath = System.getProperty("java.class.path", ""); + if (StringUtils.hasText(classPath)) { + classpathLocations.addAll(Arrays.stream(classPath.split(":")).map(File::new).toList()); + } + fileManager.setLocation(StandardLocation.CLASS_PATH, classpathLocations); // Compile the file DiagnosticCollector diagnostics = new DiagnosticCollector<>(); JavaCompiler.CompilationTask task = COMPILER.getTask(null, fileManager, diagnostics, null, null, @@ -183,12 +196,16 @@ public class ContractVerifierDslConverter implements ContractConverter "Error " + d.getMessage(Locale.getDefault()) + "on line " + .map(d -> "Error " + d.getMessage(Locale.getDefault()) + " on line " + d.getLineNumber() + " in " + d.getSource()) .collect(Collectors.joining("\n"))); } try { - Class clazz = ClassUtils.forName(fqn, null); + // Add the folder with compiled classes to the class loader + URLClassLoader urlClassLoader = new URLClassLoader("contract-classloader", + new URL[] { new URL("file://" + directory.toAbsolutePath() + "/") }, + Thread.currentThread().getContextClassLoader()); + Class clazz = ClassUtils.forName(fqn, urlClassLoader); Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); return constructor; @@ -200,6 +217,26 @@ public class ContractVerifierDslConverter implements ContractConverter files) { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + if (classLoader != null) { + appendUrlsFromClasspath(files, classLoader); + while (classLoader.getParent() != null) { + classLoader = classLoader.getParent(); + appendUrlsFromClasspath(files, classLoader); + } + } + } + + private static void appendUrlsFromClasspath(Set files, ClassLoader classLoader) { + if (classLoader instanceof URLClassLoader urlClassLoader) { + URL[] urLs = urlClassLoader.getURLs(); + if (urLs.length > 0) { + Arrays.stream(urLs).forEach(url -> files.add(new File(url.getFile()))); + } + } + } + private static boolean isJava(File dsl) { return dsl.getName().endsWith(".java"); }