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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 06c22e4f50c rest-dsl - binding part 4 (#13697)
06c22e4f50c is described below

commit 06c22e4f50c208cc20b7e2eb2e8344b5271901b6
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Thu Apr 4 15:05:52 2024 +0200

    rest-dsl - binding part 4 (#13697)
    
    CAMEL-20557: Rest DSL to use openapi spec directly
---
 .../platform/http/PlatformHttpConsumer.java        | 24 +++++++++----
 .../platform/http/PlatformHttpEndpoint.java        |  2 +-
 .../DefaultRestOpenapiProcessorStrategy.java       | 18 ++++++----
 .../rest/openapi/RestOpenApiEndpoint.java          | 10 ++++--
 .../rest/openapi/RestOpenApiProcessor.java         | 41 ++++++++++++++--------
 .../rest/openapi/RestOpenapiProcessorStrategy.java |  8 +++--
 .../camel/support/processor/RestBindingAdvice.java |  5 ++-
 7 files changed, 69 insertions(+), 39 deletions(-)

diff --git 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpConsumer.java
 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpConsumer.java
index c2020081876..bfc357db814 100644
--- 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpConsumer.java
+++ 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpConsumer.java
@@ -26,7 +26,7 @@ import org.apache.camel.support.service.ServiceHelper;
 
 public class PlatformHttpConsumer extends DefaultConsumer implements 
Suspendable, SuspendableService {
 
-    private Consumer delegatedConsumer;
+    private Consumer platformHttpConsumer;
     private boolean register = true;
 
     public PlatformHttpConsumer(Endpoint endpoint, Processor processor) {
@@ -50,19 +50,29 @@ public class PlatformHttpConsumer extends DefaultConsumer 
implements Suspendable
         this.register = register;
     }
 
+    public Consumer getDelegtePlatformHttpConsumer() {
+        return platformHttpConsumer;
+    }
+
     @Override
     protected void doInit() throws Exception {
+        platformHttpConsumer = 
getEndpoint().createPlatformHttpConsumer(getProcessor());
+        configurePlatformHttpConsumer(platformHttpConsumer);
         super.doInit();
-        delegatedConsumer = 
getEndpoint().createDelegateConsumer(getProcessor());
+        ServiceHelper.initService(platformHttpConsumer);
+    }
+
+    protected void configurePlatformHttpConsumer(Consumer 
platformHttpConsumer) {
+        // noop
     }
 
     @Override
     protected void doStart() throws Exception {
         super.doStart();
-        ServiceHelper.startService(delegatedConsumer);
+        ServiceHelper.startService(platformHttpConsumer);
         if (register) {
             getComponent().addHttpEndpoint(getEndpoint().getPath(), 
getEndpoint().getHttpMethodRestrict(),
-                    getEndpoint().getConsumes(), getEndpoint().getProduces(), 
delegatedConsumer);
+                    getEndpoint().getConsumes(), getEndpoint().getProduces(), 
platformHttpConsumer);
         }
     }
 
@@ -72,18 +82,18 @@ public class PlatformHttpConsumer extends DefaultConsumer 
implements Suspendable
         if (register) {
             getComponent().removeHttpEndpoint(getEndpoint().getPath());
         }
-        ServiceHelper.stopAndShutdownServices(delegatedConsumer);
+        ServiceHelper.stopAndShutdownServices(platformHttpConsumer);
     }
 
     @Override
     protected void doResume() throws Exception {
-        ServiceHelper.resumeService(delegatedConsumer);
+        ServiceHelper.resumeService(platformHttpConsumer);
         super.doResume();
     }
 
     @Override
     protected void doSuspend() throws Exception {
-        ServiceHelper.suspendService(delegatedConsumer);
+        ServiceHelper.suspendService(platformHttpConsumer);
         super.doSuspend();
     }
 
diff --git 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
index f2b43c9474a..dc940c1f73e 100644
--- 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
+++ 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/PlatformHttpEndpoint.java
@@ -107,7 +107,7 @@ public class PlatformHttpEndpoint extends DefaultEndpoint 
implements AsyncEndpoi
         return consumer;
     }
 
-    protected Consumer createDelegateConsumer(Processor processor) throws 
Exception {
+    protected Consumer createPlatformHttpConsumer(Processor processor) throws 
Exception {
         Consumer consumer = getOrCreateEngine().createConsumer(this, 
processor);
         configureConsumer(consumer);
         return consumer;
diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/DefaultRestOpenapiProcessorStrategy.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/DefaultRestOpenapiProcessorStrategy.java
index d98723a1ae3..bd027e97b35 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/DefaultRestOpenapiProcessorStrategy.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/DefaultRestOpenapiProcessorStrategy.java
@@ -36,6 +36,7 @@ import org.apache.camel.NonManagedService;
 import org.apache.camel.Route;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.component.platform.http.PlatformHttpComponent;
+import org.apache.camel.component.platform.http.PlatformHttpConsumer;
 import org.apache.camel.spi.PackageScanResourceResolver;
 import org.apache.camel.spi.ProducerCache;
 import org.apache.camel.spi.Resource;
@@ -66,7 +67,7 @@ public class DefaultRestOpenapiProcessorStrategy extends 
ServiceSupport
     private final List<String> uris = new ArrayList<>();
 
     @Override
-    public void validateOpenApi(OpenAPI openAPI) throws Exception {
+    public void validateOpenApi(OpenAPI openAPI, PlatformHttpConsumer 
platformHttpConsumer) throws Exception {
         List<String> ids = new ArrayList<>();
         for (var e : openAPI.getPaths().entrySet()) {
             for (var o : e.getValue().readOperations()) {
@@ -96,9 +97,9 @@ public class DefaultRestOpenapiProcessorStrategy extends 
ServiceSupport
             if ("fail".equalsIgnoreCase(missingOperation)) {
                 throw new IllegalArgumentException(msg);
             } else if ("ignore".equalsIgnoreCase(missingOperation)) {
-                LOG.warn(msg + ". This validation error is ignored.");
+                LOG.warn(msg + "\nThis validation error is ignored.");
             } else if ("mock".equalsIgnoreCase(missingOperation)) {
-                LOG.debug(msg + ". This validation error is ignored (Will 
return a mocked/empty response).");
+                LOG.debug(msg + "\nThis validation error is ignored (Will 
return a mocked/empty response).");
             }
         }
 
@@ -133,7 +134,7 @@ public class DefaultRestOpenapiProcessorStrategy extends 
ServiceSupport
                         }
                     }
                 }
-                phc.addHttpEndpoint(uri, verbs, consumes, produces, null);
+                phc.addHttpEndpoint(uri, verbs, consumes, produces, 
platformHttpConsumer.getDelegtePlatformHttpConsumer());
                 uris.add(uri);
             }
         }
@@ -167,17 +168,20 @@ public class DefaultRestOpenapiProcessorStrategy extends 
ServiceSupport
             RestBindingAdvice binding,
             Exchange exchange, AsyncCallback callback) {
 
-        if ("mock".equalsIgnoreCase(missingOperation)) {
+        if ("mock".equalsIgnoreCase(missingOperation) || 
"ignore".equalsIgnoreCase(missingOperation)) {
             // check if there is a route
             Endpoint e = camelContext.hasEndpoint(component + ":" + 
operation.getOperationId());
             if (e == null) {
-                // no route then try to load mock data as the answer
-                loadMockData(operation, path, exchange);
+                if ("mock".equalsIgnoreCase(missingOperation)) {
+                    // no route then try to load mock data as the answer
+                    loadMockData(operation, path, exchange);
+                }
                 callback.done(true);
                 return true;
             }
         }
 
+        // there is a route so process
         Map<String, Object> state;
         try {
             state = binding.before(exchange);
diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
index d955bee0773..fa49ae7dc4b 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiEndpoint.java
@@ -57,6 +57,7 @@ import org.apache.camel.ExchangePattern;
 import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.Processor;
 import org.apache.camel.Producer;
+import org.apache.camel.component.platform.http.PlatformHttpConsumer;
 import 
org.apache.camel.component.rest.openapi.validator.DefaultRequestValidator;
 import org.apache.camel.component.rest.openapi.validator.RequestValidator;
 import org.apache.camel.component.rest.openapi.validator.RestOpenApiOperation;
@@ -208,12 +209,13 @@ public final class RestOpenApiEndpoint extends 
DefaultEndpoint {
     public Consumer createConsumer(final Processor processor) throws Exception 
{
         OpenAPI doc = loadSpecificationFrom(getCamelContext(), 
specificationUri);
         String path = determineBasePath(doc);
-        Processor target = new RestOpenApiProcessor(this, doc, path, 
apiContextPath, processor, restOpenapiProcessorStrategy);
+        RestOpenApiProcessor target
+                = new RestOpenApiProcessor(this, doc, path, apiContextPath, 
processor, restOpenapiProcessorStrategy);
         CamelContextAware.trySetCamelContext(target, getCamelContext());
         return createConsumerFor(path, target);
     }
 
-    protected Consumer createConsumerFor(String basePath, Processor processor) 
throws Exception {
+    protected Consumer createConsumerFor(String basePath, RestOpenApiProcessor 
processor) throws Exception {
         RestOpenApiConsumerFactory factory = null;
         String cname = null;
         if (getConsumerComponentName() != null) {
@@ -294,7 +296,9 @@ public final class RestOpenApiEndpoint extends 
DefaultEndpoint {
         if (factory != null) {
             RestConfiguration config = 
CamelContextHelper.getRestConfiguration(getCamelContext(), cname);
             Map<String, Object> copy = new HashMap<>(parameters); // defensive 
copy of the parameters
-            Consumer consumer = factory.createConsumer(getCamelContext(), 
processor, basePath, config, copy);
+            PlatformHttpConsumer consumer
+                    = (PlatformHttpConsumer) 
factory.createConsumer(getCamelContext(), processor, basePath, config, copy);
+            processor.setPlatformHttpConsumer(consumer);
             configureConsumer(consumer);
             return consumer;
         } else {
diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
index a3801378d06..888cea1fa25 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiProcessor.java
@@ -27,9 +27,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import jakarta.xml.bind.annotation.XmlRootElement;
-
-import com.fasterxml.jackson.annotation.JsonTypeName;
 import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.Operation;
 import io.swagger.v3.oas.models.media.Content;
@@ -41,6 +38,7 @@ import org.apache.camel.CamelContextAware;
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 import org.apache.camel.StartupStep;
+import org.apache.camel.component.platform.http.PlatformHttpConsumer;
 import org.apache.camel.http.base.HttpHelper;
 import org.apache.camel.spi.DataType;
 import org.apache.camel.spi.DataTypeAware;
@@ -57,6 +55,7 @@ import 
org.apache.camel.support.processor.RestBindingAdviceFactory;
 import org.apache.camel.support.processor.RestBindingConfiguration;
 import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -78,6 +77,7 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
     private final RestOpenapiProcessorStrategy restOpenapiProcessorStrategy;
     private final AtomicBoolean packageScanInit = new AtomicBoolean();
     private final Set<Class<?>> scannedClasses = new HashSet<>();
+    private PlatformHttpConsumer platformHttpConsumer;
 
     public RestOpenApiProcessor(RestOpenApiEndpoint endpoint, OpenAPI openAPI, 
String basePath, String apiContextPath,
                                 Processor processor, 
RestOpenapiProcessorStrategy restOpenapiProcessorStrategy) {
@@ -100,16 +100,28 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
         this.camelContext = camelContext;
     }
 
+    public PlatformHttpConsumer getPlatformHttpConsumer() {
+        return platformHttpConsumer;
+    }
+
+    public void setPlatformHttpConsumer(PlatformHttpConsumer 
platformHttpConsumer) {
+        this.platformHttpConsumer = platformHttpConsumer;
+    }
+
     @Override
     public boolean process(Exchange exchange, AsyncCallback callback) {
-        String path = exchange.getMessage().getHeader(Exchange.HTTP_PATH, 
String.class);
-        if (path != null && path.startsWith(basePath)) {
-            path = path.substring(basePath.length());
+        // use HTTP_URI as this works for all runtimes
+        String uri = exchange.getMessage().getHeader(Exchange.HTTP_URI, 
String.class);
+        if (uri != null) {
+            uri = URISupport.stripQuery(uri);
+        }
+        if (uri != null && uri.startsWith(basePath)) {
+            uri = uri.substring(basePath.length());
         }
         String verb = exchange.getMessage().getHeader(Exchange.HTTP_METHOD, 
String.class);
 
         RestConsumerContextPathMatcher.ConsumerPath<Operation> m
-                = RestConsumerContextPathMatcher.matchBestPath(verb, path, 
paths);
+                = RestConsumerContextPathMatcher.matchBestPath(verb, uri, 
paths);
         if (m instanceof RestOpenApiConsumerPath rcp) {
             Operation o = rcp.getConsumer();
 
@@ -118,7 +130,7 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
             RestConfiguration.RestBindingMode bindingMode = 
config.getBindingMode();
 
             // map path-parameters from operation to camel headers
-            HttpHelper.evalPlaceholders(exchange.getMessage().getHeaders(), 
path, rcp.getConsumerPath());
+            HttpHelper.evalPlaceholders(exchange.getMessage().getHeaders(), 
uri, rcp.getConsumerPath());
 
             // we have found the op to call, but if validation is enabled then 
we need
             // to validate the incoming request first
@@ -128,17 +140,17 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
             }
 
             // process the incoming request
-            return restOpenapiProcessorStrategy.process(o, path, 
rcp.getBinding(), exchange, callback);
+            return restOpenapiProcessorStrategy.process(o, uri, 
rcp.getBinding(), exchange, callback);
         }
 
         // is it the api-context path
-        if (path != null && path.equals(apiContextPath)) {
+        if (uri != null && uri.equals(apiContextPath)) {
             return 
restOpenapiProcessorStrategy.processApiSpecification(endpoint.getSpecificationUri(),
 exchange, callback);
         }
 
         // okay we cannot process this requires so return either 404 or 405.
         // to know if its 405 then we need to check if any other HTTP method 
would have a consumer for the "same" request
-        final String contextPath = path;
+        final String contextPath = uri;
         List<String> allow = METHODS.stream()
                 .filter(v -> RestConsumerContextPathMatcher.matchBestPath(v, 
contextPath, paths) != null).toList();
         if (allow.isEmpty()) {
@@ -497,9 +509,8 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
                         "OpenAPI binding classes package scan");
                 String[] pcks = base.split(",");
                 PackageScanClassResolver resolver = 
PluginHelper.getPackageScanClassResolver(camelContext);
-                // discover POJO generated classes for JSon/XML
-                
scannedClasses.addAll(resolver.findAnnotated(JsonTypeName.class, pcks));
-                
scannedClasses.addAll(resolver.findAnnotated(XmlRootElement.class, pcks));
+                // just add all classes as the POJOs can be generated with all 
kind of tools and with and without annotations
+                
scannedClasses.addAll(resolver.findImplementations(Object.class, pcks));
                 if (!scannedClasses.isEmpty()) {
                     LOG.info("Binding package scan found {} classes in 
packages: {}", scannedClasses.size(), base);
                 }
@@ -529,7 +540,7 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
         ServiceHelper.initService(restOpenapiProcessorStrategy);
 
         // validate openapi contract
-        restOpenapiProcessorStrategy.validateOpenApi(openAPI);
+        restOpenapiProcessorStrategy.validateOpenApi(openAPI, 
platformHttpConsumer);
     }
 
     @Override
diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenapiProcessorStrategy.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenapiProcessorStrategy.java
index 90455836228..7c01fdfb107 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenapiProcessorStrategy.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenapiProcessorStrategy.java
@@ -20,6 +20,7 @@ import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.Operation;
 import org.apache.camel.AsyncCallback;
 import org.apache.camel.Exchange;
+import org.apache.camel.component.platform.http.PlatformHttpConsumer;
 import org.apache.camel.support.processor.RestBindingAdvice;
 
 /**
@@ -54,10 +55,11 @@ public interface RestOpenapiProcessorStrategy {
     /**
      * Validates the OpenAPI specification on startup
      *
-     * @param  openAPI   the openapi specification
-     * @throws Exception is thrown if validation error on startup
+     * @param  openAPI              the openapi specification
+     * @param  platformHttpConsumer the platform http consumer
+     * @throws Exception            is thrown if validation error on startup
      */
-    default void validateOpenApi(OpenAPI openAPI) throws Exception {
+    default void validateOpenApi(OpenAPI openAPI, PlatformHttpConsumer 
platformHttpConsumer) throws Exception {
         // noop
     }
 
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/processor/RestBindingAdvice.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/processor/RestBindingAdvice.java
index a548f486525..085a6c7d50f 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/processor/RestBindingAdvice.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/processor/RestBindingAdvice.java
@@ -236,15 +236,14 @@ public class RestBindingAdvice extends ServiceSupport 
implements CamelInternalPr
         }
 
         String body = null;
-        if (exchange.getIn().getBody() != null) {
-
+        if (ObjectHelper.isNotEmpty(exchange.getIn().getBody())) {
             // okay we have a binding mode, so need to check for empty body as 
that can cause the marshaller to fail
             // as they assume a non-empty body
             if (isXml || isJson) {
                 // we have binding enabled, so we need to know if there body 
is empty or not
                 // so force reading the body as a String which we can work with
                 body = MessageHelper.extractBodyAsString(exchange.getIn());
-                if (body != null) {
+                if (ObjectHelper.isNotEmpty(body)) {
                     if (exchange.getIn() instanceof DataTypeAware) {
                         ((DataTypeAware) exchange.getIn()).setBody(body, new 
DataType(isJson ? "json" : "xml"));
                     } else {

Reply via email to