You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/04/04 12:47:14 UTC
(camel) 03/03: CAMEL-20557: Rest DSL to use openapi spec directly
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch binding4
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9882096ad67f359c7ff3f7b2240ae1a4eae9775c
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Apr 4 14:41:01 2024 +0200
CAMEL-20557: Rest DSL to use openapi spec directly
---
.../platform/http/PlatformHttpConsumer.java | 24 ++++++++----
.../platform/http/PlatformHttpEndpoint.java | 2 +-
.../DefaultRestOpenapiProcessorStrategy.java | 14 ++++---
.../rest/openapi/RestOpenApiEndpoint.java | 10 +++--
.../rest/openapi/RestOpenApiProcessor.java | 44 +++++++++++++---------
.../rest/openapi/RestOpenapiProcessorStrategy.java | 8 ++--
.../camel/support/processor/RestBindingAdvice.java | 5 +--
7 files changed, 67 insertions(+), 40 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 2648b8084f2..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()) {
@@ -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 1e18999ca41..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,11 +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 javax.annotation.processing.Generated;
-
-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;
@@ -43,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;
@@ -59,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;
@@ -80,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) {
@@ -102,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();
@@ -120,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
@@ -130,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()) {
@@ -499,10 +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(Generated.class, pcks));
- 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);
}
@@ -532,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 {