From c6cffa7ec1a2b9458b665a0ef79264c538cc2819 Mon Sep 17 00:00:00 2001 From: Peter Palaga Date: Wed, 10 Apr 2024 20:35:06 +0200 Subject: [PATCH] Make the class generation fail at build time when there are two same-name sevice methods in the same Java package #1326 --- .../cxf/deployment/QuarkusCxfProcessor.java | 16 ++-- .../deployment/test/AsmNamingClashTest.java | 87 +++++++++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/AsmNamingClashTest.java diff --git a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/QuarkusCxfProcessor.java b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/QuarkusCxfProcessor.java index f57a92ec7..6e7eb92b6 100644 --- a/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/QuarkusCxfProcessor.java +++ b/extensions/core/deployment/src/main/java/io/quarkiverse/cxf/deployment/QuarkusCxfProcessor.java @@ -12,8 +12,10 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Random; @@ -659,7 +661,7 @@ public InputSource resolveEntity(String publicId, String systemId) { private static class QuarkusCapture implements GeneratedClassClassLoaderCapture { private final ClassOutput classOutput; - private final Set generatedClasses = new LinkedHashSet<>(); + private final Map generatedClasses = new LinkedHashMap<>(); QuarkusCapture(ClassOutput classOutput) { this.classOutput = classOutput; @@ -668,12 +670,16 @@ private static class QuarkusCapture implements GeneratedClassClassLoaderCapture @Override public void capture(String name, byte[] bytes) { final String dotName = name.indexOf('.') >= 0 ? name : name.replace('/', '.'); - if (!generatedClasses.contains(dotName)) { - final String slashName = name.indexOf('/') >= 0 ? name : name.replace('.', '/'); + final String slashName = name.indexOf('/') >= 0 ? name : name.replace('.', '/'); + final byte[] oldVal = generatedClasses.get(dotName); + if (oldVal != null && !Arrays.equals(oldVal, bytes)) { + throw new IllegalStateException("Cannot overwrite an existing generated class file " + slashName + + " with a different content. Is there perhaps a naming clash among the methods of your service interfaces?"); + } else { classOutput.getSourceWriter(slashName); LOGGER.debugf("Generated class %s", dotName); classOutput.write(slashName, bytes); - generatedClasses.add(dotName); + generatedClasses.put(dotName, bytes); } } @@ -682,7 +688,7 @@ public int getGeneratedClassesCount() { } public String[] getGeneratedClasses() { - return generatedClasses.toArray(new String[0]); + return generatedClasses.keySet().toArray(new String[0]); } } diff --git a/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/AsmNamingClashTest.java b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/AsmNamingClashTest.java new file mode 100644 index 000000000..53b998526 --- /dev/null +++ b/extensions/core/deployment/src/test/java/io/quarkiverse/cxf/deployment/test/AsmNamingClashTest.java @@ -0,0 +1,87 @@ +package io.quarkiverse.cxf.deployment.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import jakarta.jws.WebMethod; +import jakarta.jws.WebService; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkiverse.cxf.annotation.CXFClient; +import io.quarkus.test.QuarkusUnitTest; + +/** + * A reproducer for https://github.com/quarkiverse/quarkus-cxf/issues/1326 + */ +public class AsmNamingClashTest { + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot(root -> root.addClasses(HelloServiceString.class, HelloServiceStringImpl.class)) + .overrideConfigKey("quarkus.cxf.endpoint.\"/helloString\".implementor", HelloServiceStringImpl.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloString.service-interface", HelloServiceString.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloString.client-endpoint-url", + "http://localhost:8081/services/helloString") + .overrideConfigKey("quarkus.cxf.endpoint.\"/helloInt\".implementor", HelloServiceIntImpl.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloInt.service-interface", HelloServiceInt.class.getName()) + .overrideConfigKey("quarkus.cxf.client.helloInt.client-endpoint-url", "http://localhost:8081/services/helloInt") + .assertException(t -> Assertions.assertThat(t).isInstanceOf(IllegalStateException.class) + .hasMessageContaining( + "Cannot overwrite an existing generated class file io/quarkiverse/cxf/deployment/test/jaxws_asm/Hello with a different content")); + + @CXFClient("helloString") + HelloServiceString helloString; + + @CXFClient("helloInt") + HelloServiceInt helloInt; + + @Test + void payloadLogged() throws IOException { + + Assertions.assertThat(helloString.hello("Joe")).isEqualTo("Hello Joe!"); + Assertions.assertThat(helloInt.hello(42)).isEqualTo("Hello 42!"); + + } + + @WebService(targetNamespace = "http://deployment.logging.features.cxf.quarkiverse.io/") + public interface HelloServiceString { + + @WebMethod + String hello(String text); + + } + + @WebService(targetNamespace = "http://deployment.logging.features.cxf.quarkiverse.io/") + public interface HelloServiceInt { + + @WebMethod + String hello(int i); + + } + + @WebService(serviceName = "HelloService") + public class HelloServiceStringImpl implements HelloServiceString { + + @WebMethod + @Override + public String hello(String text) { + return "Hello " + text + "!"; + } + + } + + @WebService(serviceName = "HelloService") + public class HelloServiceIntImpl implements HelloServiceInt { + + @WebMethod + @Override + public String hello(int text) { + return "Hello " + text + "!"; + } + + } +}