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

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

commit 9ec00974ff1232320e3e0056147e24459e705f03
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Nov 30 11:12:26 2025 +0100

    CAMEL-22742: camel-core - Rest DSL contract first should have jmx statistics
---
 components/camel-platform-http-vertx/pom.xml       |   5 ++
 ...RestOpenApiConsumerRestDslRouteMetricsTest.java | 100 +++++++++++++++++++++
 ...PlatformHttpRestOpenApiConsumerRestDslTest.java |   5 +-
 .../vertx/PlatformHttpRestOpenApiConsumerTest.java |  15 +++-
 .../platform/http/DefaultPlatformHttpConsumer.java |  17 +++-
 .../http/spi/PlatformHttpConsumerAware.java        |   7 ++
 .../rest/openapi/RestOpenApiComponent.java         |   7 +-
 .../rest/openapi/RestOpenApiEndpoint.java          |  72 +++++++++++++--
 .../rest/openapi/RestOpenApiProcessor.java         |  63 ++++++-------
 .../org/apache/camel/spi/InternalProcessor.java    |   7 ++
 .../org/apache/camel/spi/RestConfiguration.java    |   2 +
 .../camel/impl/engine/CamelInternalProcessor.java  |   7 ++
 .../apache/camel/model/rest/RestDefinition.java    |   5 +-
 13 files changed, 265 insertions(+), 47 deletions(-)

diff --git a/components/camel-platform-http-vertx/pom.xml 
b/components/camel-platform-http-vertx/pom.xml
index da2d92f45a2c..bd724651ccaa 100644
--- a/components/camel-platform-http-vertx/pom.xml
+++ b/components/camel-platform-http-vertx/pom.xml
@@ -69,6 +69,11 @@
             <artifactId>camel-test-junit5</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-management</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-http</artifactId>
diff --git 
a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslRouteMetricsTest.java
 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslRouteMetricsTest.java
new file mode 100644
index 000000000000..a9d9aa34a750
--- /dev/null
+++ 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslRouteMetricsTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.component.platform.http.vertx;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.api.management.ManagedCamelContext;
+import org.apache.camel.api.management.mbean.ManagedRouteMBean;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class PlatformHttpRestOpenApiConsumerRestDslRouteMetricsTest {
+
+    @Test
+    public void testRouteMetrics() throws Exception {
+        final CamelContext context = 
VertxPlatformHttpEngineTest.createCamelContext();
+
+        try {
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() {
+                    
rest().openApi().specification("openapi-v3.json").missingOperation("ignore").routeId("myRest");
+
+                    from("direct:getPetById").routeId("getPetById")
+                            .process(e -> {
+                                assertEquals("123", 
e.getMessage().getHeader("petId"));
+                            })
+                            .setBody().constant("{\"pet\": \"tony the 
tiger\"}");
+
+                    from("direct:findPetsByStatus").routeId("findPetsByStatus")
+                            .process(e -> {
+                                assertEquals("sold", 
e.getMessage().getHeader("status"));
+                            })
+                            .setBody().constant("{\"pet\": \"jack the 
lion\"}");
+                }
+            });
+
+            context.start();
+
+            ManagedRouteMBean mr
+                    = 
context.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class).getManagedRoute("myRest");
+            assertNotNull(mr);
+            ManagedRouteMBean mr2 = 
context.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class)
+                    .getManagedRoute("getPetById");
+            assertNotNull(mr2);
+            ManagedRouteMBean mr3 = 
context.getCamelContextExtension().getContextPlugin(ManagedCamelContext.class)
+                    .getManagedRoute("findPetsByStatus");
+            assertNotNull(mr3);
+
+            Assertions.assertEquals(0, mr.getExchangesTotal());
+            Assertions.assertEquals(0, mr2.getExchangesTotal());
+            Assertions.assertEquals(0, mr3.getExchangesTotal());
+
+            given()
+                    .when()
+                    .get("/api/v3/pet/123")
+                    .then()
+                    .statusCode(200)
+                    .body(equalTo("{\"pet\": \"tony the tiger\"}"));
+
+            Assertions.assertEquals(1, mr.getExchangesTotal());
+            Assertions.assertEquals(1, mr2.getExchangesTotal());
+            Assertions.assertEquals(0, mr3.getExchangesTotal());
+
+            given()
+                    .when()
+                    .get("/api/v3/pet/findByStatus?status=sold")
+                    .then()
+                    .statusCode(200)
+                    .body(equalTo("{\"pet\": \"jack the lion\"}"));
+
+            Assertions.assertEquals(2, mr.getExchangesTotal());
+            Assertions.assertEquals(1, mr2.getExchangesTotal());
+            Assertions.assertEquals(1, mr3.getExchangesTotal());
+
+        } finally {
+            context.stop();
+        }
+    }
+
+}
diff --git 
a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslTest.java
 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslTest.java
index b81eaff10d43..c02c22a3b53b 100644
--- 
a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslTest.java
+++ 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerRestDslTest.java
@@ -147,9 +147,10 @@ public class PlatformHttpRestOpenApiConsumerRestDslTest {
 
             context.start();
             fail();
-        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
             Assertions.assertTrue(
-                    e.getMessage().startsWith("OpenAPI specification has 18 
unmapped operations to corresponding routes"));
+                    e.getCause().getMessage()
+                            .startsWith("OpenAPI specification has 18 unmapped 
operations to corresponding routes"));
         } finally {
             context.stop();
         }
diff --git 
a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerTest.java
 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerTest.java
index 9a28f9ce5ba8..fe8276b81a45 100644
--- 
a/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerTest.java
+++ 
b/components/camel-platform-http-vertx/src/test/java/org/apache/camel/component/platform/http/vertx/PlatformHttpRestOpenApiConsumerTest.java
@@ -18,6 +18,7 @@ package org.apache.camel.component.platform.http.vertx;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -37,7 +38,7 @@ public class PlatformHttpRestOpenApiConsumerTest {
                 @Override
                 public void configure() {
                     
from("rest-openapi:classpath:openapi-v3.json?missingOperation=ignore")
-                            .log("dummy");
+                            .to("mock:result");
 
                     from("direct:getPetById")
                             .setBody().constant("{\"pet\": \"tony the 
tiger\"}");
@@ -46,6 +47,9 @@ public class PlatformHttpRestOpenApiConsumerTest {
 
             context.start();
 
+            MockEndpoint mock = context.getEndpoint("mock:result", 
MockEndpoint.class);
+            mock.expectedMessageCount(1);
+
             given()
                     .when()
                     .get("/api/v3/pet/123")
@@ -53,6 +57,8 @@ public class PlatformHttpRestOpenApiConsumerTest {
                     .statusCode(200)
                     .body(equalTo("{\"pet\": \"tony the tiger\"}"));
 
+            mock.assertIsSatisfied();
+
         } finally {
             context.stop();
         }
@@ -69,7 +75,7 @@ public class PlatformHttpRestOpenApiConsumerTest {
                 @Override
                 public void configure() {
                     from("rest-openapi:classpath:openapi-v3.json")
-                            .log("dummy");
+                            .stop(); // use stop if you dont need to do 
anything after rest-dsl
 
                     from("direct:getPetById")
                             .setBody().constant("{\"pet\": \"tony the 
tiger\"}");
@@ -144,9 +150,10 @@ public class PlatformHttpRestOpenApiConsumerTest {
 
             context.start();
             fail();
-        } catch (IllegalArgumentException e) {
+        } catch (Exception e) {
             Assertions.assertTrue(
-                    e.getMessage().startsWith("OpenAPI specification has 18 
unmapped operations to corresponding routes"));
+                    e.getCause().getMessage()
+                            .startsWith("OpenAPI specification has 18 unmapped 
operations to corresponding routes"));
         } finally {
             context.stop();
         }
diff --git 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/DefaultPlatformHttpConsumer.java
 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/DefaultPlatformHttpConsumer.java
index 9a1dba53f902..3ae64ed12418 100644
--- 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/DefaultPlatformHttpConsumer.java
+++ 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/DefaultPlatformHttpConsumer.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.component.platform.http;
 
+import org.apache.camel.AfterPropertiesConfigured;
 import org.apache.camel.Endpoint;
 import org.apache.camel.Processor;
 import org.apache.camel.Suspendable;
@@ -26,10 +27,11 @@ import org.apache.camel.support.DefaultConsumer;
 import org.apache.camel.support.service.ServiceHelper;
 
 public class DefaultPlatformHttpConsumer extends DefaultConsumer
-        implements PlatformHttpConsumerAware, Suspendable, SuspendableService {
+        implements PlatformHttpConsumer, PlatformHttpConsumerAware, 
Suspendable, SuspendableService {
 
     private PlatformHttpConsumer platformHttpConsumer;
     private boolean register = true;
+    private AfterPropertiesConfigured restOpenApiProcessor;
 
     public DefaultPlatformHttpConsumer(Endpoint endpoint, Processor processor) 
{
         super(endpoint, processor);
@@ -62,16 +64,27 @@ public class DefaultPlatformHttpConsumer extends 
DefaultConsumer
         return platformHttpConsumer;
     }
 
+    @Override
+    public void registerOpenApiProcessor(AfterPropertiesConfigured processor) {
+        this.restOpenApiProcessor = processor;
+    }
+
     @Override
     protected void doInit() throws Exception {
         platformHttpConsumer = 
getEndpoint().createPlatformHttpConsumer(getProcessor());
         configurePlatformHttpConsumer(platformHttpConsumer);
         super.doInit();
+
         ServiceHelper.initService(platformHttpConsumer);
+
+        // signal to camel-rest-openapi that the platform-http consumer now 
has been configured
+        // and that rest-dsl can continue to initialize and start so it's 
ready when this consumer is started
+        if (restOpenApiProcessor != null) {
+            
restOpenApiProcessor.afterPropertiesConfigured(getEndpoint().getCamelContext());
+        }
     }
 
     protected void configurePlatformHttpConsumer(PlatformHttpConsumer 
platformHttpConsumer) {
-        // noop
     }
 
     @Override
diff --git 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/spi/PlatformHttpConsumerAware.java
 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/spi/PlatformHttpConsumerAware.java
index 4f691aebfec2..cfaa2ec91b1a 100644
--- 
a/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/spi/PlatformHttpConsumerAware.java
+++ 
b/components/camel-platform-http/src/main/java/org/apache/camel/component/platform/http/spi/PlatformHttpConsumerAware.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.component.platform.http.spi;
 
+import org.apache.camel.AfterPropertiesConfigured;
+
 /**
  * An interface to represent an object that has been injected with {@link 
PlatformHttpConsumer}.
  */
@@ -26,4 +28,9 @@ public interface PlatformHttpConsumerAware {
      */
     PlatformHttpConsumer getPlatformHttpConsumer();
 
+    /**
+     * Special when using camel-rest-openapi for contract-first Rest DSL.
+     */
+    void registerOpenApiProcessor(AfterPropertiesConfigured processor);
+
 }
diff --git 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
index 095fdfd48d39..c6d391e2cd9b 100644
--- 
a/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
+++ 
b/components/camel-rest-openapi/src/main/java/org/apache/camel/component/rest/openapi/RestOpenApiComponent.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.rest.openapi;
 import java.util.Map;
 
 import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
 import org.apache.camel.Endpoint;
 import org.apache.camel.SSLContextParametersAware;
 import org.apache.camel.spi.Metadata;
@@ -141,7 +142,7 @@ public final class RestOpenApiComponent extends 
DefaultComponent implements SSLC
     @Metadata(label = "consumer", description = "Sets the context-path to use 
for servicing the OpenAPI specification")
     private String apiContextPath;
     @Metadata(description = "To use a custom strategy for how to process Rest 
DSL requests", label = "consumer,advanced")
-    private RestOpenapiProcessorStrategy restOpenapiProcessorStrategy = new 
DefaultRestOpenapiProcessorStrategy();
+    private RestOpenapiProcessorStrategy restOpenapiProcessorStrategy;
     @Metadata(description = "Enable usage of global SSL context parameters.", 
label = "security")
     private boolean useGlobalSslContextParameters;
     @Metadata(description = "Customize TLS parameters used by the component. 
If not set defaults to the TLS parameters set in the Camel context ",
@@ -195,6 +196,10 @@ public final class RestOpenApiComponent extends 
DefaultComponent implements SSLC
             }
             bindingPackageScan = base;
         }
+        if (restOpenapiProcessorStrategy == null) {
+            restOpenapiProcessorStrategy = new 
DefaultRestOpenapiProcessorStrategy();
+            CamelContextAware.trySetCamelContext(restOpenapiProcessorStrategy, 
getCamelContext());
+        }
     }
 
     public String getBasePath() {
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 20429aa8e895..e258e66b6af9 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
@@ -51,14 +51,18 @@ import org.apache.camel.Category;
 import org.apache.camel.Component;
 import org.apache.camel.Consumer;
 import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
 import org.apache.camel.ExchangePattern;
 import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.Ordered;
 import org.apache.camel.Processor;
 import org.apache.camel.Producer;
 import org.apache.camel.component.platform.http.spi.PlatformHttpConsumerAware;
 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;
+import org.apache.camel.spi.CamelInternalProcessorAdvice;
+import org.apache.camel.spi.InternalProcessor;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.RestConfiguration;
@@ -69,6 +73,7 @@ import org.apache.camel.spi.UriPath;
 import org.apache.camel.support.CamelContextHelper;
 import org.apache.camel.support.DefaultEndpoint;
 import org.apache.camel.support.ResourceHelper;
+import org.apache.camel.support.processor.RestBindingAdvice;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.UnsafeUriCharactersEncoder;
@@ -210,15 +215,33 @@ public final class RestOpenApiEndpoint extends 
DefaultEndpoint {
     public Consumer createConsumer(final Processor processor) throws Exception 
{
         OpenAPI doc = loadSpecificationFrom(getCamelContext(), 
specificationUri);
         String path = determineBasePath(doc);
-        RestOpenApiProcessor target
-                = new RestOpenApiProcessor(this, doc, path, apiContextPath, 
processor, restOpenapiProcessorStrategy);
-        CamelContextAware.trySetCamelContext(target, getCamelContext());
-        Consumer consumer = createConsumerFor(path, target);
-        target.setConsumer(consumer);
+
+        RestOpenApiProcessor openApiProcessor
+                = new RestOpenApiProcessor(this, doc, path, apiContextPath, 
restOpenapiProcessorStrategy);
+        CamelContextAware.trySetCamelContext(openApiProcessor, 
getCamelContext());
+
+        // use an advice to call the processor that is responsible for routing 
to the route that matches the
+        // operation id, and also do validation of the incoming request
+        // any camel route is just a dummy facade that is not in use
+        if (processor instanceof InternalProcessor ip) {
+            // remove existing rest binding advice because 
RestOpenApiProcessorAdvice has its own binding
+            RestBindingAdvice advice = ip.getAdvice(RestBindingAdvice.class);
+            if (advice != null) {
+                ip.removeAdvice(advice);
+            }
+            ip.addAdvice(new RestOpenApiProcessorAdvice(openApiProcessor));
+        }
+
+        Consumer consumer = createConsumerFor(path, openApiProcessor, 
processor);
+        openApiProcessor.setConsumer(consumer);
+        if (consumer instanceof PlatformHttpConsumerAware phca) {
+            phca.registerOpenApiProcessor(openApiProcessor);
+        }
         return consumer;
     }
 
-    protected Consumer createConsumerFor(String basePath, RestOpenApiProcessor 
processor) throws Exception {
+    protected Consumer createConsumerFor(String basePath, RestOpenApiProcessor 
openApiProcessor, Processor processor)
+            throws Exception {
         RestOpenApiConsumerFactory factory = null;
         String cname = null;
         if (getConsumerComponentName() != null) {
@@ -305,7 +328,7 @@ public final class RestOpenApiEndpoint extends 
DefaultEndpoint {
             }
             Consumer consumer = factory.createConsumer(getCamelContext(), 
processor, basePath, config, copy);
             if (consumer instanceof PlatformHttpConsumerAware phca) {
-                processor.setPlatformHttpConsumer(phca);
+                openApiProcessor.setPlatformHttpConsumer(phca);
             }
             configureConsumer(consumer);
             return consumer;
@@ -950,4 +973,39 @@ public final class RestOpenApiEndpoint extends 
DefaultEndpoint {
 
         return expression.toString();
     }
+
+    private static class RestOpenApiProcessorAdvice implements 
CamelInternalProcessorAdvice<Object>, Ordered {
+
+        private final RestOpenApiProcessor openApiProcessor;
+
+        public RestOpenApiProcessorAdvice(RestOpenApiProcessor 
openApiProcessor) {
+            this.openApiProcessor = openApiProcessor;
+        }
+
+        @Override
+        public boolean hasState() {
+            return false;
+        }
+
+        @Override
+        public Object before(Exchange exchange) throws Exception {
+            try {
+                openApiProcessor.process(exchange);
+            } catch (Exception e) {
+                exchange.setException(e);
+            }
+            return null;
+        }
+
+        @Override
+        public void after(Exchange exchange, Object data) throws Exception {
+            // noop
+        }
+
+        @Override
+        public int getOrder() {
+            // should be lowest so all existing advices are triggered first
+            return Ordered.LOWEST;
+        }
+    }
 }
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 61ea55d6ef65..531b401a7d00 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,18 +27,14 @@ import org.apache.camel.*;
 import org.apache.camel.component.platform.http.spi.PlatformHttpConsumerAware;
 import org.apache.camel.http.base.HttpHelper;
 import org.apache.camel.spi.RestConfiguration;
+import org.apache.camel.support.AsyncProcessorSupport;
 import org.apache.camel.support.RestConsumerContextPathMatcher;
-import org.apache.camel.support.processor.DelegateAsyncProcessor;
 import org.apache.camel.support.processor.RestBindingAdvice;
 import org.apache.camel.support.processor.RestBindingAdviceFactory;
 import org.apache.camel.support.processor.RestBindingConfiguration;
 import org.apache.camel.support.service.ServiceHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-public class RestOpenApiProcessor extends DelegateAsyncProcessor implements 
CamelContextAware {
-
-    private static final Logger LOG = 
LoggerFactory.getLogger(RestOpenApiProcessor.class);
+public class RestOpenApiProcessor extends AsyncProcessorSupport implements 
CamelContextAware, AfterPropertiesConfigured {
 
     // just use the most common verbs
     private static final List<String> METHODS = Arrays.asList("GET", "HEAD", 
"POST", "PUT", "DELETE", "PATCH");
@@ -55,8 +51,7 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
     private OpenApiUtils openApiUtils;
 
     public RestOpenApiProcessor(RestOpenApiEndpoint endpoint, OpenAPI openAPI, 
String basePath, String apiContextPath,
-                                Processor processor, 
RestOpenapiProcessorStrategy restOpenapiProcessorStrategy) {
-        super(processor);
+                                RestOpenapiProcessorStrategy 
restOpenapiProcessorStrategy) {
         this.endpoint = endpoint;
         this.basePath = basePath;
         // ensure starts with leading slash
@@ -95,9 +90,6 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
     public boolean process(Exchange exchange, AsyncCallback callback) {
         // use HTTP_URI as this works for all runtimes
         String path = exchange.getMessage().getHeader(Exchange.HTTP_PATH, 
String.class);
-        //        if (path != null) {
-        //            path = URISupport.stripQuery(path);
-        //        }
         if (path != null && path.startsWith(basePath)) {
             path = path.substring(basePath.length());
         }
@@ -147,9 +139,16 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
     @Override
     protected void doInit() throws Exception {
         super.doInit();
-        this.openApiUtils = new OpenApiUtils(camelContext, 
endpoint.getBindingPackageScan(), openAPI.getComponents());
         CamelContextAware.trySetCamelContext(restOpenapiProcessorStrategy, 
getCamelContext());
+    }
 
+    @Override
+    public void afterPropertiesConfigured(CamelContext camelContext) {
+        // this method is triggered by platformHttpConsumer when it has been 
initialized and would be possible
+        // to know the actual url of the http server that would service the 
incoming requests
+        // this is required to build the paths with all the details
+
+        this.openApiUtils = new OpenApiUtils(camelContext, 
endpoint.getBindingPackageScan(), openAPI.getComponents());
         // register all openapi paths
         for (var e : openAPI.getPaths().entrySet()) {
             String path = e.getKey(); // path
@@ -174,23 +173,38 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
                 camelContext.getRestRegistry().addRestService(consumer, true, 
url, path, basePath, null, v, bc.getConsumes(),
                         bc.getProduces(), bc.getType(), bc.getOutType(), 
routeId, desc);
 
-                RestBindingAdvice binding = 
RestBindingAdviceFactory.build(camelContext, bc);
-
-                ServiceHelper.buildService(binding);
-                paths.add(new RestOpenApiConsumerPath(v, path, o.getValue(), 
binding));
+                try {
+                    RestBindingAdvice binding = 
RestBindingAdviceFactory.build(camelContext, bc);
+                    ServiceHelper.buildService(binding);
+                    paths.add(new RestOpenApiConsumerPath(v, path, 
o.getValue(), binding));
+                } catch (Exception ex) {
+                    throw new RuntimeException(ex);
+                }
             }
         }
         openApiUtils.clear(); // no longer needed
 
+        for (var p : paths) {
+            if (p instanceof RestOpenApiConsumerPath rcp) {
+                ServiceHelper.startService(rcp.getBinding());
+            }
+        }
+
         
restOpenapiProcessorStrategy.setMissingOperation(endpoint.getMissingOperation());
         
restOpenapiProcessorStrategy.setMockIncludePattern(endpoint.getMockIncludePattern());
         ServiceHelper.initService(restOpenapiProcessorStrategy);
 
-        // validate openapi contract
-        restOpenapiProcessorStrategy.validateOpenApi(openAPI, basePath, 
platformHttpConsumer);
+        try {
+            // validate openapi contract
+            restOpenapiProcessorStrategy.validateOpenApi(openAPI, basePath, 
platformHttpConsumer);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+
+        ServiceHelper.startService(restOpenapiProcessorStrategy);
     }
 
-    private RestBindingConfiguration createRestBindingConfiguration(Operation 
o) throws Exception {
+    private RestBindingConfiguration createRestBindingConfiguration(Operation 
o) {
         RestConfiguration config = camelContext.getRestConfiguration();
         RestConfiguration.RestBindingMode mode = config.getBindingMode();
 
@@ -222,17 +236,6 @@ public class RestOpenApiProcessor extends 
DelegateAsyncProcessor implements Came
         return bc;
     }
 
-    @Override
-    protected void doStart() throws Exception {
-        super.doStart();
-        ServiceHelper.startService(restOpenapiProcessorStrategy);
-        for (var p : paths) {
-            if (p instanceof RestOpenApiConsumerPath rcp) {
-                ServiceHelper.startService(rcp.getBinding());
-            }
-        }
-    }
-
     @Override
     protected void doStop() throws Exception {
         super.doStop();
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/InternalProcessor.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/InternalProcessor.java
index 9716f3c6cdfa..8b4136a0efb1 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/InternalProcessor.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/InternalProcessor.java
@@ -54,6 +54,13 @@ public interface InternalProcessor extends AsyncProcessor {
      */
     <T> T getAdvice(Class<T> type);
 
+    /**
+     * Removes an {@link CamelInternalProcessorAdvice} advice from the list of 
advices.
+     *
+     * @param advice the advice to remove
+     */
+    void removeAdvice(CamelInternalProcessorAdvice<?> advice);
+
     /**
      * Adds advice for handling {@link RoutePolicy} for the route
      */
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/RestConfiguration.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/RestConfiguration.java
index 413e9538f346..65b2021d28e2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RestConfiguration.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RestConfiguration.java
@@ -34,6 +34,8 @@ public class RestConfiguration {
 
     public static final String DEFAULT_REST_CONFIGURATION_ID = 
"rest-configuration";
 
+    public static final String CONTRACT_FIRST_PROCESSOR_REF = 
"camel-contract-first-openapi-";
+
     public enum RestBindingMode {
         auto,
         off,
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
index c2b0e649127a..9c3fa20dd89a 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/CamelInternalProcessor.java
@@ -169,6 +169,13 @@ public class CamelInternalProcessor extends 
DelegateAsyncProcessor implements In
         }
     }
 
+    @Override
+    public void removeAdvice(CamelInternalProcessorAdvice<?> advice) {
+        if (advices.remove(advice) && advice.hasState()) {
+            statefulAdvices--;
+        }
+    }
+
     @Override
     public <T> T getAdvice(Class<T> type) {
         for (CamelInternalProcessorAdvice<?> task : advices) {
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index 379d02acb6cc..6ae96d49ba56 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -38,6 +38,7 @@ import org.apache.camel.Endpoint;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.builder.EndpointProducerBuilder;
 import org.apache.camel.model.OptionalIdentifiedDefinition;
+import org.apache.camel.model.ProcessDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.StopDefinition;
 import org.apache.camel.model.ToDefinition;
@@ -48,6 +49,7 @@ import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.ResourceAware;
 import org.apache.camel.spi.RestConfiguration;
 import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.processor.DelegateProcessor;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
@@ -65,6 +67,7 @@ import static 
org.apache.camel.support.CamelContextHelper.parseText;
 public class RestDefinition extends 
OptionalIdentifiedDefinition<RestDefinition> implements ResourceAware {
 
     public static final String MISSING_VERB = "Must add verb first, such as 
get/post/delete";
+
     @XmlAttribute
     private String path;
     @XmlAttribute
@@ -1044,7 +1047,7 @@ public class RestDefinition extends 
OptionalIdentifiedDefinition<RestDefinition>
         if (openApi.getRouteId() != null) {
             route.routeId(parseText(camelContext, openApi.getRouteId()));
         }
-        // add dummy empty stop
+        // add dummy empty stop (not in use)
         route.getOutputs().add(new StopDefinition());
 
         // local configuration can override global

Reply via email to