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
