This is an automated email from the ASF dual-hosted git repository.

lburgazzoli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit c3ef475aa92c3acb26d16aa9f77ace9e2788c063
Author: lburgazzoli <[email protected]>
AuthorDate: Thu Apr 2 10:56:15 2020 +0200

    Workaround for RestProducer on Java 11
---
 .../component/rest/deployment/RestProcessor.java   |  72 ++++++-
 extensions/rest/runtime/pom.xml                    |   5 +
 .../component/rest/QuarkusRestComponent.java       | 194 +++++++++++++++++++
 .../component/rest/QuarkusRestEndpoint.java        | 204 +++++++++++++++++++
 .../component/rest/QuarkusRestProducer.java        | 215 +++++++++++++++++++++
 .../quarkus/component/rest/RestRecorder.java}      |  18 +-
 .../component/rest/graal/NoJAXBContext.java}       |  21 +-
 .../rest/graal/RestProducerSubstitution.java}      |  21 +-
 8 files changed, 717 insertions(+), 33 deletions(-)

diff --git 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
 
b/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
index daa2fdb..ffed329 100644
--- 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
+++ 
b/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
@@ -16,11 +16,21 @@
  */
 package org.apache.camel.quarkus.component.rest.deployment;
 
+import io.quarkus.deployment.Capabilities;
+import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
+import org.apache.camel.component.rest.RestComponent;
+import org.apache.camel.quarkus.component.rest.RestRecorder;
+import org.apache.camel.quarkus.component.rest.graal.NoJAXBContext;
+import org.apache.camel.quarkus.core.deployment.CamelBeanBuildItem;
+import org.apache.camel.quarkus.core.deployment.CamelServiceFilter;
+import org.apache.camel.quarkus.core.deployment.CamelServiceFilterBuildItem;
+import org.apache.camel.quarkus.support.common.CamelCapabilities;
 
 class RestProcessor {
-
     private static final String FEATURE = "camel-rest";
 
     @BuildStep
@@ -28,4 +38,64 @@ class RestProcessor {
         return new FeatureBuildItem(FEATURE);
     }
 
+    //
+    // RestAssured brings XML bind APIs to the classpath:
+    //
+    //     [INFO] +- io.rest-assured:rest-assured:jar:4.3.0:test
+    //      [INFO] |  +- org.codehaus.groovy:groovy:jar:3.0.2:test
+    //      [INFO] |  +- org.codehaus.groovy:groovy-xml:jar:3.0.2:test
+    //      [INFO] |  +- org.apache.httpcomponents:httpclient:jar:4.5.11:test
+    //      [INFO] |  |  +- org.apache.httpcomponents:httpcore:jar:4.4.13:test
+    //      [INFO] |  |  \- commons-codec:commons-codec:jar:1.13:test
+    //      [INFO] |  +- org.apache.httpcomponents:httpmime:jar:4.5.3:test
+    //      [INFO] |  +- org.hamcrest:hamcrest:jar:2.1:test
+    //      [INFO] |  +- org.ccil.cowan.tagsoup:tagsoup:jar:1.2.1:test
+    //      [INFO] |  +- io.rest-assured:json-path:jar:4.3.0:test
+    //      [INFO] |  |  +- org.codehaus.groovy:groovy-json:jar:3.0.2:test
+    //      [INFO] |  |  \- io.rest-assured:rest-assured-common:jar:4.3.0:test
+    //   >> [INFO] |  \- io.rest-assured:xml-path:jar:4.3.0:test
+    //      [INFO] |     +- org.apache.commons:commons-lang3:jar:3.9:test
+    //   >> [INFO] |     +- 
jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.2:test
+    //      [INFO] |     |  \- 
jakarta.activation:jakarta.activation-api:jar:1.2.1:test
+    //      [INFO] |     \- 
org.apache.sling:org.apache.sling.javax.activation:jar:0.1.0:test
+    //
+    // For tests in JVM mode the condition NoJAXBContext is always false as a 
consequence of
+    // RestAssured transitive dependencies so we need an additional check on 
the presence of
+    // the org.apache.camel.xml.jaxb feature to make the behaviour consistent 
ion both modes.
+    //
+    // Excluding io.rest-assured:xml-path from the transitive dependencies 
does not seem to work
+    // as it lead to the RestAssured framework to fail to instantiate.
+    //
+
+    @BuildStep(onlyIf = NoJAXBContext.class)
+    void serviceFilter(
+            Capabilities capabilities,
+            BuildProducer<CamelServiceFilterBuildItem> serviceFilter) {
+
+        // if jaxb is configured, don't replace the method
+        if (capabilities.isCapabilityPresent(CamelCapabilities.XML_JAXB)) {
+            return;
+        }
+
+        serviceFilter.produce(new 
CamelServiceFilterBuildItem(CamelServiceFilter.forComponent("rest")));
+    }
+
+    @Record(ExecutionTime.STATIC_INIT)
+    @BuildStep(onlyIf = NoJAXBContext.class)
+    void restComponent(
+            RestRecorder recorder,
+            Capabilities capabilities,
+            BuildProducer<CamelBeanBuildItem> camelBeans) {
+
+        // if jaxb is configured, don't replace the method
+        if (capabilities.isCapabilityPresent(CamelCapabilities.XML_JAXB)) {
+            return;
+        }
+
+        camelBeans.produce(
+                new CamelBeanBuildItem(
+                        "rest",
+                        RestComponent.class.getName(),
+                        recorder.createRestComponent()));
+    }
 }
diff --git a/extensions/rest/runtime/pom.xml b/extensions/rest/runtime/pom.xml
index 24f85f8..68e3328 100644
--- a/extensions/rest/runtime/pom.xml
+++ b/extensions/rest/runtime/pom.xml
@@ -56,6 +56,11 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-rest</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.graalvm.nativeimage</groupId>
+            <artifactId>svm</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestComponent.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestComponent.java
new file mode 100644
index 0000000..13925b9
--- /dev/null
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestComponent.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.rest;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.component.rest.RestComponent;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.util.FileUtil;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
+
+public class QuarkusRestComponent extends RestComponent {
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) throws Exception {
+        String cname = getAndRemoveParameter(parameters, 
"consumerComponentName", String.class, getConsumerComponentName());
+        String pname = getAndRemoveParameter(parameters, 
"producerComponentName", String.class, getProducerComponentName());
+
+        QuarkusRestEndpoint answer = new QuarkusRestEndpoint(uri, this);
+        answer.setConsumerComponentName(cname);
+        answer.setProducerComponentName(pname);
+        answer.setApiDoc(getApiDoc());
+
+        RestConfiguration config = new RestConfiguration();
+        mergeConfigurations(getCamelContext(), config, 
findGlobalRestConfiguration());
+        mergeConfigurations(getCamelContext(), config, 
getCamelContext().getRestConfiguration(cname, false));
+        mergeConfigurations(getCamelContext(), config, 
getCamelContext().getRestConfiguration(pname, false));
+
+        // if no explicit host was given, then fallback and use default 
configured host
+        String h = getAndRemoveOrResolveReferenceParameter(parameters, "host", 
String.class, getHost());
+        if (h == null) {
+            h = config.getHost();
+            int port = config.getPort();
+            // is there a custom port number
+            if (port > 0 && port != 80 && port != 443) {
+                h += ":" + port;
+            }
+        }
+        // host must start with http:// or https://
+        if (h != null && !(h.startsWith("http://";) || 
h.startsWith("https://";))) {
+            h = "http://"; + h;
+        }
+        answer.setHost(h);
+
+        setProperties(answer, parameters);
+        if (!parameters.isEmpty()) {
+            // use only what remains and at this point parameters that have 
been used have been removed
+            // without overwriting any query parameters set via 
queryParameters endpoint option
+            final Map<String, Object> queryParameters = new 
LinkedHashMap<>(parameters);
+            final Map<String, Object> existingQueryParameters = 
URISupport.parseQuery(answer.getQueryParameters());
+            queryParameters.putAll(existingQueryParameters);
+
+            final String remainingParameters = 
URISupport.createQueryString(queryParameters);
+            answer.setQueryParameters(remainingParameters);
+        }
+
+        answer.setParameters(parameters);
+
+        if (!remaining.contains(":")) {
+            throw new IllegalArgumentException(
+                    "Invalid syntax. Must be rest:method:path[:uriTemplate] 
where uriTemplate is optional");
+        }
+
+        String method = StringHelper.before(remaining, ":");
+        String s = StringHelper.after(remaining, ":");
+
+        String path;
+        String uriTemplate;
+        if (s != null && s.contains(":")) {
+            path = StringHelper.before(s, ":");
+            uriTemplate = StringHelper.after(s, ":");
+        } else {
+            path = s;
+            uriTemplate = null;
+        }
+
+        // remove trailing slashes
+        path = FileUtil.stripTrailingSeparator(path);
+        uriTemplate = FileUtil.stripTrailingSeparator(uriTemplate);
+
+        answer.setMethod(method);
+        answer.setPath(path);
+        answer.setUriTemplate(uriTemplate);
+
+        // if no explicit component name was given, then fallback and use 
default configured component name
+        if (answer.getProducerComponentName() == null) {
+            String name = config.getProducerComponent();
+            answer.setProducerComponentName(name);
+        }
+        if (answer.getConsumerComponentName() == null) {
+            String name = config.getComponent();
+            answer.setConsumerComponentName(name);
+        }
+        // if no explicit producer api was given, then fallback and use 
default configured
+        if (answer.getApiDoc() == null) {
+            answer.setApiDoc(config.getProducerApiDoc());
+        }
+
+        return answer;
+    }
+
+    private RestConfiguration findGlobalRestConfiguration() {
+        CamelContext context = getCamelContext();
+
+        RestConfiguration conf = CamelContextHelper.lookup(context, 
DEFAULT_REST_CONFIGURATION_ID, RestConfiguration.class);
+        if (conf == null) {
+            conf = CamelContextHelper.findByType(getCamelContext(), 
RestConfiguration.class);
+        }
+
+        return conf;
+    }
+
+    private RestConfiguration mergeConfigurations(CamelContext camelContext, 
RestConfiguration conf, RestConfiguration from)
+            throws Exception {
+        if (conf == from) {
+            return conf;
+        }
+        if (from != null) {
+            // Merge properties
+            conf.setComponent(or(conf.getComponent(), from.getComponent()));
+            conf.setApiComponent(or(conf.getApiComponent(), 
from.getApiComponent()));
+            conf.setProducerComponent(or(conf.getProducerComponent(), 
from.getProducerComponent()));
+            conf.setProducerApiDoc(or(conf.getProducerApiDoc(), 
from.getProducerApiDoc()));
+            conf.setScheme(or(conf.getScheme(), from.getScheme()));
+            conf.setHost(or(conf.getHost(), from.getHost()));
+            conf.setUseXForwardHeaders(or(conf.isUseXForwardHeaders(), 
from.isUseXForwardHeaders()));
+            conf.setApiHost(or(conf.getApiHost(), from.getApiHost()));
+            conf.setPort(or(conf.getPort(), from.getPort()));
+            conf.setContextPath(or(conf.getContextPath(), 
from.getContextPath()));
+            conf.setApiContextPath(or(conf.getApiContextPath(), 
from.getApiContextPath()));
+            conf.setApiContextRouteId(or(conf.getApiContextRouteId(), 
from.getApiContextRouteId()));
+            conf.setApiContextIdPattern(or(conf.getApiContextIdPattern(), 
from.getApiContextIdPattern()));
+            conf.setApiContextListing(or(conf.isApiContextListing(), 
from.isApiContextListing()));
+            conf.setApiVendorExtension(or(conf.isApiVendorExtension(), 
from.isApiVendorExtension()));
+            conf.setHostNameResolver(or(conf.getHostNameResolver(), 
from.getHostNameResolver(),
+                    RestConfiguration.RestHostNameResolver.allLocalIp));
+            conf.setBindingMode(or(conf.getBindingMode(), 
from.getBindingMode(), RestConfiguration.RestBindingMode.off));
+            conf.setSkipBindingOnErrorCode(or(conf.isSkipBindingOnErrorCode(), 
from.isSkipBindingOnErrorCode()));
+            
conf.setClientRequestValidation(or(conf.isClientRequestValidation(), 
from.isClientRequestValidation()));
+            conf.setEnableCORS(or(conf.isEnableCORS(), from.isEnableCORS()));
+            conf.setJsonDataFormat(or(conf.getJsonDataFormat(), 
from.getJsonDataFormat()));
+            conf.setXmlDataFormat(or(conf.getXmlDataFormat(), 
from.getXmlDataFormat()));
+            
conf.setComponentProperties(mergeProperties(conf.getComponentProperties(), 
from.getComponentProperties()));
+            
conf.setEndpointProperties(mergeProperties(conf.getEndpointProperties(), 
from.getEndpointProperties()));
+            
conf.setConsumerProperties(mergeProperties(conf.getConsumerProperties(), 
from.getConsumerProperties()));
+            
conf.setDataFormatProperties(mergeProperties(conf.getDataFormatProperties(), 
from.getDataFormatProperties()));
+            conf.setApiProperties(mergeProperties(conf.getApiProperties(), 
from.getApiProperties()));
+            conf.setCorsHeaders(mergeProperties(conf.getCorsHeaders(), 
from.getCorsHeaders()));
+        }
+
+        return conf;
+    }
+
+    private <T> T or(T t1, T t2) {
+        return t2 != null ? t2 : t1;
+    }
+
+    private <T> T or(T t1, T t2, T def) {
+        return t2 != null && t2 != def ? t2 : t1;
+    }
+
+    private <T> Map<String, T> mergeProperties(Map<String, T> base, 
Map<String, T> addons) {
+        if (base != null || addons != null) {
+            Map<String, T> result = new HashMap<>();
+            if (base != null) {
+                result.putAll(base);
+            }
+            if (addons != null) {
+                result.putAll(addons);
+            }
+            return result;
+        }
+        return base;
+    }
+}
diff --git 
a/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestEndpoint.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestEndpoint.java
new file mode 100644
index 0000000..6c1281c
--- /dev/null
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestEndpoint.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.rest;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.Component;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.NoFactoryAvailableException;
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.Producer;
+import org.apache.camel.component.rest.RestComponent;
+import org.apache.camel.component.rest.RestEndpoint;
+import org.apache.camel.spi.FactoryFinder;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.spi.RestProducerFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static 
org.apache.camel.support.RestProducerFactoryHelper.setupComponent;
+
+public class QuarkusRestEndpoint extends RestEndpoint {
+    private static final Logger LOG = 
LoggerFactory.getLogger(QuarkusRestEndpoint.class);
+
+    public QuarkusRestEndpoint(String endpointUri, RestComponent component) {
+        super(endpointUri, component);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Producer createProducer() throws Exception {
+        if (ObjectHelper.isEmpty(getHost())) {
+            // hostname must be provided
+            throw new IllegalArgumentException("Hostname must be configured on 
either restConfiguration"
+                    + " or in the rest endpoint uri as a query parameter with 
name host, eg rest:" + getMethod() + ":"
+                    + getPath() + "?host=someserver");
+        }
+
+        RestProducerFactory apiDocFactory = null;
+        RestProducerFactory factory = null;
+
+        if (getApiDoc() != null) {
+            LOG.debug("Discovering camel-openapi-java on classpath for using 
api-doc: {}", getApiDoc());
+            // lookup on classpath using factory finder to automatic find it 
(just add camel-openapi-java to classpath etc)
+            FactoryFinder finder = null;
+            try {
+                finder = 
getCamelContext().adapt(ExtendedCamelContext.class).getFactoryFinder(RESOURCE_PATH);
+                apiDocFactory = finder.newInstance(DEFAULT_API_COMPONENT_NAME, 
RestProducerFactory.class).orElse(null);
+                if (apiDocFactory == null) {
+                    throw new NoFactoryAvailableException("Cannot find 
camel-openapi-java on classpath");
+                }
+                getParameters().put("apiDoc", getApiDoc());
+            } catch (NoFactoryAvailableException e) {
+                try {
+                    LOG.debug("Discovering camel-swagger-java on classpath as 
fallback for using api-doc: {}", getApiDoc());
+                    Object instance = finder.newInstance("swagger").get();
+                    if (instance instanceof RestProducerFactory) {
+                        // this factory from camel-swagger-java will facade 
the http component in use
+                        apiDocFactory = (RestProducerFactory) instance;
+                    }
+                    getParameters().put("apiDoc", getApiDoc());
+                } catch (Exception ex) {
+
+                    throw new IllegalStateException(
+                            "Cannot find camel-openapi-java neither 
camel-swagger-java on classpath to use with api-doc: "
+                                    + getApiDoc());
+                }
+
+            }
+        }
+
+        String pname = getProducerComponentName();
+        if (pname != null) {
+            Object comp = getCamelContext().getRegistry().lookupByName(pname);
+            if (comp instanceof RestProducerFactory) {
+                factory = (RestProducerFactory) comp;
+            } else {
+                comp = setupComponent(getProducerComponentName(), 
getCamelContext(),
+                        (Map<String, Object>) 
getParameters().get("component"));
+                if (comp instanceof RestProducerFactory) {
+                    factory = (RestProducerFactory) comp;
+                }
+            }
+
+            if (factory == null) {
+                if (comp != null) {
+                    throw new IllegalArgumentException("Component " + pname + 
" is not a RestProducerFactory");
+                } else {
+                    throw new NoSuchBeanException(getProducerComponentName(), 
RestProducerFactory.class.getName());
+                }
+            }
+        }
+
+        // try all components
+        if (factory == null) {
+            for (String name : getCamelContext().getComponentNames()) {
+                Component comp = setupComponent(name, getCamelContext(),
+                        (Map<String, Object>) 
getParameters().get("component"));
+                if (comp instanceof RestProducerFactory) {
+                    factory = (RestProducerFactory) comp;
+                    pname = name;
+                    break;
+                }
+            }
+        }
+
+        // fallback to use consumer name as it may be producer capable too
+        if (pname == null && getConsumerComponentName() != null) {
+            String cname = getConsumerComponentName();
+            Object comp = getCamelContext().getRegistry().lookupByName(cname);
+            if (comp instanceof RestProducerFactory) {
+                factory = (RestProducerFactory) comp;
+                pname = cname;
+            } else {
+                comp = setupComponent(cname, getCamelContext(), (Map<String, 
Object>) getParameters().get("component"));
+                if (comp instanceof RestProducerFactory) {
+                    factory = (RestProducerFactory) comp;
+                    pname = cname;
+                }
+            }
+        }
+
+        getParameters().put("producerComponentName", pname);
+
+        // lookup in registry
+        if (factory == null) {
+            Set<RestProducerFactory> factories = 
getCamelContext().getRegistry().findByType(RestProducerFactory.class);
+            if (factories != null && factories.size() == 1) {
+                factory = factories.iterator().next();
+            }
+        }
+
+        // no explicit factory found then try to see if we can find any of the 
default rest producer components
+        // and there must only be exactly one so we safely can pick this one
+        if (factory == null) {
+            RestProducerFactory found = null;
+            String foundName = null;
+            for (String name : DEFAULT_REST_PRODUCER_COMPONENTS) {
+                Object comp = setupComponent(name, getCamelContext(), 
(Map<String, Object>) getParameters().get("component"));
+                if (comp instanceof RestProducerFactory) {
+                    if (found == null) {
+                        found = (RestProducerFactory) comp;
+                        foundName = name;
+                    } else {
+                        throw new IllegalArgumentException(
+                                "Multiple RestProducerFactory found on 
classpath. Configure explicit which component to use");
+                    }
+                }
+            }
+            if (found != null) {
+                LOG.debug("Auto discovered {} as RestProducerFactory", 
foundName);
+                factory = found;
+            }
+        }
+
+        if (factory != null) {
+            LOG.debug("Using RestProducerFactory: {}", factory);
+
+            RestConfiguration config = 
getCamelContext().getRestConfiguration(pname, false);
+            if (config == null) {
+                config = getCamelContext().getRestConfiguration();
+            }
+            if (config == null) {
+                config = getCamelContext().getRestConfiguration(pname, true);
+            }
+
+            Producer producer;
+            if (apiDocFactory != null) {
+                // wrap the factory using the api doc factory which will use 
the factory
+                getParameters().put("restProducerFactory", factory);
+                producer = apiDocFactory.createProducer(getCamelContext(), 
getHost(), getMethod(), getPath(), getUriTemplate(),
+                        getQueryParameters(), getConsumes(), getProduces(), 
config, getParameters());
+            } else {
+                producer = factory.createProducer(getCamelContext(), 
getHost(), getMethod(), getPath(), getUriTemplate(),
+                        getQueryParameters(), getConsumes(), getProduces(), 
config, getParameters());
+            }
+
+            QuarkusRestProducer answer = new QuarkusRestProducer(this, 
producer, config);
+            answer.setOutType(getOutType());
+            answer.setType(getInType());
+            answer.setBindingMode(getBindingMode());
+
+            return answer;
+        } else {
+            throw new IllegalStateException("Cannot find RestProducerFactory 
in Registry or as a Component to use");
+        }
+    }
+}
diff --git 
a/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestProducer.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestProducer.java
new file mode 100644
index 0000000..b454cce
--- /dev/null
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/QuarkusRestProducer.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.quarkus.component.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.AsyncProcessor;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.ExtendedCamelContext;
+import org.apache.camel.Producer;
+import org.apache.camel.component.rest.RestProducer;
+import org.apache.camel.component.rest.RestProducerBindingProcessor;
+import org.apache.camel.spi.BeanIntrospection;
+import org.apache.camel.spi.DataFormat;
+import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.support.AsyncProcessorConverterHelper;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.service.ServiceHelper;
+
+public class QuarkusRestProducer extends RestProducer {
+    private final RestConfiguration configuration;
+    private final CamelContext camelContext;
+
+    // the producer of the Camel component that is used as the HTTP client to 
call the REST service
+    private AsyncProcessor producer;
+    // if binding is enabled then this processor should be used to wrap the 
call with binding before/after
+    private AsyncProcessor binding;
+
+    public QuarkusRestProducer(Endpoint endpoint, Producer producer, 
RestConfiguration configuration) {
+        super(endpoint, producer, configuration);
+
+        this.configuration = configuration;
+        this.camelContext = endpoint.getCamelContext();
+        this.producer = AsyncProcessorConverterHelper.convert(producer);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        // create binding processor (returns null if binding is not in use)
+        binding = createBindingProcessor();
+
+        ServiceHelper.startService(binding, producer);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+        ServiceHelper.stopService(producer, binding);
+    }
+
+    @Override
+    public boolean process(Exchange exchange, AsyncCallback callback) {
+        try {
+            prepareExchange(exchange);
+            if (binding != null) {
+                return binding.process(exchange, callback);
+            } else {
+                // no binding in use call the producer directly
+                return producer.process(exchange, callback);
+            }
+        } catch (Throwable e) {
+            exchange.setException(e);
+            callback.done(true);
+            return true;
+        }
+    }
+
+    @Override
+    protected AsyncProcessor createBindingProcessor() throws Exception {
+        // these options can be overridden per endpoint
+        String mode = configuration.getBindingMode().name();
+        if (getBindingMode() != null) {
+            mode = getBindingMode().name();
+        }
+        boolean skip = configuration.isSkipBindingOnErrorCode();
+        if (getSkipBindingOnErrorCode() != null) {
+            skip = getSkipBindingOnErrorCode();
+        }
+
+        if (mode == null || "off".equals(mode)) {
+            // binding mode is off
+            return null;
+        }
+
+        // setup json data format
+        String name = configuration.getJsonDataFormat();
+        if (name != null) {
+            // must only be a name, not refer to an existing instance
+            Object instance = camelContext.getRegistry().lookupByName(name);
+            if (instance != null) {
+                throw new IllegalArgumentException(
+                        "JsonDataFormat name: " + name + " must not be an 
existing bean instance from the registry");
+            }
+        } else {
+            name = "json-jackson";
+        }
+        // this will create a new instance as the name was not already 
pre-created
+        DataFormat json = camelContext.resolveDataFormat(name);
+        DataFormat outJson = camelContext.resolveDataFormat(name);
+
+        // is json binding required?
+        if (mode.contains("json") && json == null) {
+            throw new IllegalArgumentException("JSon DataFormat " + name + " 
not found.");
+        }
+
+        BeanIntrospection beanIntrospection = 
camelContext.adapt(ExtendedCamelContext.class).getBeanIntrospection();
+        if (json != null) {
+            Class<?> clazz = null;
+            if (getType() != null) {
+                String typeName = getType().endsWith("[]") ? 
getType().substring(0, getType().length() - 2) : getType();
+                clazz = 
camelContext.getClassResolver().resolveMandatoryClass(typeName);
+            }
+            if (clazz != null) {
+                beanIntrospection.setProperty(camelContext, json, 
"unmarshalType", clazz);
+                beanIntrospection.setProperty(camelContext, json, "useList", 
getType().endsWith("[]"));
+            }
+            setAdditionalConfiguration(configuration, camelContext, json, 
"json.in.");
+
+            Class<?> outClazz = null;
+            if (getOutType() != null) {
+                String typeName = getOutType().endsWith("[]") ? 
getOutType().substring(0, getOutType().length() - 2)
+                        : getOutType();
+                outClazz = 
camelContext.getClassResolver().resolveMandatoryClass(typeName);
+            }
+            if (outClazz != null) {
+                beanIntrospection.setProperty(camelContext, outJson, 
"unmarshalType", outClazz);
+                beanIntrospection.setProperty(camelContext, outJson, 
"useList", getOutType().endsWith("[]"));
+            }
+            setAdditionalConfiguration(configuration, camelContext, outJson, 
"json.out.");
+        }
+
+        // setup xml data format
+        name = configuration.getXmlDataFormat();
+        if (name != null) {
+            // must only be a name, not refer to an existing instance
+            Object instance = camelContext.getRegistry().lookupByName(name);
+            if (instance != null) {
+                throw new IllegalArgumentException(
+                        "XmlDataFormat name: " + name + " must not be an 
existing bean instance from the registry");
+            }
+        } else {
+            name = "jaxb";
+        }
+        // this will create a new instance as the name was not already 
pre-created
+        DataFormat jaxb = camelContext.resolveDataFormat(name);
+        DataFormat outJaxb = camelContext.resolveDataFormat(name);
+
+        // is xml binding required?
+        if (mode.contains("xml") && jaxb == null) {
+            throw new IllegalArgumentException("XML DataFormat " + name + " 
not found.");
+        }
+
+        if (jaxb != null) {
+            throw new IllegalArgumentException(
+                    "Unsupported XmlDataFormat name: " + name + ": Please add 
a dependency to camel-quarkus-xml-jaxb");
+        }
+
+        return new RestProducerBindingProcessor(producer, camelContext, json, 
jaxb, outJson, outJaxb, mode, skip, getOutType());
+    }
+
+    private void setAdditionalConfiguration(RestConfiguration config, 
CamelContext context,
+            DataFormat dataFormat, String prefix) throws Exception {
+        if (config.getDataFormatProperties() != null && 
!config.getDataFormatProperties().isEmpty()) {
+            // must use a copy as otherwise the options gets removed during 
introspection setProperties
+            Map<String, Object> copy = new HashMap<>();
+
+            // filter keys on prefix
+            // - either its a known prefix and must match the prefix parameter
+            // - or its a common configuration that we should always use
+            for (Map.Entry<String, Object> entry : 
config.getDataFormatProperties().entrySet()) {
+                String key = entry.getKey();
+                String copyKey;
+                boolean known = isKeyKnownPrefix(key);
+                if (known) {
+                    // remove the prefix from the key to use
+                    copyKey = key.substring(prefix.length());
+                } else {
+                    // use the key as is
+                    copyKey = key;
+                }
+                if (!known || key.startsWith(prefix)) {
+                    copy.put(copyKey, entry.getValue());
+                }
+            }
+
+            // set reference properties first as they use # syntax that fools 
the regular properties setter
+            PropertyBindingSupport.bindProperties(context, dataFormat, copy);
+        }
+    }
+
+    private boolean isKeyKnownPrefix(String key) {
+        return key.startsWith("json.in.") || key.startsWith("json.out.") || 
key.startsWith("xml.in.")
+                || key.startsWith("xml.out.");
+    }
+}
diff --git 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/RestRecorder.java
similarity index 70%
copy from 
extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
copy to 
extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/RestRecorder.java
index daa2fdb..cef7d79 100644
--- 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/RestRecorder.java
@@ -14,18 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.rest.deployment;
+package org.apache.camel.quarkus.component.rest;
 
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
 
-class RestProcessor {
-
-    private static final String FEATURE = "camel-rest";
-
-    @BuildStep
-    FeatureBuildItem feature() {
-        return new FeatureBuildItem(FEATURE);
+@Recorder
+public class RestRecorder {
+    public RuntimeValue<QuarkusRestComponent> createRestComponent() {
+        return new RuntimeValue<>(new QuarkusRestComponent());
     }
-
 }
diff --git 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/NoJAXBContext.java
similarity index 68%
copy from 
extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
copy to 
extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/NoJAXBContext.java
index daa2fdb..6f67393 100644
--- 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/NoJAXBContext.java
@@ -14,18 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.rest.deployment;
+package org.apache.camel.quarkus.component.rest.graal;
 
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
+import java.util.function.BooleanSupplier;
 
-class RestProcessor {
+public final class NoJAXBContext implements BooleanSupplier {
+    @Override
+    public boolean getAsBoolean() {
+        try {
+            Class.forName("javax.xml.bind.JAXBContext");
+        } catch (ClassNotFoundException e) {
+            return true;
+        }
 
-    private static final String FEATURE = "camel-rest";
-
-    @BuildStep
-    FeatureBuildItem feature() {
-        return new FeatureBuildItem(FEATURE);
+        return false;
     }
-
 }
diff --git 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/RestProducerSubstitution.java
similarity index 59%
copy from 
extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
copy to 
extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/RestProducerSubstitution.java
index daa2fdb..14511ed 100644
--- 
a/extensions/rest/deployment/src/main/java/org/apache/camel/quarkus/component/rest/deployment/RestProcessor.java
+++ 
b/extensions/rest/runtime/src/main/java/org/apache/camel/quarkus/component/rest/graal/RestProducerSubstitution.java
@@ -14,18 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.quarkus.component.rest.deployment;
+package org.apache.camel.quarkus.component.rest.graal;
 
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import org.apache.camel.AsyncProcessor;
+import org.apache.camel.component.rest.RestProducer;
 
-class RestProcessor {
-
-    private static final String FEATURE = "camel-rest";
-
-    @BuildStep
-    FeatureBuildItem feature() {
-        return new FeatureBuildItem(FEATURE);
+@TargetClass(value = RestProducer.class, onlyWith = NoJAXBContext.class)
+final class RestProducerSubstitution {
+    @Substitute
+    protected AsyncProcessor createBindingProcessor() throws Exception {
+        throw new UnsupportedOperationException("Please add a dependency to 
camel-quarkus-xml-jaxb");
     }
-
 }

Reply via email to