From 6051b4f1f302af62ecce918333c4bcd4b6cff780 Mon Sep 17 00:00:00 2001 From: Guillaume Dufour Date: Sat, 14 Sep 2019 03:58:39 +0200 Subject: [PATCH] Init CXF extension fix #4005 --- bom/deployment/pom.xml | 5 + bom/runtime/pom.xml | 22 ++++ .../builditem/FeatureBuildItem.java | 1 + .../common/src/main/filtered/extensions.json | 13 +++ extensions/cxf/deployment/pom.xml | 63 +++++++++++ .../io/quarkus/cxf/deployment/CxfConfig.java | 23 ++++ .../quarkus/cxf/deployment/CxfProcessor.java | 105 ++++++++++++++++++ .../deployment/CxfServerConfigBuildItem.java | 28 +++++ .../cxf/deployment/test/CxfServiceTest.java | 58 ++++++++++ .../cxf/deployment/test/HelloWebService.java | 10 ++ .../deployment/test/HelloWebServiceImpl.java | 12 ++ .../src/test/resources/application.properties | 2 + extensions/cxf/pom.xml | 21 ++++ extensions/cxf/runtime/pom.xml | 55 +++++++++ .../cxf/runtime/CXFQuarkusServlet.java | 76 +++++++++++++ extensions/pom.xml | 3 + 16 files changed, 497 insertions(+) create mode 100644 extensions/cxf/deployment/pom.xml create mode 100644 extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfConfig.java create mode 100755 extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfProcessor.java create mode 100644 extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfServerConfigBuildItem.java create mode 100644 extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/CxfServiceTest.java create mode 100644 extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebService.java create mode 100644 extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebServiceImpl.java create mode 100644 extensions/cxf/deployment/src/test/resources/application.properties create mode 100644 extensions/cxf/pom.xml create mode 100644 extensions/cxf/runtime/pom.xml create mode 100644 extensions/cxf/runtime/src/main/java/io/quarkus/cxf/runtime/CXFQuarkusServlet.java diff --git a/bom/deployment/pom.xml b/bom/deployment/pom.xml index 36db4d9d9e542c..1a140bda658f89 100644 --- a/bom/deployment/pom.xml +++ b/bom/deployment/pom.xml @@ -226,6 +226,11 @@ quarkus-resteasy-jsonb-deployment ${project.version} + + io.quarkus + quarkus-cxf-deployment + ${project.version} + io.quarkus quarkus-smallrye-jwt-deployment diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml index 5d013febb2d554..018459cc489d3e 100644 --- a/bom/runtime/pom.xml +++ b/bom/runtime/pom.xml @@ -164,6 +164,8 @@ 2.1.9.RELEASE 2.4.4.Final 3.0.0 + 3.3.3 + 2.0.0.Final @@ -463,6 +465,11 @@ quarkus-resteasy-server-common ${project.version} + + io.quarkus + quarkus-cxf + ${project.version} + io.quarkus quarkus-narayana-jta @@ -1116,6 +1123,16 @@ artemis-jms-client ${artemis.version} + + org.apache.cxf + cxf-rt-frontend-jaxws + ${cxf.version} + + + org.apache.cxf + cxf-rt-transports-http + ${cxf.version} + org.apache.httpcomponents httpclient @@ -1441,6 +1458,11 @@ narayana-jts-integration ${narayana.version} + + org.jboss.spec.javax.xml.ws + jboss-jaxws-api_2.3_spec + ${jaxws.version} + org.jboss.resteasy resteasy-core diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/FeatureBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/FeatureBuildItem.java index d210e04984d7e7..3199b4a207edd4 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/FeatureBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/FeatureBuildItem.java @@ -13,6 +13,7 @@ public final class FeatureBuildItem extends MultiBuildItem { public static final String ARTEMIS_CORE = "artemis-core"; public static final String ARTEMIS_JMS = "artemis-jms"; public static final String CDI = "cdi"; + public static final String CXF = "cxf"; public static final String DYNAMODB = "dynamodb"; public static final String ELASTICSEARCH_REST_CLIENT = "elasticsearch-rest-client"; public static final String FLYWAY = "flyway"; diff --git a/devtools/common/src/main/filtered/extensions.json b/devtools/common/src/main/filtered/extensions.json index 22644948d994a3..029c8ae0b3c11b 100644 --- a/devtools/common/src/main/filtered/extensions.json +++ b/devtools/common/src/main/filtered/extensions.json @@ -658,5 +658,18 @@ "groupId": "io.quarkus", "artifactId": "quarkus-vertx", "guide": "https://quarkus.io/guides/using-vertx" + }, + { + "name": "CXF", + "shortName": "jax-ws", + "labels": [ + "cxf", + "jaxws", + "web", + "soap" + ], + "groupId": "io.quarkus", + "artifactId": "quarkus-cxf", + "guide": "https://quarkus.io/guides/cxf-guide" } ] diff --git a/extensions/cxf/deployment/pom.xml b/extensions/cxf/deployment/pom.xml new file mode 100644 index 00000000000000..a6fe4de089f52b --- /dev/null +++ b/extensions/cxf/deployment/pom.xml @@ -0,0 +1,63 @@ + + + + quarkus-cxf-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-cxf-deployment + Quarkus - CXF - Deployment + + + + io.quarkus + quarkus-core-deployment + + + io.quarkus + quarkus-cxf + + + io.quarkus + quarkus-undertow-deployment + + + org.jboss.spec.javax.xml.ws + jboss-jaxws-api_2.3_spec + + + + io.quarkus + quarkus-junit5-internal + test + + + commons-io + commons-io + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + + diff --git a/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfConfig.java b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfConfig.java new file mode 100644 index 00000000000000..6efba752f1f6ac --- /dev/null +++ b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfConfig.java @@ -0,0 +1,23 @@ +package io.quarkus.cxf.deployment; + +import java.util.Map; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigRoot; + +@ConfigRoot +final class CxfConfig { + + /** + * Set this to override the default path for JAX-RS resources if there are no + * annotated application classes. + */ + @ConfigItem(defaultValue = "/") + String path; + + /** + * Choose the path of each web services. + */ + @ConfigItem(name = "webservice") + Map webServicesPaths; +} \ No newline at end of file diff --git a/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfProcessor.java b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfProcessor.java new file mode 100755 index 00000000000000..c5c030df78d483 --- /dev/null +++ b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfProcessor.java @@ -0,0 +1,105 @@ +package io.quarkus.cxf.deployment; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +import org.jboss.jandex.AnnotationInstance; +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.DotName; +import org.jboss.jandex.IndexView; + +import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.cxf.runtime.CXFQuarkusServlet; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.substrate.ReflectiveHierarchyBuildItem; +import io.quarkus.deployment.builditem.substrate.RuntimeInitializedClassBuildItem; +import io.quarkus.deployment.builditem.substrate.SubstrateProxyDefinitionBuildItem; +import io.quarkus.deployment.builditem.substrate.SubstrateResourceBuildItem; +import io.quarkus.undertow.deployment.ServletBuildItem; +import io.quarkus.undertow.deployment.ServletInitParamBuildItem; + +/** + * Processor that finds JAX-RS classes in the deployment + */ +public class CxfProcessor { + + private static final String JAX_WS_CXF_SERVLET = "org.apache.cxf.transport.servlet.CXFNonSpringServlet"; + + private static final DotName WEBSERVICE_ANNOTATION = DotName.createSimple("javax.jws.WebService"); + + /** + * JAX-RS configuration. + */ + CxfConfig cxfConfig; + + @BuildStep + public void build( + BuildProducer reflectiveClass, + BuildProducer reflectiveHierarchy, + BuildProducer proxyDefinition, + BuildProducer resource, + BuildProducer runtimeClasses, + BuildProducer transformers, + BuildProducer cxfServerConfig, + BuildProducer unremovableBeans, + CombinedIndexBuildItem combinedIndexBuildItem, + BeanArchiveIndexBuildItem beanArchiveIndexBuildItem) throws Exception { + IndexView index = combinedIndexBuildItem.getIndex(); + + for (AnnotationInstance annotation : index.getAnnotations(WEBSERVICE_ANNOTATION)) { + if (annotation.target().kind() == AnnotationTarget.Kind.CLASS) { + reflectiveClass + .produce(new ReflectiveClassBuildItem(true, true, annotation.target().asClass().name().toString())); + } + } + + Map cxfInitParameters = new HashMap<>(); + + cxfServerConfig.produce(new CxfServerConfigBuildItem(cxfConfig.path, cxfInitParameters)); + } + + @BuildStep + public void build( + Optional cxfServerConfig, + BuildProducer feature, + BuildProducer servlet, + BuildProducer reflectiveClass, + BuildProducer servletInitParameters) throws Exception { + feature.produce(new FeatureBuildItem(FeatureBuildItem.CXF)); + + if (cxfServerConfig.isPresent()) { + String path = cxfServerConfig.get().getPath(); + + String mappingPath = getMappingPath(path); + servlet.produce(ServletBuildItem.builder(JAX_WS_CXF_SERVLET, CXFQuarkusServlet.class.getName()) + .setLoadOnStartup(1).addMapping(mappingPath).setAsyncSupported(true).build()); + reflectiveClass.produce(new ReflectiveClassBuildItem(false, false, CXFQuarkusServlet.class.getName())); + + for (Entry initParameter : cxfServerConfig.get().getInitParameters().entrySet()) { + servletInitParameters.produce(new ServletInitParamBuildItem(initParameter.getKey(), initParameter.getValue())); + } + + for (Entry webServicesByPath : cxfConfig.webServicesPaths.entrySet()) { + CXFQuarkusServlet.publish(webServicesByPath.getKey(), webServicesByPath.getValue()); + } + } + } + + private String getMappingPath(String path) { + String mappingPath; + if (path.endsWith("/")) { + mappingPath = path + "*"; + } else { + mappingPath = path + "/*"; + } + return mappingPath; + } +} diff --git a/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfServerConfigBuildItem.java b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfServerConfigBuildItem.java new file mode 100644 index 00000000000000..f989b8724c6c0c --- /dev/null +++ b/extensions/cxf/deployment/src/main/java/io/quarkus/cxf/deployment/CxfServerConfigBuildItem.java @@ -0,0 +1,28 @@ +package io.quarkus.cxf.deployment; + +import java.util.Map; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * A build item that represents the configuration of the RESTEasy server. + */ +public final class CxfServerConfigBuildItem extends SimpleBuildItem { + + private final String path; + + private final Map initParameters; + + public CxfServerConfigBuildItem(String path, Map initParameters) { + this.path = path; + this.initParameters = initParameters; + } + + public String getPath() { + return path; + } + + public Map getInitParameters() { + return initParameters; + } +} diff --git a/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/CxfServiceTest.java b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/CxfServiceTest.java new file mode 100644 index 00000000000000..d56674dcce5438 --- /dev/null +++ b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/CxfServiceTest.java @@ -0,0 +1,58 @@ +package io.quarkus.cxf.deployment.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.function.Supplier; + +import javax.xml.namespace.QName; +import javax.xml.ws.Service; +import javax.xml.ws.soap.SOAPBinding; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class CxfServiceTest { + + @RegisterExtension + public static final QuarkusDevModeTest test = new QuarkusDevModeTest() + .setArchiveProducer(new Supplier() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClass(HelloWebServiceImpl.class) + .addClass(HelloWebService.class) + .addAsResource("application.properties"); + } + }); + + private static QName SERVICE_NAME = new QName("http://test.deployment.cxf.quarkus.io/", "HelloWebService"); + private static QName PORT_NAME = new QName("http://test.deployment.cxf.quarkus.io/", "HelloWebServicePort"); + + private Service service; + private HelloWebService helloProxy; + private HelloWebServiceImpl helloImpl; + + { + service = Service.create(SERVICE_NAME); + final String endpointAddress = "http://localhost:8080/hello"; + service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress); + } + + @BeforeEach + public void reinstantiateBaeldungInstances() { + helloImpl = new HelloWebServiceImpl(); + helloProxy = service.getPort(PORT_NAME, HelloWebService.class); + } + + @Test + public void whenUsingHelloMethod_thenCorrect() { + final String endpointResponse = helloProxy.sayHi("Quarkus"); + final String localResponse = helloImpl.sayHi("Quarkus"); + assertEquals(localResponse, endpointResponse); + } +} diff --git a/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebService.java b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebService.java new file mode 100644 index 00000000000000..cc69375ef03e7c --- /dev/null +++ b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebService.java @@ -0,0 +1,10 @@ +package io.quarkus.cxf.deployment.test; + +import javax.jws.WebService; + +@WebService +public interface HelloWebService { + + String sayHi(String name); + +} \ No newline at end of file diff --git a/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebServiceImpl.java b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebServiceImpl.java new file mode 100644 index 00000000000000..20661c416ab973 --- /dev/null +++ b/extensions/cxf/deployment/src/test/java/io/quarkus/cxf/deployment/test/HelloWebServiceImpl.java @@ -0,0 +1,12 @@ +package io.quarkus.cxf.deployment.test; + +import javax.jws.WebService; + +@WebService(endpointInterface = "io.quarkus.cxf.deployment.test.HelloWebService") +public class HelloWebServiceImpl implements HelloWebService { + + @Override + public String sayHi(String name) { + return "Hello " + name; + } +} diff --git a/extensions/cxf/deployment/src/test/resources/application.properties b/extensions/cxf/deployment/src/test/resources/application.properties new file mode 100644 index 00000000000000..b1972127ecf108 --- /dev/null +++ b/extensions/cxf/deployment/src/test/resources/application.properties @@ -0,0 +1,2 @@ +quarkus.cxf.path=/ +quarkus.cxf.webservice."/hello"=io.quarkus.cxf.deployment.test.HelloWebService diff --git a/extensions/cxf/pom.xml b/extensions/cxf/pom.xml new file mode 100644 index 00000000000000..c9cb89668fa13a --- /dev/null +++ b/extensions/cxf/pom.xml @@ -0,0 +1,21 @@ + + + + quarkus-build-parent + io.quarkus + 999-SNAPSHOT + ../../build-parent/pom.xml + + 4.0.0 + + quarkus-cxf-parent + Quarkus - CXF + pom + + deployment + runtime + + + diff --git a/extensions/cxf/runtime/pom.xml b/extensions/cxf/runtime/pom.xml new file mode 100644 index 00000000000000..942ee3b5a3d367 --- /dev/null +++ b/extensions/cxf/runtime/pom.xml @@ -0,0 +1,55 @@ + + + + quarkus-cxf-parent + io.quarkus + 999-SNAPSHOT + ../ + + 4.0.0 + + quarkus-cxf + Quarkus - CXF - Runtime + + + + org.apache.cxf + cxf-rt-frontend-jaxws + + + org.apache.cxf + cxf-rt-transports-http + + + org.jboss.spec.javax.servlet + jboss-servlet-api_4.0_spec + + + org.slf4j + slf4j-api + + + + + + + io.quarkus + quarkus-bootstrap-maven-plugin + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${project.version} + + + + + + + diff --git a/extensions/cxf/runtime/src/main/java/io/quarkus/cxf/runtime/CXFQuarkusServlet.java b/extensions/cxf/runtime/src/main/java/io/quarkus/cxf/runtime/CXFQuarkusServlet.java new file mode 100644 index 00000000000000..70acf0718b761d --- /dev/null +++ b/extensions/cxf/runtime/src/main/java/io/quarkus/cxf/runtime/CXFQuarkusServlet.java @@ -0,0 +1,76 @@ +package io.quarkus.cxf.runtime; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletConfig; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.frontend.ServerFactoryBean; +import org.apache.cxf.transport.servlet.CXFNonSpringServlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CXFQuarkusServlet extends CXFNonSpringServlet { + + private static final Logger LOGGER = LoggerFactory.getLogger(CXFQuarkusServlet.class); + + public static class WebServiceConfig { + private String path; + private String className; + + public WebServiceConfig(String path, String className) { + super(); + this.path = path; + this.className = className; + } + + public String getClassName() { + return className; + } + + public String getPath() { + return path; + } + + @Override + public String toString() { + return "Web Service " + className + " on " + path; + } + + } + + private static final long serialVersionUID = 1L; + + private static final List WEB_SERVICES = new ArrayList<>(); + + @Override + public void loadBus(ServletConfig servletConfig) { + super.loadBus(servletConfig); + + // You could add the endpoint publish codes here + Bus bus = getBus(); + BusFactory.setDefaultBus(bus); + + // You can als use the simple frontend API to do this + ServerFactoryBean factory = new ServerFactoryBean(); + factory.setBus(bus); + + for (WebServiceConfig config : WEB_SERVICES) { + try { + Class serviceClass = Thread.currentThread().getContextClassLoader().loadClass(config.getClassName()); + + factory.setServiceClass(serviceClass); + factory.setAddress(config.getPath()); + factory.create(); + } catch (ClassNotFoundException e) { + LOGGER.error("Cannot initialize " + config.toString(), e); + } + } + } + + public static void publish(String path, String webService) { + WEB_SERVICES.add(new WebServiceConfig(path, webService)); + } +} diff --git a/extensions/pom.xml b/extensions/pom.xml index a7c5552c02d952..756fe8c17e3159 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -48,6 +48,9 @@ smallrye-openapi swagger-ui + + cxf + vertx vertx-web