diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index f7228e1f749ae..02725fdb6c32d 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -136,6 +136,11 @@
camel-api
4.24.0-SNAPSHOT
+
+ org.apache.camel
+ camel-apicurio-registry
+ 4.24.0-SNAPSHOT
+
org.apache.camel
camel-arangodb
diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml
index a87cb4a7b19a7..16542806b7f01 100644
--- a/catalog/camel-allcomponents/pom.xml
+++ b/catalog/camel-allcomponents/pom.xml
@@ -137,6 +137,11 @@
camel-amqp
${project.version}
+
+ org.apache.camel
+ camel-apicurio-registry
+ ${project.version}
+
org.apache.camel
camel-arangodb
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
index a72d3a6c6980d..c4510b3bcbb27 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
@@ -12,6 +12,7 @@ alibaba-ots
alibaba-sls
alibaba-sms
amqp
+apicurio-registry
arangodb
as2
asterisk
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/apicurio-registry.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/apicurio-registry.json
new file mode 100644
index 0000000000000..a2e2a9aeb014e
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/apicurio-registry.json
@@ -0,0 +1,115 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "apicurio-registry",
+ "title": "Apicurio Registry",
+ "description": "Manage artifacts, versions, and groups in Apicurio Registry v3.",
+ "deprecated": false,
+ "firstVersion": "4.24.0",
+ "label": "cloud,api",
+ "javaType": "org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent",
+ "supportLevel": "Preview",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-apicurio-registry",
+ "version": "4.24.0-SNAPSHOT",
+ "scheme": "apicurio-registry",
+ "extendsScheme": "",
+ "syntax": "apicurio-registry:groupId\/artifactId",
+ "async": false,
+ "api": false,
+ "consumerOnly": false,
+ "producerOnly": false,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": true
+ },
+ "componentProperties": {
+ "registryUrl": { "index": 0, "kind": "property", "displayName": "Registry Url", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The Apicurio Registry base URL" },
+ "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "fetchContent": { "index": 2, "kind": "property", "displayName": "Fetch Content", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to fetch the content for each new version found by the consumer" },
+ "artifactType": { "index": 3, "kind": "property", "displayName": "Artifact Type", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "JSON", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The default artifact type" },
+ "failOnValidation": { "index": 4, "kind": "property", "displayName": "Fail On Validation", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to throw an exception on validation failure (validate operation). When false, sets result headers instead." },
+ "ifExists": { "index": 5, "kind": "property", "displayName": "If Exists", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "FAIL", "CREATE_VERSION", "FIND_OR_CREATE_VERSION" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "FAIL", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Behavior when artifact already exists" },
+ "lazyStartProducer": { "index": 6, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
+ "operation": { "index": 7, "kind": "property", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "createArtifact", "updateArtifact", "deleteArtifact", "getArtifactContent", "getArtifactMetadata", "searchArtifacts", "listVersions", "createGroup", "testCompatibility", "validate" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The operation to perform" },
+ "autowiredEnabled": { "index": 8, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." },
+ "configuration": { "index": 9, "kind": "property", "displayName": "Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The component configuration" },
+ "vertx": { "index": 10, "kind": "property", "displayName": "Vertx", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "io.vertx.core.Vertx", "deprecated": false, "autowired": true, "secret": false, "description": "To use an existing Vert.x instance for the registry clients. If not set, the component creates one shared Vert.x instance on first use and closes it when the component stops." },
+ "proxyHost": { "index": 11, "kind": "property", "displayName": "Proxy Host", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy host to use for registry (and OAuth2 token) requests" },
+ "proxyPassword": { "index": 12, "kind": "property", "displayName": "Proxy Password", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The password for proxy authentication" },
+ "proxyPort": { "index": 13, "kind": "property", "displayName": "Proxy Port", "group": "proxy", "label": "proxy", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy port" },
+ "proxyUsername": { "index": 14, "kind": "property", "displayName": "Proxy Username", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The username for proxy authentication" },
+ "authType": { "index": 15, "kind": "property", "displayName": "Auth Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "none", "basic", "oidc" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The authentication type to use" },
+ "clientId": { "index": 16, "kind": "property", "displayName": "Client Id", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client ID" },
+ "clientSecret": { "index": 17, "kind": "property", "displayName": "Client Secret", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client secret" },
+ "password": { "index": 18, "kind": "property", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Password for basic authentication" },
+ "scope": { "index": 19, "kind": "property", "displayName": "Scope", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 scope" },
+ "sslContextParameters": { "index": 20, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "To configure TLS, such as a trust store for a registry with a private CA or a client certificate (mTLS), using the Camel JSSE configuration. Also applies to the OAuth2 token endpoint." },
+ "tokenEndpoint": { "index": 21, "kind": "property", "displayName": "Token Endpoint", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 token endpoint URL" },
+ "username": { "index": 22, "kind": "property", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Username for basic authentication" }
+ },
+ "headers": {
+ "CamelApicurioRegistryOperation": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation to perform", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_OPERATION" },
+ "CamelApicurioRegistryGroupId": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact group ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_GROUP_ID" },
+ "CamelApicurioRegistryArtifactId": { "index": 2, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_ID" },
+ "CamelApicurioRegistryArtifactType": { "index": 3, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact type (e.g. AVRO, PROTOBUF, JSON, OPENAPI)", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_TYPE" },
+ "CamelApicurioRegistryVersion": { "index": 4, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact version expression", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VERSION" },
+ "CamelApicurioRegistryArtifactName": { "index": 5, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact name", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_NAME" },
+ "CamelApicurioRegistryArtifactDescription": { "index": 6, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact description", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_DESCRIPTION" },
+ "CamelApicurioRegistryIfExists": { "index": 7, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Behavior when artifact already exists (FAIL, CREATE_VERSION, FIND_OR_CREATE_VERSION)", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_IF_EXISTS" },
+ "CamelApicurioRegistryContentType": { "index": 8, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content type of the artifact", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_CONTENT_TYPE" },
+ "CamelApicurioRegistryGlobalId": { "index": 9, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The version global ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_GLOBAL_ID" },
+ "CamelApicurioRegistryContentId": { "index": 10, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_CONTENT_ID" },
+ "CamelApicurioRegistryVersionState": { "index": 11, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The version state", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VERSION_STATE" },
+ "CamelApicurioRegistryValidationResult": { "index": 12, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the check passed. Set by validate and testCompatibility.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VALIDATION_RESULT" },
+ "CamelApicurioRegistryValidationErrors": { "index": 13, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Rule violation details from validate or testCompatibility. Cleared before each check.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VALIDATION_ERRORS" },
+ "CamelApicurioRegistryLabels": { "index": 14, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Labels to filter searchArtifacts by, as key:value pairs. Either a comma-separated String or a collection.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_LABELS" },
+ "CamelApicurioRegistrySearchOffset": { "index": 15, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of results to skip in searchArtifacts. Defaults to 0.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_OFFSET" },
+ "CamelApicurioRegistrySearchLimit": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The maximum number of results returned by searchArtifacts. The registry defaults to 20.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_LIMIT" },
+ "CamelApicurioRegistrySearchOrder": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The sort order for searchArtifacts: asc or desc", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_ORDER" },
+ "CamelApicurioRegistrySearchOrderBy": { "index": 18, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The field to sort searchArtifacts by: groupId, artifactId, createdOn, modifiedOn, artifactType or name", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_ORDER_BY" }
+ },
+ "properties": {
+ "groupId": { "index": 0, "kind": "path", "displayName": "Group Id", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The artifact group ID" },
+ "artifactId": { "index": 1, "kind": "path", "displayName": "Artifact Id", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The artifact ID" },
+ "registryUrl": { "index": 2, "kind": "parameter", "displayName": "Registry Url", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The Apicurio Registry base URL" },
+ "fetchContent": { "index": 3, "kind": "parameter", "displayName": "Fetch Content", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to fetch the content for each new version found by the consumer" },
+ "sendEmptyMessageWhenIdle": { "index": 4, "kind": "parameter", "displayName": "Send Empty Message When Idle", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead." },
+ "bridgeErrorHandler": { "index": 5, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "exceptionHandler": { "index": 6, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "exchangePattern": { "index": 7, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+ "pollStrategy": { "index": 8, "kind": "parameter", "displayName": "Poll Strategy", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.PollingConsumerPollStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation to control error handling usually occurred during the poll operation before an Exchange have been created and being routed in Camel." },
+ "artifactType": { "index": 9, "kind": "parameter", "displayName": "Artifact Type", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "JSON", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The default artifact type" },
+ "failOnValidation": { "index": 10, "kind": "parameter", "displayName": "Fail On Validation", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to throw an exception on validation failure (validate operation). When false, sets result headers instead." },
+ "ifExists": { "index": 11, "kind": "parameter", "displayName": "If Exists", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "FAIL", "CREATE_VERSION", "FIND_OR_CREATE_VERSION" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "FAIL", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Behavior when artifact already exists" },
+ "operation": { "index": 12, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "createArtifact", "updateArtifact", "deleteArtifact", "getArtifactContent", "getArtifactMetadata", "searchArtifacts", "listVersions", "createGroup", "testCompatibility", "validate" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The operation to perform" },
+ "lazyStartProducer": { "index": 13, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
+ "registryClient": { "index": 14, "kind": "parameter", "displayName": "Registry Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "io.apicurio.registry.rest.client.RegistryClient", "deprecated": false, "autowired": false, "secret": false, "description": "To use a pre-configured RegistryClient instance" },
+ "proxyHost": { "index": 15, "kind": "parameter", "displayName": "Proxy Host", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy host to use for registry (and OAuth2 token) requests" },
+ "proxyPassword": { "index": 16, "kind": "parameter", "displayName": "Proxy Password", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The password for proxy authentication" },
+ "proxyPort": { "index": 17, "kind": "parameter", "displayName": "Proxy Port", "group": "proxy", "label": "proxy", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy port" },
+ "proxyUsername": { "index": 18, "kind": "parameter", "displayName": "Proxy Username", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The username for proxy authentication" },
+ "backoffErrorThreshold": { "index": 19, "kind": "parameter", "displayName": "Backoff Error Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in." },
+ "backoffIdleThreshold": { "index": 20, "kind": "parameter", "displayName": "Backoff Idle Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent idle polls that should happen before the backoffMultipler should kick-in." },
+ "backoffMultiplier": { "index": 21, "kind": "parameter", "displayName": "Backoff Multiplier", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To let the scheduled polling consumer backoff if there has been a number of subsequent idles\/errors in a row. The multiplier is then the number of polls that will be skipped before the next actual attempt is happening again. When this option is in use then backoffIdleThreshold and\/or backoffErrorThreshold must also be configured." },
+ "delay": { "index": 22, "kind": "parameter", "displayName": "Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 500, "description": "Milliseconds before the next poll." },
+ "greedy": { "index": 23, "kind": "parameter", "displayName": "Greedy", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages." },
+ "initialDelay": { "index": 24, "kind": "parameter", "displayName": "Initial Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Milliseconds before the first poll starts." },
+ "repeatCount": { "index": 25, "kind": "parameter", "displayName": "Repeat Count", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Specifies a maximum limit of number of fires. So if you set it to 1, the scheduler will only fire once. If you set it to 5, it will only fire five times. A value of zero or negative means fire forever." },
+ "runLoggingLevel": { "index": 26, "kind": "parameter", "displayName": "Run Logging Level", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "enum", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TRACE", "description": "The consumer logs a start\/complete log line when it polls. This option allows you to configure the logging level for that." },
+ "scheduledExecutorService": { "index": 27, "kind": "parameter", "displayName": "Scheduled Executor Service", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Allows for configuring a custom\/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool." },
+ "scheduler": { "index": 28, "kind": "parameter", "displayName": "Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "To use a cron scheduler from either camel-spring or camel-quartz component. Use value spring or quartz for built in scheduler" },
+ "schedulerProperties": { "index": 29, "kind": "parameter", "displayName": "Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "scheduler.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure additional properties when using a custom scheduler or any of the Quartz, Spring based scheduler. This is a multi-value option with prefix: scheduler." },
+ "startScheduler": { "index": 30, "kind": "parameter", "displayName": "Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started." },
+ "timeUnit": { "index": 31, "kind": "parameter", "displayName": "Time Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "enum", "javaType": "java.util.concurrent.TimeUnit", "enum": [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and delay options." },
+ "useFixedDelay": { "index": 32, "kind": "parameter", "displayName": "Use Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details." },
+ "authType": { "index": 33, "kind": "parameter", "displayName": "Auth Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "none", "basic", "oidc" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The authentication type to use" },
+ "clientId": { "index": 34, "kind": "parameter", "displayName": "Client Id", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client ID" },
+ "clientSecret": { "index": 35, "kind": "parameter", "displayName": "Client Secret", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client secret" },
+ "password": { "index": 36, "kind": "parameter", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Password for basic authentication" },
+ "scope": { "index": 37, "kind": "parameter", "displayName": "Scope", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 scope" },
+ "sslContextParameters": { "index": 38, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "To configure TLS, such as a trust store for a registry with a private CA or a client certificate (mTLS), using the Camel JSSE configuration. Also applies to the OAuth2 token endpoint." },
+ "tokenEndpoint": { "index": 39, "kind": "parameter", "displayName": "Token Endpoint", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 token endpoint URL" },
+ "username": { "index": 40, "kind": "parameter", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Username for basic authentication" }
+ }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
index f952957e6e25d..f14dbffb0b0ab 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs.properties
@@ -18,6 +18,7 @@ alibaba-ots-component
alibaba-sls-component
alibaba-sms-component
amqp-component
+apicurio-registry-component
arangodb-component
as2-component
asn1-dataformat
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/apicurio-registry-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/apicurio-registry-component.adoc
new file mode 100644
index 0000000000000..caab1458777c1
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/apicurio-registry-component.adoc
@@ -0,0 +1,304 @@
+= Apicurio Registry Component
+:doctitle: Apicurio Registry
+:shortname: apicurio-registry
+:artifactid: camel-apicurio-registry
+:description: Manage artifacts, versions, and groups in Apicurio Registry v3.
+:since: 4.24
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Both producer and consumer are supported
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Apicurio Registry component provides integration with https://www.apicur.io/registry/[Apicurio Registry] v3
+using the https://github.com/Apicurio/apicurio-registry[Apicurio Registry Java SDK].
+
+It supports CRUD operations on artifacts, groups, and versions, as well as schema validation
+and polling for new artifact versions.
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+----
+
+ org.apache.camel
+ camel-apicurio-registry
+ x.x.x
+
+
+----
+
+== URI format
+
+----
+apicurio-registry:groupId/artifactId[?options]
+----
+
+Where `groupId` and `artifactId` identify the registry artifact. Both are optional for
+operations that do not require them (e.g., `searchArtifacts`, `createGroup`).
+
+The first `/` separates the group ID from the artifact ID. Group IDs containing `/`
+cannot be represented in the URI path. Producers can instead supply the group ID using
+the `CamelApicurioRegistryGroupId` header, which overrides the URI value.
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+include::partial$component-endpoint-headers.adoc[]
+// component options: END
+
+== Producer Operations
+
+The producer supports the following operations, set via the `operation` endpoint option or
+the `CamelApicurioRegistryOperation` header:
+
+[cols="1,3"]
+|===
+| Operation | Description
+
+| `createArtifact` | Create a new artifact. Body contains the artifact content.
+| `updateArtifact` | Create a new version of an existing artifact. Body contains the new content.
+| `deleteArtifact` | Delete an artifact. Apicurio Registry 3.x disables artifact deletion by default; the registry must run with `apicurio.rest.deletion.artifact.enabled=true` (environment variable `APICURIO_REST_DELETION_ARTIFACT_ENABLED=true`), otherwise the operation fails with HTTP 405.
+| `getArtifactContent` | Retrieve the content of an artifact version.
+| `getArtifactMetadata` | Retrieve artifact metadata.
+| `searchArtifacts` | Search for artifacts. See the "Searching artifacts" section below.
+| `listVersions` | List all versions of an artifact, ordered by global ID. The component follows the registry's pagination and returns every page.
+| `createGroup` | Create a new group.
+| `testCompatibility` | Test schema compatibility (dry run). Returns `true`/`false` in the body.
+| `validate` | Validate the message body against the artifact's rules (dry run). Throws `ApicurioRegistryValidationException` on failure when `failOnValidation=true`.
+|===
+
+Artifact content supplied to `createArtifact`, `updateArtifact`, `validate`, and
+`testCompatibility` is converted to a string for the SDK's `VersionContent` model.
+Its content type is taken from the `CamelApicurioRegistryContentType` header. Without the header, it is
+derived from the content: `application/json` when it starts with `{` or `[`, `application/xml` when it
+starts with `<`, `application/x-protobuf` or `application/graphql` for PROTOBUF and GRAPHQL artifacts,
+and `application/x-yaml` otherwise (for example OpenAPI or AsyncAPI documents written in YAML).
+For AVRO and PROTOBUF, supply the schema definition, rather than serialized message data.
+`getArtifactContent` returns a `byte[]`; the component closes the SDK response stream
+before passing the content to the route.
+
+`validate` and `testCompatibility` check a proposed artifact version against the registry's
+configured rules without storing it. They do not validate application messages against a
+stored schema. Authentication, connectivity, and other non-rule failures propagate as
+exceptions, rather than returning a negative validation result.
+
+Both operations set `CamelApicurioRegistryValidationErrors` to the rule violation details
+when a check fails and clear any previous value before checking. Both set
+`CamelApicurioRegistryValidationResult`; `testCompatibility` also returns the boolean result
+in the body, while `validate` preserves the body.
+
+Operations that act on a specific group or artifact fail with an `IllegalArgumentException`
+when the group or artifact ID is set neither in the endpoint path nor in a header.
+
+=== Searching artifacts
+
+`searchArtifacts` filters by group ID and artifact ID (endpoint path or headers), and by the
+`CamelApicurioRegistryArtifactName`, `CamelApicurioRegistryArtifactDescription`,
+`CamelApicurioRegistryArtifactType` and `CamelApicurioRegistryLabels` headers. The body is an
+`ArtifactSearchResults` holding one page of results and the total `count`.
+
+Results are paged: the registry returns at most 20 artifacts unless you set
+`CamelApicurioRegistrySearchLimit`. Use `CamelApicurioRegistrySearchOffset` to request further
+pages, and `CamelApicurioRegistrySearchOrder` (`asc` or `desc`) and
+`CamelApicurioRegistrySearchOrderBy` (`groupId`, `artifactId`, `createdOn`, `modifiedOn`,
+`artifactType` or `name`) to sort them.
+
+=== Headers from a consumer
+
+The consumer sets the group, artifact and version headers on every exchange, and producers give
+these headers precedence over their endpoint path. When a route forwards consumed versions to
+another registry endpoint, remove them first so the producer uses its own path:
+
+[source,java]
+----
+from("apicurio-registry:dev/orders?registryUrl={{dev.registry}}&fetchContent=true")
+ .removeHeaders("CamelApicurioRegistry*")
+ .to("apicurio-registry:prod/orders?registryUrl={{prod.registry}}&operation=updateArtifact");
+----
+
+== Consumer
+
+The consumer polls the registry for new versions of the specified artifact. It tracks the last
+seen `globalId` and only delivers new versions. Set the `delay` option to control the polling
+interval (default: 500ms).
+
+On first start, all existing versions of the artifact are emitted as exchanges. Subsequent polls
+only deliver versions created after the consumer started. The watermark is held in memory and
+resets on route/context restart.
+
+Once it has a watermark, each poll first asks only for the newest version, so an idle poll
+transfers a single entry. When there is something new, the consumer requests versions newest
+first, one page at a time, and stops at the first version it has already seen. Paging is
+offset-based: if a version is deleted while the consumer is reading pages, an older new version
+can move to a page that was already read and be skipped.
+
+All version states are listed, so `DRAFT` versions are delivered as soon as they appear. If the
+registry allows changing the content of a version, a version is not delivered again after its
+content changes. With `fetchContent=true`, `DISABLED` versions are skipped, because the registry
+does not return their content. If the content of a version is not found (HTTP 404), the consumer
+retries it on later polls, since a registry node with replicated storage may not have the version
+yet; if it is still not found once at least one polling `delay` has passed (also with `greedy=true`),
+the version is skipped with a warning. Skipped versions are not
+delivered again, even if a disabled version is enabled later.
+
+If fetching content fails, or the route fails to process a version (including failures
+handled by the error handler without being rethrown, such as with the default error handler),
+the consumer stops the current poll without advancing the watermark. The failed version and any
+newer ones are delivered again on the next poll. A version that always fails therefore blocks
+newer versions. To skip such a version, mark the failure as handled, which lets the consumer
+advance past it:
+
+[source,java]
+----
+onException(MyPermanentException.class).handled(true).to("log:skipped");
+----
+
+When `fetchContent=true`, the consumer fetches the version content and sets it as a `byte[]` exchange body,
+closing the SDK response stream before processing the exchange.
+Otherwise, the version metadata object is set as the body.
+
+== Examples
+
+=== Create an artifact
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:create")
+ .setBody(constant("{\"type\":\"record\",\"name\":\"Test\",\"fields\":[]}"))
+ .to("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=createArtifact&artifactType=AVRO");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+ {"type":"record","name":"Test","fields":[]}
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:create
+ steps:
+ - setBody:
+ constant: '{"type":"record","name":"Test","fields":[]}'
+ - to:
+ uri: apicurio-registry:myGroup/myArtifact
+ parameters:
+ registryUrl: http://localhost:8080/apis/registry/v3
+ operation: createArtifact
+ artifactType: AVRO
+----
+====
+
+=== Poll for new versions
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3&delay=5000")
+ .log("New version detected: ${header.CamelApicurioRegistryVersion}")
+ .to("log:newVersions");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: apicurio-registry:myGroup/myArtifact
+ parameters:
+ registryUrl: http://localhost:8080/apis/registry/v3
+ delay: 5000
+ steps:
+ - log:
+ message: "New version detected: ${header.CamelApicurioRegistryVersion}"
+ - to:
+ uri: log:newVersions
+----
+====
+
+=== Validate a proposed schema version
+
+[source,java]
+----
+from("direct:validateSchema")
+ .doTry()
+ .to("apicurio-registry:my-group/order-schema?operation=validate®istryUrl=http://localhost:8080/apis/registry/v3")
+ .to("direct:publishSchema")
+ .doCatch(ApicurioRegistryValidationException.class)
+ .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
+ .setBody(simple("${exception.validationErrors}"))
+ .end();
+----
+
+== Connection settings
+
+All endpoints of the component share a single Vert.x instance for their SDK clients. The
+component creates it on first use and closes it when the component stops. To reuse an existing
+Vert.x instance, set the `vertx` component option or bind a single `Vertx` bean in the registry;
+the component does not close a Vert.x instance it did not create.
+
+A client supplied through `registryClient` remains caller-owned;
+the caller is responsible for its underlying HTTP resources.
+
+For HTTPS registries that use a private CA or require client certificates (mTLS), configure
+`sslContextParameters`. Use `proxyHost`, `proxyPort`, `proxyUsername` and `proxyPassword` to go
+through an HTTP proxy. Both settings also apply to the OAuth2 token endpoint.
+
+== Authentication
+
+The component never follows HTTP redirects, so credentials are never sent to another host. Configure
+`registryUrl` with the final address of the registry API.
+
+The producer makes blocking calls. It cannot run on a Vert.x event loop thread, for example in a route
+started by an event-loop consumer of the same Vert.x instance; it fails with an `IllegalStateException`
+there. Hand the exchange to a worker thread first, for example with `threads()`.
+
+The component supports three authentication modes via the `authType` option:
+
+* `none` (default): No authentication.
+* `basic`: Basic authentication using `username` and `password`.
+* `oidc`: OAuth2/OIDC using `tokenEndpoint`, `clientId`, `clientSecret`, and optionally `scope`.
+
+Example with OIDC:
+
+[source,java]
+----
+from("direct:secured")
+ .to("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=getArtifactContent"
+ + "&authType=oidc"
+ + "&tokenEndpoint=http://keycloak:8080/realms/registry/protocol/openid-connect/token"
+ + "&clientId=registry-client&clientSecret=secret");
+----
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
index 834a1750bf196..0979943c1d8d0 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/test-infra/metadata.json
@@ -592,6 +592,17 @@
"version" : "4.24.0-SNAPSHOT",
"serviceVersion" : "camel",
"uiSupported" : false
+}, {
+ "service" : "org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryInfraService",
+ "description" : "Apicurio Registry is an API/Schema registry",
+ "implementation" : "org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryLocalContainerInfraService",
+ "alias" : [ "apicurio-registry" ],
+ "aliasImplementation" : [ ],
+ "groupId" : "org.apache.camel",
+ "artifactId" : "camel-test-infra-apicurio-registry",
+ "version" : "4.24.0-SNAPSHOT",
+ "serviceVersion" : "3.3.3",
+ "uiSupported" : false
}, {
"service" : "org.apache.camel.test.infra.postgres.services.PostgresInfraService",
"description" : "PostgreSQL with pgvector extension for vector similarity search",
diff --git a/components/camel-apicurio-registry/pom.xml b/components/camel-apicurio-registry/pom.xml
new file mode 100644
index 0000000000000..cdd4da625d43a
--- /dev/null
+++ b/components/camel-apicurio-registry/pom.xml
@@ -0,0 +1,109 @@
+
+
+
+
+ 4.0.0
+
+
+ components
+ org.apache.camel
+ 4.24.0-SNAPSHOT
+
+
+ camel-apicurio-registry
+ jar
+
+ Camel :: Apicurio Registry
+ Camel Apicurio Registry component
+
+
+
+
+ org.apache.camel
+ camel-support
+
+
+
+ org.apache.camel
+ camel-vertx-common
+
+
+
+
+ io.vertx
+ vertx-web-client
+ ${vertx-version}
+
+
+ io.vertx
+ vertx-auth-oauth2
+ ${vertx-version}
+
+
+
+
+ io.apicurio
+ apicurio-registry-java-sdk
+
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.apache.camel
+ camel-test-junit6
+ test
+
+
+ org.apache.camel
+ camel-direct
+ test
+
+
+ org.apache.camel
+ camel-mock
+ test
+
+
+ org.awaitility
+ awaitility
+ ${awaitility-version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito-version}
+ test
+
+
+
+
+ org.apache.camel
+ camel-test-infra-apicurio-registry
+ ${project.version}
+ test
+
+
+
+
diff --git a/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentConfigurer.java b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentConfigurer.java
new file mode 100644
index 0000000000000..a46160f41d4e4
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentConfigurer.java
@@ -0,0 +1,177 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.apicurioregistry;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class ApicurioRegistryComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ private org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration getOrCreateConfiguration(ApicurioRegistryComponent target) {
+ if (target.getConfiguration() == null) {
+ target.setConfiguration(new org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration());
+ }
+ return target.getConfiguration();
+ }
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+ ApicurioRegistryComponent target = (ApicurioRegistryComponent) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": getOrCreateConfiguration(target).setArtifactType(property(camelContext, java.lang.String.class, value)); return true;
+ case "authtype":
+ case "authType": getOrCreateConfiguration(target).setAuthType(property(camelContext, java.lang.String.class, value)); return true;
+ case "autowiredenabled":
+ case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+ case "clientid":
+ case "clientId": getOrCreateConfiguration(target).setClientId(property(camelContext, java.lang.String.class, value)); return true;
+ case "clientsecret":
+ case "clientSecret": getOrCreateConfiguration(target).setClientSecret(property(camelContext, java.lang.String.class, value)); return true;
+ case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration.class, value)); return true;
+ case "failonvalidation":
+ case "failOnValidation": getOrCreateConfiguration(target).setFailOnValidation(property(camelContext, boolean.class, value)); return true;
+ case "fetchcontent":
+ case "fetchContent": getOrCreateConfiguration(target).setFetchContent(property(camelContext, boolean.class, value)); return true;
+ case "ifexists":
+ case "ifExists": getOrCreateConfiguration(target).setIfExists(property(camelContext, java.lang.String.class, value)); return true;
+ case "lazystartproducer":
+ case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+ case "operation": getOrCreateConfiguration(target).setOperation(property(camelContext, java.lang.String.class, value)); return true;
+ case "password": getOrCreateConfiguration(target).setPassword(property(camelContext, java.lang.String.class, value)); return true;
+ case "proxyhost":
+ case "proxyHost": getOrCreateConfiguration(target).setProxyHost(property(camelContext, java.lang.String.class, value)); return true;
+ case "proxypassword":
+ case "proxyPassword": getOrCreateConfiguration(target).setProxyPassword(property(camelContext, java.lang.String.class, value)); return true;
+ case "proxyport":
+ case "proxyPort": getOrCreateConfiguration(target).setProxyPort(property(camelContext, java.lang.Integer.class, value)); return true;
+ case "proxyusername":
+ case "proxyUsername": getOrCreateConfiguration(target).setProxyUsername(property(camelContext, java.lang.String.class, value)); return true;
+ case "registryurl":
+ case "registryUrl": getOrCreateConfiguration(target).setRegistryUrl(property(camelContext, java.lang.String.class, value)); return true;
+ case "scope": getOrCreateConfiguration(target).setScope(property(camelContext, java.lang.String.class, value)); return true;
+ case "sslcontextparameters":
+ case "sslContextParameters": getOrCreateConfiguration(target).setSslContextParameters(property(camelContext, org.apache.camel.support.jsse.SSLContextParameters.class, value)); return true;
+ case "tokenendpoint":
+ case "tokenEndpoint": getOrCreateConfiguration(target).setTokenEndpoint(property(camelContext, java.lang.String.class, value)); return true;
+ case "username": getOrCreateConfiguration(target).setUsername(property(camelContext, java.lang.String.class, value)); return true;
+ case "vertx": target.setVertx(property(camelContext, io.vertx.core.Vertx.class, value)); return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public String[] getAutowiredNames() {
+ return new String[]{"vertx"};
+ }
+
+ @Override
+ public Class> getOptionType(String name, boolean ignoreCase) {
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": return java.lang.String.class;
+ case "authtype":
+ case "authType": return java.lang.String.class;
+ case "autowiredenabled":
+ case "autowiredEnabled": return boolean.class;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return boolean.class;
+ case "clientid":
+ case "clientId": return java.lang.String.class;
+ case "clientsecret":
+ case "clientSecret": return java.lang.String.class;
+ case "configuration": return org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration.class;
+ case "failonvalidation":
+ case "failOnValidation": return boolean.class;
+ case "fetchcontent":
+ case "fetchContent": return boolean.class;
+ case "ifexists":
+ case "ifExists": return java.lang.String.class;
+ case "lazystartproducer":
+ case "lazyStartProducer": return boolean.class;
+ case "operation": return java.lang.String.class;
+ case "password": return java.lang.String.class;
+ case "proxyhost":
+ case "proxyHost": return java.lang.String.class;
+ case "proxypassword":
+ case "proxyPassword": return java.lang.String.class;
+ case "proxyport":
+ case "proxyPort": return java.lang.Integer.class;
+ case "proxyusername":
+ case "proxyUsername": return java.lang.String.class;
+ case "registryurl":
+ case "registryUrl": return java.lang.String.class;
+ case "scope": return java.lang.String.class;
+ case "sslcontextparameters":
+ case "sslContextParameters": return org.apache.camel.support.jsse.SSLContextParameters.class;
+ case "tokenendpoint":
+ case "tokenEndpoint": return java.lang.String.class;
+ case "username": return java.lang.String.class;
+ case "vertx": return io.vertx.core.Vertx.class;
+ default: return null;
+ }
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ ApicurioRegistryComponent target = (ApicurioRegistryComponent) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": return getOrCreateConfiguration(target).getArtifactType();
+ case "authtype":
+ case "authType": return getOrCreateConfiguration(target).getAuthType();
+ case "autowiredenabled":
+ case "autowiredEnabled": return target.isAutowiredEnabled();
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+ case "clientid":
+ case "clientId": return getOrCreateConfiguration(target).getClientId();
+ case "clientsecret":
+ case "clientSecret": return getOrCreateConfiguration(target).getClientSecret();
+ case "configuration": return target.getConfiguration();
+ case "failonvalidation":
+ case "failOnValidation": return getOrCreateConfiguration(target).isFailOnValidation();
+ case "fetchcontent":
+ case "fetchContent": return getOrCreateConfiguration(target).isFetchContent();
+ case "ifexists":
+ case "ifExists": return getOrCreateConfiguration(target).getIfExists();
+ case "lazystartproducer":
+ case "lazyStartProducer": return target.isLazyStartProducer();
+ case "operation": return getOrCreateConfiguration(target).getOperation();
+ case "password": return getOrCreateConfiguration(target).getPassword();
+ case "proxyhost":
+ case "proxyHost": return getOrCreateConfiguration(target).getProxyHost();
+ case "proxypassword":
+ case "proxyPassword": return getOrCreateConfiguration(target).getProxyPassword();
+ case "proxyport":
+ case "proxyPort": return getOrCreateConfiguration(target).getProxyPort();
+ case "proxyusername":
+ case "proxyUsername": return getOrCreateConfiguration(target).getProxyUsername();
+ case "registryurl":
+ case "registryUrl": return getOrCreateConfiguration(target).getRegistryUrl();
+ case "scope": return getOrCreateConfiguration(target).getScope();
+ case "sslcontextparameters":
+ case "sslContextParameters": return getOrCreateConfiguration(target).getSslContextParameters();
+ case "tokenendpoint":
+ case "tokenEndpoint": return getOrCreateConfiguration(target).getTokenEndpoint();
+ case "username": return getOrCreateConfiguration(target).getUsername();
+ case "vertx": return target.getVertx();
+ default: return null;
+ }
+ }
+}
+
diff --git a/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointConfigurer.java b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointConfigurer.java
new file mode 100644
index 0000000000000..43633a06447fa
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointConfigurer.java
@@ -0,0 +1,267 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.apicurioregistry;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class ApicurioRegistryEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+ ApicurioRegistryEndpoint target = (ApicurioRegistryEndpoint) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": target.getConfiguration().setArtifactType(property(camelContext, java.lang.String.class, value)); return true;
+ case "authtype":
+ case "authType": target.getConfiguration().setAuthType(property(camelContext, java.lang.String.class, value)); return true;
+ case "backofferrorthreshold":
+ case "backoffErrorThreshold": target.setBackoffErrorThreshold(property(camelContext, int.class, value)); return true;
+ case "backoffidlethreshold":
+ case "backoffIdleThreshold": target.setBackoffIdleThreshold(property(camelContext, int.class, value)); return true;
+ case "backoffmultiplier":
+ case "backoffMultiplier": target.setBackoffMultiplier(property(camelContext, int.class, value)); return true;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+ case "clientid":
+ case "clientId": target.getConfiguration().setClientId(property(camelContext, java.lang.String.class, value)); return true;
+ case "clientsecret":
+ case "clientSecret": target.getConfiguration().setClientSecret(property(camelContext, java.lang.String.class, value)); return true;
+ case "delay": target.setDelay(property(camelContext, long.class, value)); return true;
+ case "exceptionhandler":
+ case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true;
+ case "exchangepattern":
+ case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true;
+ case "failonvalidation":
+ case "failOnValidation": target.getConfiguration().setFailOnValidation(property(camelContext, boolean.class, value)); return true;
+ case "fetchcontent":
+ case "fetchContent": target.getConfiguration().setFetchContent(property(camelContext, boolean.class, value)); return true;
+ case "greedy": target.setGreedy(property(camelContext, boolean.class, value)); return true;
+ case "ifexists":
+ case "ifExists": target.getConfiguration().setIfExists(property(camelContext, java.lang.String.class, value)); return true;
+ case "initialdelay":
+ case "initialDelay": target.setInitialDelay(property(camelContext, long.class, value)); return true;
+ case "lazystartproducer":
+ case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+ case "operation": target.getConfiguration().setOperation(property(camelContext, java.lang.String.class, value)); return true;
+ case "password": target.getConfiguration().setPassword(property(camelContext, java.lang.String.class, value)); return true;
+ case "pollstrategy":
+ case "pollStrategy": target.setPollStrategy(property(camelContext, org.apache.camel.spi.PollingConsumerPollStrategy.class, value)); return true;
+ case "proxyhost":
+ case "proxyHost": target.getConfiguration().setProxyHost(property(camelContext, java.lang.String.class, value)); return true;
+ case "proxypassword":
+ case "proxyPassword": target.getConfiguration().setProxyPassword(property(camelContext, java.lang.String.class, value)); return true;
+ case "proxyport":
+ case "proxyPort": target.getConfiguration().setProxyPort(property(camelContext, java.lang.Integer.class, value)); return true;
+ case "proxyusername":
+ case "proxyUsername": target.getConfiguration().setProxyUsername(property(camelContext, java.lang.String.class, value)); return true;
+ case "registryclient":
+ case "registryClient": target.setRegistryClient(property(camelContext, io.apicurio.registry.rest.client.RegistryClient.class, value)); return true;
+ case "registryurl":
+ case "registryUrl": target.getConfiguration().setRegistryUrl(property(camelContext, java.lang.String.class, value)); return true;
+ case "repeatcount":
+ case "repeatCount": target.setRepeatCount(property(camelContext, long.class, value)); return true;
+ case "runlogginglevel":
+ case "runLoggingLevel": target.setRunLoggingLevel(property(camelContext, org.apache.camel.LoggingLevel.class, value)); return true;
+ case "scheduledexecutorservice":
+ case "scheduledExecutorService": target.setScheduledExecutorService(property(camelContext, java.util.concurrent.ScheduledExecutorService.class, value)); return true;
+ case "scheduler": target.setScheduler(property(camelContext, java.lang.Object.class, value)); return true;
+ case "schedulerproperties":
+ case "schedulerProperties": target.setSchedulerProperties(property(camelContext, java.util.Map.class, value)); return true;
+ case "scope": target.getConfiguration().setScope(property(camelContext, java.lang.String.class, value)); return true;
+ case "sendemptymessagewhenidle":
+ case "sendEmptyMessageWhenIdle": target.setSendEmptyMessageWhenIdle(property(camelContext, boolean.class, value)); return true;
+ case "sslcontextparameters":
+ case "sslContextParameters": target.getConfiguration().setSslContextParameters(property(camelContext, org.apache.camel.support.jsse.SSLContextParameters.class, value)); return true;
+ case "startscheduler":
+ case "startScheduler": target.setStartScheduler(property(camelContext, boolean.class, value)); return true;
+ case "timeunit":
+ case "timeUnit": target.setTimeUnit(property(camelContext, java.util.concurrent.TimeUnit.class, value)); return true;
+ case "tokenendpoint":
+ case "tokenEndpoint": target.getConfiguration().setTokenEndpoint(property(camelContext, java.lang.String.class, value)); return true;
+ case "usefixeddelay":
+ case "useFixedDelay": target.setUseFixedDelay(property(camelContext, boolean.class, value)); return true;
+ case "username": target.getConfiguration().setUsername(property(camelContext, java.lang.String.class, value)); return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public Class> getOptionType(String name, boolean ignoreCase) {
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": return java.lang.String.class;
+ case "authtype":
+ case "authType": return java.lang.String.class;
+ case "backofferrorthreshold":
+ case "backoffErrorThreshold": return int.class;
+ case "backoffidlethreshold":
+ case "backoffIdleThreshold": return int.class;
+ case "backoffmultiplier":
+ case "backoffMultiplier": return int.class;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return boolean.class;
+ case "clientid":
+ case "clientId": return java.lang.String.class;
+ case "clientsecret":
+ case "clientSecret": return java.lang.String.class;
+ case "delay": return long.class;
+ case "exceptionhandler":
+ case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class;
+ case "exchangepattern":
+ case "exchangePattern": return org.apache.camel.ExchangePattern.class;
+ case "failonvalidation":
+ case "failOnValidation": return boolean.class;
+ case "fetchcontent":
+ case "fetchContent": return boolean.class;
+ case "greedy": return boolean.class;
+ case "ifexists":
+ case "ifExists": return java.lang.String.class;
+ case "initialdelay":
+ case "initialDelay": return long.class;
+ case "lazystartproducer":
+ case "lazyStartProducer": return boolean.class;
+ case "operation": return java.lang.String.class;
+ case "password": return java.lang.String.class;
+ case "pollstrategy":
+ case "pollStrategy": return org.apache.camel.spi.PollingConsumerPollStrategy.class;
+ case "proxyhost":
+ case "proxyHost": return java.lang.String.class;
+ case "proxypassword":
+ case "proxyPassword": return java.lang.String.class;
+ case "proxyport":
+ case "proxyPort": return java.lang.Integer.class;
+ case "proxyusername":
+ case "proxyUsername": return java.lang.String.class;
+ case "registryclient":
+ case "registryClient": return io.apicurio.registry.rest.client.RegistryClient.class;
+ case "registryurl":
+ case "registryUrl": return java.lang.String.class;
+ case "repeatcount":
+ case "repeatCount": return long.class;
+ case "runlogginglevel":
+ case "runLoggingLevel": return org.apache.camel.LoggingLevel.class;
+ case "scheduledexecutorservice":
+ case "scheduledExecutorService": return java.util.concurrent.ScheduledExecutorService.class;
+ case "scheduler": return java.lang.Object.class;
+ case "schedulerproperties":
+ case "schedulerProperties": return java.util.Map.class;
+ case "scope": return java.lang.String.class;
+ case "sendemptymessagewhenidle":
+ case "sendEmptyMessageWhenIdle": return boolean.class;
+ case "sslcontextparameters":
+ case "sslContextParameters": return org.apache.camel.support.jsse.SSLContextParameters.class;
+ case "startscheduler":
+ case "startScheduler": return boolean.class;
+ case "timeunit":
+ case "timeUnit": return java.util.concurrent.TimeUnit.class;
+ case "tokenendpoint":
+ case "tokenEndpoint": return java.lang.String.class;
+ case "usefixeddelay":
+ case "useFixedDelay": return boolean.class;
+ case "username": return java.lang.String.class;
+ default: return null;
+ }
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ ApicurioRegistryEndpoint target = (ApicurioRegistryEndpoint) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "artifacttype":
+ case "artifactType": return target.getConfiguration().getArtifactType();
+ case "authtype":
+ case "authType": return target.getConfiguration().getAuthType();
+ case "backofferrorthreshold":
+ case "backoffErrorThreshold": return target.getBackoffErrorThreshold();
+ case "backoffidlethreshold":
+ case "backoffIdleThreshold": return target.getBackoffIdleThreshold();
+ case "backoffmultiplier":
+ case "backoffMultiplier": return target.getBackoffMultiplier();
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+ case "clientid":
+ case "clientId": return target.getConfiguration().getClientId();
+ case "clientsecret":
+ case "clientSecret": return target.getConfiguration().getClientSecret();
+ case "delay": return target.getDelay();
+ case "exceptionhandler":
+ case "exceptionHandler": return target.getExceptionHandler();
+ case "exchangepattern":
+ case "exchangePattern": return target.getExchangePattern();
+ case "failonvalidation":
+ case "failOnValidation": return target.getConfiguration().isFailOnValidation();
+ case "fetchcontent":
+ case "fetchContent": return target.getConfiguration().isFetchContent();
+ case "greedy": return target.isGreedy();
+ case "ifexists":
+ case "ifExists": return target.getConfiguration().getIfExists();
+ case "initialdelay":
+ case "initialDelay": return target.getInitialDelay();
+ case "lazystartproducer":
+ case "lazyStartProducer": return target.isLazyStartProducer();
+ case "operation": return target.getConfiguration().getOperation();
+ case "password": return target.getConfiguration().getPassword();
+ case "pollstrategy":
+ case "pollStrategy": return target.getPollStrategy();
+ case "proxyhost":
+ case "proxyHost": return target.getConfiguration().getProxyHost();
+ case "proxypassword":
+ case "proxyPassword": return target.getConfiguration().getProxyPassword();
+ case "proxyport":
+ case "proxyPort": return target.getConfiguration().getProxyPort();
+ case "proxyusername":
+ case "proxyUsername": return target.getConfiguration().getProxyUsername();
+ case "registryclient":
+ case "registryClient": return target.getRegistryClient();
+ case "registryurl":
+ case "registryUrl": return target.getConfiguration().getRegistryUrl();
+ case "repeatcount":
+ case "repeatCount": return target.getRepeatCount();
+ case "runlogginglevel":
+ case "runLoggingLevel": return target.getRunLoggingLevel();
+ case "scheduledexecutorservice":
+ case "scheduledExecutorService": return target.getScheduledExecutorService();
+ case "scheduler": return target.getScheduler();
+ case "schedulerproperties":
+ case "schedulerProperties": return target.getSchedulerProperties();
+ case "scope": return target.getConfiguration().getScope();
+ case "sendemptymessagewhenidle":
+ case "sendEmptyMessageWhenIdle": return target.isSendEmptyMessageWhenIdle();
+ case "sslcontextparameters":
+ case "sslContextParameters": return target.getConfiguration().getSslContextParameters();
+ case "startscheduler":
+ case "startScheduler": return target.isStartScheduler();
+ case "timeunit":
+ case "timeUnit": return target.getTimeUnit();
+ case "tokenendpoint":
+ case "tokenEndpoint": return target.getConfiguration().getTokenEndpoint();
+ case "usefixeddelay":
+ case "useFixedDelay": return target.isUseFixedDelay();
+ case "username": return target.getConfiguration().getUsername();
+ default: return null;
+ }
+ }
+
+ @Override
+ public Object getCollectionValueType(Object target, String name, boolean ignoreCase) {
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "schedulerproperties":
+ case "schedulerProperties": return java.lang.Object.class;
+ default: return null;
+ }
+ }
+}
+
diff --git a/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointUriFactory.java b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointUriFactory.java
new file mode 100644
index 0000000000000..81d7141cc1983
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointUriFactory.java
@@ -0,0 +1,124 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.apicurioregistry;
+
+import javax.annotation.processing.Generated;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo")
+public class ApicurioRegistryEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+ private static final String BASE = ":groupId/artifactId";
+
+ private static final Set PROPERTY_NAMES;
+ private static final Set SECRET_PROPERTY_NAMES;
+ private static final Set ENDPOINT_IDENTITY_PROPERTY_NAMES;
+ private static final Map MULTI_VALUE_PREFIXES;
+ static {
+ Set props = new HashSet<>(41);
+ props.add("artifactId");
+ props.add("artifactType");
+ props.add("authType");
+ props.add("backoffErrorThreshold");
+ props.add("backoffIdleThreshold");
+ props.add("backoffMultiplier");
+ props.add("bridgeErrorHandler");
+ props.add("clientId");
+ props.add("clientSecret");
+ props.add("delay");
+ props.add("exceptionHandler");
+ props.add("exchangePattern");
+ props.add("failOnValidation");
+ props.add("fetchContent");
+ props.add("greedy");
+ props.add("groupId");
+ props.add("ifExists");
+ props.add("initialDelay");
+ props.add("lazyStartProducer");
+ props.add("operation");
+ props.add("password");
+ props.add("pollStrategy");
+ props.add("proxyHost");
+ props.add("proxyPassword");
+ props.add("proxyPort");
+ props.add("proxyUsername");
+ props.add("registryClient");
+ props.add("registryUrl");
+ props.add("repeatCount");
+ props.add("runLoggingLevel");
+ props.add("scheduledExecutorService");
+ props.add("scheduler");
+ props.add("schedulerProperties");
+ props.add("scope");
+ props.add("sendEmptyMessageWhenIdle");
+ props.add("sslContextParameters");
+ props.add("startScheduler");
+ props.add("timeUnit");
+ props.add("tokenEndpoint");
+ props.add("useFixedDelay");
+ props.add("username");
+ PROPERTY_NAMES = Collections.unmodifiableSet(props);
+ Set secretProps = new HashSet<>(3);
+ secretProps.add("clientSecret");
+ secretProps.add("password");
+ secretProps.add("proxyPassword");
+ SECRET_PROPERTY_NAMES = Collections.unmodifiableSet(secretProps);
+ ENDPOINT_IDENTITY_PROPERTY_NAMES = Collections.emptySet();
+ Map prefixes = new HashMap<>(1);
+ prefixes.put("schedulerProperties", "scheduler.");
+ MULTI_VALUE_PREFIXES = Collections.unmodifiableMap(prefixes);
+ }
+
+ @Override
+ public boolean isEnabled(String scheme) {
+ return "apicurio-registry".equals(scheme);
+ }
+
+ @Override
+ public String buildUri(String scheme, Map properties, boolean encode) throws URISyntaxException {
+ String syntax = scheme + BASE;
+ String uri = syntax;
+
+ Map copy = copyParameters(properties);
+
+ uri = buildPathParameter(syntax, uri, "groupId", null, false, copy);
+ uri = buildPathParameter(syntax, uri, "artifactId", null, false, copy);
+ uri = buildQueryParameters(uri, copy, encode);
+ return uri;
+ }
+
+ @Override
+ public Set propertyNames() {
+ return PROPERTY_NAMES;
+ }
+
+ @Override
+ public Set secretPropertyNames() {
+ return SECRET_PROPERTY_NAMES;
+ }
+
+ @Override
+ public Set endpointIdentityPropertyNames() {
+ return ENDPOINT_IDENTITY_PROPERTY_NAMES;
+ }
+
+ @Override
+ public Map multiValuePrefixes() {
+ return MULTI_VALUE_PREFIXES;
+ }
+
+ @Override
+ public boolean isLenientProperties() {
+ return false;
+ }
+}
+
diff --git a/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerInvokeOnHeaderFactory.java b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerInvokeOnHeaderFactory.java
new file mode 100644
index 0000000000000..265b89d29e472
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerInvokeOnHeaderFactory.java
@@ -0,0 +1,46 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.apicurioregistry;
+
+import javax.annotation.processing.Generated;
+
+import org.apache.camel.AsyncCallback;
+import org.apache.camel.Exchange;
+import org.apache.camel.spi.InvokeOnHeaderStrategy;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+@Generated("org.apache.camel.maven.packaging.GenerateInvokeOnHeaderMojo")
+public class ApicurioRegistryProducerInvokeOnHeaderFactory implements InvokeOnHeaderStrategy {
+
+ @Override
+ public Object invoke(Object obj, String key, Exchange exchange, AsyncCallback callback) throws Exception {
+ org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer target = (org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer) obj;
+ switch (key) {
+ case "createartifact":
+ case "createArtifact": target.createArtifact(exchange.getMessage()); return null;
+ case "creategroup":
+ case "createGroup": target.createGroup(exchange.getMessage()); return null;
+ case "deleteartifact":
+ case "deleteArtifact": target.deleteArtifact(exchange.getMessage()); return null;
+ case "getartifactcontent":
+ case "getArtifactContent": target.getArtifactContent(exchange.getMessage()); return null;
+ case "getartifactmetadata":
+ case "getArtifactMetadata": target.getArtifactMetadata(exchange.getMessage()); return null;
+ case "listversions":
+ case "listVersions": target.listVersions(exchange.getMessage()); return null;
+ case "searchartifacts":
+ case "searchArtifacts": target.searchArtifacts(exchange.getMessage()); return null;
+ case "testcompatibility":
+ case "testCompatibility": target.testCompatibility(exchange.getMessage()); return null;
+ case "updateartifact":
+ case "updateArtifact": target.updateArtifact(exchange.getMessage()); return null;
+ case "validate": target.validate(exchange.getMessage()); return null;
+ default: return InvokeOnHeaderStrategy.NO_METHOD;
+ }
+ }
+
+}
+
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/org/apache/camel/component/apicurioregistry/apicurio-registry.json b/components/camel-apicurio-registry/src/generated/resources/META-INF/org/apache/camel/component/apicurioregistry/apicurio-registry.json
new file mode 100644
index 0000000000000..a2e2a9aeb014e
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/org/apache/camel/component/apicurioregistry/apicurio-registry.json
@@ -0,0 +1,115 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "apicurio-registry",
+ "title": "Apicurio Registry",
+ "description": "Manage artifacts, versions, and groups in Apicurio Registry v3.",
+ "deprecated": false,
+ "firstVersion": "4.24.0",
+ "label": "cloud,api",
+ "javaType": "org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent",
+ "supportLevel": "Preview",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-apicurio-registry",
+ "version": "4.24.0-SNAPSHOT",
+ "scheme": "apicurio-registry",
+ "extendsScheme": "",
+ "syntax": "apicurio-registry:groupId\/artifactId",
+ "async": false,
+ "api": false,
+ "consumerOnly": false,
+ "producerOnly": false,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": true
+ },
+ "componentProperties": {
+ "registryUrl": { "index": 0, "kind": "property", "displayName": "Registry Url", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The Apicurio Registry base URL" },
+ "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "fetchContent": { "index": 2, "kind": "property", "displayName": "Fetch Content", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to fetch the content for each new version found by the consumer" },
+ "artifactType": { "index": 3, "kind": "property", "displayName": "Artifact Type", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "JSON", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The default artifact type" },
+ "failOnValidation": { "index": 4, "kind": "property", "displayName": "Fail On Validation", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to throw an exception on validation failure (validate operation). When false, sets result headers instead." },
+ "ifExists": { "index": 5, "kind": "property", "displayName": "If Exists", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "FAIL", "CREATE_VERSION", "FIND_OR_CREATE_VERSION" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "FAIL", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Behavior when artifact already exists" },
+ "lazyStartProducer": { "index": 6, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
+ "operation": { "index": 7, "kind": "property", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "createArtifact", "updateArtifact", "deleteArtifact", "getArtifactContent", "getArtifactMetadata", "searchArtifacts", "listVersions", "createGroup", "testCompatibility", "validate" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The operation to perform" },
+ "autowiredEnabled": { "index": 8, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." },
+ "configuration": { "index": 9, "kind": "property", "displayName": "Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The component configuration" },
+ "vertx": { "index": 10, "kind": "property", "displayName": "Vertx", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "io.vertx.core.Vertx", "deprecated": false, "autowired": true, "secret": false, "description": "To use an existing Vert.x instance for the registry clients. If not set, the component creates one shared Vert.x instance on first use and closes it when the component stops." },
+ "proxyHost": { "index": 11, "kind": "property", "displayName": "Proxy Host", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy host to use for registry (and OAuth2 token) requests" },
+ "proxyPassword": { "index": 12, "kind": "property", "displayName": "Proxy Password", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The password for proxy authentication" },
+ "proxyPort": { "index": 13, "kind": "property", "displayName": "Proxy Port", "group": "proxy", "label": "proxy", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy port" },
+ "proxyUsername": { "index": 14, "kind": "property", "displayName": "Proxy Username", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The username for proxy authentication" },
+ "authType": { "index": 15, "kind": "property", "displayName": "Auth Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "none", "basic", "oidc" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The authentication type to use" },
+ "clientId": { "index": 16, "kind": "property", "displayName": "Client Id", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client ID" },
+ "clientSecret": { "index": 17, "kind": "property", "displayName": "Client Secret", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client secret" },
+ "password": { "index": 18, "kind": "property", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Password for basic authentication" },
+ "scope": { "index": 19, "kind": "property", "displayName": "Scope", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 scope" },
+ "sslContextParameters": { "index": 20, "kind": "property", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "To configure TLS, such as a trust store for a registry with a private CA or a client certificate (mTLS), using the Camel JSSE configuration. Also applies to the OAuth2 token endpoint." },
+ "tokenEndpoint": { "index": 21, "kind": "property", "displayName": "Token Endpoint", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 token endpoint URL" },
+ "username": { "index": 22, "kind": "property", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Username for basic authentication" }
+ },
+ "headers": {
+ "CamelApicurioRegistryOperation": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation to perform", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_OPERATION" },
+ "CamelApicurioRegistryGroupId": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact group ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_GROUP_ID" },
+ "CamelApicurioRegistryArtifactId": { "index": 2, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_ID" },
+ "CamelApicurioRegistryArtifactType": { "index": 3, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact type (e.g. AVRO, PROTOBUF, JSON, OPENAPI)", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_TYPE" },
+ "CamelApicurioRegistryVersion": { "index": 4, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact version expression", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VERSION" },
+ "CamelApicurioRegistryArtifactName": { "index": 5, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact name", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_NAME" },
+ "CamelApicurioRegistryArtifactDescription": { "index": 6, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The artifact description", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_ARTIFACT_DESCRIPTION" },
+ "CamelApicurioRegistryIfExists": { "index": 7, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Behavior when artifact already exists (FAIL, CREATE_VERSION, FIND_OR_CREATE_VERSION)", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_IF_EXISTS" },
+ "CamelApicurioRegistryContentType": { "index": 8, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content type of the artifact", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_CONTENT_TYPE" },
+ "CamelApicurioRegistryGlobalId": { "index": 9, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The version global ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_GLOBAL_ID" },
+ "CamelApicurioRegistryContentId": { "index": 10, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The content ID", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_CONTENT_ID" },
+ "CamelApicurioRegistryVersionState": { "index": 11, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The version state", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VERSION_STATE" },
+ "CamelApicurioRegistryValidationResult": { "index": 12, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether the check passed. Set by validate and testCompatibility.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VALIDATION_RESULT" },
+ "CamelApicurioRegistryValidationErrors": { "index": 13, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Rule violation details from validate or testCompatibility. Cleared before each check.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_VALIDATION_ERRORS" },
+ "CamelApicurioRegistryLabels": { "index": 14, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Labels to filter searchArtifacts by, as key:value pairs. Either a comma-separated String or a collection.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_LABELS" },
+ "CamelApicurioRegistrySearchOffset": { "index": 15, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of results to skip in searchArtifacts. Defaults to 0.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_OFFSET" },
+ "CamelApicurioRegistrySearchLimit": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The maximum number of results returned by searchArtifacts. The registry defaults to 20.", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_LIMIT" },
+ "CamelApicurioRegistrySearchOrder": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The sort order for searchArtifacts: asc or desc", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_ORDER" },
+ "CamelApicurioRegistrySearchOrderBy": { "index": 18, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The field to sort searchArtifacts by: groupId, artifactId, createdOn, modifiedOn, artifactType or name", "constantName": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants#HEADER_SEARCH_ORDER_BY" }
+ },
+ "properties": {
+ "groupId": { "index": 0, "kind": "path", "displayName": "Group Id", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The artifact group ID" },
+ "artifactId": { "index": 1, "kind": "path", "displayName": "Artifact Id", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The artifact ID" },
+ "registryUrl": { "index": 2, "kind": "parameter", "displayName": "Registry Url", "group": "common", "label": "common", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The Apicurio Registry base URL" },
+ "fetchContent": { "index": 3, "kind": "parameter", "displayName": "Fetch Content", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to fetch the content for each new version found by the consumer" },
+ "sendEmptyMessageWhenIdle": { "index": 4, "kind": "parameter", "displayName": "Send Empty Message When Idle", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If the polling consumer did not poll any files, you can enable this option to send an empty message (no body) instead." },
+ "bridgeErrorHandler": { "index": 5, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. Important: This is only possible if the 3rd party component allows Camel to be alerted if an exception was thrown. Some components handle this internally only, and therefore bridgeErrorHandler is not possible. In other situations we may improve the Camel component to hook into the 3rd party component and make this possible for future releases. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "exceptionHandler": { "index": 6, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored." },
+ "exchangePattern": { "index": 7, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+ "pollStrategy": { "index": 8, "kind": "parameter", "displayName": "Poll Strategy", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.PollingConsumerPollStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your custom implementation to control error handling usually occurred during the poll operation before an Exchange have been created and being routed in Camel." },
+ "artifactType": { "index": 9, "kind": "parameter", "displayName": "Artifact Type", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "JSON", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The default artifact type" },
+ "failOnValidation": { "index": 10, "kind": "parameter", "displayName": "Fail On Validation", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Whether to throw an exception on validation failure (validate operation). When false, sets result headers instead." },
+ "ifExists": { "index": 11, "kind": "parameter", "displayName": "If Exists", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "FAIL", "CREATE_VERSION", "FIND_OR_CREATE_VERSION" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "FAIL", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Behavior when artifact already exists" },
+ "operation": { "index": 12, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "createArtifact", "updateArtifact", "deleteArtifact", "getArtifactContent", "getArtifactMetadata", "searchArtifacts", "listVersions", "createGroup", "testCompatibility", "validate" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The operation to perform" },
+ "lazyStartProducer": { "index": 13, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." },
+ "registryClient": { "index": 14, "kind": "parameter", "displayName": "Registry Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "io.apicurio.registry.rest.client.RegistryClient", "deprecated": false, "autowired": false, "secret": false, "description": "To use a pre-configured RegistryClient instance" },
+ "proxyHost": { "index": 15, "kind": "parameter", "displayName": "Proxy Host", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy host to use for registry (and OAuth2 token) requests" },
+ "proxyPassword": { "index": 16, "kind": "parameter", "displayName": "Proxy Password", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The password for proxy authentication" },
+ "proxyPort": { "index": 17, "kind": "parameter", "displayName": "Proxy Port", "group": "proxy", "label": "proxy", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The proxy port" },
+ "proxyUsername": { "index": 18, "kind": "parameter", "displayName": "Proxy Username", "group": "proxy", "label": "proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The username for proxy authentication" },
+ "backoffErrorThreshold": { "index": 19, "kind": "parameter", "displayName": "Backoff Error Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent error polls (failed due some error) that should happen before the backoffMultipler should kick-in." },
+ "backoffIdleThreshold": { "index": 20, "kind": "parameter", "displayName": "Backoff Idle Threshold", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The number of subsequent idle polls that should happen before the backoffMultipler should kick-in." },
+ "backoffMultiplier": { "index": 21, "kind": "parameter", "displayName": "Backoff Multiplier", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "To let the scheduled polling consumer backoff if there has been a number of subsequent idles\/errors in a row. The multiplier is then the number of polls that will be skipped before the next actual attempt is happening again. When this option is in use then backoffIdleThreshold and\/or backoffErrorThreshold must also be configured." },
+ "delay": { "index": 22, "kind": "parameter", "displayName": "Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 500, "description": "Milliseconds before the next poll." },
+ "greedy": { "index": 23, "kind": "parameter", "displayName": "Greedy", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "If greedy is enabled, then the ScheduledPollConsumer will run immediately again, if the previous run polled 1 or more messages." },
+ "initialDelay": { "index": 24, "kind": "parameter", "displayName": "Initial Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "Milliseconds before the first poll starts." },
+ "repeatCount": { "index": 25, "kind": "parameter", "displayName": "Repeat Count", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Specifies a maximum limit of number of fires. So if you set it to 1, the scheduler will only fire once. If you set it to 5, it will only fire five times. A value of zero or negative means fire forever." },
+ "runLoggingLevel": { "index": 26, "kind": "parameter", "displayName": "Run Logging Level", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "enum", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "TRACE", "description": "The consumer logs a start\/complete log line when it polls. This option allows you to configure the logging level for that." },
+ "scheduledExecutorService": { "index": 27, "kind": "parameter", "displayName": "Scheduled Executor Service", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.concurrent.ScheduledExecutorService", "deprecated": false, "autowired": false, "secret": false, "description": "Allows for configuring a custom\/shared thread pool to use for the consumer. By default each consumer has its own single threaded thread pool." },
+ "scheduler": { "index": 28, "kind": "parameter", "displayName": "Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "description": "To use a cron scheduler from either camel-spring or camel-quartz component. Use value spring or quartz for built in scheduler" },
+ "schedulerProperties": { "index": 29, "kind": "parameter", "displayName": "Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "scheduler.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "To configure additional properties when using a custom scheduler or any of the Quartz, Spring based scheduler. This is a multi-value option with prefix: scheduler." },
+ "startScheduler": { "index": 30, "kind": "parameter", "displayName": "Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the scheduler should be auto started." },
+ "timeUnit": { "index": 31, "kind": "parameter", "displayName": "Time Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "enum", "javaType": "java.util.concurrent.TimeUnit", "enum": [ "NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", "DAYS" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and delay options." },
+ "useFixedDelay": { "index": 32, "kind": "parameter", "displayName": "Use Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in JDK for details." },
+ "authType": { "index": 33, "kind": "parameter", "displayName": "Auth Type", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "none", "basic", "oidc" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "none", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "The authentication type to use" },
+ "clientId": { "index": 34, "kind": "parameter", "displayName": "Client Id", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client ID" },
+ "clientSecret": { "index": 35, "kind": "parameter", "displayName": "Client Secret", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 client secret" },
+ "password": { "index": 36, "kind": "parameter", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Password for basic authentication" },
+ "scope": { "index": 37, "kind": "parameter", "displayName": "Scope", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 scope" },
+ "sslContextParameters": { "index": 38, "kind": "parameter", "displayName": "Ssl Context Parameters", "group": "security", "label": "security", "required": false, "type": "object", "javaType": "org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "To configure TLS, such as a trust store for a registry with a private CA or a client certificate (mTLS), using the Camel JSSE configuration. Also applies to the OAuth2 token endpoint." },
+ "tokenEndpoint": { "index": 39, "kind": "parameter", "displayName": "Token Endpoint", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "OAuth2 token endpoint URL" },
+ "username": { "index": 40, "kind": "parameter", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration", "configurationField": "configuration", "description": "Username for basic authentication" }
+ }
+}
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 0000000000000..2abaa1e313852
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=apicurio-registry
+groupId=org.apache.camel
+artifactId=camel-apicurio-registry
+version=4.24.0-SNAPSHOT
+projectName=Camel :: Apicurio Registry
+projectDescription=Camel Apicurio Registry component
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component/apicurio-registry b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component/apicurio-registry
new file mode 100644
index 0000000000000..50039a4ef48d3
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/component/apicurio-registry
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-component b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-component
new file mode 100644
index 0000000000000..a094536fd4c8a
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.apicurioregistry.ApicurioRegistryComponentConfigurer
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-endpoint b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-endpoint
new file mode 100644
index 0000000000000..b6875b4afe891
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/configurer/apicurio-registry-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.apicurioregistry.ApicurioRegistryEndpointConfigurer
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/invoke-on-header/org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/invoke-on-header/org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer
new file mode 100644
index 0000000000000..7ae68a23a7bc4
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/invoke-on-header/org.apache.camel.component.apicurioregistry.ApicurioRegistryProducer
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.apicurioregistry.ApicurioRegistryProducerInvokeOnHeaderFactory
diff --git a/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/urifactory/apicurio-registry-endpoint b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/urifactory/apicurio-registry-endpoint
new file mode 100644
index 0000000000000..a92bb3a88709f
--- /dev/null
+++ b/components/camel-apicurio-registry/src/generated/resources/META-INF/services/org/apache/camel/urifactory/apicurio-registry-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.apicurioregistry.ApicurioRegistryEndpointUriFactory
diff --git a/components/camel-apicurio-registry/src/main/docs/apicurio-registry-component.adoc b/components/camel-apicurio-registry/src/main/docs/apicurio-registry-component.adoc
new file mode 100644
index 0000000000000..caab1458777c1
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/docs/apicurio-registry-component.adoc
@@ -0,0 +1,304 @@
+= Apicurio Registry Component
+:doctitle: Apicurio Registry
+:shortname: apicurio-registry
+:artifactid: camel-apicurio-registry
+:description: Manage artifacts, versions, and groups in Apicurio Registry v3.
+:since: 4.24
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Both producer and consumer are supported
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Apicurio Registry component provides integration with https://www.apicur.io/registry/[Apicurio Registry] v3
+using the https://github.com/Apicurio/apicurio-registry[Apicurio Registry Java SDK].
+
+It supports CRUD operations on artifacts, groups, and versions, as well as schema validation
+and polling for new artifact versions.
+
+Maven users will need to add the following dependency to their `pom.xml`
+for this component:
+
+[source,xml]
+----
+
+ org.apache.camel
+ camel-apicurio-registry
+ x.x.x
+
+
+----
+
+== URI format
+
+----
+apicurio-registry:groupId/artifactId[?options]
+----
+
+Where `groupId` and `artifactId` identify the registry artifact. Both are optional for
+operations that do not require them (e.g., `searchArtifacts`, `createGroup`).
+
+The first `/` separates the group ID from the artifact ID. Group IDs containing `/`
+cannot be represented in the URI path. Producers can instead supply the group ID using
+the `CamelApicurioRegistryGroupId` header, which overrides the URI value.
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+include::partial$component-endpoint-headers.adoc[]
+// component options: END
+
+== Producer Operations
+
+The producer supports the following operations, set via the `operation` endpoint option or
+the `CamelApicurioRegistryOperation` header:
+
+[cols="1,3"]
+|===
+| Operation | Description
+
+| `createArtifact` | Create a new artifact. Body contains the artifact content.
+| `updateArtifact` | Create a new version of an existing artifact. Body contains the new content.
+| `deleteArtifact` | Delete an artifact. Apicurio Registry 3.x disables artifact deletion by default; the registry must run with `apicurio.rest.deletion.artifact.enabled=true` (environment variable `APICURIO_REST_DELETION_ARTIFACT_ENABLED=true`), otherwise the operation fails with HTTP 405.
+| `getArtifactContent` | Retrieve the content of an artifact version.
+| `getArtifactMetadata` | Retrieve artifact metadata.
+| `searchArtifacts` | Search for artifacts. See the "Searching artifacts" section below.
+| `listVersions` | List all versions of an artifact, ordered by global ID. The component follows the registry's pagination and returns every page.
+| `createGroup` | Create a new group.
+| `testCompatibility` | Test schema compatibility (dry run). Returns `true`/`false` in the body.
+| `validate` | Validate the message body against the artifact's rules (dry run). Throws `ApicurioRegistryValidationException` on failure when `failOnValidation=true`.
+|===
+
+Artifact content supplied to `createArtifact`, `updateArtifact`, `validate`, and
+`testCompatibility` is converted to a string for the SDK's `VersionContent` model.
+Its content type is taken from the `CamelApicurioRegistryContentType` header. Without the header, it is
+derived from the content: `application/json` when it starts with `{` or `[`, `application/xml` when it
+starts with `<`, `application/x-protobuf` or `application/graphql` for PROTOBUF and GRAPHQL artifacts,
+and `application/x-yaml` otherwise (for example OpenAPI or AsyncAPI documents written in YAML).
+For AVRO and PROTOBUF, supply the schema definition, rather than serialized message data.
+`getArtifactContent` returns a `byte[]`; the component closes the SDK response stream
+before passing the content to the route.
+
+`validate` and `testCompatibility` check a proposed artifact version against the registry's
+configured rules without storing it. They do not validate application messages against a
+stored schema. Authentication, connectivity, and other non-rule failures propagate as
+exceptions, rather than returning a negative validation result.
+
+Both operations set `CamelApicurioRegistryValidationErrors` to the rule violation details
+when a check fails and clear any previous value before checking. Both set
+`CamelApicurioRegistryValidationResult`; `testCompatibility` also returns the boolean result
+in the body, while `validate` preserves the body.
+
+Operations that act on a specific group or artifact fail with an `IllegalArgumentException`
+when the group or artifact ID is set neither in the endpoint path nor in a header.
+
+=== Searching artifacts
+
+`searchArtifacts` filters by group ID and artifact ID (endpoint path or headers), and by the
+`CamelApicurioRegistryArtifactName`, `CamelApicurioRegistryArtifactDescription`,
+`CamelApicurioRegistryArtifactType` and `CamelApicurioRegistryLabels` headers. The body is an
+`ArtifactSearchResults` holding one page of results and the total `count`.
+
+Results are paged: the registry returns at most 20 artifacts unless you set
+`CamelApicurioRegistrySearchLimit`. Use `CamelApicurioRegistrySearchOffset` to request further
+pages, and `CamelApicurioRegistrySearchOrder` (`asc` or `desc`) and
+`CamelApicurioRegistrySearchOrderBy` (`groupId`, `artifactId`, `createdOn`, `modifiedOn`,
+`artifactType` or `name`) to sort them.
+
+=== Headers from a consumer
+
+The consumer sets the group, artifact and version headers on every exchange, and producers give
+these headers precedence over their endpoint path. When a route forwards consumed versions to
+another registry endpoint, remove them first so the producer uses its own path:
+
+[source,java]
+----
+from("apicurio-registry:dev/orders?registryUrl={{dev.registry}}&fetchContent=true")
+ .removeHeaders("CamelApicurioRegistry*")
+ .to("apicurio-registry:prod/orders?registryUrl={{prod.registry}}&operation=updateArtifact");
+----
+
+== Consumer
+
+The consumer polls the registry for new versions of the specified artifact. It tracks the last
+seen `globalId` and only delivers new versions. Set the `delay` option to control the polling
+interval (default: 500ms).
+
+On first start, all existing versions of the artifact are emitted as exchanges. Subsequent polls
+only deliver versions created after the consumer started. The watermark is held in memory and
+resets on route/context restart.
+
+Once it has a watermark, each poll first asks only for the newest version, so an idle poll
+transfers a single entry. When there is something new, the consumer requests versions newest
+first, one page at a time, and stops at the first version it has already seen. Paging is
+offset-based: if a version is deleted while the consumer is reading pages, an older new version
+can move to a page that was already read and be skipped.
+
+All version states are listed, so `DRAFT` versions are delivered as soon as they appear. If the
+registry allows changing the content of a version, a version is not delivered again after its
+content changes. With `fetchContent=true`, `DISABLED` versions are skipped, because the registry
+does not return their content. If the content of a version is not found (HTTP 404), the consumer
+retries it on later polls, since a registry node with replicated storage may not have the version
+yet; if it is still not found once at least one polling `delay` has passed (also with `greedy=true`),
+the version is skipped with a warning. Skipped versions are not
+delivered again, even if a disabled version is enabled later.
+
+If fetching content fails, or the route fails to process a version (including failures
+handled by the error handler without being rethrown, such as with the default error handler),
+the consumer stops the current poll without advancing the watermark. The failed version and any
+newer ones are delivered again on the next poll. A version that always fails therefore blocks
+newer versions. To skip such a version, mark the failure as handled, which lets the consumer
+advance past it:
+
+[source,java]
+----
+onException(MyPermanentException.class).handled(true).to("log:skipped");
+----
+
+When `fetchContent=true`, the consumer fetches the version content and sets it as a `byte[]` exchange body,
+closing the SDK response stream before processing the exchange.
+Otherwise, the version metadata object is set as the body.
+
+== Examples
+
+=== Create an artifact
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:create")
+ .setBody(constant("{\"type\":\"record\",\"name\":\"Test\",\"fields\":[]}"))
+ .to("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=createArtifact&artifactType=AVRO");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+ {"type":"record","name":"Test","fields":[]}
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:create
+ steps:
+ - setBody:
+ constant: '{"type":"record","name":"Test","fields":[]}'
+ - to:
+ uri: apicurio-registry:myGroup/myArtifact
+ parameters:
+ registryUrl: http://localhost:8080/apis/registry/v3
+ operation: createArtifact
+ artifactType: AVRO
+----
+====
+
+=== Poll for new versions
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3&delay=5000")
+ .log("New version detected: ${header.CamelApicurioRegistryVersion}")
+ .to("log:newVersions");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: apicurio-registry:myGroup/myArtifact
+ parameters:
+ registryUrl: http://localhost:8080/apis/registry/v3
+ delay: 5000
+ steps:
+ - log:
+ message: "New version detected: ${header.CamelApicurioRegistryVersion}"
+ - to:
+ uri: log:newVersions
+----
+====
+
+=== Validate a proposed schema version
+
+[source,java]
+----
+from("direct:validateSchema")
+ .doTry()
+ .to("apicurio-registry:my-group/order-schema?operation=validate®istryUrl=http://localhost:8080/apis/registry/v3")
+ .to("direct:publishSchema")
+ .doCatch(ApicurioRegistryValidationException.class)
+ .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
+ .setBody(simple("${exception.validationErrors}"))
+ .end();
+----
+
+== Connection settings
+
+All endpoints of the component share a single Vert.x instance for their SDK clients. The
+component creates it on first use and closes it when the component stops. To reuse an existing
+Vert.x instance, set the `vertx` component option or bind a single `Vertx` bean in the registry;
+the component does not close a Vert.x instance it did not create.
+
+A client supplied through `registryClient` remains caller-owned;
+the caller is responsible for its underlying HTTP resources.
+
+For HTTPS registries that use a private CA or require client certificates (mTLS), configure
+`sslContextParameters`. Use `proxyHost`, `proxyPort`, `proxyUsername` and `proxyPassword` to go
+through an HTTP proxy. Both settings also apply to the OAuth2 token endpoint.
+
+== Authentication
+
+The component never follows HTTP redirects, so credentials are never sent to another host. Configure
+`registryUrl` with the final address of the registry API.
+
+The producer makes blocking calls. It cannot run on a Vert.x event loop thread, for example in a route
+started by an event-loop consumer of the same Vert.x instance; it fails with an `IllegalStateException`
+there. Hand the exchange to a worker thread first, for example with `threads()`.
+
+The component supports three authentication modes via the `authType` option:
+
+* `none` (default): No authentication.
+* `basic`: Basic authentication using `username` and `password`.
+* `oidc`: OAuth2/OIDC using `tokenEndpoint`, `clientId`, `clientSecret`, and optionally `scope`.
+
+Example with OIDC:
+
+[source,java]
+----
+from("direct:secured")
+ .to("apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=getArtifactContent"
+ + "&authType=oidc"
+ + "&tokenEndpoint=http://keycloak:8080/realms/registry/protocol/openid-connect/token"
+ + "&clientId=registry-client&clientSecret=secret");
+----
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponent.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponent.java
new file mode 100644
index 0000000000000..6ed705f779a67
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponent.java
@@ -0,0 +1,124 @@
+/*
+ * 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.apicurioregistry;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import io.vertx.core.Vertx;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+
+@Component("apicurio-registry")
+public class ApicurioRegistryComponent extends DefaultComponent {
+
+ @Metadata(label = "advanced", description = "The component configuration")
+ private ApicurioRegistryConfiguration configuration = new ApicurioRegistryConfiguration();
+
+ @Metadata(label = "advanced", autowired = true,
+ description = "To use an existing Vert.x instance for the registry clients. If not set, the component creates"
+ + " one shared Vert.x instance on first use and closes it when the component stops.")
+ private Vertx vertx;
+
+ private boolean managedVertx;
+ private Vertx replacedManagedVertx;
+
+ public ApicurioRegistryComponent() {
+ }
+
+ public ApicurioRegistryComponent(CamelContext context) {
+ super(context);
+ }
+
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
+ ApicurioRegistryConfiguration config = this.configuration.copy();
+
+ String groupId = null;
+ String artifactId = null;
+ if (remaining != null && !remaining.isEmpty()) {
+ String[] parts = remaining.split("/", 2);
+ groupId = parts[0].isEmpty() ? null : parts[0];
+ if (parts.length > 1 && !parts[1].isEmpty()) {
+ artifactId = parts[1];
+ }
+ }
+
+ ApicurioRegistryEndpoint endpoint = new ApicurioRegistryEndpoint(uri, this, config, groupId, artifactId);
+ setProperties(endpoint, parameters);
+ return endpoint;
+ }
+
+ synchronized Vertx getOrCreateVertx() {
+ if (vertx == null) {
+ vertx = Vertx.vertx();
+ managedVertx = true;
+ }
+ return vertx;
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+ Vertx toClose = null;
+ Vertx replaced;
+ synchronized (this) {
+ if (managedVertx && vertx != null) {
+ toClose = vertx;
+ vertx = null;
+ managedVertx = false;
+ }
+ replaced = replacedManagedVertx;
+ replacedManagedVertx = null;
+ }
+ closeVertx(toClose);
+ closeVertx(replaced);
+ }
+
+ private static void closeVertx(Vertx vertx) throws Exception {
+ if (vertx != null) {
+ vertx.close().toCompletionStage().toCompletableFuture().get(30, TimeUnit.SECONDS);
+ }
+ }
+
+ public synchronized Vertx getVertx() {
+ return vertx;
+ }
+
+ /**
+ * Sets the Vert.x instance to use. The component never closes a Vert.x instance set here. If the component had
+ * already created its own instance, that one is still closed when the component stops.
+ */
+ public synchronized void setVertx(Vertx vertx) {
+ if (managedVertx && this.vertx != null && this.vertx != vertx) {
+ replacedManagedVertx = this.vertx;
+ }
+ this.vertx = vertx;
+ this.managedVertx = false;
+ }
+
+ public ApicurioRegistryConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(ApicurioRegistryConfiguration configuration) {
+ this.configuration = configuration;
+ }
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConfiguration.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConfiguration.java
new file mode 100644
index 0000000000000..5787747606f60
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConfiguration.java
@@ -0,0 +1,247 @@
+/*
+ * 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.apicurioregistry;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.apache.camel.support.jsse.SSLContextParameters;
+
+@UriParams
+public class ApicurioRegistryConfiguration implements Cloneable {
+
+ @UriParam(label = "common", description = "The Apicurio Registry base URL")
+ @Metadata(required = true)
+ private String registryUrl;
+
+ @UriParam(label = "producer",
+ enums = "createArtifact,updateArtifact,deleteArtifact,getArtifactContent,getArtifactMetadata,searchArtifacts,listVersions,createGroup,testCompatibility,validate",
+ description = "The operation to perform")
+ private String operation;
+
+ @UriParam(label = "producer", description = "The default artifact type", defaultValue = "JSON")
+ private String artifactType = "JSON";
+
+ @UriParam(label = "security", enums = "none,basic,oidc", defaultValue = "none",
+ description = "The authentication type to use")
+ private String authType = "none";
+
+ @UriParam(label = "security", description = "Username for basic authentication")
+ private String username;
+
+ @UriParam(label = "security", security = "secret", description = "Password for basic authentication")
+ private String password;
+
+ @UriParam(label = "security", description = "OAuth2 token endpoint URL")
+ private String tokenEndpoint;
+
+ @UriParam(label = "security", description = "OAuth2 client ID")
+ private String clientId;
+
+ @UriParam(label = "security", security = "secret", description = "OAuth2 client secret")
+ private String clientSecret;
+
+ @UriParam(label = "security", description = "OAuth2 scope")
+ private String scope;
+
+ @UriParam(label = "producer",
+ description = "Behavior when artifact already exists",
+ enums = "FAIL,CREATE_VERSION,FIND_OR_CREATE_VERSION",
+ defaultValue = "FAIL")
+ private String ifExists = "FAIL";
+
+ @UriParam(label = "consumer",
+ description = "Whether to fetch the content for each new version found by the consumer",
+ defaultValue = "false")
+ private boolean fetchContent;
+
+ @UriParam(label = "producer",
+ description = "Whether to throw an exception on validation failure (validate operation). When false, sets result headers instead.",
+ defaultValue = "true")
+ private boolean failOnValidation = true;
+
+ @UriParam(label = "security",
+ description = "To configure TLS, such as a trust store for a registry with a private CA or a client"
+ + " certificate (mTLS), using the Camel JSSE configuration. Also applies to the OAuth2"
+ + " token endpoint.")
+ private SSLContextParameters sslContextParameters;
+
+ @UriParam(label = "proxy", description = "The proxy host to use for registry (and OAuth2 token) requests")
+ private String proxyHost;
+
+ @UriParam(label = "proxy", description = "The proxy port")
+ private Integer proxyPort;
+
+ @UriParam(label = "proxy", description = "The username for proxy authentication")
+ private String proxyUsername;
+
+ @UriParam(label = "proxy", security = "secret", description = "The password for proxy authentication")
+ private String proxyPassword;
+
+ public SSLContextParameters getSslContextParameters() {
+ return sslContextParameters;
+ }
+
+ public void setSslContextParameters(SSLContextParameters sslContextParameters) {
+ this.sslContextParameters = sslContextParameters;
+ }
+
+ public String getProxyHost() {
+ return proxyHost;
+ }
+
+ public void setProxyHost(String proxyHost) {
+ this.proxyHost = proxyHost;
+ }
+
+ public Integer getProxyPort() {
+ return proxyPort;
+ }
+
+ public void setProxyPort(Integer proxyPort) {
+ this.proxyPort = proxyPort;
+ }
+
+ public String getProxyUsername() {
+ return proxyUsername;
+ }
+
+ public void setProxyUsername(String proxyUsername) {
+ this.proxyUsername = proxyUsername;
+ }
+
+ public String getProxyPassword() {
+ return proxyPassword;
+ }
+
+ public void setProxyPassword(String proxyPassword) {
+ this.proxyPassword = proxyPassword;
+ }
+
+ public String getRegistryUrl() {
+ return registryUrl;
+ }
+
+ public void setRegistryUrl(String registryUrl) {
+ this.registryUrl = registryUrl;
+ }
+
+ public String getOperation() {
+ return operation;
+ }
+
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(String authType) {
+ this.authType = authType;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getTokenEndpoint() {
+ return tokenEndpoint;
+ }
+
+ public void setTokenEndpoint(String tokenEndpoint) {
+ this.tokenEndpoint = tokenEndpoint;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getClientSecret() {
+ return clientSecret;
+ }
+
+ public void setClientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getIfExists() {
+ return ifExists;
+ }
+
+ public void setIfExists(String ifExists) {
+ this.ifExists = ifExists;
+ }
+
+ public boolean isFetchContent() {
+ return fetchContent;
+ }
+
+ public void setFetchContent(boolean fetchContent) {
+ this.fetchContent = fetchContent;
+ }
+
+ public boolean isFailOnValidation() {
+ return failOnValidation;
+ }
+
+ public void setFailOnValidation(boolean failOnValidation) {
+ this.failOnValidation = failOnValidation;
+ }
+
+ public ApicurioRegistryConfiguration copy() {
+ try {
+ return (ApicurioRegistryConfiguration) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeCamelException(e);
+ }
+ }
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConstants.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConstants.java
new file mode 100644
index 0000000000000..79a0fff5601d1
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConstants.java
@@ -0,0 +1,101 @@
+/*
+ * 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.apicurioregistry;
+
+import org.apache.camel.spi.Metadata;
+
+public interface ApicurioRegistryConstants {
+
+ @Metadata(description = "The operation to perform", javaType = "String")
+ String HEADER_OPERATION = "CamelApicurioRegistryOperation";
+
+ @Metadata(description = "The artifact group ID", javaType = "String")
+ String HEADER_GROUP_ID = "CamelApicurioRegistryGroupId";
+
+ @Metadata(description = "The artifact ID", javaType = "String")
+ String HEADER_ARTIFACT_ID = "CamelApicurioRegistryArtifactId";
+
+ @Metadata(description = "The artifact type (e.g. AVRO, PROTOBUF, JSON, OPENAPI)", javaType = "String")
+ String HEADER_ARTIFACT_TYPE = "CamelApicurioRegistryArtifactType";
+
+ @Metadata(description = "The artifact version expression", javaType = "String")
+ String HEADER_VERSION = "CamelApicurioRegistryVersion";
+
+ @Metadata(description = "The artifact name", javaType = "String")
+ String HEADER_ARTIFACT_NAME = "CamelApicurioRegistryArtifactName";
+
+ @Metadata(description = "The artifact description", javaType = "String")
+ String HEADER_ARTIFACT_DESCRIPTION = "CamelApicurioRegistryArtifactDescription";
+
+ @Metadata(description = "Behavior when artifact already exists (FAIL, CREATE_VERSION, FIND_OR_CREATE_VERSION)",
+ javaType = "String")
+ String HEADER_IF_EXISTS = "CamelApicurioRegistryIfExists";
+
+ @Metadata(description = "The content type of the artifact", javaType = "String")
+ String HEADER_CONTENT_TYPE = "CamelApicurioRegistryContentType";
+
+ @Metadata(label = "consumer", description = "The version global ID", javaType = "Long")
+ String HEADER_GLOBAL_ID = "CamelApicurioRegistryGlobalId";
+
+ @Metadata(label = "consumer", description = "The content ID", javaType = "Long")
+ String HEADER_CONTENT_ID = "CamelApicurioRegistryContentId";
+
+ @Metadata(label = "consumer", description = "The version state", javaType = "String")
+ String HEADER_VERSION_STATE = "CamelApicurioRegistryVersionState";
+
+ @Metadata(description = "Whether the check passed. Set by validate and testCompatibility.", javaType = "Boolean")
+ String HEADER_VALIDATION_RESULT = "CamelApicurioRegistryValidationResult";
+
+ @Metadata(description = "Rule violation details from validate or testCompatibility. Cleared before each check.",
+ javaType = "String")
+ String HEADER_VALIDATION_ERRORS = "CamelApicurioRegistryValidationErrors";
+
+ @Metadata(label = "producer",
+ description = "Labels to filter searchArtifacts by, as key:value pairs. Either a comma-separated String"
+ + " or a collection.",
+ javaType = "String")
+ String HEADER_LABELS = "CamelApicurioRegistryLabels";
+
+ @Metadata(label = "producer", description = "The number of results to skip in searchArtifacts. Defaults to 0.",
+ javaType = "Integer")
+ String HEADER_SEARCH_OFFSET = "CamelApicurioRegistrySearchOffset";
+
+ @Metadata(label = "producer",
+ description = "The maximum number of results returned by searchArtifacts. The registry defaults to 20.",
+ javaType = "Integer")
+ String HEADER_SEARCH_LIMIT = "CamelApicurioRegistrySearchLimit";
+
+ @Metadata(label = "producer", description = "The sort order for searchArtifacts: asc or desc", javaType = "String")
+ String HEADER_SEARCH_ORDER = "CamelApicurioRegistrySearchOrder";
+
+ @Metadata(label = "producer",
+ description = "The field to sort searchArtifacts by: groupId, artifactId, createdOn, modifiedOn,"
+ + " artifactType or name",
+ javaType = "String")
+ String HEADER_SEARCH_ORDER_BY = "CamelApicurioRegistrySearchOrderBy";
+
+ String OPERATION_CREATE_ARTIFACT = "createArtifact";
+ String OPERATION_UPDATE_ARTIFACT = "updateArtifact";
+ String OPERATION_DELETE_ARTIFACT = "deleteArtifact";
+ String OPERATION_GET_ARTIFACT_CONTENT = "getArtifactContent";
+ String OPERATION_GET_ARTIFACT_METADATA = "getArtifactMetadata";
+ String OPERATION_SEARCH_ARTIFACTS = "searchArtifacts";
+ String OPERATION_LIST_VERSIONS = "listVersions";
+ String OPERATION_CREATE_GROUP = "createGroup";
+ String OPERATION_TEST_COMPATIBILITY = "testCompatibility";
+ String OPERATION_VALIDATE = "validate";
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumer.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumer.java
new file mode 100644
index 0000000000000..81e70b914e13e
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumer.java
@@ -0,0 +1,205 @@
+/*
+ * 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.apicurioregistry;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.apicurio.registry.rest.client.models.ProblemDetails;
+import io.apicurio.registry.rest.client.models.SearchedVersion;
+import io.apicurio.registry.rest.client.models.SortOrder;
+import io.apicurio.registry.rest.client.models.VersionSearchResults;
+import io.apicurio.registry.rest.client.models.VersionSortBy;
+import io.apicurio.registry.rest.client.models.VersionState;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.support.ScheduledPollConsumer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ApicurioRegistryConsumer extends ScheduledPollConsumer {
+
+ static final int PAGE_SIZE = 100;
+
+ private static final Logger LOG = LoggerFactory.getLogger(ApicurioRegistryConsumer.class);
+
+ private final ApicurioRegistryEndpoint endpoint;
+ private final ApicurioRegistryConfiguration configuration;
+ private volatile Long lastSeenGlobalId;
+ // only accessed from the single poll thread managed by ScheduledPollConsumer, so volatile is not needed
+ private Long notFoundGlobalId;
+ private long notFoundSince;
+
+ public ApicurioRegistryConsumer(ApicurioRegistryEndpoint endpoint, Processor processor,
+ ApicurioRegistryConfiguration configuration) {
+ super(endpoint, processor);
+ this.endpoint = endpoint;
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected int poll() throws Exception {
+ String groupId = endpoint.getGroupId();
+ String artifactId = endpoint.getArtifactId();
+ RegistryClient client = endpoint.getRegistryClient();
+
+ int count = 0;
+ for (SearchedVersion version : fetchNewVersions(client, groupId, artifactId)) {
+ Long globalId = version.getGlobalId();
+ if (configuration.isFetchContent() && version.getState() == VersionState.DISABLED) {
+ // the registry answers 404 for the content of a disabled version, so it can never be delivered
+ LOG.info("Skipping disabled artifact version {} (globalId {}): its content cannot be fetched and it will not be"
+ + " delivered again if it is enabled later",
+ version.getVersion(), globalId);
+ lastSeenGlobalId = globalId;
+ continue;
+ }
+ Exchange exchange = createExchange(false);
+ try {
+ Message message = exchange.getIn();
+ message.setHeader(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ message.setHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ message.setHeader(ApicurioRegistryConstants.HEADER_VERSION, version.getVersion());
+ message.setHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, globalId);
+ message.setHeader(ApicurioRegistryConstants.HEADER_CONTENT_ID, version.getContentId());
+ message.setHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, version.getArtifactType());
+ if (version.getState() != null) {
+ message.setHeader(ApicurioRegistryConstants.HEADER_VERSION_STATE, version.getState().getValue());
+ }
+
+ if (configuration.isFetchContent()) {
+ byte[] content;
+ try {
+ content = fetchContent(client, groupId, artifactId, version);
+ } catch (ProblemDetails e) {
+ if (e.getStatus() == null || e.getStatus() != 404) {
+ throw e;
+ }
+ // a 404 is not always final: with replicated storage another registry node may not have
+ // the version yet, so only give up on it once at least one polling delay has passed. This
+ // also holds when greedy polling runs the next poll immediately.
+ long now = System.currentTimeMillis();
+ if (!globalId.equals(notFoundGlobalId)) {
+ notFoundGlobalId = globalId;
+ notFoundSince = now;
+ }
+ if (now - notFoundSince < getDelay()) {
+ LOG.debug("Content of artifact version {} (globalId {}) not found, retrying on a later poll",
+ version.getVersion(), globalId);
+ break;
+ }
+ LOG.warn("Skipping artifact version {} (globalId {}) whose content is still not available (HTTP 404)"
+ + " after {} ms; it will not be delivered: {}",
+ version.getVersion(), globalId, now - notFoundSince, e.getDetail());
+ notFoundGlobalId = null;
+ lastSeenGlobalId = globalId;
+ continue;
+ }
+ notFoundGlobalId = null;
+ message.setBody(content);
+ } else {
+ message.setBody(version);
+ }
+
+ getProcessor().process(exchange);
+ if (exchange.getException() != null) {
+ // do not advance the watermark so the version is retried on the next poll
+ if (exchange.getExchangeExtension().isErrorHandlerHandledSet()) {
+ // the error handler has already logged the failure
+ LOG.debug("Artifact version with globalId {} failed and will be retried on the next poll", globalId);
+ } else {
+ getExceptionHandler().handleException(
+ "Error processing artifact version with globalId " + globalId, exchange,
+ exchange.getException());
+ }
+ break;
+ }
+ lastSeenGlobalId = globalId;
+ count++;
+ } finally {
+ releaseExchange(exchange, false);
+ }
+ }
+ return count;
+ }
+
+ private static byte[] fetchContent(
+ RegistryClient client, String groupId, String artifactId, SearchedVersion version)
+ throws Exception {
+ try (InputStream content = client.groups().byGroupId(groupId).artifacts().byArtifactId(artifactId)
+ .versions().byVersionExpression(version.getVersion()).content().get()) {
+ return content.readAllBytes();
+ }
+ }
+
+ /**
+ * Fetches the versions newer than the watermark, in ascending globalId order. Pages are requested newest first so
+ * that polling stops as soon as an already seen version is reached.
+ */
+ private List fetchNewVersions(RegistryClient client, String groupId, String artifactId) {
+ // keyed by globalId: a version created between page requests shifts the pages and may repeat one entry
+ Map answer = new TreeMap<>();
+ if (lastSeenGlobalId != null) {
+ // cheap probe so that an idle poll only transfers the newest version
+ VersionSearchResults newest = client.groups().byGroupId(groupId).artifacts().byArtifactId(artifactId)
+ .versions().get(config -> {
+ config.queryParameters.orderby = VersionSortBy.GlobalId;
+ config.queryParameters.order = SortOrder.Desc;
+ config.queryParameters.offset = 0;
+ config.queryParameters.limit = 1;
+ });
+ if (newest == null || newest.getVersions() == null || newest.getVersions().isEmpty()
+ || newest.getVersions().get(0).getGlobalId() <= lastSeenGlobalId) {
+ return List.of();
+ }
+ }
+ int offset = 0;
+ while (true) {
+ final int pageOffset = offset;
+ VersionSearchResults page = client.groups().byGroupId(groupId).artifacts().byArtifactId(artifactId)
+ .versions().get(config -> {
+ config.queryParameters.orderby = VersionSortBy.GlobalId;
+ config.queryParameters.order = SortOrder.Desc;
+ config.queryParameters.offset = pageOffset;
+ config.queryParameters.limit = PAGE_SIZE;
+ });
+ List versions = page != null ? page.getVersions() : null;
+ if (versions == null || versions.isEmpty()) {
+ break;
+ }
+ boolean reachedWatermark = false;
+ for (SearchedVersion version : versions) {
+ if (lastSeenGlobalId != null && version.getGlobalId() <= lastSeenGlobalId) {
+ reachedWatermark = true;
+ break;
+ }
+ answer.put(version.getGlobalId(), version);
+ }
+ offset += versions.size();
+ if (reachedWatermark || versions.size() < PAGE_SIZE
+ || page.getCount() != null && offset >= page.getCount()) {
+ break;
+ }
+ }
+ return new ArrayList<>(answer.values());
+ }
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpoint.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpoint.java
new file mode 100644
index 0000000000000..0b2d6bf724a60
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpoint.java
@@ -0,0 +1,278 @@
+/*
+ * 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.apicurioregistry;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Collections;
+
+import io.apicurio.registry.client.RegistryClientFactory;
+import io.apicurio.registry.client.common.RegistryClientOptions;
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.netty.handler.codec.http.cookie.Cookie;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.net.ProxyOptions;
+import io.vertx.ext.auth.oauth2.OAuth2Auth;
+import io.vertx.ext.auth.oauth2.OAuth2FlowType;
+import io.vertx.ext.auth.oauth2.OAuth2Options;
+import io.vertx.ext.auth.oauth2.Oauth2Credentials;
+import io.vertx.ext.web.client.OAuth2WebClient;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.client.WebClientOptions;
+import io.vertx.ext.web.client.WebClientSession;
+import io.vertx.ext.web.client.spi.CookieStore;
+import org.apache.camel.Category;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.component.vertx.common.VertxHelper;
+import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.ScheduledPollEndpoint;
+import org.apache.camel.util.ObjectHelper;
+
+/**
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ */
+@UriEndpoint(firstVersion = "4.24.0", scheme = "apicurio-registry", title = "Apicurio Registry",
+ syntax = "apicurio-registry:groupId/artifactId",
+ category = { Category.CLOUD, Category.API }, headersClass = ApicurioRegistryConstants.class)
+public class ApicurioRegistryEndpoint extends ScheduledPollEndpoint implements EndpointServiceLocation {
+
+ @UriPath(description = "The artifact group ID")
+ private String groupId;
+
+ @UriPath(description = "The artifact ID")
+ private String artifactId;
+
+ @UriParam
+ private ApicurioRegistryConfiguration configuration;
+
+ @UriParam(label = "advanced", description = "To use a pre-configured RegistryClient instance")
+ private RegistryClient registryClient;
+
+ private WebClient ownedWebClient;
+ private HttpClient ownedHttpClient;
+ private OAuth2Auth oauth2;
+
+ ApicurioRegistryEndpoint(String uri, ApicurioRegistryComponent component,
+ ApicurioRegistryConfiguration configuration,
+ String groupId, String artifactId) {
+ super(uri, component);
+ this.configuration = configuration;
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ }
+
+ @Override
+ public Producer createProducer() throws Exception {
+ return new ApicurioRegistryProducer(this, configuration);
+ }
+
+ @Override
+ public Consumer createConsumer(Processor processor) throws Exception {
+ if (ObjectHelper.isEmpty(groupId) || ObjectHelper.isEmpty(artifactId)) {
+ throw new IllegalArgumentException(
+ "Both groupId and artifactId are required for the consumer, for example apicurio-registry:myGroup/myArtifact");
+ }
+ ApicurioRegistryConsumer consumer = new ApicurioRegistryConsumer(this, processor, configuration);
+ configureConsumer(consumer);
+ return consumer;
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ if (registryClient == null) {
+ Vertx vertx = ((ApicurioRegistryComponent) getComponent()).getOrCreateVertx();
+ try {
+ ownedWebClient = createWebClient(vertx);
+ registryClient = RegistryClientFactory.create(
+ RegistryClientOptions.create(configuration.getRegistryUrl(), vertx).customWebClient(ownedWebClient));
+ } catch (Exception e) {
+ closeOwnedResources();
+ throw e;
+ }
+ }
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+ // only clean up what this endpoint created; a client set with setRegistryClient() survives a restart
+ if (ownedWebClient != null) {
+ registryClient = null;
+ closeOwnedResources();
+ }
+ }
+
+ private void closeOwnedResources() {
+ // the SDK client has no close method, so the HTTP connection pool lives in the WebClient we created
+ if (ownedWebClient != null) {
+ ownedWebClient.close();
+ ownedWebClient = null;
+ }
+ if (ownedHttpClient != null) {
+ ownedHttpClient.close();
+ ownedHttpClient = null;
+ }
+ if (oauth2 != null) {
+ oauth2.close();
+ oauth2 = null;
+ }
+ }
+
+ WebClientOptions createWebClientOptions() throws Exception {
+ WebClientOptions options = new WebClientOptions().setFollowRedirects(false);
+ if (configuration.getSslContextParameters() != null) {
+ VertxHelper.setupSSLOptions(getCamelContext(), configuration.getSslContextParameters(), options);
+ }
+ if (ObjectHelper.isNotEmpty(configuration.getProxyHost())) {
+ ProxyOptions proxy = new ProxyOptions().setHost(configuration.getProxyHost());
+ if (configuration.getProxyPort() != null) {
+ proxy.setPort(configuration.getProxyPort());
+ }
+ if (ObjectHelper.isNotEmpty(configuration.getProxyUsername())) {
+ proxy.setUsername(configuration.getProxyUsername());
+ proxy.setPassword(configuration.getProxyPassword());
+ }
+ options.setProxyOptions(proxy);
+ }
+ return options;
+ }
+
+ private void validateAuthentication() {
+ String authType = configuration.getAuthType();
+ if (ObjectHelper.isEmpty(authType) || "none".equalsIgnoreCase(authType)) {
+ return;
+ }
+ if ("basic".equalsIgnoreCase(authType)) {
+ requireOption(configuration.getUsername(), "username", authType);
+ requireOption(configuration.getPassword(), "password", authType);
+ } else if ("oidc".equalsIgnoreCase(authType)) {
+ requireOption(configuration.getTokenEndpoint(), "tokenEndpoint", authType);
+ requireOption(configuration.getClientId(), "clientId", authType);
+ requireOption(configuration.getClientSecret(), "clientSecret", authType);
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported authType '" + authType + "'. Supported values are none, basic and oidc");
+ }
+ }
+
+ private static void requireOption(String value, String option, String authType) {
+ if (ObjectHelper.isEmpty(value)) {
+ throw new IllegalArgumentException("The " + option + " option is required when authType=" + authType);
+ }
+ }
+
+ private WebClient createWebClient(Vertx vertx) throws Exception {
+ validateAuthentication();
+ WebClientOptions options = createWebClientOptions();
+ // The SDK's Vert.x adapter turns redirect following on for every request, so the option above is not enough.
+ // The client adds credentials (Basic header or OAuth2 token) to every request, and following a redirect to
+ // another host would send them there, so the redirect handler of the underlying client never follows one.
+ ownedHttpClient = vertx.httpClientBuilder().with(options).withRedirectHandler(response -> null).build();
+ WebClient webClient = WebClient.wrap(ownedHttpClient, options);
+ try {
+ String authType = configuration.getAuthType();
+ if ("basic".equalsIgnoreCase(authType)) {
+ String credentials = configuration.getUsername() + ":" + configuration.getPassword();
+ // the session is only used to add the Authorization header: it gets a cookie store that keeps
+ // nothing, so response cookies are not shared between the requests of this endpoint
+ return WebClientSession.create(webClient, NoCookieStore.INSTANCE).addHeader("Authorization",
+ "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)));
+ } else if ("oidc".equalsIgnoreCase(authType)) {
+ oauth2 = OAuth2Auth.create(vertx, new OAuth2Options()
+ .setHttpClientOptions(options)
+ .setClientId(configuration.getClientId())
+ .setClientSecret(configuration.getClientSecret())
+ .setTokenPath(configuration.getTokenEndpoint()));
+ Oauth2Credentials credentials = new Oauth2Credentials().setFlow(OAuth2FlowType.CLIENT);
+ if (configuration.getScope() != null) {
+ credentials.addScope(configuration.getScope());
+ }
+ return OAuth2WebClient.create(webClient, oauth2).withCredentials(credentials);
+ }
+ return webClient;
+ } catch (Exception e) {
+ webClient.close();
+ ownedHttpClient.close();
+ ownedHttpClient = null;
+ throw e;
+ }
+ }
+
+ /**
+ * A cookie store that never keeps a cookie.
+ */
+ private static final class NoCookieStore implements CookieStore {
+
+ static final NoCookieStore INSTANCE = new NoCookieStore();
+
+ @Override
+ public Iterable get(Boolean ssl, String domain, String path) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public CookieStore put(Cookie cookie) {
+ return this;
+ }
+
+ @Override
+ public CookieStore remove(Cookie cookie) {
+ return this;
+ }
+ }
+
+ public RegistryClient getRegistryClient() {
+ return registryClient;
+ }
+
+ public void setRegistryClient(RegistryClient registryClient) {
+ this.registryClient = registryClient;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public ApicurioRegistryConfiguration getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(ApicurioRegistryConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public String getServiceUrl() {
+ return configuration.getRegistryUrl();
+ }
+
+ @Override
+ public String getServiceProtocol() {
+ return "http";
+ }
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducer.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducer.java
new file mode 100644
index 0000000000000..b9de5239660f8
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducer.java
@@ -0,0 +1,367 @@
+/*
+ * 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.apicurioregistry;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.apicurio.registry.rest.client.models.ArtifactMetaData;
+import io.apicurio.registry.rest.client.models.ArtifactSearchResults;
+import io.apicurio.registry.rest.client.models.ArtifactSortBy;
+import io.apicurio.registry.rest.client.models.CreateArtifact;
+import io.apicurio.registry.rest.client.models.CreateArtifactResponse;
+import io.apicurio.registry.rest.client.models.CreateGroup;
+import io.apicurio.registry.rest.client.models.CreateVersion;
+import io.apicurio.registry.rest.client.models.GroupMetaData;
+import io.apicurio.registry.rest.client.models.IfArtifactExists;
+import io.apicurio.registry.rest.client.models.RuleViolationProblemDetails;
+import io.apicurio.registry.rest.client.models.SearchedVersion;
+import io.apicurio.registry.rest.client.models.SortOrder;
+import io.apicurio.registry.rest.client.models.VersionContent;
+import io.apicurio.registry.rest.client.models.VersionMetaData;
+import io.apicurio.registry.rest.client.models.VersionSearchResults;
+import io.apicurio.registry.rest.client.models.VersionSortBy;
+import io.vertx.core.Context;
+import org.apache.camel.Message;
+import org.apache.camel.spi.InvokeOnHeader;
+import org.apache.camel.support.HeaderSelectorProducer;
+import org.apache.camel.util.ObjectHelper;
+
+public class ApicurioRegistryProducer extends HeaderSelectorProducer {
+
+ private static final int PAGE_SIZE = 100;
+
+ private final ApicurioRegistryEndpoint endpoint;
+ private final ApicurioRegistryConfiguration configuration;
+
+ public ApicurioRegistryProducer(ApicurioRegistryEndpoint endpoint,
+ ApicurioRegistryConfiguration configuration) {
+ super(endpoint, ApicurioRegistryConstants.HEADER_OPERATION, configuration::getOperation);
+ this.endpoint = endpoint;
+ this.configuration = configuration;
+ }
+
+ private RegistryClient getClient() {
+ if (Context.isOnEventLoopThread()) {
+ // the SDK blocks until the response arrives; on an event loop of the Vert.x the client uses, the response
+ // could never be handled and the call would hang
+ throw new IllegalStateException(
+ "The apicurio-registry producer makes blocking calls and cannot run on a Vert.x event loop thread."
+ + " Hand the exchange to a worker thread first, for example with threads()");
+ }
+ return endpoint.getRegistryClient();
+ }
+
+ /**
+ * The content type from the header, or one derived from the content: JSON or XML when the content starts like it,
+ * the registry's own types for Protobuf and GraphQL, and YAML otherwise (for example OpenAPI or AsyncAPI documents
+ * written in YAML).
+ */
+ private String resolveContentType(Message message, String content) {
+ String contentType = message.getHeader(ApicurioRegistryConstants.HEADER_CONTENT_TYPE, String.class);
+ if (contentType != null) {
+ return contentType;
+ }
+ String trimmed = content != null ? content.stripLeading() : "";
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
+ return "application/json";
+ }
+ if (trimmed.startsWith("<")) {
+ return "application/xml";
+ }
+ String artifactType = message.getHeader(
+ ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, configuration.getArtifactType(), String.class);
+ if ("PROTOBUF".equalsIgnoreCase(artifactType)) {
+ return "application/x-protobuf";
+ }
+ if ("GRAPHQL".equalsIgnoreCase(artifactType)) {
+ return "application/graphql";
+ }
+ return "application/x-yaml";
+ }
+
+ private String resolveGroupId(Message message) {
+ String gid = message.getHeader(ApicurioRegistryConstants.HEADER_GROUP_ID, String.class);
+ return gid != null ? gid : endpoint.getGroupId();
+ }
+
+ private String resolveArtifactId(Message message) {
+ String aid = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, String.class);
+ return aid != null ? aid : endpoint.getArtifactId();
+ }
+
+ private String requireGroupId(Message message, String operation) {
+ return require(resolveGroupId(message), "groupId", ApicurioRegistryConstants.HEADER_GROUP_ID, operation);
+ }
+
+ private String requireArtifactId(Message message, String operation) {
+ return require(resolveArtifactId(message), "artifactId", ApicurioRegistryConstants.HEADER_ARTIFACT_ID, operation);
+ }
+
+ private static String require(String value, String name, String header, String operation) {
+ if (ObjectHelper.isEmpty(value)) {
+ throw new IllegalArgumentException(
+ "The " + operation + " operation requires the " + name + ": set it in the endpoint path or the "
+ + header + " header");
+ }
+ return value;
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT)
+ public void createArtifact(Message message) {
+ String groupId = requireGroupId(message, "createArtifact");
+ String artifactId = resolveArtifactId(message);
+ String artifactType = message.getHeader(
+ ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, configuration.getArtifactType(), String.class);
+ String name = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_NAME, String.class);
+ String description = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_DESCRIPTION, String.class);
+ String content = message.getBody(String.class);
+ String contentType = resolveContentType(message, content);
+ String ifExistsVal = message.getHeader(
+ ApicurioRegistryConstants.HEADER_IF_EXISTS, configuration.getIfExists(), String.class);
+
+ CreateArtifact createArtifact = new CreateArtifact();
+ createArtifact.setArtifactId(artifactId);
+ createArtifact.setArtifactType(artifactType);
+ createArtifact.setName(name);
+ createArtifact.setDescription(description);
+
+ if (content != null) {
+ CreateVersion firstVersion = new CreateVersion();
+ VersionContent vc = new VersionContent();
+ vc.setContent(content);
+ vc.setContentType(contentType);
+ firstVersion.setContent(vc);
+ createArtifact.setFirstVersion(firstVersion);
+ }
+
+ CreateArtifactResponse result = getClient().groups().byGroupId(groupId).artifacts()
+ .post(createArtifact, config -> {
+ if (ifExistsVal != null) {
+ config.queryParameters.ifExists = parseEnum(IfArtifactExists.forValue(ifExistsVal), ifExistsVal,
+ ApicurioRegistryConstants.HEADER_IF_EXISTS);
+ }
+ });
+ message.setBody(result);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_UPDATE_ARTIFACT)
+ public void updateArtifact(Message message) {
+ String groupId = requireGroupId(message, "updateArtifact");
+ String artifactId = requireArtifactId(message, "updateArtifact");
+ String content = message.getBody(String.class);
+ String version = message.getHeader(ApicurioRegistryConstants.HEADER_VERSION, String.class);
+ String contentType = resolveContentType(message, content);
+
+ CreateVersion createVersion = new CreateVersion();
+ createVersion.setVersion(version);
+ VersionContent vc = new VersionContent();
+ vc.setContent(content);
+ vc.setContentType(contentType);
+ createVersion.setContent(vc);
+
+ VersionMetaData result = getClient().groups().byGroupId(groupId).artifacts()
+ .byArtifactId(artifactId).versions().post(createVersion);
+ message.setBody(result);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_DELETE_ARTIFACT)
+ public void deleteArtifact(Message message) {
+ String groupId = requireGroupId(message, "deleteArtifact");
+ String artifactId = requireArtifactId(message, "deleteArtifact");
+ getClient().groups().byGroupId(groupId).artifacts().byArtifactId(artifactId).delete();
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_GET_ARTIFACT_CONTENT)
+ public void getArtifactContent(Message message) throws Exception {
+ String groupId = requireGroupId(message, "getArtifactContent");
+ String artifactId = requireArtifactId(message, "getArtifactContent");
+ String version = message.getHeader(
+ ApicurioRegistryConstants.HEADER_VERSION, "branch=latest", String.class);
+
+ try (InputStream content = getClient().groups().byGroupId(groupId).artifacts()
+ .byArtifactId(artifactId).versions().byVersionExpression(version).content().get()) {
+ message.setBody(content.readAllBytes());
+ }
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_GET_ARTIFACT_METADATA)
+ public void getArtifactMetadata(Message message) {
+ String groupId = requireGroupId(message, "getArtifactMetadata");
+ String artifactId = requireArtifactId(message, "getArtifactMetadata");
+
+ ArtifactMetaData metadata = getClient().groups().byGroupId(groupId).artifacts()
+ .byArtifactId(artifactId).get();
+ message.setBody(metadata);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_SEARCH_ARTIFACTS)
+ public void searchArtifacts(Message message) {
+ String name = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_NAME, String.class);
+ String groupId = resolveGroupId(message);
+ String artifactId = resolveArtifactId(message);
+ String description = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_DESCRIPTION, String.class);
+ // only an explicit header: the artifactType endpoint option has a default that would filter every search
+ String artifactType = message.getHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, String.class);
+ String[] labels = toLabels(message.getHeader(ApicurioRegistryConstants.HEADER_LABELS));
+ Integer offset = message.getHeader(ApicurioRegistryConstants.HEADER_SEARCH_OFFSET, Integer.class);
+ Integer limit = message.getHeader(ApicurioRegistryConstants.HEADER_SEARCH_LIMIT, Integer.class);
+ String order = message.getHeader(ApicurioRegistryConstants.HEADER_SEARCH_ORDER, String.class);
+ String orderBy = message.getHeader(ApicurioRegistryConstants.HEADER_SEARCH_ORDER_BY, String.class);
+
+ ArtifactSearchResults results = getClient().search().artifacts().get(config -> {
+ var query = config.queryParameters;
+ query.name = name;
+ query.groupId = groupId;
+ query.artifactId = artifactId;
+ query.description = description;
+ query.artifactType = artifactType;
+ query.labels = labels;
+ query.offset = offset;
+ query.limit = limit;
+ if (order != null) {
+ query.order = parseEnum(SortOrder.forValue(order), order, ApicurioRegistryConstants.HEADER_SEARCH_ORDER);
+ }
+ if (orderBy != null) {
+ query.orderby = parseEnum(ArtifactSortBy.forValue(orderBy), orderBy,
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER_BY);
+ }
+ });
+ message.setBody(results);
+ }
+
+ private static T parseEnum(T value, String text, String header) {
+ if (value == null) {
+ throw new IllegalArgumentException("Unsupported value '" + text + "' for header " + header);
+ }
+ return value;
+ }
+
+ private static String[] toLabels(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String[] array) {
+ return array;
+ }
+ if (value instanceof Collection> collection) {
+ return collection.stream().map(String::valueOf).toArray(String[]::new);
+ }
+ return Arrays.stream(value.toString().split(",")).map(String::trim).filter(l -> !l.isEmpty())
+ .toArray(String[]::new);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_LIST_VERSIONS)
+ public void listVersions(Message message) {
+ String groupId = requireGroupId(message, "listVersions");
+ String artifactId = requireArtifactId(message, "listVersions");
+
+ List all = new ArrayList<>();
+ Integer total = null;
+ int offset = 0;
+ while (true) {
+ final int pageOffset = offset;
+ VersionSearchResults page = getClient().groups().byGroupId(groupId).artifacts()
+ .byArtifactId(artifactId).versions().get(config -> {
+ config.queryParameters.orderby = VersionSortBy.GlobalId;
+ config.queryParameters.order = SortOrder.Asc;
+ config.queryParameters.offset = pageOffset;
+ config.queryParameters.limit = PAGE_SIZE;
+ });
+ List versions = page != null ? page.getVersions() : null;
+ if (page != null && page.getCount() != null) {
+ total = page.getCount();
+ }
+ if (versions == null || versions.isEmpty()) {
+ break;
+ }
+ all.addAll(versions);
+ offset += versions.size();
+ if (versions.size() < PAGE_SIZE || total != null && offset >= total) {
+ break;
+ }
+ }
+ VersionSearchResults results = new VersionSearchResults();
+ results.setVersions(all);
+ results.setCount(total != null ? total : all.size());
+ message.setBody(results);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_CREATE_GROUP)
+ public void createGroup(Message message) {
+ String groupId = requireGroupId(message, "createGroup");
+ String description = message.getHeader(
+ ApicurioRegistryConstants.HEADER_ARTIFACT_DESCRIPTION, String.class);
+
+ CreateGroup createGroup = new CreateGroup();
+ createGroup.setGroupId(groupId);
+ createGroup.setDescription(description);
+
+ GroupMetaData result = getClient().groups().post(createGroup);
+ message.setBody(result);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_TEST_COMPATIBILITY)
+ public void testCompatibility(Message message) throws Exception {
+ boolean compatible = doDryRun(message, ApicurioRegistryConstants.OPERATION_TEST_COMPATIBILITY);
+ message.setHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, compatible);
+ message.setBody(compatible);
+ }
+
+ @InvokeOnHeader(ApicurioRegistryConstants.OPERATION_VALIDATE)
+ public void validate(Message message) throws Exception {
+ boolean valid = doDryRun(message, ApicurioRegistryConstants.OPERATION_VALIDATE);
+ message.setHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, valid);
+ if (!valid && configuration.isFailOnValidation()) {
+ String errors = message.getHeader(
+ ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS, String.class);
+ String groupId = resolveGroupId(message);
+ String artifactId = resolveArtifactId(message);
+ throw new ApicurioRegistryValidationException(
+ "Validation failed for artifact " + groupId + "/" + artifactId, errors);
+ }
+ }
+
+ private boolean doDryRun(Message message, String operation) throws Exception {
+ message.removeHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS);
+ String groupId = requireGroupId(message, operation);
+ String artifactId = requireArtifactId(message, operation);
+ String content = message.getBody(String.class);
+ String contentType = resolveContentType(message, content);
+
+ CreateVersion createVersion = new CreateVersion();
+ VersionContent vc = new VersionContent();
+ vc.setContent(content);
+ vc.setContentType(contentType);
+ createVersion.setContent(vc);
+
+ try {
+ getClient().groups().byGroupId(groupId).artifacts()
+ .byArtifactId(artifactId).versions()
+ .post(createVersion, config -> config.queryParameters.dryRun = true);
+ return true;
+ } catch (RuleViolationProblemDetails e) {
+ message.setHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS,
+ e.getDetail() != null ? e.getDetail() : e.getTitle());
+ return false;
+ }
+ }
+}
diff --git a/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryValidationException.java b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryValidationException.java
new file mode 100644
index 0000000000000..19855437cb093
--- /dev/null
+++ b/components/camel-apicurio-registry/src/main/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryValidationException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.apicurioregistry;
+
+import org.apache.camel.CamelException;
+
+public class ApicurioRegistryValidationException extends CamelException {
+
+ private final String validationErrors;
+
+ public ApicurioRegistryValidationException(String message, String validationErrors) {
+ super(message);
+ this.validationErrors = validationErrors;
+ }
+
+ public String getValidationErrors() {
+ return validationErrors;
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentTest.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentTest.java
new file mode 100644
index 0000000000000..6145c7506da7a
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryComponentTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.apicurioregistry;
+
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ApicurioRegistryComponentTest extends CamelTestSupport {
+
+ @Test
+ void testEmptyGroupIdNormalized() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getComponent("apicurio-registry")
+ .createEndpoint("apicurio-registry:/someArtifact?registryUrl=http://localhost:8080/apis/registry/v3");
+ assertThat(endpoint.getGroupId()).isNull();
+ assertThat(endpoint.getArtifactId()).isEqualTo("someArtifact");
+ }
+
+ @Test
+ void testEndpointCreatedWithGroupAndArtifact() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(
+ "apicurio-registry:myGroup/myArtifact?registryUrl=http://localhost:8080/apis/registry/v3");
+ assertThat(endpoint.getGroupId()).isEqualTo("myGroup");
+ assertThat(endpoint.getArtifactId()).isEqualTo("myArtifact");
+ assertThat(endpoint.getConfiguration().getRegistryUrl()).isEqualTo("http://localhost:8080/apis/registry/v3");
+ }
+
+ @Test
+ void testEndpointCreatedWithGroupOnly() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(
+ "apicurio-registry:myGroup?registryUrl=http://localhost:8080/apis/registry/v3");
+ assertThat(endpoint.getGroupId()).isEqualTo("myGroup");
+ assertThat(endpoint.getArtifactId()).isNull();
+ }
+
+ @Test
+ void testEndpointWithAuthOptions() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&authType=basic&username=user&password=pass");
+ assertThat(endpoint.getConfiguration().getAuthType()).isEqualTo("basic");
+ assertThat(endpoint.getConfiguration().getUsername()).isEqualTo("user");
+ assertThat(endpoint.getConfiguration().getPassword()).isEqualTo("pass");
+ }
+
+ @Test
+ void testEndpointWithOperationOption() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=createArtifact");
+ assertThat(endpoint.getConfiguration().getOperation()).isEqualTo("createArtifact");
+ }
+
+ @Test
+ void testConfigurationCopy() {
+ ApicurioRegistryConfiguration config = new ApicurioRegistryConfiguration();
+ config.setRegistryUrl("http://localhost:8080");
+ config.setOperation("createArtifact");
+ config.setAuthType("basic");
+ config.setUsername("user");
+
+ ApicurioRegistryConfiguration copy = config.copy();
+ assertThat(copy).isNotSameAs(config);
+ assertThat(copy.getRegistryUrl()).isEqualTo(config.getRegistryUrl());
+ assertThat(copy.getOperation()).isEqualTo(config.getOperation());
+ assertThat(copy.getAuthType()).isEqualTo(config.getAuthType());
+ assertThat(copy.getUsername()).isEqualTo(config.getUsername());
+ }
+
+ @Test
+ void testEndpointServiceLocation() throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3");
+ assertThat(endpoint.getServiceUrl()).isEqualTo("http://localhost:8080/apis/registry/v3");
+ assertThat(endpoint.getServiceProtocol()).isEqualTo("http");
+ }
+
+ @Test
+ void testEndpointWithOidcOptions() {
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&authType=oidc&tokenEndpoint=http://localhost:8080/token"
+ + "&clientId=registry-client&clientSecret=secret&scope=registry",
+ ApicurioRegistryEndpoint.class);
+ ApicurioRegistryConfiguration configuration = endpoint.getConfiguration();
+ assertThat(configuration.getAuthType()).isEqualTo("oidc");
+ assertThat(configuration.getTokenEndpoint()).isEqualTo("http://localhost:8080/token");
+ assertThat(configuration.getClientId()).isEqualTo("registry-client");
+ assertThat(configuration.getClientSecret()).isEqualTo("secret");
+ assertThat(configuration.getScope()).isEqualTo("registry");
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumerTest.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumerTest.java
new file mode 100644
index 0000000000000..148faafa16745
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryConsumerTest.java
@@ -0,0 +1,384 @@
+/*
+ * 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.apicurioregistry;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.LongStream;
+
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.apicurio.registry.rest.client.models.ProblemDetails;
+import io.apicurio.registry.rest.client.models.SearchedVersion;
+import io.apicurio.registry.rest.client.models.VersionState;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.PooledExchangeFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.awaitility.Awaitility.await;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ApicurioRegistryConsumerTest {
+
+ private static final String ENDPOINT_URI
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&delay=100";
+
+ private final RegistryClient mockClient = mock(RegistryClient.class, RETURNS_DEEP_STUBS);
+ private CamelContext context;
+ private FakeVersionPages pages;
+
+ @BeforeEach
+ void setUp() {
+ context = new DefaultCamelContext();
+ ApicurioRegistryComponent component = new ApicurioRegistryComponent(context);
+ component.getConfiguration().setRegistryUrl("http://localhost:8080/apis/registry/v3");
+ context.addComponent("apicurio-registry", component);
+ pages = new FakeVersionPages(
+ mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact").versions());
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (context != null) {
+ context.stop();
+ }
+ }
+
+ private ApicurioRegistryConsumer createConsumer(String uri, List received) throws Exception {
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(uri, ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(mockClient);
+ return (ApicurioRegistryConsumer) endpoint.createConsumer(received::add);
+ }
+
+ private static List globalIds(List exchanges) {
+ return exchanges.stream()
+ .map(e -> e.getIn().getHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, Long.class))
+ .toList();
+ }
+
+ @Test
+ void testPollNewVersionsInRoute() throws Exception {
+ pages.add(1, 2);
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(ENDPOINT_URI, ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(mockClient);
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from(ENDPOINT_URI).to("mock:result");
+ }
+ });
+ context.start();
+
+ MockEndpoint mock = context.getEndpoint("mock:result", MockEndpoint.class);
+ mock.expectedHeaderValuesReceivedInAnyOrder(ApicurioRegistryConstants.HEADER_GLOBAL_ID, 1L, 2L);
+ MockEndpoint.assertIsSatisfied(context, 10, TimeUnit.SECONDS);
+ }
+
+ @Test
+ void testPollOutOfOrderGlobalIds() throws Exception {
+ pages.add(5, 2, 8);
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI, received);
+
+ assertThat(consumer.poll()).isEqualTo(3);
+ assertThat(globalIds(received)).containsExactly(2L, 5L, 8L);
+ }
+
+ @Test
+ void testPollOnlyNewVersionsAfterInitial() throws Exception {
+ pages.add(1);
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI, received);
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(consumer.poll()).isZero();
+
+ pages.add(2);
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(consumer.poll()).isZero();
+ assertThat(globalIds(received)).containsExactly(1L, 2L);
+ }
+
+ @Test
+ void testPollPagesThroughManyVersionsAndStopsAtWatermark() throws Exception {
+ pages.addRange(1, 250);
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI, received);
+
+ // more than the registry's default page of 20 versions, and more than one consumer page
+ assertThat(consumer.poll()).isEqualTo(250);
+ assertThat(globalIds(received)).containsExactlyElementsOf(LongStream.rangeClosed(1, 250).boxed().toList());
+ assertThat(pages.requestedOffsets).containsExactly(0, 100, 200);
+
+ // an idle poll only probes the newest version
+ pages.requestedOffsets.clear();
+ pages.requestedLimits.clear();
+ assertThat(consumer.poll()).isZero();
+ assertThat(pages.requestedLimits).containsExactly(1);
+
+ pages.requestedOffsets.clear();
+ pages.requestedLimits.clear();
+ pages.add(251);
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(globalIds(received)).endsWith(251L);
+ // the probe finds a new version, then the first page reaches the watermark, so older pages are not fetched
+ assertThat(pages.requestedOffsets).containsExactly(0, 0);
+ assertThat(pages.requestedLimits).containsExactly(1, ApicurioRegistryConsumer.PAGE_SIZE);
+ }
+
+ @Test
+ void testFetchContentClosesStream() throws Exception {
+ pages.add(1);
+ byte[] content = "{\"type\":\"object\"}".getBytes(StandardCharsets.UTF_8);
+ ByteArrayInputStream stream = spy(new ByteArrayInputStream(content));
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("1").content().get()).thenReturn(stream);
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI + "&fetchContent=true", received);
+
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(received.get(0).getIn().getBody()).isEqualTo(content);
+ verify(stream).close();
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "testGroup", "testGroup/", "/someArtifact" })
+ void testMissingIdentifierFailsWhenCreatingConsumer(String path) throws Exception {
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:" + path + "?registryUrl=http://localhost:8080/apis/registry/v3",
+ ApicurioRegistryEndpoint.class);
+ assertThatThrownBy(() -> endpoint.createConsumer(exchange -> {
+ })).isInstanceOf(IllegalArgumentException.class)
+ .hasMessageStartingWith("Both groupId and artifactId are required for the consumer");
+ }
+
+ @Test
+ void testFetchContentSkipsDisabledVersions() throws Exception {
+ SearchedVersion disabled = FakeVersionPages.version(2);
+ disabled.setState(VersionState.DISABLED);
+ pages.add(1).add(disabled).add(3);
+ stubContent("1", new byte[] { 1 });
+ stubContent("3", new byte[] { 3 });
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI + "&fetchContent=true", received);
+
+ assertThat(consumer.poll()).isEqualTo(2);
+ assertThat(globalIds(received)).containsExactly(1L, 3L);
+ verify(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("2").content(), never()).get();
+ assertThat(consumer.poll()).isZero();
+ }
+
+ @Test
+ void testContentNotFoundIsRetriedUntilDelayPassedThenSkipped() throws Exception {
+ pages.add(1, 2);
+ stubContent("1", new byte[] { 1 });
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("2").content().get()).thenThrow(notFound());
+ List received = new ArrayList<>();
+ // a short delay so the test does not wait long before the version is given up
+ ApicurioRegistryConsumer consumer = createConsumer(
+ "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&delay=200&fetchContent=true",
+ received);
+
+ // the first 404 stops the poll without moving the watermark past version 2
+ assertThat(consumer.poll()).isEqualTo(1);
+ // an immediate retry, as with greedy polling, does not give up on the version yet
+ assertThat(consumer.poll()).isZero();
+ pages.add(3);
+ stubContent("3", new byte[] { 3 });
+ assertThat(consumer.poll()).isZero();
+ assertThat(globalIds(received)).containsExactly(1L);
+
+ // once a polling delay has passed, version 2 is skipped and version 3 is delivered
+ await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS)
+ .untilAsserted(() -> {
+ consumer.poll();
+ assertThat(globalIds(received)).containsExactly(1L, 3L);
+ });
+ }
+
+ @Test
+ void testContentNotFoundOnceIsDeliveredOnNextPoll() throws Exception {
+ pages.add(1);
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("1").content().get())
+ .thenThrow(notFound()).thenReturn(new ByteArrayInputStream(new byte[] { 1 }));
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI + "&fetchContent=true", received);
+
+ assertThat(consumer.poll()).isZero();
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(received.get(0).getIn().getBody()).isEqualTo(new byte[] { 1 });
+ }
+
+ private void stubContent(String version, byte[] content) {
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression(version).content().get())
+ .thenReturn(new ByteArrayInputStream(content));
+ }
+
+ private static ProblemDetails notFound() {
+ ProblemDetails notFound = new ProblemDetails();
+ notFound.setStatus(404);
+ notFound.setName("VersionNotFoundException");
+ return notFound;
+ }
+
+ @Test
+ void testOtherContentErrorsAreRetried() throws Exception {
+ pages.add(1);
+ ProblemDetails unavailable = new ProblemDetails();
+ unavailable.setStatus(503);
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("1").content().get())
+ .thenThrow(unavailable).thenReturn(new ByteArrayInputStream(new byte[] { 1 }));
+ List received = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = createConsumer(ENDPOINT_URI + "&fetchContent=true", received);
+
+ assertThatThrownBy(consumer::poll).isSameAs(unavailable);
+ assertThat(consumer.poll()).isEqualTo(1);
+ assertThat(globalIds(received)).containsExactly(1L);
+ }
+
+ @Test
+ void testHandledFailureAdvancesPastVersion() throws Exception {
+ pages.add(1, 2);
+ AtomicInteger attemptsForFirstVersion = new AtomicInteger();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(ENDPOINT_URI, ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(mockClient);
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ onException(IllegalStateException.class).handled(true).to("mock:skipped");
+ from(ENDPOINT_URI)
+ .process(exchange -> {
+ if (exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, Long.class) == 1L) {
+ attemptsForFirstVersion.incrementAndGet();
+ throw new IllegalStateException("always fails");
+ }
+ })
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ MockEndpoint result = context.getEndpoint("mock:result", MockEndpoint.class);
+ result.expectedHeaderValuesReceivedInAnyOrder(ApicurioRegistryConstants.HEADER_GLOBAL_ID, 2L);
+ MockEndpoint skipped = context.getEndpoint("mock:skipped", MockEndpoint.class);
+ skipped.expectedHeaderValuesReceivedInAnyOrder(ApicurioRegistryConstants.HEADER_GLOBAL_ID, 1L);
+ MockEndpoint.assertIsSatisfied(context, 10, TimeUnit.SECONDS);
+ assertThat(attemptsForFirstVersion.get()).isEqualTo(1);
+ }
+
+ @Test
+ void testRouteFailureHandledByErrorHandlerIsRetried() throws Exception {
+ pages.add(1, 2);
+ AtomicInteger attemptsForFirstVersion = new AtomicInteger();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(ENDPOINT_URI, ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(mockClient);
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ // the default error handler stores the failure on the exchange instead of rethrowing it
+ from(ENDPOINT_URI)
+ .process(exchange -> {
+ long globalId = exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, Long.class);
+ if (globalId == 1L && attemptsForFirstVersion.incrementAndGet() == 1) {
+ throw new IllegalStateException("simulated failure");
+ }
+ })
+ .to("mock:result");
+ }
+ });
+ context.start();
+
+ MockEndpoint mock = context.getEndpoint("mock:result", MockEndpoint.class);
+ mock.expectedHeaderValuesReceivedInAnyOrder(ApicurioRegistryConstants.HEADER_GLOBAL_ID, 1L, 2L);
+ MockEndpoint.assertIsSatisfied(context, 10, TimeUnit.SECONDS);
+
+ // version 2 is only delivered after the failed version 1 was retried, in order
+ assertThat(globalIds(mock.getReceivedExchanges())).containsExactly(1L, 2L);
+ assertThat(attemptsForFirstVersion.get()).isEqualTo(2);
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = { false, true })
+ void testPooledExchangeReleasedOnFailureAndRetry(boolean fetchContent) throws Exception {
+ context.getCamelContextExtension().setExchangeFactory(new PooledExchangeFactory());
+ context.getCamelContextExtension().getExchangeFactory().setStatisticsEnabled(true);
+ context.getCamelContextExtension().getExchangeFactoryManager().setStatisticsEnabled(true);
+ context.start();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ ENDPOINT_URI + "&startScheduler=false&fetchContent=" + fetchContent, ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(mockClient);
+ pages.add(2, 1);
+
+ IllegalStateException failure = new IllegalStateException("failed first version");
+ if (fetchContent) {
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("1").content().get())
+ .thenThrow(failure).thenReturn(new ByteArrayInputStream(new byte[] { 1 }));
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("2").content().get())
+ .thenReturn(new ByteArrayInputStream(new byte[] { 2 }));
+ }
+ List delivered = new ArrayList<>();
+ List attempted = new ArrayList<>();
+ ApicurioRegistryConsumer consumer = (ApicurioRegistryConsumer) endpoint.createConsumer(exchange -> {
+ attempted.add(exchange);
+ if (!fetchContent && attempted.size() == 1) {
+ throw failure;
+ }
+ delivered.add(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, Long.class));
+ });
+ consumer.start();
+ try {
+ var statistics = context.getCamelContextExtension().getExchangeFactoryManager().getStatistics();
+ assertThatThrownBy(consumer::poll).isSameAs(failure);
+ assertThat(statistics.getReleasedCounter()).isEqualTo(1);
+ assertThat(delivered).isEmpty();
+
+ assertThat(consumer.poll()).isEqualTo(2);
+ assertThat(delivered).containsExactly(1L, 2L);
+ assertThat(statistics.getCreatedCounter()).isEqualTo(1);
+ assertThat(statistics.getAcquiredCounter()).isEqualTo(2);
+ assertThat(statistics.getReleasedCounter()).isEqualTo(3);
+ assertThat(consumer.poll()).isZero();
+ } finally {
+ consumer.stop();
+ }
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointTest.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointTest.java
new file mode 100644
index 0000000000000..21f14236beeb7
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryEndpointTest.java
@@ -0,0 +1,372 @@
+/*
+ * 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.apicurioregistry;
+
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.sun.net.httpserver.HttpServer;
+import io.apicurio.registry.client.RegistryClientFactory;
+import io.apicurio.registry.client.common.RegistryClientOptions;
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.vertx.core.Future;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpClient;
+import io.vertx.core.http.HttpClientAgent;
+import io.vertx.core.http.HttpClientBuilder;
+import io.vertx.ext.auth.oauth2.OAuth2Auth;
+import io.vertx.ext.auth.oauth2.OAuth2Options;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.ext.web.client.WebClientOptions;
+import org.apache.camel.ResolveEndpointFailedException;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.jsse.KeyStoreParameters;
+import org.apache.camel.support.jsse.SSLContextParameters;
+import org.apache.camel.support.jsse.TrustManagersParameters;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.MockedStatic;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.RETURNS_SELF;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ApicurioRegistryEndpointTest {
+
+ @Test
+ void testBasicAuthentication() throws Exception {
+ testAuthentication("basic&username=user&password=pass", "Basic dXNlcjpwYXNz", false);
+ }
+
+ @Test
+ void testOidcAuthentication() throws Exception {
+ testAuthentication("oidc&clientId=registry-client&clientSecret=secret&scope=registry", "Bearer test-token", true);
+ }
+
+ private void testAuthentication(String options, String authorization, boolean oidc) throws Exception {
+ AtomicReference registryAuthorization = new AtomicReference<>();
+ List registryCookies = new CopyOnWriteArrayList<>();
+ AtomicReference tokenRequest = new AtomicReference<>();
+ AtomicReference tokenAuthorization = new AtomicReference<>();
+ HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+ server.createContext("/token", request -> {
+ tokenRequest.set(new String(request.getRequestBody().readAllBytes(), StandardCharsets.UTF_8));
+ tokenAuthorization.set(request.getRequestHeaders().getFirst("Authorization"));
+ byte[] response = "{\"access_token\":\"test-token\",\"token_type\":\"Bearer\",\"expires_in\":300}"
+ .getBytes(StandardCharsets.UTF_8);
+ request.getResponseHeaders().set("Content-Type", "application/json");
+ request.sendResponseHeaders(200, response.length);
+ try (var body = request.getResponseBody()) {
+ body.write(response);
+ }
+ });
+ server.createContext("/apis/registry/v3/groups/g/artifacts/a", request -> {
+ registryAuthorization.set(request.getRequestHeaders().getFirst("Authorization"));
+ registryCookies.add(String.valueOf(request.getRequestHeaders().getFirst("Cookie")));
+ request.getResponseHeaders().add("Set-Cookie", "LB=node1; Path=/");
+ byte[] response = "{\"artifactId\":\"a\",\"groupId\":\"g\"}".getBytes(StandardCharsets.UTF_8);
+ request.getResponseHeaders().set("Content-Type", "application/json");
+ request.sendResponseHeaders(200, response.length);
+ try (var body = request.getResponseBody()) {
+ body.write(response);
+ }
+ });
+ server.start();
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ String baseUrl = "http://localhost:" + server.getAddress().getPort();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=" + baseUrl + "/apis/registry/v3&authType=" + options
+ + (oidc ? "&tokenEndpoint=" + baseUrl + "/token" : ""),
+ ApicurioRegistryEndpoint.class);
+ endpoint.start();
+ try {
+ assertThat(endpoint.getRegistryClient().groups().byGroupId("g").artifacts().byArtifactId("a")
+ .get().getArtifactId()).isEqualTo("a");
+ assertThat(registryAuthorization.get()).isEqualTo(authorization);
+ // a second request still authenticates, and response cookies are not sent back
+ endpoint.getRegistryClient().groups().byGroupId("g").artifacts().byArtifactId("a").get();
+ assertThat(registryAuthorization.get()).isEqualTo(authorization);
+ assertThat(registryCookies).containsExactly("null", "null");
+ if (oidc) {
+ assertThat(tokenRequest.get()).contains("grant_type=client_credentials", "scope=registry");
+ assertThat(tokenAuthorization.get()).isEqualTo("Basic cmVnaXN0cnktY2xpZW50OnNlY3JldA==");
+ }
+ } finally {
+ endpoint.stop();
+ }
+ } finally {
+ server.stop(0);
+ }
+ }
+
+ @Test
+ void testEndpointsShareComponentVertxAndCloseTheirWebClients() throws Exception {
+ Vertx vertx = mockVertx();
+ when(vertx.close()).thenReturn(Future.succeededFuture());
+ List clientVertx = new ArrayList<>();
+ List clientWebClients = new ArrayList<>();
+ List created = new ArrayList<>();
+ try (MockedStatic vertxFactory = mockStatic(Vertx.class);
+ MockedStatic webClientFactory = mockStatic(WebClient.class);
+ MockedStatic clientFactory = mockStatic(RegistryClientFactory.class);
+ DefaultCamelContext context = new DefaultCamelContext()) {
+ vertxFactory.when(Vertx::vertx).thenReturn(vertx);
+ webClientFactory.when(() -> WebClient.wrap(any(HttpClient.class), any(WebClientOptions.class))).thenAnswer(call -> {
+ WebClient webClient = mock(WebClient.class);
+ created.add(webClient);
+ return webClient;
+ });
+ clientFactory.when(() -> RegistryClientFactory.create(any())).thenAnswer(call -> {
+ RegistryClientOptions options = call.getArgument(0, RegistryClientOptions.class);
+ clientVertx.add(options.getVertx());
+ clientWebClients.add(options.getWebClient());
+ return mock(RegistryClient.class);
+ });
+ context.start();
+ ApicurioRegistryEndpoint first = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3", ApicurioRegistryEndpoint.class);
+ ApicurioRegistryEndpoint second = context.getEndpoint(
+ "apicurio-registry:g/b?registryUrl=http://localhost:8080/apis/registry/v3", ApicurioRegistryEndpoint.class);
+ first.start();
+ second.start();
+ assertThat(clientVertx).containsExactly(vertx, vertx);
+ assertThat(clientWebClients).containsExactlyElementsOf(created);
+ vertxFactory.verify(Vertx::vertx, times(1));
+
+ // stopping an endpoint closes its WebClient, but keeps the shared Vert.x instance
+ first.stop();
+ assertThat(first.getRegistryClient()).isNull();
+ verify(created.get(0)).close();
+ verify(created.get(1), never()).close();
+ first.start();
+ assertThat(first.getRegistryClient()).isNotNull();
+ assertThat(created).hasSize(3);
+ verify(vertx, never()).close();
+ vertxFactory.verify(Vertx::vertx, times(1));
+
+ second.stop();
+ first.stop();
+ created.forEach(webClient -> verify(webClient).close());
+ context.getComponent("apicurio-registry").stop();
+ verify(vertx).close();
+ }
+ }
+
+ @Test
+ void testProvidedVertxIsNotClosed() throws Exception {
+ Vertx vertx = mockVertx();
+ try (MockedStatic vertxFactory = mockStatic(Vertx.class);
+ MockedStatic webClientFactory = mockStatic(WebClient.class);
+ MockedStatic clientFactory = mockStatic(RegistryClientFactory.class);
+ DefaultCamelContext context = new DefaultCamelContext()) {
+ webClientFactory.when(() -> WebClient.wrap(any(HttpClient.class), any(WebClientOptions.class)))
+ .thenAnswer(call -> mock(WebClient.class));
+ clientFactory.when(() -> RegistryClientFactory.create(any())).thenAnswer(call -> {
+ assertThat(call.getArgument(0, RegistryClientOptions.class).getVertx()).isSameAs(vertx);
+ return mock(RegistryClient.class);
+ });
+ context.getComponent("apicurio-registry", ApicurioRegistryComponent.class).setVertx(vertx);
+ context.start();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3", ApicurioRegistryEndpoint.class);
+ endpoint.start();
+ context.stop();
+ vertxFactory.verify(Vertx::vertx, never());
+ verify(vertx, never()).close();
+ }
+ }
+
+ @Test
+ void testVertxSetAfterComponentCreatedOneIsNotClosed() throws Exception {
+ Vertx managed = mockVertx();
+ when(managed.close()).thenReturn(Future.succeededFuture());
+ Vertx provided = mockVertx();
+ try (MockedStatic vertxFactory = mockStatic(Vertx.class);
+ DefaultCamelContext context = new DefaultCamelContext()) {
+ vertxFactory.when(Vertx::vertx).thenReturn(managed);
+ ApicurioRegistryComponent component = context.getComponent("apicurio-registry", ApicurioRegistryComponent.class);
+ context.start();
+ assertThat(component.getOrCreateVertx()).isSameAs(managed);
+
+ component.setVertx(provided);
+ assertThat(component.getOrCreateVertx()).isSameAs(provided);
+ component.stop();
+ verify(provided, never()).close();
+ // the instance the component created itself is still closed
+ verify(managed).close();
+ }
+ }
+
+ @Test
+ void testTlsAndProxyOptions() throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ SSLContextParameters ssl = new SSLContextParameters();
+ KeyStoreParameters trustStore = new KeyStoreParameters();
+ trustStore.setResource("classpath:truststore.p12");
+ trustStore.setType("PKCS12");
+ trustStore.setPassword("changeit");
+ TrustManagersParameters trustManagers = new TrustManagersParameters();
+ trustManagers.setKeyStore(trustStore);
+ ssl.setTrustManagers(trustManagers);
+ context.getRegistry().bind("ssl", ssl);
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=https://registry:8443/apis/registry/v3&sslContextParameters=#ssl"
+ + "&proxyHost=proxy&proxyPort=3128&proxyUsername=user&proxyPassword=pass",
+ ApicurioRegistryEndpoint.class);
+
+ WebClientOptions options = endpoint.createWebClientOptions();
+ assertThat(options.isSsl()).isTrue();
+ assertThat(options.getTrustOptions()).isNotNull();
+ assertThat(options.getProxyOptions().getHost()).isEqualTo("proxy");
+ assertThat(options.getProxyOptions().getPort()).isEqualTo(3128);
+ assertThat(options.getProxyOptions().getUsername()).isEqualTo("user");
+ assertThat(options.getProxyOptions().getPassword()).isEqualTo("pass");
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource(delimiter = '|', value = {
+ "authType=basic&password=pass|The username option is required when authType=basic",
+ "authType=basic&username=user|The password option is required when authType=basic",
+ "authType=oidc&clientId=c&clientSecret=s|The tokenEndpoint option is required when authType=oidc",
+ "authType=oidc&tokenEndpoint=http://t&clientSecret=s|The clientId option is required when authType=oidc",
+ "authType=oidc&tokenEndpoint=http://t&clientId=c|The clientSecret option is required when authType=oidc",
+ "authType=oauth|Unsupported authType 'oauth'. Supported values are none, basic and oidc" })
+ void testInvalidAuthenticationFailsOnStart(String options, String message) throws Exception {
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ context.start();
+ // endpoints resolved from a started context are started immediately
+ assertThatThrownBy(() -> context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3&" + options))
+ .isInstanceOf(ResolveEndpointFailedException.class)
+ .hasRootCauseInstanceOf(IllegalArgumentException.class)
+ .hasRootCauseMessage(message);
+ }
+ }
+
+ @Test
+ void testWebClientClosedWhenOAuth2SetupFails() throws Exception {
+ WebClient webClient = mock(WebClient.class);
+ try (MockedStatic webClientFactory = mockStatic(WebClient.class);
+ MockedStatic oauth2Factory = mockStatic(OAuth2Auth.class);
+ DefaultCamelContext context = new DefaultCamelContext()) {
+ webClientFactory.when(() -> WebClient.wrap(any(HttpClient.class), any(WebClientOptions.class)))
+ .thenReturn(webClient);
+ oauth2Factory.when(() -> OAuth2Auth.create(any(Vertx.class), any(OAuth2Options.class)))
+ .thenThrow(new IllegalStateException("bad oauth2 configuration"));
+ context.start();
+ assertThatThrownBy(() -> context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3&authType=oidc"
+ + "&tokenEndpoint=http://localhost/token&clientId=c&clientSecret=s"))
+ .hasRootCauseMessage("bad oauth2 configuration");
+ verify(webClient).close();
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "basic&username=user&password=pass", "oidc&clientId=c&clientSecret=s" })
+ void testCredentialsAreNotSentToRedirectTarget(String auth) throws Exception {
+ List otherHostRequests = new CopyOnWriteArrayList<>();
+ HttpServer otherHost = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+ otherHost.createContext("/", request -> {
+ otherHostRequests.add(String.valueOf(request.getRequestHeaders().getFirst("Authorization")));
+ request.sendResponseHeaders(200, -1);
+ request.close();
+ });
+ HttpServer registry = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+ registry.createContext("/token", request -> {
+ byte[] response = "{\"access_token\":\"t\",\"token_type\":\"Bearer\",\"expires_in\":300}"
+ .getBytes(StandardCharsets.UTF_8);
+ request.getResponseHeaders().set("Content-Type", "application/json");
+ request.sendResponseHeaders(200, response.length);
+ try (var body = request.getResponseBody()) {
+ body.write(response);
+ }
+ });
+ registry.createContext("/apis", request -> {
+ request.getResponseHeaders().set("Location",
+ "http://localhost:" + otherHost.getAddress().getPort() + request.getRequestURI());
+ request.sendResponseHeaders(302, -1);
+ request.close();
+ });
+ otherHost.start();
+ registry.start();
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ String baseUrl = "http://localhost:" + registry.getAddress().getPort();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=" + baseUrl + "/apis/registry/v3&tokenEndpoint=" + baseUrl
+ + "/token&authType=" + auth,
+ ApicurioRegistryEndpoint.class);
+ endpoint.start();
+ try {
+ AtomicInteger redirects = new AtomicInteger();
+ try {
+ endpoint.getRegistryClient().groups().byGroupId("g").artifacts().byArtifactId("a").get();
+ } catch (Exception e) {
+ // the unfollowed redirect may surface as an error; either way it must not reach the other host
+ redirects.incrementAndGet();
+ }
+ assertThat(otherHostRequests).isEmpty();
+ } finally {
+ endpoint.stop();
+ }
+ } finally {
+ registry.stop(0);
+ otherHost.stop(0);
+ }
+ }
+
+ /**
+ * A mocked Vert.x whose HTTP client builder returns a mocked client, for tests that also mock WebClient.wrap.
+ */
+ private static Vertx mockVertx() {
+ Vertx vertx = mock(Vertx.class);
+ HttpClientBuilder builder = mock(HttpClientBuilder.class, RETURNS_SELF);
+ when(builder.build()).thenAnswer(call -> mock(HttpClientAgent.class));
+ when(vertx.httpClientBuilder()).thenReturn(builder);
+ return vertx;
+ }
+
+ @Test
+ void testCallerOwnedClientSurvivesRestart() throws Exception {
+ RegistryClient client = mock(RegistryClient.class);
+ try (DefaultCamelContext context = new DefaultCamelContext()) {
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:g/a?registryUrl=http://localhost:8080/apis/registry/v3", ApicurioRegistryEndpoint.class);
+ endpoint.setRegistryClient(client);
+ endpoint.start();
+ endpoint.stop();
+ endpoint.start();
+ assertThat(endpoint.getRegistryClient()).isSameAs(client);
+ endpoint.stop();
+ }
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerTest.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerTest.java
new file mode 100644
index 0000000000000..31fa5424d24d2
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/ApicurioRegistryProducerTest.java
@@ -0,0 +1,540 @@
+/*
+ * 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.apicurioregistry;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.LongStream;
+
+import com.microsoft.kiota.ApiException;
+import io.apicurio.registry.rest.client.RegistryClient;
+import io.apicurio.registry.rest.client.groups.item.artifacts.ArtifactsRequestBuilder;
+import io.apicurio.registry.rest.client.models.ArtifactMetaData;
+import io.apicurio.registry.rest.client.models.ArtifactSearchResults;
+import io.apicurio.registry.rest.client.models.ArtifactSortBy;
+import io.apicurio.registry.rest.client.models.CreateArtifact;
+import io.apicurio.registry.rest.client.models.CreateArtifactResponse;
+import io.apicurio.registry.rest.client.models.CreateGroup;
+import io.apicurio.registry.rest.client.models.CreateVersion;
+import io.apicurio.registry.rest.client.models.GroupMetaData;
+import io.apicurio.registry.rest.client.models.IfArtifactExists;
+import io.apicurio.registry.rest.client.models.ProblemDetails;
+import io.apicurio.registry.rest.client.models.RuleViolationProblemDetails;
+import io.apicurio.registry.rest.client.models.SearchedVersion;
+import io.apicurio.registry.rest.client.models.SortOrder;
+import io.apicurio.registry.rest.client.models.VersionMetaData;
+import io.apicurio.registry.rest.client.models.VersionSearchResults;
+import io.vertx.core.Vertx;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.ArgumentCaptor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ApicurioRegistryProducerTest extends CamelTestSupport {
+
+ private final RegistryClient mockClient = mock(RegistryClient.class, RETURNS_DEEP_STUBS);
+
+ @BindToRegistry("apicurio-registry")
+ public ApicurioRegistryComponent getComponent() {
+ ApicurioRegistryComponent component = new ApicurioRegistryComponent(context);
+ component.getConfiguration().setRegistryUrl("http://localhost:8080/apis/registry/v3");
+ return component;
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:createArtifact")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=createArtifact");
+
+ from("direct:deleteArtifact")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=deleteArtifact");
+
+ from("direct:getArtifactMetadata")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=getArtifactMetadata");
+
+ from("direct:getArtifactContent")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=getArtifactContent");
+
+ from("direct:listVersions")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=listVersions");
+
+ from("direct:createGroup")
+ .to("apicurio-registry:newGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=createGroup");
+
+ from("direct:updateArtifact")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=updateArtifact");
+
+ from("direct:searchArtifacts")
+ .to("apicurio-registry:testGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=searchArtifacts");
+
+ from("direct:testCompatibility")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=testCompatibility");
+
+ from("direct:validate")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=validate&failOnValidation=false");
+
+ from("direct:validateFail")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=validate&failOnValidation=true");
+
+ from("direct:operationFromHeader")
+ .to("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3");
+ }
+ };
+ }
+
+ private void injectMockClient(String endpointUri) throws Exception {
+ ApicurioRegistryEndpoint endpoint = (ApicurioRegistryEndpoint) context.getEndpoint(endpointUri);
+ endpoint.setRegistryClient(mockClient);
+ }
+
+ @Test
+ void testCreateArtifact() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=createArtifact";
+ injectMockClient(endpointUri);
+
+ CreateArtifactResponse mockResponse = new CreateArtifactResponse();
+ when(mockClient.groups().byGroupId(anyString()).artifacts().post(any(CreateArtifact.class), any()))
+ .thenReturn(mockResponse);
+
+ Object result = template.requestBody("direct:createArtifact", "{\"test\":true}");
+ assertThat(result).isSameAs(mockResponse);
+ verify(mockClient.groups().byGroupId("testGroup").artifacts()).post(any(CreateArtifact.class), any());
+ }
+
+ @Test
+ void testCreateHeaderOverrides() throws Exception {
+ injectMockClient("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=createArtifact");
+ when(mockClient.groups().byGroupId("other-group").artifacts().post(any(CreateArtifact.class), any()))
+ .thenAnswer(call -> {
+ CreateArtifact request = call.getArgument(0);
+ assertThat(request.getArtifactId()).isEqualTo("other-artifact");
+ assertThat(request.getArtifactType()).isEqualTo("AVRO");
+ assertThat(request.getFirstVersion().getContent().getContent()).isEqualTo("{\"type\":\"string\"}");
+ ArtifactsRequestBuilder builder = mockClient.groups().byGroupId("other-group").artifacts();
+ var config = builder.new PostRequestConfiguration();
+ Consumer configure = call.getArgument(1);
+ configure.accept(config);
+ assertThat(config.queryParameters.ifExists).isEqualTo(IfArtifactExists.CREATE_VERSION);
+ return new CreateArtifactResponse();
+ });
+ Object response = template.requestBodyAndHeaders("direct:createArtifact", "{\"type\":\"string\"}", Map.of(
+ ApicurioRegistryConstants.HEADER_GROUP_ID, "other-group",
+ ApicurioRegistryConstants.HEADER_ARTIFACT_ID, "other-artifact",
+ ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "AVRO",
+ ApicurioRegistryConstants.HEADER_IF_EXISTS, "CREATE_VERSION"));
+ assertThat(response).isInstanceOf(CreateArtifactResponse.class);
+ }
+
+ @Test
+ void testDeleteArtifact() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=deleteArtifact";
+ injectMockClient(endpointUri);
+
+ template.sendBody("direct:deleteArtifact", null);
+ verify(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")).delete();
+ }
+
+ @Test
+ void testGetArtifactMetadata() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=getArtifactMetadata";
+ injectMockClient(endpointUri);
+
+ ArtifactMetaData mockMetadata = new ArtifactMetaData();
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact").get())
+ .thenReturn(mockMetadata);
+
+ Object result = template.requestBody("direct:getArtifactMetadata", (Object) null);
+ assertThat(result).isSameAs(mockMetadata);
+ }
+
+ @Test
+ void testGetArtifactContent() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=getArtifactContent";
+ injectMockClient(endpointUri);
+
+ ByteArrayInputStream mockContent = spy(new ByteArrayInputStream("{\"test\":true}".getBytes(StandardCharsets.UTF_8)));
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().byVersionExpression("branch=latest").content().get())
+ .thenReturn(mockContent);
+
+ Object result = template.requestBody("direct:getArtifactContent", (Object) null);
+ assertThat(result).isEqualTo("{\"test\":true}".getBytes(StandardCharsets.UTF_8));
+ verify(mockContent).close();
+ }
+
+ @Test
+ void testListVersionsReturnsEveryPage() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=listVersions";
+ injectMockClient(endpointUri);
+ FakeVersionPages pages = new FakeVersionPages(
+ mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact").versions())
+ .addRange(1, 250);
+
+ VersionSearchResults result
+ = template.requestBody("direct:listVersions", (Object) null, VersionSearchResults.class);
+ assertThat(result.getCount()).isEqualTo(250);
+ assertThat(result.getVersions()).extracting(SearchedVersion::getGlobalId)
+ .containsExactlyElementsOf(LongStream.rangeClosed(1, 250).boxed().toList());
+ assertThat(pages.requestedOffsets).containsExactly(0, 100, 200);
+ }
+
+ @Test
+ void testCreateGroup() throws Exception {
+ String endpointUri
+ = "apicurio-registry:newGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=createGroup";
+ injectMockClient(endpointUri);
+
+ GroupMetaData mockGroupMeta = new GroupMetaData();
+ when(mockClient.groups().post(any(CreateGroup.class))).thenReturn(mockGroupMeta);
+
+ Object result = template.requestBody("direct:createGroup", (Object) null);
+ assertThat(result).isSameAs(mockGroupMeta);
+ }
+
+ @Test
+ void testUpdateArtifact() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=updateArtifact";
+ injectMockClient(endpointUri);
+
+ VersionMetaData mockVersionMeta = new VersionMetaData();
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class)))
+ .thenReturn(mockVersionMeta);
+
+ Object result = template.requestBody("direct:updateArtifact", "{\"updated\":true}");
+ assertThat(result).isSameAs(mockVersionMeta);
+ }
+
+ @Test
+ void testOperationFromHeader() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3";
+ injectMockClient(endpointUri);
+
+ ArtifactMetaData mockMetadata = new ArtifactMetaData();
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact").get())
+ .thenReturn(mockMetadata);
+
+ Object result = template.requestBodyAndHeader("direct:operationFromHeader", null,
+ ApicurioRegistryConstants.HEADER_OPERATION, "getArtifactMetadata");
+ assertThat(result).isSameAs(mockMetadata);
+ }
+
+ @Test
+ void testSearchArtifacts() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=searchArtifacts";
+ injectMockClient(endpointUri);
+
+ ArtifactSearchResults mockResults = new ArtifactSearchResults();
+ when(mockClient.search().artifacts().get(any())).thenReturn(mockResults);
+
+ Object result = template.requestBody("direct:searchArtifacts", (Object) null);
+ assertThat(result).isSameAs(mockResults);
+ }
+
+ @Test
+ void testSearchArtifactsAppliesFiltersAndPaging() throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=searchArtifacts");
+ ArtifactSearchResults mockResults = new ArtifactSearchResults();
+ when(mockClient.search().artifacts().get(any())).thenAnswer(call -> {
+ io.apicurio.registry.rest.client.search.artifacts.ArtifactsRequestBuilder builder = mockClient.search().artifacts();
+ var config = builder.new GetRequestConfiguration();
+ Consumer configurer
+ = call.getArgument(0);
+ configurer.accept(config);
+ var query = config.queryParameters;
+ assertThat(query.groupId).isEqualTo("testGroup");
+ assertThat(query.artifactId).isEqualTo("orders");
+ assertThat(query.name).isEqualTo("Orders");
+ assertThat(query.artifactType).isEqualTo("AVRO");
+ assertThat(query.labels).containsExactly("team:payments", "env:prod");
+ assertThat(query.offset).isEqualTo(40);
+ assertThat(query.limit).isEqualTo(50);
+ assertThat(query.order).isEqualTo(SortOrder.Desc);
+ assertThat(query.orderby).isEqualTo(ArtifactSortBy.CreatedOn);
+ return mockResults;
+ });
+
+ Object result = template.requestBodyAndHeaders("direct:searchArtifacts", null, Map.of(
+ ApicurioRegistryConstants.HEADER_ARTIFACT_ID, "orders",
+ ApicurioRegistryConstants.HEADER_ARTIFACT_NAME, "Orders",
+ ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "AVRO",
+ ApicurioRegistryConstants.HEADER_LABELS, "team:payments, env:prod",
+ ApicurioRegistryConstants.HEADER_SEARCH_OFFSET, 40,
+ ApicurioRegistryConstants.HEADER_SEARCH_LIMIT, "50",
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER, "desc",
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER_BY, "createdOn"));
+ assertThat(result).isSameAs(mockResults);
+ }
+
+ @Test
+ void testSearchArtifactsRejectsUnknownSort() throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=searchArtifacts");
+ when(mockClient.search().artifacts().get(any())).thenAnswer(call -> {
+ io.apicurio.registry.rest.client.search.artifacts.ArtifactsRequestBuilder builder = mockClient.search().artifacts();
+ Consumer configurer
+ = call.getArgument(0);
+ configurer.accept(builder.new GetRequestConfiguration());
+ return new ArtifactSearchResults();
+ });
+
+ assertThatThrownBy(() -> template.requestBodyAndHeader("direct:searchArtifacts", null,
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER_BY, "size"))
+ .hasCauseInstanceOf(IllegalArgumentException.class)
+ .hasRootCauseMessage("Unsupported value 'size' for header CamelApicurioRegistrySearchOrderBy");
+ }
+
+ @Test
+ void testUnknownIfExistsFailsClearly() throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=createArtifact");
+ when(mockClient.groups().byGroupId("testGroup").artifacts().post(any(CreateArtifact.class), any()))
+ .thenAnswer(call -> {
+ ArtifactsRequestBuilder builder = mockClient.groups().byGroupId("testGroup").artifacts();
+ Consumer configurer = call.getArgument(1);
+ configurer.accept(builder.new PostRequestConfiguration());
+ return new CreateArtifactResponse();
+ });
+
+ assertThatThrownBy(() -> template.requestBodyAndHeader("direct:createArtifact", "{}",
+ ApicurioRegistryConstants.HEADER_IF_EXISTS, "CREATE_VERSIONS"))
+ .hasRootCauseMessage("Unsupported value 'CREATE_VERSIONS' for header CamelApicurioRegistryIfExists");
+ }
+
+ @ParameterizedTest
+ @CsvSource(delimiter = '|', value = {
+ "{\"type\":\"object\"}|JSON|application/json",
+ " [1]|JSON|application/json",
+ "|XSD|application/xml",
+ "syntax = \"proto3\";|PROTOBUF|application/x-protobuf",
+ "type Query { a: Int }|GRAPHQL|application/graphql",
+ "openapi: 3.0.3|OPENAPI|application/x-yaml",
+ "asyncapi: 2.6.0|ASYNCAPI|application/x-yaml" })
+ void testContentTypeDerivedFromContent(String content, String artifactType, String expected) throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=updateArtifact");
+ template.requestBodyAndHeader("direct:updateArtifact", content, ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE,
+ artifactType);
+ ArgumentCaptor request = ArgumentCaptor.forClass(CreateVersion.class);
+ verify(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact").versions())
+ .post(request.capture());
+ assertThat(request.getValue().getContent().getContentType()).isEqualTo(expected);
+ }
+
+ @Test
+ void testProducerOnVertxEventLoopFailsFast() throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=getArtifactMetadata");
+ Vertx vertx = Vertx.vertx();
+ try {
+ CompletableFuture result = new CompletableFuture<>();
+ vertx.runOnContext(v -> result.complete(template.request("direct:getArtifactMetadata", exchange -> {
+ })));
+ Exchange exchange = result.get(10, TimeUnit.SECONDS);
+ assertThat(exchange.getException()).isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("cannot run on a Vert.x event loop thread");
+ } finally {
+ vertx.close().toCompletionStage().toCompletableFuture().get(10, TimeUnit.SECONDS);
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "getArtifactMetadata", "deleteArtifact", "listVersions", "validate" })
+ void testMissingArtifactIdFailsClearly(String operation) throws Exception {
+ injectMockClient(
+ "apicurio-registry:testGroup?registryUrl=http://localhost:8080/apis/registry/v3&operation=searchArtifacts");
+ assertThatThrownBy(() -> template.requestBodyAndHeader("direct:searchArtifacts", "{}",
+ ApicurioRegistryConstants.HEADER_OPERATION, operation))
+ .hasCauseInstanceOf(IllegalArgumentException.class)
+ .hasRootCauseMessage("The " + operation + " operation requires the artifactId: set it in the endpoint path"
+ + " or the CamelApicurioRegistryArtifactId header");
+ }
+
+ @Test
+ void testTestCompatibilitySuccess() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=testCompatibility";
+ injectMockClient(endpointUri);
+
+ VersionMetaData mockVersion = new VersionMetaData();
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenReturn(mockVersion);
+
+ var result = template.request("direct:testCompatibility", exchange -> {
+ exchange.getIn().setBody("{\"test\":true}");
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS, "stale error");
+ });
+ assertThat(result.getException()).isNull();
+ assertThat(result.getIn().getBody()).isEqualTo(true);
+ assertThat(result.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, Boolean.class)).isTrue();
+ assertThat(result.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS)).isNull();
+ }
+
+ @Test
+ void testTestCompatibilityFailure() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=testCompatibility";
+ injectMockClient(endpointUri);
+
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenThrow(ruleViolation("incompatible"));
+
+ var result = template.request("direct:testCompatibility", exchange -> exchange.getIn().setBody("{\"bad\":true}"));
+ assertThat(result.getException()).isNull();
+ assertThat(result.getIn().getBody()).isEqualTo(false);
+ assertThat(result.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, Boolean.class)).isFalse();
+ assertThat(result.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS)).isEqualTo("incompatible");
+ }
+
+ @Test
+ void testValidateSuccess() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=validate&failOnValidation=false";
+ injectMockClient(endpointUri);
+
+ VersionMetaData mockVersion = new VersionMetaData();
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenReturn(mockVersion);
+
+ var exchange = template.request("direct:validate", ex -> {
+ ex.getIn().setBody("{\"test\":true}");
+ ex.getIn().setHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS, "stale error");
+ });
+ assertThat(exchange.getException()).isNull();
+ assertThat(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, Boolean.class)).isTrue();
+ assertThat(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS)).isNull();
+ assertThat(exchange.getIn().getBody()).isEqualTo("{\"test\":true}");
+ }
+
+ @Test
+ void testValidateFailureNoThrow() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=validate&failOnValidation=false";
+ injectMockClient(endpointUri);
+
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenThrow(ruleViolation("validation error"));
+
+ var exchange = template.request("direct:validate", ex -> ex.getIn().setBody("{\"bad\":true}"));
+ assertThat(exchange.getException()).isNull();
+ assertThat(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT, Boolean.class)).isFalse();
+ assertThat(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS))
+ .isEqualTo("validation error");
+ }
+
+ @Test
+ void testValidateFailureThrows() throws Exception {
+ String endpointUri
+ = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3&operation=validate&failOnValidation=true";
+ injectMockClient(endpointUri);
+
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenThrow(ruleViolation("validation error"));
+
+ assertThatThrownBy(() -> template.requestBody("direct:validateFail", "{\"bad\":true}"))
+ .isInstanceOf(CamelExecutionException.class)
+ .hasCauseInstanceOf(ApicurioRegistryValidationException.class);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "testCompatibility", "validate" })
+ void testNonValidationFailuresPropagate(String operation) throws Exception {
+ String uri = "apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=" + operation + ("validate".equals(operation) ? "&failOnValidation=false" : "");
+ injectMockClient(uri);
+ ProblemDetails unauthorized = new ProblemDetails();
+ unauthorized.setName("NotAuthorizedException");
+ unauthorized.setStatus(401);
+ ApiException serverError = new ApiException("server error");
+ IllegalStateException connectionError = new IllegalStateException("connection failed");
+ when(mockClient.groups().byGroupId("testGroup").artifacts().byArtifactId("testArtifact")
+ .versions().post(any(CreateVersion.class), any()))
+ .thenThrow(unauthorized, serverError, connectionError);
+
+ for (Exception failure : List.of(unauthorized, serverError, connectionError)) {
+ var exchange = template.request("direct:" + operation, ex -> ex.getIn().setBody("{}"));
+ assertThat(exchange.getException()).isSameAs(failure);
+ assertThat(exchange.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS)).isNull();
+ }
+ }
+
+ @Test
+ void testUpdateHeaderOverrides() throws Exception {
+ injectMockClient("apicurio-registry:testGroup/testArtifact?registryUrl=http://localhost:8080/apis/registry/v3"
+ + "&operation=updateArtifact");
+ template.requestBodyAndHeaders("direct:updateArtifact", "syntax = \"proto3\";", Map.of(
+ ApicurioRegistryConstants.HEADER_GROUP_ID, "other/group",
+ ApicurioRegistryConstants.HEADER_ARTIFACT_ID, "other-artifact",
+ ApicurioRegistryConstants.HEADER_VERSION, "2",
+ ApicurioRegistryConstants.HEADER_CONTENT_TYPE, "application/x-protobuf"));
+
+ ArgumentCaptor request = ArgumentCaptor.forClass(CreateVersion.class);
+ verify(mockClient.groups().byGroupId("other/group").artifacts().byArtifactId("other-artifact").versions())
+ .post(request.capture());
+ assertThat(request.getValue().getVersion()).isEqualTo("2");
+ assertThat(request.getValue().getContent().getContentType()).isEqualTo("application/x-protobuf");
+ assertThat(request.getValue().getContent().getContent()).isEqualTo("syntax = \"proto3\";");
+ }
+
+ private static RuleViolationProblemDetails ruleViolation(String message) {
+ RuleViolationProblemDetails error = new RuleViolationProblemDetails();
+ error.setName("RuleViolationException");
+ error.setStatus(409);
+ error.setTitle(message);
+ return error;
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/FakeVersionPages.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/FakeVersionPages.java
new file mode 100644
index 0000000000000..bcd7156b52663
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/FakeVersionPages.java
@@ -0,0 +1,99 @@
+/*
+ * 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.apicurioregistry;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Consumer;
+
+import io.apicurio.registry.rest.client.groups.item.artifacts.item.versions.VersionsRequestBuilder;
+import io.apicurio.registry.rest.client.models.SearchedVersion;
+import io.apicurio.registry.rest.client.models.SortOrder;
+import io.apicurio.registry.rest.client.models.VersionSearchResults;
+import io.apicurio.registry.rest.client.models.VersionSortBy;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+/**
+ * Stubs a mocked versions endpoint so that it behaves like the registry: it honours the orderby, order, offset and
+ * limit query parameters and applies a default page size of 20 when no limit is given.
+ */
+final class FakeVersionPages {
+
+ static final int REGISTRY_DEFAULT_LIMIT = 20;
+
+ final List versions = new ArrayList<>();
+ final List requestedOffsets = new ArrayList<>();
+ final List requestedLimits = new ArrayList<>();
+
+ @SuppressWarnings("unchecked")
+ FakeVersionPages(VersionsRequestBuilder builder) {
+ when(builder.get()).thenAnswer(call -> page(builder, null));
+ when(builder.get(any(Consumer.class))).thenAnswer(
+ call -> page(builder, (Consumer) call.getArgument(0)));
+ }
+
+ static SearchedVersion version(long globalId) {
+ SearchedVersion version = new SearchedVersion();
+ version.setGlobalId(globalId);
+ version.setVersion(String.valueOf(globalId));
+ version.setArtifactType("JSON");
+ return version;
+ }
+
+ FakeVersionPages add(long... globalIds) {
+ for (long globalId : globalIds) {
+ versions.add(version(globalId));
+ }
+ return this;
+ }
+
+ FakeVersionPages add(SearchedVersion version) {
+ versions.add(version);
+ return this;
+ }
+
+ FakeVersionPages addRange(long fromInclusive, long toInclusive) {
+ for (long id = fromInclusive; id <= toInclusive; id++) {
+ versions.add(version(id));
+ }
+ return this;
+ }
+
+ private VersionSearchResults page(
+ VersionsRequestBuilder builder, Consumer configurer) {
+ VersionsRequestBuilder.GetRequestConfiguration config = builder.new GetRequestConfiguration();
+ if (configurer != null) {
+ configurer.accept(config);
+ }
+ List sorted = new ArrayList<>(versions);
+ if (config.queryParameters.orderby == VersionSortBy.GlobalId) {
+ Comparator byGlobalId = Comparator.comparingLong(SearchedVersion::getGlobalId);
+ sorted.sort(config.queryParameters.order == SortOrder.Desc ? byGlobalId.reversed() : byGlobalId);
+ }
+ int offset = config.queryParameters.offset != null ? config.queryParameters.offset : 0;
+ int limit = config.queryParameters.limit != null ? config.queryParameters.limit : REGISTRY_DEFAULT_LIMIT;
+ requestedOffsets.add(offset);
+ requestedLimits.add(limit);
+ VersionSearchResults results = new VersionSearchResults();
+ results.setCount(sorted.size());
+ results.setVersions(sorted.subList(Math.min(offset, sorted.size()), Math.min(offset + limit, sorted.size())));
+ return results;
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryArtifactTypesIT.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryArtifactTypesIT.java
new file mode 100644
index 0000000000000..cfcc3f8592249
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryArtifactTypesIT.java
@@ -0,0 +1,154 @@
+/*
+ * 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.apicurioregistry.integration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import io.apicurio.registry.rest.client.models.CreateRule;
+import io.apicurio.registry.rest.client.models.RuleType;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryEndpoint;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Artifacts of several types created, updated and validated without a content type header, in a group with a FULL
+ * validity rule, so the registry rejects content sent with the wrong content type.
+ */
+class ApicurioRegistryArtifactTypesIT extends ApicurioRegistryTestSupport {
+
+ private static final String OPENAPI_V1 = """
+ openapi: 3.0.3
+ info:
+ title: Orders
+ version: "1"
+ paths: {}
+ """;
+
+ private static final String OPENAPI_V2 = """
+ openapi: 3.0.3
+ info:
+ title: Orders
+ version: "2"
+ paths: {}
+ """;
+
+ private static final String ASYNCAPI = """
+ asyncapi: 2.6.0
+ info:
+ title: Events
+ version: "1"
+ channels: {}
+ """;
+
+ private static final String PROTOBUF = """
+ syntax = "proto3";
+ message Order {
+ string id = 1;
+ }
+ """;
+
+ private static final String GRAPHQL = """
+ type Query {
+ order(id: ID!): String
+ }
+ """;
+
+ private static final String XSD = """
+
+
+
+
+ """;
+
+ private static final Map CONTENT = Map.of(
+ "OPENAPI", new String[] { OPENAPI_V1, OPENAPI_V2 },
+ "ASYNCAPI", new String[] { ASYNCAPI, ASYNCAPI.replace("version: \"1\"", "version: \"2\"") },
+ "PROTOBUF", new String[] { PROTOBUF, PROTOBUF.replace("string id = 1;", "string id = 1;\n int32 qty = 2;") },
+ "GRAPHQL", new String[] { GRAPHQL, GRAPHQL.replace("String", "Int") },
+ "XSD", new String[] { XSD, XSD.replace("xs:string", "xs:int") });
+
+ private final String groupId = "types-group-" + UUID.randomUUID();
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:operation")
+ .toD("apicurio-registry:${header.CamelApicurioRegistryGroupId}/${header.CamelApicurioRegistryArtifactId}"
+ + "?registryUrl=" + getRegistryUrl());
+ }
+ };
+ }
+
+ @BeforeEach
+ void createGroupWithValidityRule() {
+ Exchange group = operation(ApicurioRegistryConstants.OPERATION_CREATE_GROUP, null, null, null);
+ assertThat(group.getException()).isNull();
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:" + groupId + "?registryUrl=" + getRegistryUrl(), ApicurioRegistryEndpoint.class);
+ CreateRule rule = new CreateRule();
+ rule.setRuleType(RuleType.VALIDITY);
+ rule.setConfig("FULL");
+ endpoint.getRegistryClient().groups().byGroupId(groupId).rules().post(rule);
+ }
+
+ @ParameterizedTest
+ @CsvSource({ "OPENAPI", "ASYNCAPI", "PROTOBUF", "GRAPHQL", "XSD" })
+ void testCreateUpdateAndValidateWithoutContentTypeHeader(String artifactType) {
+ String artifactId = artifactType.toLowerCase() + "-" + UUID.randomUUID();
+ String[] content = CONTENT.get(artifactType);
+
+ Exchange created = operation(ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT, artifactId, artifactType,
+ content[0]);
+ assertThat(created.getException()).isNull();
+
+ Exchange updated = operation(ApicurioRegistryConstants.OPERATION_UPDATE_ARTIFACT, artifactId, artifactType,
+ content[1]);
+ assertThat(updated.getException()).isNull();
+
+ Exchange validated = operation(ApicurioRegistryConstants.OPERATION_VALIDATE, artifactId, artifactType,
+ content[1]);
+ assertThat(validated.getException()).isNull();
+ assertThat(validated.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT)).isEqualTo(true);
+ }
+
+ private Exchange operation(String operation, String artifactId, String artifactType, String body) {
+ Map headers = new HashMap<>();
+ headers.put(ApicurioRegistryConstants.HEADER_OPERATION, operation);
+ headers.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ if (artifactId != null) {
+ headers.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ }
+ if (artifactType != null) {
+ headers.put(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, artifactType);
+ }
+ return template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(headers);
+ exchange.getIn().setBody(body);
+ });
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryConsumerIT.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryConsumerIT.java
new file mode 100644
index 0000000000000..bb1a753febfca
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryConsumerIT.java
@@ -0,0 +1,112 @@
+/*
+ * 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.apicurioregistry.integration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ApicurioRegistryConsumerIT extends ApicurioRegistryTestSupport {
+
+ private static final String JSON_SCHEMA = """
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"}
+ }
+ }
+ """;
+
+ private final String groupId = "consumer-test-" + UUID.randomUUID();
+ private final String artifactId = "consumer-artifact-" + UUID.randomUUID();
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // Producer route to create artifacts
+ from("direct:createForConsumer")
+ .toD("apicurio-registry:${header.CamelApicurioRegistryGroupId}/${header.CamelApicurioRegistryArtifactId}"
+ + "?registryUrl=" + getRegistryUrl());
+
+ // Consumer route
+ from("apicurio-registry:" + groupId + "/" + artifactId
+ + "?registryUrl=" + getRegistryUrl() + "&delay=1000&fetchContent=true")
+ .to("mock:consumed");
+ }
+ };
+ }
+
+ @Test
+ void testConsumerReceivesNewVersions() throws Exception {
+ // Create the group first
+ Map groupHeaders = new HashMap<>();
+ groupHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_GROUP);
+ groupHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ Exchange group = template.request("direct:createForConsumer", exchange -> exchange.getIn().setHeaders(groupHeaders));
+ assertThat(group.getException()).isNull();
+
+ // Create the artifact
+ Map createHeaders = new HashMap<>();
+ createHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "JSON");
+ createHeaders.put(ApicurioRegistryConstants.HEADER_IF_EXISTS, "FIND_OR_CREATE_VERSION");
+ Exchange artifact = template.request("direct:createForConsumer", exchange -> {
+ exchange.getIn().setHeaders(createHeaders);
+ exchange.getIn().setBody(JSON_SCHEMA);
+ });
+ assertThat(artifact.getException()).isNull();
+
+ MockEndpoint mock = getMockEndpoint("mock:consumed");
+ mock.expectedMessageCount(1);
+ MockEndpoint.assertIsSatisfied(context, 30, TimeUnit.SECONDS);
+
+ // Verify headers set by consumer
+ Map receivedHeaders = mock.getReceivedExchanges().get(0).getIn().getHeaders();
+ assertThat(receivedHeaders.get(ApicurioRegistryConstants.HEADER_GROUP_ID)).isEqualTo(groupId);
+ assertThat(receivedHeaders.get(ApicurioRegistryConstants.HEADER_ARTIFACT_ID)).isEqualTo(artifactId);
+ assertThat(mock.getExchanges().get(0).getIn().getBody(String.class)).isEqualTo(JSON_SCHEMA);
+
+ createHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_UPDATE_ARTIFACT);
+ String secondSchema = "{\"type\":\"string\"}";
+ Exchange update = template.request("direct:createForConsumer", exchange -> {
+ exchange.getIn().setHeaders(createHeaders);
+ exchange.getIn().setBody(secondSchema);
+ });
+ assertThat(update.getException()).isNull();
+ mock.expectedMessageCount(2);
+ mock.setAssertPeriod(1500);
+ MockEndpoint.assertIsSatisfied(context, 30, TimeUnit.SECONDS);
+ assertThat(mock.getExchanges().get(1).getIn().getBody(String.class)).isEqualTo(secondSchema);
+ assertThat(mock.getExchanges().get(1).getIn().getHeader(ApicurioRegistryConstants.HEADER_GLOBAL_ID, Long.class))
+ .isGreaterThan((Long) receivedHeaders.get(ApicurioRegistryConstants.HEADER_GLOBAL_ID));
+ }
+
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryProducerIT.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryProducerIT.java
new file mode 100644
index 0000000000000..1ba743cb7adda
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryProducerIT.java
@@ -0,0 +1,241 @@
+/*
+ * 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.apicurioregistry.integration;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import io.apicurio.registry.rest.client.models.ArtifactMetaData;
+import io.apicurio.registry.rest.client.models.ArtifactSearchResults;
+import io.apicurio.registry.rest.client.models.CreateArtifactResponse;
+import io.apicurio.registry.rest.client.models.GroupMetaData;
+import io.apicurio.registry.rest.client.models.ProblemDetails;
+import io.apicurio.registry.rest.client.models.VersionMetaData;
+import io.apicurio.registry.rest.client.models.VersionSearchResults;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ApicurioRegistryProducerIT extends ApicurioRegistryTestSupport {
+
+ private static final String JSON_SCHEMA = """
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "integer"}
+ }
+ }
+ """;
+
+ private static final String JSON_SCHEMA_V2 = """
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "integer"},
+ "email": {"type": "string"}
+ }
+ }
+ """;
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:operation")
+ .toD("apicurio-registry:${header.CamelApicurioRegistryGroupId}/${header.CamelApicurioRegistryArtifactId}"
+ + "?registryUrl=" + getRegistryUrl());
+ }
+ };
+ }
+
+ @Test
+ void testFullArtifactLifecycle() throws Exception {
+ String groupId = "test-group-" + UUID.randomUUID();
+ String artifactId = "test-artifact-" + UUID.randomUUID();
+
+ // 1. Create group
+ Map headers = new HashMap<>();
+ headers.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_GROUP);
+ headers.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ Exchange groupResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(headers);
+ });
+ assertThat(groupResult.getException()).isNull();
+ assertThat(groupResult.getIn().getBody()).isInstanceOf(GroupMetaData.class);
+
+ // 2. Create artifact
+ Map createHeaders = new HashMap<>();
+ createHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "JSON");
+ createHeaders.put(ApicurioRegistryConstants.HEADER_IF_EXISTS, "FIND_OR_CREATE_VERSION");
+ Exchange createResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(createHeaders);
+ exchange.getIn().setBody(JSON_SCHEMA);
+ });
+ assertThat(createResult.getException()).isNull();
+ assertThat(createResult.getIn().getBody()).isInstanceOf(CreateArtifactResponse.class);
+
+ // 3. Get artifact metadata
+ Map getMetaHeaders = new HashMap<>();
+ getMetaHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION,
+ ApicurioRegistryConstants.OPERATION_GET_ARTIFACT_METADATA);
+ getMetaHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ getMetaHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ Exchange metaResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(getMetaHeaders);
+ });
+ assertThat(metaResult.getIn().getBody()).isInstanceOf(ArtifactMetaData.class);
+ ArtifactMetaData metadata = (ArtifactMetaData) metaResult.getIn().getBody();
+ assertThat(metadata.getArtifactId()).isEqualTo(artifactId);
+
+ // 4. Get artifact content
+ Map getContentHeaders = new HashMap<>();
+ getContentHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION,
+ ApicurioRegistryConstants.OPERATION_GET_ARTIFACT_CONTENT);
+ getContentHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ getContentHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ Exchange contentResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(getContentHeaders);
+ });
+ byte[] contentBody = contentResult.getIn().getBody(byte[].class);
+ assertThat(contentBody).isNotNull();
+ String contentStr = new String(contentBody, StandardCharsets.UTF_8);
+ assertThat(contentStr).isEqualTo(JSON_SCHEMA);
+
+ // 5. Update artifact (new version)
+ Map updateHeaders = new HashMap<>();
+ updateHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION,
+ ApicurioRegistryConstants.OPERATION_UPDATE_ARTIFACT);
+ updateHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ updateHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ Exchange updateResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(updateHeaders);
+ exchange.getIn().setBody(JSON_SCHEMA_V2);
+ });
+ assertThat(updateResult.getIn().getBody()).isInstanceOf(VersionMetaData.class);
+
+ // 6. List versions
+ Map listHeaders = new HashMap<>();
+ listHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION,
+ ApicurioRegistryConstants.OPERATION_LIST_VERSIONS);
+ listHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ listHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ Exchange listResult = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(listHeaders);
+ });
+ assertThat(listResult.getIn().getBody()).isInstanceOf(VersionSearchResults.class);
+ VersionSearchResults versions = (VersionSearchResults) listResult.getIn().getBody();
+ assertThat(versions.getCount()).isEqualTo(2);
+
+ // 7. Delete artifact
+ Map deleteHeaders = new HashMap<>();
+ deleteHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION,
+ ApicurioRegistryConstants.OPERATION_DELETE_ARTIFACT);
+ deleteHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ deleteHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ Exchange deleted = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeaders(deleteHeaders);
+ });
+ assertThat(deleted.getException()).isNull();
+ }
+
+ @Test
+ void testSearchArtifacts() throws Exception {
+ String groupId = "search-group-" + UUID.randomUUID();
+ String suffix = UUID.randomUUID().toString();
+ List artifactIds = List.of("a-" + suffix, "b-" + suffix, "c-" + suffix);
+
+ operation(ApicurioRegistryConstants.OPERATION_CREATE_GROUP, groupId, null, null);
+ for (String artifactId : artifactIds) {
+ Exchange created = operation(ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT, groupId, artifactId,
+ Map.of(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "JSON"));
+ assertThat(created.getException()).isNull();
+ }
+
+ // the artifact ID filter is applied
+ ArtifactSearchResults byId = search(groupId, artifactIds.get(1), Map.of());
+ assertThat(byId.getArtifacts()).extracting("artifactId").containsExactly(artifactIds.get(1));
+
+ // results are paged and sorted
+ Map firstPageHeaders = Map.of(
+ ApicurioRegistryConstants.HEADER_SEARCH_LIMIT, 2,
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER_BY, "artifactId",
+ ApicurioRegistryConstants.HEADER_SEARCH_ORDER, "asc");
+ ArtifactSearchResults firstPage = search(groupId, null, firstPageHeaders);
+ assertThat(firstPage.getCount()).isEqualTo(3);
+ assertThat(firstPage.getArtifacts()).extracting("artifactId").containsExactlyElementsOf(artifactIds.subList(0, 2));
+
+ Map secondPageHeaders = new HashMap<>(firstPageHeaders);
+ secondPageHeaders.put(ApicurioRegistryConstants.HEADER_SEARCH_OFFSET, 2);
+ ArtifactSearchResults secondPage = search(groupId, null, secondPageHeaders);
+ assertThat(secondPage.getArtifacts()).extracting("artifactId").containsExactly(artifactIds.get(2));
+
+ for (String artifactId : artifactIds) {
+ Exchange deleted = operation(ApicurioRegistryConstants.OPERATION_DELETE_ARTIFACT, groupId, artifactId, null);
+ assertThat(deleted.getException()).isNull();
+ }
+ }
+
+ private ArtifactSearchResults search(String groupId, String artifactId, Map extraHeaders) {
+ Exchange result = operation(ApicurioRegistryConstants.OPERATION_SEARCH_ARTIFACTS, groupId, artifactId, extraHeaders);
+ assertThat(result.getException()).isNull();
+ return result.getIn().getBody(ArtifactSearchResults.class);
+ }
+
+ private Exchange operation(String operation, String groupId, String artifactId, Map extraHeaders) {
+ return template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_OPERATION, operation);
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ if (artifactId != null) {
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ }
+ if (extraHeaders != null) {
+ extraHeaders.forEach(exchange.getIn()::setHeader);
+ }
+ if (ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT.equals(operation)) {
+ exchange.getIn().setBody(JSON_SCHEMA);
+ }
+ });
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "deleteArtifact", "updateArtifact" })
+ void testMissingArtifact(String operation) {
+ Exchange result = template.request("direct:operation", exchange -> {
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_OPERATION, operation);
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_GROUP_ID, "missing-group-" + UUID.randomUUID());
+ exchange.getIn().setHeader(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, "missing-artifact");
+ exchange.getIn().setBody(JSON_SCHEMA);
+ });
+ assertThat(result.getException()).isInstanceOf(ProblemDetails.class);
+ assertThat(((ProblemDetails) result.getException()).getStatus()).isEqualTo(404);
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryTestSupport.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryTestSupport.java
new file mode 100644
index 0000000000000..9ae3540c81ccd
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryTestSupport.java
@@ -0,0 +1,41 @@
+/*
+ * 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.apicurioregistry.integration;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent;
+import org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryService;
+import org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryServiceFactory;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class ApicurioRegistryTestSupport extends CamelTestSupport {
+
+ @RegisterExtension
+ public static ApicurioRegistryService service = ApicurioRegistryServiceFactory.createService();
+
+ @BindToRegistry("apicurio-registry")
+ public ApicurioRegistryComponent getApicurioRegistryComponent() {
+ ApicurioRegistryComponent component = new ApicurioRegistryComponent();
+ component.getConfiguration().setRegistryUrl(service.getApicurioRegistryUrl());
+ return component;
+ }
+
+ protected String getRegistryUrl() {
+ return service.getApicurioRegistryUrl();
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryValidateIT.java b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryValidateIT.java
new file mode 100644
index 0000000000000..cc48fe3a47103
--- /dev/null
+++ b/components/camel-apicurio-registry/src/test/java/org/apache/camel/component/apicurioregistry/integration/ApicurioRegistryValidateIT.java
@@ -0,0 +1,176 @@
+/*
+ * 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.apicurioregistry.integration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import io.apicurio.registry.rest.client.models.CreateRule;
+import io.apicurio.registry.rest.client.models.RuleType;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryConstants;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryEndpoint;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryValidationException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ApicurioRegistryValidateIT extends ApicurioRegistryTestSupport {
+
+ private static final String JSON_SCHEMA = """
+ {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "integer"}
+ },
+ "required": ["name"]
+ }
+ """;
+
+ private final String groupId = "validate-group-" + UUID.randomUUID();
+ private final String artifactId = "validate-artifact-" + UUID.randomUUID();
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:setup")
+ .toD("apicurio-registry:${header.CamelApicurioRegistryGroupId}/${header.CamelApicurioRegistryArtifactId}"
+ + "?registryUrl=" + getRegistryUrl());
+
+ from("direct:validate")
+ .to("apicurio-registry:" + groupId + "/" + artifactId
+ + "?registryUrl=" + getRegistryUrl()
+ + "&operation=validate&failOnValidation=false");
+
+ from("direct:testCompatibility")
+ .to("apicurio-registry:" + groupId + "/" + artifactId
+ + "?registryUrl=" + getRegistryUrl()
+ + "&operation=testCompatibility");
+
+ from("direct:validateFail")
+ .to("apicurio-registry:" + groupId + "/" + artifactId
+ + "?registryUrl=" + getRegistryUrl()
+ + "&operation=validate");
+ }
+ };
+ }
+
+ @BeforeEach
+ void setupArtifact() {
+ // Create group
+ Map groupHeaders = new HashMap<>();
+ groupHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_GROUP);
+ groupHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ Exchange group = template.request("direct:setup", exchange -> exchange.getIn().setHeaders(groupHeaders));
+ assertThat(group.getException()).isNull();
+
+ // Create artifact with JSON Schema
+ Map createHeaders = new HashMap<>();
+ createHeaders.put(ApicurioRegistryConstants.HEADER_OPERATION, ApicurioRegistryConstants.OPERATION_CREATE_ARTIFACT);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_GROUP_ID, groupId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_ID, artifactId);
+ createHeaders.put(ApicurioRegistryConstants.HEADER_ARTIFACT_TYPE, "JSON");
+ createHeaders.put(ApicurioRegistryConstants.HEADER_IF_EXISTS, "FIND_OR_CREATE_VERSION");
+ Exchange artifact = template.request("direct:setup", exchange -> {
+ exchange.getIn().setHeaders(createHeaders);
+ exchange.getIn().setBody(JSON_SCHEMA);
+ });
+ assertThat(artifact.getException()).isNull();
+
+ ApicurioRegistryEndpoint endpoint = context.getEndpoint(
+ "apicurio-registry:" + groupId + "/" + artifactId + "?registryUrl=" + getRegistryUrl()
+ + "&operation=validate",
+ ApicurioRegistryEndpoint.class);
+ CreateRule rule = new CreateRule();
+ rule.setRuleType(RuleType.COMPATIBILITY);
+ rule.setConfig("BACKWARD");
+ endpoint.getRegistryClient().groups().byGroupId(groupId).artifacts().byArtifactId(artifactId).rules().post(rule);
+ }
+
+ @Test
+ void testValidateCompatibleContent() {
+ String compatibleSchema = """
+ {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "number"}
+ },
+ "required": ["name"]
+ }
+ """;
+
+ Exchange result = template.request("direct:validate", exchange -> {
+ exchange.getIn().setBody(compatibleSchema);
+ });
+
+ assertThat(result.getException()).isNull();
+ assertThat(result.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT)).isEqualTo(true);
+ }
+
+ @Test
+ void testTestCompatibility() {
+ String compatibleSchema = """
+ {
+ "type": "object",
+ "properties": {
+ "name": {"type": "string"},
+ "age": {"type": "integer"}
+ }
+ }
+ """;
+
+ Exchange result = template.request("direct:testCompatibility", exchange -> {
+ exchange.getIn().setBody(compatibleSchema);
+ });
+
+ assertThat(result.getException()).isNull();
+ assertThat(result.getIn().getBody()).isEqualTo(true);
+ }
+
+ @Test
+ void testIncompatibleContent() {
+ String incompatible = "{\"type\":\"integer\"}";
+ Exchange validation = template.request("direct:validate", exchange -> exchange.getIn().setBody(incompatible));
+ assertThat(validation.getException()).isNull();
+ assertThat(validation.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_RESULT)).isEqualTo(false);
+ assertThat(validation.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS, String.class)).isNotBlank();
+ assertThat(validation.getIn().getBody()).isEqualTo(incompatible);
+
+ Exchange compatibility
+ = template.request("direct:testCompatibility", exchange -> exchange.getIn().setBody(incompatible));
+ assertThat(compatibility.getException()).isNull();
+ assertThat(compatibility.getIn().getBody()).isEqualTo(false);
+ assertThat(compatibility.getIn().getHeader(ApicurioRegistryConstants.HEADER_VALIDATION_ERRORS, String.class))
+ .isNotBlank();
+
+ assertThatThrownBy(() -> template.requestBody("direct:validateFail", incompatible))
+ .isInstanceOf(CamelExecutionException.class)
+ .hasCauseInstanceOf(ApicurioRegistryValidationException.class);
+ }
+}
diff --git a/components/camel-apicurio-registry/src/test/resources/truststore.p12 b/components/camel-apicurio-registry/src/test/resources/truststore.p12
new file mode 100644
index 0000000000000..6f477b0df7318
Binary files /dev/null and b/components/camel-apicurio-registry/src/test/resources/truststore.p12 differ
diff --git a/components/pom.xml b/components/pom.xml
index af716a407a8cb..6ec84729bcad7 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -82,6 +82,7 @@
camel-ai
camel-alibaba
camel-amqp
+ camel-apicurio-registry
camel-arangodb
camel-as2
camel-asn1
diff --git a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
index a72d3a6c6980d..c4510b3bcbb27 100644
--- a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
+++ b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
@@ -12,6 +12,7 @@ alibaba-ots
alibaba-sls
alibaba-sms
amqp
+apicurio-registry
arangodb
as2
asterisk
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
index 4ca0e601d7d6e..8261b34c9909e 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/ArtifactUtils.java
@@ -48,6 +48,7 @@ Map. entry("alibaba-ots", "camel-alibaba-ots"),
Map. entry("alibaba-sls", "camel-alibaba-sls"),
Map. entry("alibaba-sms", "camel-alibaba-sms"),
Map. entry("amqp", "camel-amqp"),
+ Map. entry("apicurio-registry", "camel-apicurio-registry"),
Map. entry("arangodb", "camel-arangodb"),
Map. entry("as2", "camel-as2"),
Map. entry("asterisk", "camel-asterisk"),
diff --git a/docs/components/modules/ROOT/examples/json/apicurio-registry.json b/docs/components/modules/ROOT/examples/json/apicurio-registry.json
new file mode 120000
index 0000000000000..2f1692df4e7d7
--- /dev/null
+++ b/docs/components/modules/ROOT/examples/json/apicurio-registry.json
@@ -0,0 +1 @@
+../../../../../../components/camel-apicurio-registry/src/generated/resources/META-INF/org/apache/camel/component/apicurioregistry/apicurio-registry.json
\ No newline at end of file
diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index beb1d440dfe5a..246f96a4c38fe 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -42,6 +42,7 @@
*** xref:alibaba-sls-component.adoc[Alibaba Simple Log Service (SLS)]
*** xref:alibaba-ots-component.adoc[Alibaba Tablestore (OTS)]
** xref:amqp-component.adoc[AMQP]
+** xref:apicurio-registry-component.adoc[Apicurio Registry]
** xref:arangodb-component.adoc[ArangoDb]
** xref:as2-component.adoc[AS2]
** xref:asterisk-component.adoc[Asterisk]
diff --git a/docs/components/modules/ROOT/pages/apicurio-registry-component.adoc b/docs/components/modules/ROOT/pages/apicurio-registry-component.adoc
new file mode 120000
index 0000000000000..cac7658d3701c
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/apicurio-registry-component.adoc
@@ -0,0 +1 @@
+../../../../../components/camel-apicurio-registry/src/main/docs/apicurio-registry-component.adoc
\ No newline at end of file
diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
index 0134a73fbe96e..e0478d5e9fe3f 100644
--- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
+++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
@@ -232,6 +232,19 @@ static AlibabaSmsComponentBuilderFactory.AlibabaSmsComponentBuilder alibabaSms()
static AmqpComponentBuilderFactory.AmqpComponentBuilder amqp() {
return AmqpComponentBuilderFactory.amqp();
}
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * @return the dsl builder
+ */
+ static ApicurioRegistryComponentBuilderFactory.ApicurioRegistryComponentBuilder apicurioRegistry() {
+ return ApicurioRegistryComponentBuilderFactory.apicurioRegistry();
+ }
/**
* ArangoDb (camel-arangodb)
* Perform operations on ArangoDb when used as a Document Database, or as a
diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ApicurioRegistryComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ApicurioRegistryComponentBuilderFactory.java
new file mode 100644
index 0000000000000..6806a704e7cd2
--- /dev/null
+++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ApicurioRegistryComponentBuilderFactory.java
@@ -0,0 +1,493 @@
+/* Generated by camel build tools - do NOT edit this file! */
+/*
+ * 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.builder.component.dsl;
+
+import javax.annotation.processing.Generated;
+import org.apache.camel.Component;
+import org.apache.camel.builder.component.AbstractComponentBuilder;
+import org.apache.camel.builder.component.ComponentBuilder;
+import org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent;
+
+/**
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.ComponentDslMojo")
+public interface ApicurioRegistryComponentBuilderFactory {
+
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * @return the dsl builder
+ */
+ static ApicurioRegistryComponentBuilder apicurioRegistry() {
+ return new ApicurioRegistryComponentBuilderImpl();
+ }
+
+ /**
+ * Builder for the Apicurio Registry component.
+ */
+ interface ApicurioRegistryComponentBuilder extends ComponentBuilder {
+
+ /**
+ * The Apicurio Registry base URL.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: common
+ *
+ * @param registryUrl the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder registryUrl(java.lang.String registryUrl) {
+ doSetProperty("registryUrl", registryUrl);
+ return this;
+ }
+
+
+ /**
+ * Allows for bridging the consumer to the Camel routing Error Handler,
+ * which mean any exceptions (if possible) occurred while the Camel
+ * consumer is trying to pickup incoming messages, or the likes, will
+ * now be processed as a message and handled by the routing Error
+ * Handler. Important: This is only possible if the 3rd party component
+ * allows Camel to be alerted if an exception was thrown. Some
+ * components handle this internally only, and therefore
+ * bridgeErrorHandler is not possible. In other situations we may
+ * improve the Camel component to hook into the 3rd party component and
+ * make this possible for future releases. By default the consumer will
+ * use the org.apache.camel.spi.ExceptionHandler to deal with
+ * exceptions, that will be logged at WARN or ERROR level and ignored.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param bridgeErrorHandler the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder bridgeErrorHandler(boolean bridgeErrorHandler) {
+ doSetProperty("bridgeErrorHandler", bridgeErrorHandler);
+ return this;
+ }
+
+
+ /**
+ * Whether to fetch the content for each new version found by the
+ * consumer.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param fetchContent the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder fetchContent(boolean fetchContent) {
+ doSetProperty("fetchContent", fetchContent);
+ return this;
+ }
+
+
+ /**
+ * The default artifact type.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: JSON
+ * Group: producer
+ *
+ * @param artifactType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder artifactType(java.lang.String artifactType) {
+ doSetProperty("artifactType", artifactType);
+ return this;
+ }
+
+
+ /**
+ * Whether to throw an exception on validation failure (validate
+ * operation). When false, sets result headers instead.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param failOnValidation the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder failOnValidation(boolean failOnValidation) {
+ doSetProperty("failOnValidation", failOnValidation);
+ return this;
+ }
+
+
+ /**
+ * Behavior when artifact already exists.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: FAIL
+ * Group: producer
+ *
+ * @param ifExists the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder ifExists(java.lang.String ifExists) {
+ doSetProperty("ifExists", ifExists);
+ return this;
+ }
+
+
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: false
+ * Group: producer
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder lazyStartProducer(boolean lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+
+ /**
+ * The operation to perform.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: producer
+ *
+ * @param operation the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder operation(java.lang.String operation) {
+ doSetProperty("operation", operation);
+ return this;
+ }
+
+
+ /**
+ * Whether autowiring is enabled. This is used for automatic autowiring
+ * options (the option must be marked as autowired) by looking up in the
+ * registry to find if there is a single instance of matching type,
+ * which then gets configured on the component. This can be used for
+ * automatic configuring JDBC data sources, JMS connection factories,
+ * AWS Clients, etc.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: advanced
+ *
+ * @param autowiredEnabled the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder autowiredEnabled(boolean autowiredEnabled) {
+ doSetProperty("autowiredEnabled", autowiredEnabled);
+ return this;
+ }
+
+ /**
+ * The component configuration.
+ *
+ * The option is a:
+ * <code>org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration</code> type.
+ *
+ * Group: advanced
+ *
+ * @param configuration the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder configuration(org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration configuration) {
+ doSetProperty("configuration", configuration);
+ return this;
+ }
+
+ /**
+ * To use an existing Vert.x instance for the registry clients. If not
+ * set, the component creates one shared Vert.x instance on first use
+ * and closes it when the component stops.
+ *
+ * The option is a: <code>io.vertx.core.Vertx</code> type.
+ *
+ * Group: advanced
+ *
+ * @param vertx the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder vertx(io.vertx.core.Vertx vertx) {
+ doSetProperty("vertx", vertx);
+ return this;
+ }
+
+ /**
+ * The proxy host to use for registry (and OAuth2 token) requests.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: proxy
+ *
+ * @param proxyHost the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder proxyHost(java.lang.String proxyHost) {
+ doSetProperty("proxyHost", proxyHost);
+ return this;
+ }
+
+ /**
+ * The password for proxy authentication.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPassword the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder proxyPassword(java.lang.String proxyPassword) {
+ doSetProperty("proxyPassword", proxyPassword);
+ return this;
+ }
+
+ /**
+ * The proxy port.
+ *
+ * The option is a: <code>java.lang.Integer</code> type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder proxyPort(java.lang.Integer proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+
+ /**
+ * The username for proxy authentication.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: proxy
+ *
+ * @param proxyUsername the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder proxyUsername(java.lang.String proxyUsername) {
+ doSetProperty("proxyUsername", proxyUsername);
+ return this;
+ }
+
+
+ /**
+ * The authentication type to use.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: none
+ * Group: security
+ *
+ * @param authType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder authType(java.lang.String authType) {
+ doSetProperty("authType", authType);
+ return this;
+ }
+
+ /**
+ * OAuth2 client ID.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param clientId the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder clientId(java.lang.String clientId) {
+ doSetProperty("clientId", clientId);
+ return this;
+ }
+
+ /**
+ * OAuth2 client secret.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param clientSecret the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder clientSecret(java.lang.String clientSecret) {
+ doSetProperty("clientSecret", clientSecret);
+ return this;
+ }
+
+ /**
+ * Password for basic authentication.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param password the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder password(java.lang.String password) {
+ doSetProperty("password", password);
+ return this;
+ }
+
+ /**
+ * OAuth2 scope.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param scope the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder scope(java.lang.String scope) {
+ doSetProperty("scope", scope);
+ return this;
+ }
+
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option is a:
+ * <code>org.apache.camel.support.jsse.SSLContextParameters</code> type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder sslContextParameters(org.apache.camel.support.jsse.SSLContextParameters sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+
+ /**
+ * OAuth2 token endpoint URL.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param tokenEndpoint the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder tokenEndpoint(java.lang.String tokenEndpoint) {
+ doSetProperty("tokenEndpoint", tokenEndpoint);
+ return this;
+ }
+
+ /**
+ * Username for basic authentication.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param username the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryComponentBuilder username(java.lang.String username) {
+ doSetProperty("username", username);
+ return this;
+ }
+ }
+
+ class ApicurioRegistryComponentBuilderImpl
+ extends AbstractComponentBuilder
+ implements ApicurioRegistryComponentBuilder {
+ @Override
+ protected ApicurioRegistryComponent buildConcreteComponent() {
+ return new ApicurioRegistryComponent();
+ }
+ private org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration getOrCreateConfiguration(ApicurioRegistryComponent component) {
+ if (component.getConfiguration() == null) {
+ component.setConfiguration(new org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration());
+ }
+ return component.getConfiguration();
+ }
+ @Override
+ protected boolean setPropertyOnComponent(
+ Component component,
+ String name,
+ Object value) {
+ switch (name) {
+ case "registryUrl": getOrCreateConfiguration((ApicurioRegistryComponent) component).setRegistryUrl((java.lang.String) value); return true;
+ case "bridgeErrorHandler": ((ApicurioRegistryComponent) component).setBridgeErrorHandler((boolean) value); return true;
+ case "fetchContent": getOrCreateConfiguration((ApicurioRegistryComponent) component).setFetchContent((boolean) value); return true;
+ case "artifactType": getOrCreateConfiguration((ApicurioRegistryComponent) component).setArtifactType((java.lang.String) value); return true;
+ case "failOnValidation": getOrCreateConfiguration((ApicurioRegistryComponent) component).setFailOnValidation((boolean) value); return true;
+ case "ifExists": getOrCreateConfiguration((ApicurioRegistryComponent) component).setIfExists((java.lang.String) value); return true;
+ case "lazyStartProducer": ((ApicurioRegistryComponent) component).setLazyStartProducer((boolean) value); return true;
+ case "operation": getOrCreateConfiguration((ApicurioRegistryComponent) component).setOperation((java.lang.String) value); return true;
+ case "autowiredEnabled": ((ApicurioRegistryComponent) component).setAutowiredEnabled((boolean) value); return true;
+ case "configuration": ((ApicurioRegistryComponent) component).setConfiguration((org.apache.camel.component.apicurioregistry.ApicurioRegistryConfiguration) value); return true;
+ case "vertx": ((ApicurioRegistryComponent) component).setVertx((io.vertx.core.Vertx) value); return true;
+ case "proxyHost": getOrCreateConfiguration((ApicurioRegistryComponent) component).setProxyHost((java.lang.String) value); return true;
+ case "proxyPassword": getOrCreateConfiguration((ApicurioRegistryComponent) component).setProxyPassword((java.lang.String) value); return true;
+ case "proxyPort": getOrCreateConfiguration((ApicurioRegistryComponent) component).setProxyPort((java.lang.Integer) value); return true;
+ case "proxyUsername": getOrCreateConfiguration((ApicurioRegistryComponent) component).setProxyUsername((java.lang.String) value); return true;
+ case "authType": getOrCreateConfiguration((ApicurioRegistryComponent) component).setAuthType((java.lang.String) value); return true;
+ case "clientId": getOrCreateConfiguration((ApicurioRegistryComponent) component).setClientId((java.lang.String) value); return true;
+ case "clientSecret": getOrCreateConfiguration((ApicurioRegistryComponent) component).setClientSecret((java.lang.String) value); return true;
+ case "password": getOrCreateConfiguration((ApicurioRegistryComponent) component).setPassword((java.lang.String) value); return true;
+ case "scope": getOrCreateConfiguration((ApicurioRegistryComponent) component).setScope((java.lang.String) value); return true;
+ case "sslContextParameters": getOrCreateConfiguration((ApicurioRegistryComponent) component).setSslContextParameters((org.apache.camel.support.jsse.SSLContextParameters) value); return true;
+ case "tokenEndpoint": getOrCreateConfiguration((ApicurioRegistryComponent) component).setTokenEndpoint((java.lang.String) value); return true;
+ case "username": getOrCreateConfiguration((ApicurioRegistryComponent) component).setUsername((java.lang.String) value); return true;
+ default: return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
index 3b739d5ed91ac..5e1d3209f6423 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilderFactory.java
@@ -42,6 +42,7 @@ public interface EndpointBuilderFactory
org.apache.camel.builder.endpoint.dsl.AlibabaEventBridgeEndpointBuilderFactory.AlibabaEventBridgeBuilders,
org.apache.camel.builder.endpoint.dsl.AlibabaOtsEndpointBuilderFactory.AlibabaOtsBuilders,
org.apache.camel.builder.endpoint.dsl.AlibabaSlsEndpointBuilderFactory.AlibabaSlsBuilders,
+ org.apache.camel.builder.endpoint.dsl.ApicurioRegistryEndpointBuilderFactory.ApicurioRegistryBuilders,
org.apache.camel.builder.endpoint.dsl.ArangoDbEndpointBuilderFactory.ArangoDbBuilders,
org.apache.camel.builder.endpoint.dsl.AsteriskEndpointBuilderFactory.AsteriskBuilders,
org.apache.camel.builder.endpoint.dsl.Athena2EndpointBuilderFactory.Athena2Builders,
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
index 4585eeaea46a2..6ba759ca18eff 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointBuilders.java
@@ -39,6 +39,7 @@ public interface EndpointBuilders
org.apache.camel.builder.endpoint.dsl.AlibabaEventBridgeEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.AlibabaOtsEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.AlibabaSlsEndpointBuilderFactory,
+ org.apache.camel.builder.endpoint.dsl.ApicurioRegistryEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.ArangoDbEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.AsteriskEndpointBuilderFactory,
org.apache.camel.builder.endpoint.dsl.Athena2EndpointBuilderFactory,
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
index 507e46a89f832..4f0d3b261e86e 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/EndpointHeaderBuilders.java
@@ -211,6 +211,19 @@ public static SMSEndpointBuilderFactory.SMSHeaderNameBuilder alibabaSms() {
public static AMQPEndpointBuilderFactory.AMQPHeaderNameBuilder amqp() {
return AMQPEndpointBuilderFactory.AMQPHeaderNameBuilder.INSTANCE;
}
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * @return the dsl builder for the headers' name.
+ */
+ public static ApicurioRegistryEndpointBuilderFactory.ApicurioRegistryHeaderNameBuilder apicurioRegistry() {
+ return ApicurioRegistryEndpointBuilderFactory.ApicurioRegistryHeaderNameBuilder.INSTANCE;
+ }
/**
* ArangoDb (camel-arangodb)
* Perform operations on ArangoDb when used as a Document Database, or as a
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
index 0de9679652cef..dc3ca6fd34ced 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
@@ -656,6 +656,52 @@ public static AMQPEndpointBuilderFactory.AMQPEndpointBuilder amqp(String path) {
public static AMQPEndpointBuilderFactory.AMQPEndpointBuilder amqp(String componentName, String path) {
return AMQPEndpointBuilderFactory.endpointBuilder(componentName, path);
}
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * Syntax: apicurio-registry:groupId/artifactId
+ *
+ * Path parameter: groupId
+ * The artifact group ID
+ *
+ * Path parameter: artifactId
+ * The artifact ID
+ *
+ * @param path groupId/artifactId
+ * @return the dsl builder
+ */
+ public static ApicurioRegistryEndpointBuilderFactory.ApicurioRegistryEndpointBuilder apicurioRegistry(String path) {
+ return apicurioRegistry("apicurio-registry", path);
+ }
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * Syntax: apicurio-registry:groupId/artifactId
+ *
+ * Path parameter: groupId
+ * The artifact group ID
+ *
+ * Path parameter: artifactId
+ * The artifact ID
+ *
+ * @param componentName to use a custom component name for the endpoint
+ * instead of the default name
+ * @param path groupId/artifactId
+ * @return the dsl builder
+ */
+ public static ApicurioRegistryEndpointBuilderFactory.ApicurioRegistryEndpointBuilder apicurioRegistry(String componentName, String path) {
+ return ApicurioRegistryEndpointBuilderFactory.endpointBuilder(componentName, path);
+ }
/**
* ArangoDb (camel-arangodb)
* Perform operations on ArangoDb when used as a Document Database, or as a
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ApicurioRegistryEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ApicurioRegistryEndpointBuilderFactory.java
new file mode 100644
index 0000000000000..5d513694366a6
--- /dev/null
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ApicurioRegistryEndpointBuilderFactory.java
@@ -0,0 +1,1967 @@
+/* Generated by camel build tools - do NOT edit this file! */
+/*
+ * 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.builder.endpoint.dsl;
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.function.*;
+import java.util.stream.*;
+import javax.annotation.processing.Generated;
+import org.apache.camel.builder.EndpointConsumerBuilder;
+import org.apache.camel.builder.EndpointProducerBuilder;
+import org.apache.camel.builder.endpoint.AbstractEndpointBuilder;
+
+/**
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointDslMojo")
+public interface ApicurioRegistryEndpointBuilderFactory {
+
+ /**
+ * Builder for endpoint consumers for the Apicurio Registry component.
+ */
+ public interface ApicurioRegistryEndpointConsumerBuilder
+ extends
+ EndpointConsumerBuilder {
+ default AdvancedApicurioRegistryEndpointConsumerBuilder advanced() {
+ return (AdvancedApicurioRegistryEndpointConsumerBuilder) this;
+ }
+ /**
+ * The Apicurio Registry base URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Required: true
+ * Group: common
+ *
+ * @param registryUrl the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder registryUrl(String registryUrl) {
+ doSetProperty("registryUrl", registryUrl);
+ return this;
+ }
+ /**
+ * Whether to fetch the content for each new version found by the
+ * consumer.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param fetchContent the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder fetchContent(boolean fetchContent) {
+ doSetProperty("fetchContent", fetchContent);
+ return this;
+ }
+ /**
+ * Whether to fetch the content for each new version found by the
+ * consumer.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param fetchContent the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder fetchContent(String fetchContent) {
+ doSetProperty("fetchContent", fetchContent);
+ return this;
+ }
+ /**
+ * If the polling consumer did not poll any files, you can enable this
+ * option to send an empty message (no body) instead.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param sendEmptyMessageWhenIdle the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder sendEmptyMessageWhenIdle(boolean sendEmptyMessageWhenIdle) {
+ doSetProperty("sendEmptyMessageWhenIdle", sendEmptyMessageWhenIdle);
+ return this;
+ }
+ /**
+ * If the polling consumer did not poll any files, you can enable this
+ * option to send an empty message (no body) instead.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param sendEmptyMessageWhenIdle the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder sendEmptyMessageWhenIdle(String sendEmptyMessageWhenIdle) {
+ doSetProperty("sendEmptyMessageWhenIdle", sendEmptyMessageWhenIdle);
+ return this;
+ }
+ /**
+ * The proxy host to use for registry (and OAuth2 token) requests.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyHost the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder proxyHost(String proxyHost) {
+ doSetProperty("proxyHost", proxyHost);
+ return this;
+ }
+ /**
+ * The password for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPassword the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder proxyPassword(String proxyPassword) {
+ doSetProperty("proxyPassword", proxyPassword);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option is a: java.lang.Integer type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder proxyPort(Integer proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option will be converted to a java.lang.Integer
+ * type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder proxyPort(String proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The username for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyUsername the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder proxyUsername(String proxyUsername) {
+ doSetProperty("proxyUsername", proxyUsername);
+ return this;
+ }
+ /**
+ * The number of subsequent error polls (failed due some error) that
+ * should happen before the backoffMultipler should kick-in.
+ *
+ * The option is a: int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffErrorThreshold the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffErrorThreshold(int backoffErrorThreshold) {
+ doSetProperty("backoffErrorThreshold", backoffErrorThreshold);
+ return this;
+ }
+ /**
+ * The number of subsequent error polls (failed due some error) that
+ * should happen before the backoffMultipler should kick-in.
+ *
+ * The option will be converted to a int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffErrorThreshold the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffErrorThreshold(String backoffErrorThreshold) {
+ doSetProperty("backoffErrorThreshold", backoffErrorThreshold);
+ return this;
+ }
+ /**
+ * The number of subsequent idle polls that should happen before the
+ * backoffMultipler should kick-in.
+ *
+ * The option is a: int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffIdleThreshold the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffIdleThreshold(int backoffIdleThreshold) {
+ doSetProperty("backoffIdleThreshold", backoffIdleThreshold);
+ return this;
+ }
+ /**
+ * The number of subsequent idle polls that should happen before the
+ * backoffMultipler should kick-in.
+ *
+ * The option will be converted to a int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffIdleThreshold the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffIdleThreshold(String backoffIdleThreshold) {
+ doSetProperty("backoffIdleThreshold", backoffIdleThreshold);
+ return this;
+ }
+ /**
+ * To let the scheduled polling consumer backoff if there has been a
+ * number of subsequent idles/errors in a row. The multiplier is then
+ * the number of polls that will be skipped before the next actual
+ * attempt is happening again. When this option is in use then
+ * backoffIdleThreshold and/or backoffErrorThreshold must also be
+ * configured.
+ *
+ * The option is a: int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffMultiplier the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffMultiplier(int backoffMultiplier) {
+ doSetProperty("backoffMultiplier", backoffMultiplier);
+ return this;
+ }
+ /**
+ * To let the scheduled polling consumer backoff if there has been a
+ * number of subsequent idles/errors in a row. The multiplier is then
+ * the number of polls that will be skipped before the next actual
+ * attempt is happening again. When this option is in use then
+ * backoffIdleThreshold and/or backoffErrorThreshold must also be
+ * configured.
+ *
+ * The option will be converted to a int type.
+ *
+ * Group: scheduler
+ *
+ * @param backoffMultiplier the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder backoffMultiplier(String backoffMultiplier) {
+ doSetProperty("backoffMultiplier", backoffMultiplier);
+ return this;
+ }
+ /**
+ * Milliseconds before the next poll.
+ *
+ * The option is a: long type.
+ *
+ * Default: 500
+ * Group: scheduler
+ *
+ * @param delay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder delay(long delay) {
+ doSetProperty("delay", delay);
+ return this;
+ }
+ /**
+ * Milliseconds before the next poll.
+ *
+ * The option will be converted to a long type.
+ *
+ * Default: 500
+ * Group: scheduler
+ *
+ * @param delay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder delay(String delay) {
+ doSetProperty("delay", delay);
+ return this;
+ }
+ /**
+ * If greedy is enabled, then the ScheduledPollConsumer will run
+ * immediately again, if the previous run polled 1 or more messages.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: scheduler
+ *
+ * @param greedy the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder greedy(boolean greedy) {
+ doSetProperty("greedy", greedy);
+ return this;
+ }
+ /**
+ * If greedy is enabled, then the ScheduledPollConsumer will run
+ * immediately again, if the previous run polled 1 or more messages.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: scheduler
+ *
+ * @param greedy the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder greedy(String greedy) {
+ doSetProperty("greedy", greedy);
+ return this;
+ }
+ /**
+ * Milliseconds before the first poll starts.
+ *
+ * The option is a: long type.
+ *
+ * Default: 1000
+ * Group: scheduler
+ *
+ * @param initialDelay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder initialDelay(long initialDelay) {
+ doSetProperty("initialDelay", initialDelay);
+ return this;
+ }
+ /**
+ * Milliseconds before the first poll starts.
+ *
+ * The option will be converted to a long type.
+ *
+ * Default: 1000
+ * Group: scheduler
+ *
+ * @param initialDelay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder initialDelay(String initialDelay) {
+ doSetProperty("initialDelay", initialDelay);
+ return this;
+ }
+ /**
+ * Specifies a maximum limit of number of fires. So if you set it to 1,
+ * the scheduler will only fire once. If you set it to 5, it will only
+ * fire five times. A value of zero or negative means fire forever.
+ *
+ * The option is a: long type.
+ *
+ * Default: 0
+ * Group: scheduler
+ *
+ * @param repeatCount the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder repeatCount(long repeatCount) {
+ doSetProperty("repeatCount", repeatCount);
+ return this;
+ }
+ /**
+ * Specifies a maximum limit of number of fires. So if you set it to 1,
+ * the scheduler will only fire once. If you set it to 5, it will only
+ * fire five times. A value of zero or negative means fire forever.
+ *
+ * The option will be converted to a long type.
+ *
+ * Default: 0
+ * Group: scheduler
+ *
+ * @param repeatCount the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder repeatCount(String repeatCount) {
+ doSetProperty("repeatCount", repeatCount);
+ return this;
+ }
+ /**
+ * The consumer logs a start/complete log line when it polls. This
+ * option allows you to configure the logging level for that.
+ *
+ * The option is a: org.apache.camel.LoggingLevel type.
+ *
+ * Default: TRACE
+ * Group: scheduler
+ *
+ * @param runLoggingLevel the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder runLoggingLevel(org.apache.camel.LoggingLevel runLoggingLevel) {
+ doSetProperty("runLoggingLevel", runLoggingLevel);
+ return this;
+ }
+ /**
+ * The consumer logs a start/complete log line when it polls. This
+ * option allows you to configure the logging level for that.
+ *
+ * The option will be converted to a
+ * org.apache.camel.LoggingLevel type.
+ *
+ * Default: TRACE
+ * Group: scheduler
+ *
+ * @param runLoggingLevel the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder runLoggingLevel(String runLoggingLevel) {
+ doSetProperty("runLoggingLevel", runLoggingLevel);
+ return this;
+ }
+ /**
+ * Allows for configuring a custom/shared thread pool to use for the
+ * consumer. By default each consumer has its own single threaded thread
+ * pool.
+ *
+ * The option is a:
+ * java.util.concurrent.ScheduledExecutorService type.
+ *
+ * Group: scheduler
+ *
+ * @param scheduledExecutorService the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
+ doSetProperty("scheduledExecutorService", scheduledExecutorService);
+ return this;
+ }
+ /**
+ * Allows for configuring a custom/shared thread pool to use for the
+ * consumer. By default each consumer has its own single threaded thread
+ * pool.
+ *
+ * The option will be converted to a
+ * java.util.concurrent.ScheduledExecutorService type.
+ *
+ * Group: scheduler
+ *
+ * @param scheduledExecutorService the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder scheduledExecutorService(String scheduledExecutorService) {
+ doSetProperty("scheduledExecutorService", scheduledExecutorService);
+ return this;
+ }
+ /**
+ * To use a cron scheduler from either camel-spring or camel-quartz
+ * component. Use value spring or quartz for built in scheduler.
+ *
+ * The option is a: java.lang.Object type.
+ *
+ * Default: none
+ * Group: scheduler
+ *
+ * @param scheduler the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder scheduler(Object scheduler) {
+ doSetProperty("scheduler", scheduler);
+ return this;
+ }
+ /**
+ * To use a cron scheduler from either camel-spring or camel-quartz
+ * component. Use value spring or quartz for built in scheduler.
+ *
+ * The option will be converted to a java.lang.Object type.
+ *
+ * Default: none
+ * Group: scheduler
+ *
+ * @param scheduler the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder scheduler(String scheduler) {
+ doSetProperty("scheduler", scheduler);
+ return this;
+ }
+ /**
+ * To configure additional properties when using a custom scheduler or
+ * any of the Quartz, Spring based scheduler. This is a multi-value
+ * option with prefix: scheduler.
+ *
+ * The option is a: java.util.Map<java.lang.String,
+ * java.lang.Object> type.
+ * The option is multivalued, and you can use the
+ * schedulerProperties(String, Object) method to add a value (call the
+ * method multiple times to set more values).
+ *
+ * Group: scheduler
+ *
+ * @param key the option key
+ * @param value the option value
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder schedulerProperties(String key, Object value) {
+ doSetMultiValueProperty("schedulerProperties", "scheduler." + key, value);
+ return this;
+ }
+ /**
+ * To configure additional properties when using a custom scheduler or
+ * any of the Quartz, Spring based scheduler. This is a multi-value
+ * option with prefix: scheduler.
+ *
+ * The option is a: java.util.Map<java.lang.String,
+ * java.lang.Object> type.
+ * The option is multivalued, and you can use the
+ * schedulerProperties(String, Object) method to add a value (call the
+ * method multiple times to set more values).
+ *
+ * Group: scheduler
+ *
+ * @param values the values
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder schedulerProperties(Map values) {
+ doSetMultiValueProperties("schedulerProperties", "scheduler.", values);
+ return this;
+ }
+ /**
+ * Whether the scheduler should be auto started.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: true
+ * Group: scheduler
+ *
+ * @param startScheduler the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder startScheduler(boolean startScheduler) {
+ doSetProperty("startScheduler", startScheduler);
+ return this;
+ }
+ /**
+ * Whether the scheduler should be auto started.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: true
+ * Group: scheduler
+ *
+ * @param startScheduler the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder startScheduler(String startScheduler) {
+ doSetProperty("startScheduler", startScheduler);
+ return this;
+ }
+ /**
+ * Time unit for initialDelay and delay options.
+ *
+ * The option is a: java.util.concurrent.TimeUnit type.
+ *
+ * Default: MILLISECONDS
+ * Group: scheduler
+ *
+ * @param timeUnit the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder timeUnit(TimeUnit timeUnit) {
+ doSetProperty("timeUnit", timeUnit);
+ return this;
+ }
+ /**
+ * Time unit for initialDelay and delay options.
+ *
+ * The option will be converted to a
+ * java.util.concurrent.TimeUnit type.
+ *
+ * Default: MILLISECONDS
+ * Group: scheduler
+ *
+ * @param timeUnit the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder timeUnit(String timeUnit) {
+ doSetProperty("timeUnit", timeUnit);
+ return this;
+ }
+ /**
+ * Controls if fixed delay or fixed rate is used. See
+ * ScheduledExecutorService in JDK for details.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: true
+ * Group: scheduler
+ *
+ * @param useFixedDelay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder useFixedDelay(boolean useFixedDelay) {
+ doSetProperty("useFixedDelay", useFixedDelay);
+ return this;
+ }
+ /**
+ * Controls if fixed delay or fixed rate is used. See
+ * ScheduledExecutorService in JDK for details.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: true
+ * Group: scheduler
+ *
+ * @param useFixedDelay the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder useFixedDelay(String useFixedDelay) {
+ doSetProperty("useFixedDelay", useFixedDelay);
+ return this;
+ }
+ /**
+ * The authentication type to use.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Default: none
+ * Group: security
+ *
+ * @param authType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder authType(String authType) {
+ doSetProperty("authType", authType);
+ return this;
+ }
+ /**
+ * OAuth2 client ID.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientId the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder clientId(String clientId) {
+ doSetProperty("clientId", clientId);
+ return this;
+ }
+ /**
+ * OAuth2 client secret.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientSecret the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder clientSecret(String clientSecret) {
+ doSetProperty("clientSecret", clientSecret);
+ return this;
+ }
+ /**
+ * Password for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param password the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder password(String password) {
+ doSetProperty("password", password);
+ return this;
+ }
+ /**
+ * OAuth2 scope.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param scope the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder scope(String scope) {
+ doSetProperty("scope", scope);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option is a:
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder sslContextParameters(org.apache.camel.support.jsse.SSLContextParameters sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option will be converted to a
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder sslContextParameters(String sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * OAuth2 token endpoint URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param tokenEndpoint the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder tokenEndpoint(String tokenEndpoint) {
+ doSetProperty("tokenEndpoint", tokenEndpoint);
+ return this;
+ }
+ /**
+ * Username for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param username the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointConsumerBuilder username(String username) {
+ doSetProperty("username", username);
+ return this;
+ }
+ }
+
+ /**
+ * Advanced builder for endpoint consumers for the Apicurio Registry component.
+ */
+ public interface AdvancedApicurioRegistryEndpointConsumerBuilder
+ extends
+ EndpointConsumerBuilder {
+ default ApicurioRegistryEndpointConsumerBuilder basic() {
+ return (ApicurioRegistryEndpointConsumerBuilder) this;
+ }
+ /**
+ * Allows for bridging the consumer to the Camel routing Error Handler,
+ * which mean any exceptions (if possible) occurred while the Camel
+ * consumer is trying to pickup incoming messages, or the likes, will
+ * now be processed as a message and handled by the routing Error
+ * Handler. Important: This is only possible if the 3rd party component
+ * allows Camel to be alerted if an exception was thrown. Some
+ * components handle this internally only, and therefore
+ * bridgeErrorHandler is not possible. In other situations we may
+ * improve the Camel component to hook into the 3rd party component and
+ * make this possible for future releases. By default the consumer will
+ * use the org.apache.camel.spi.ExceptionHandler to deal with
+ * exceptions, that will be logged at WARN or ERROR level and ignored.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: consumer (advanced)
+ *
+ * @param bridgeErrorHandler the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder bridgeErrorHandler(boolean bridgeErrorHandler) {
+ doSetProperty("bridgeErrorHandler", bridgeErrorHandler);
+ return this;
+ }
+ /**
+ * Allows for bridging the consumer to the Camel routing Error Handler,
+ * which mean any exceptions (if possible) occurred while the Camel
+ * consumer is trying to pickup incoming messages, or the likes, will
+ * now be processed as a message and handled by the routing Error
+ * Handler. Important: This is only possible if the 3rd party component
+ * allows Camel to be alerted if an exception was thrown. Some
+ * components handle this internally only, and therefore
+ * bridgeErrorHandler is not possible. In other situations we may
+ * improve the Camel component to hook into the 3rd party component and
+ * make this possible for future releases. By default the consumer will
+ * use the org.apache.camel.spi.ExceptionHandler to deal with
+ * exceptions, that will be logged at WARN or ERROR level and ignored.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: consumer (advanced)
+ *
+ * @param bridgeErrorHandler the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder bridgeErrorHandler(String bridgeErrorHandler) {
+ doSetProperty("bridgeErrorHandler", bridgeErrorHandler);
+ return this;
+ }
+ /**
+ * To let the consumer use a custom ExceptionHandler. Notice if the
+ * option bridgeErrorHandler is enabled then this option is not in use.
+ * By default the consumer will deal with exceptions, that will be
+ * logged at WARN or ERROR level and ignored.
+ *
+ * The option is a: org.apache.camel.spi.ExceptionHandler
+ * type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param exceptionHandler the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder exceptionHandler(org.apache.camel.spi.ExceptionHandler exceptionHandler) {
+ doSetProperty("exceptionHandler", exceptionHandler);
+ return this;
+ }
+ /**
+ * To let the consumer use a custom ExceptionHandler. Notice if the
+ * option bridgeErrorHandler is enabled then this option is not in use.
+ * By default the consumer will deal with exceptions, that will be
+ * logged at WARN or ERROR level and ignored.
+ *
+ * The option will be converted to a
+ * org.apache.camel.spi.ExceptionHandler type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param exceptionHandler the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder exceptionHandler(String exceptionHandler) {
+ doSetProperty("exceptionHandler", exceptionHandler);
+ return this;
+ }
+ /**
+ * Sets the exchange pattern when the consumer creates an exchange.
+ *
+ * The option is a: org.apache.camel.ExchangePattern type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param exchangePattern the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder exchangePattern(org.apache.camel.ExchangePattern exchangePattern) {
+ doSetProperty("exchangePattern", exchangePattern);
+ return this;
+ }
+ /**
+ * Sets the exchange pattern when the consumer creates an exchange.
+ *
+ * The option will be converted to a
+ * org.apache.camel.ExchangePattern type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param exchangePattern the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder exchangePattern(String exchangePattern) {
+ doSetProperty("exchangePattern", exchangePattern);
+ return this;
+ }
+ /**
+ * A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing
+ * you to provide your custom implementation to control error handling
+ * usually occurred during the poll operation before an Exchange have
+ * been created and being routed in Camel.
+ *
+ * The option is a:
+ * org.apache.camel.spi.PollingConsumerPollStrategy type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param pollStrategy the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder pollStrategy(org.apache.camel.spi.PollingConsumerPollStrategy pollStrategy) {
+ doSetProperty("pollStrategy", pollStrategy);
+ return this;
+ }
+ /**
+ * A pluggable org.apache.camel.PollingConsumerPollingStrategy allowing
+ * you to provide your custom implementation to control error handling
+ * usually occurred during the poll operation before an Exchange have
+ * been created and being routed in Camel.
+ *
+ * The option will be converted to a
+ * org.apache.camel.spi.PollingConsumerPollStrategy type.
+ *
+ * Group: consumer (advanced)
+ *
+ * @param pollStrategy the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder pollStrategy(String pollStrategy) {
+ doSetProperty("pollStrategy", pollStrategy);
+ return this;
+ }
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option is a:
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder registryClient(io.apicurio.registry.rest.client.RegistryClient registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option will be converted to a
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointConsumerBuilder registryClient(String registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ }
+
+ /**
+ * Builder for endpoint producers for the Apicurio Registry component.
+ */
+ public interface ApicurioRegistryEndpointProducerBuilder
+ extends
+ EndpointProducerBuilder {
+ default AdvancedApicurioRegistryEndpointProducerBuilder advanced() {
+ return (AdvancedApicurioRegistryEndpointProducerBuilder) this;
+ }
+
+ /**
+ * The Apicurio Registry base URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Required: true
+ * Group: common
+ *
+ * @param registryUrl the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder registryUrl(String registryUrl) {
+ doSetProperty("registryUrl", registryUrl);
+ return this;
+ }
+ /**
+ * The default artifact type.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Default: JSON
+ * Group: producer
+ *
+ * @param artifactType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder artifactType(String artifactType) {
+ doSetProperty("artifactType", artifactType);
+ return this;
+ }
+ /**
+ * Whether to throw an exception on validation failure (validate
+ * operation). When false, sets result headers instead.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param failOnValidation the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder failOnValidation(boolean failOnValidation) {
+ doSetProperty("failOnValidation", failOnValidation);
+ return this;
+ }
+ /**
+ * Whether to throw an exception on validation failure (validate
+ * operation). When false, sets result headers instead.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param failOnValidation the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder failOnValidation(String failOnValidation) {
+ doSetProperty("failOnValidation", failOnValidation);
+ return this;
+ }
+ /**
+ * Behavior when artifact already exists.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Default: FAIL
+ * Group: producer
+ *
+ * @param ifExists the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder ifExists(String ifExists) {
+ doSetProperty("ifExists", ifExists);
+ return this;
+ }
+ /**
+ * The operation to perform.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: producer
+ *
+ * @param operation the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder operation(String operation) {
+ doSetProperty("operation", operation);
+ return this;
+ }
+ /**
+ * The proxy host to use for registry (and OAuth2 token) requests.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyHost the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder proxyHost(String proxyHost) {
+ doSetProperty("proxyHost", proxyHost);
+ return this;
+ }
+ /**
+ * The password for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPassword the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder proxyPassword(String proxyPassword) {
+ doSetProperty("proxyPassword", proxyPassword);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option is a: java.lang.Integer type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder proxyPort(Integer proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option will be converted to a java.lang.Integer
+ * type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder proxyPort(String proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The username for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyUsername the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder proxyUsername(String proxyUsername) {
+ doSetProperty("proxyUsername", proxyUsername);
+ return this;
+ }
+ /**
+ * The authentication type to use.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Default: none
+ * Group: security
+ *
+ * @param authType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder authType(String authType) {
+ doSetProperty("authType", authType);
+ return this;
+ }
+ /**
+ * OAuth2 client ID.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientId the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder clientId(String clientId) {
+ doSetProperty("clientId", clientId);
+ return this;
+ }
+ /**
+ * OAuth2 client secret.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientSecret the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder clientSecret(String clientSecret) {
+ doSetProperty("clientSecret", clientSecret);
+ return this;
+ }
+ /**
+ * Password for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param password the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder password(String password) {
+ doSetProperty("password", password);
+ return this;
+ }
+ /**
+ * OAuth2 scope.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param scope the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder scope(String scope) {
+ doSetProperty("scope", scope);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option is a:
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder sslContextParameters(org.apache.camel.support.jsse.SSLContextParameters sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option will be converted to a
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder sslContextParameters(String sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * OAuth2 token endpoint URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param tokenEndpoint the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder tokenEndpoint(String tokenEndpoint) {
+ doSetProperty("tokenEndpoint", tokenEndpoint);
+ return this;
+ }
+ /**
+ * Username for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param username the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointProducerBuilder username(String username) {
+ doSetProperty("username", username);
+ return this;
+ }
+ }
+
+ /**
+ * Advanced builder for endpoint producers for the Apicurio Registry component.
+ */
+ public interface AdvancedApicurioRegistryEndpointProducerBuilder extends EndpointProducerBuilder {
+ default ApicurioRegistryEndpointProducerBuilder basic() {
+ return (ApicurioRegistryEndpointProducerBuilder) this;
+ }
+
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option is a: boolean type.
+ *
+ * Default: false
+ * Group: producer (advanced)
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointProducerBuilder lazyStartProducer(boolean lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+ /**
+ * Whether the producer should be started lazy (on the first message).
+ * By starting lazy you can use this to allow CamelContext and routes to
+ * startup in situations where a producer may otherwise fail during
+ * starting and cause the route to fail being started. By deferring this
+ * startup to be lazy then the startup failure can be handled during
+ * routing messages via Camel's routing error handlers. Beware that when
+ * the first message is processed then creating and starting the
+ * producer may take a little time and prolong the total processing time
+ * of the processing.
+ *
+ * The option will be converted to a boolean type.
+ *
+ * Default: false
+ * Group: producer (advanced)
+ *
+ * @param lazyStartProducer the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointProducerBuilder lazyStartProducer(String lazyStartProducer) {
+ doSetProperty("lazyStartProducer", lazyStartProducer);
+ return this;
+ }
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option is a:
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointProducerBuilder registryClient(io.apicurio.registry.rest.client.RegistryClient registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option will be converted to a
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointProducerBuilder registryClient(String registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ }
+
+ /**
+ * Builder for endpoint for the Apicurio Registry component.
+ */
+ public interface ApicurioRegistryEndpointBuilder
+ extends
+ ApicurioRegistryEndpointConsumerBuilder,
+ ApicurioRegistryEndpointProducerBuilder {
+ default AdvancedApicurioRegistryEndpointBuilder advanced() {
+ return (AdvancedApicurioRegistryEndpointBuilder) this;
+ }
+
+ /**
+ * The Apicurio Registry base URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Required: true
+ * Group: common
+ *
+ * @param registryUrl the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder registryUrl(String registryUrl) {
+ doSetProperty("registryUrl", registryUrl);
+ return this;
+ }
+ /**
+ * The proxy host to use for registry (and OAuth2 token) requests.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyHost the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder proxyHost(String proxyHost) {
+ doSetProperty("proxyHost", proxyHost);
+ return this;
+ }
+ /**
+ * The password for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPassword the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder proxyPassword(String proxyPassword) {
+ doSetProperty("proxyPassword", proxyPassword);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option is a: java.lang.Integer type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder proxyPort(Integer proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The proxy port.
+ *
+ * The option will be converted to a java.lang.Integer
+ * type.
+ *
+ * Group: proxy
+ *
+ * @param proxyPort the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder proxyPort(String proxyPort) {
+ doSetProperty("proxyPort", proxyPort);
+ return this;
+ }
+ /**
+ * The username for proxy authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: proxy
+ *
+ * @param proxyUsername the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder proxyUsername(String proxyUsername) {
+ doSetProperty("proxyUsername", proxyUsername);
+ return this;
+ }
+ /**
+ * The authentication type to use.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Default: none
+ * Group: security
+ *
+ * @param authType the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder authType(String authType) {
+ doSetProperty("authType", authType);
+ return this;
+ }
+ /**
+ * OAuth2 client ID.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientId the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder clientId(String clientId) {
+ doSetProperty("clientId", clientId);
+ return this;
+ }
+ /**
+ * OAuth2 client secret.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param clientSecret the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder clientSecret(String clientSecret) {
+ doSetProperty("clientSecret", clientSecret);
+ return this;
+ }
+ /**
+ * Password for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param password the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder password(String password) {
+ doSetProperty("password", password);
+ return this;
+ }
+ /**
+ * OAuth2 scope.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param scope the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder scope(String scope) {
+ doSetProperty("scope", scope);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option is a:
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder sslContextParameters(org.apache.camel.support.jsse.SSLContextParameters sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * To configure TLS, such as a trust store for a registry with a private
+ * CA or a client certificate (mTLS), using the Camel JSSE
+ * configuration. Also applies to the OAuth2 token endpoint.
+ *
+ * The option will be converted to a
+ * org.apache.camel.support.jsse.SSLContextParameters type.
+ *
+ * Group: security
+ *
+ * @param sslContextParameters the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder sslContextParameters(String sslContextParameters) {
+ doSetProperty("sslContextParameters", sslContextParameters);
+ return this;
+ }
+ /**
+ * OAuth2 token endpoint URL.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param tokenEndpoint the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder tokenEndpoint(String tokenEndpoint) {
+ doSetProperty("tokenEndpoint", tokenEndpoint);
+ return this;
+ }
+ /**
+ * Username for basic authentication.
+ *
+ * The option is a: java.lang.String type.
+ *
+ * Group: security
+ *
+ * @param username the value to set
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder username(String username) {
+ doSetProperty("username", username);
+ return this;
+ }
+ }
+
+ /**
+ * Advanced builder for endpoint for the Apicurio Registry component.
+ */
+ public interface AdvancedApicurioRegistryEndpointBuilder
+ extends
+ AdvancedApicurioRegistryEndpointConsumerBuilder,
+ AdvancedApicurioRegistryEndpointProducerBuilder {
+ default ApicurioRegistryEndpointBuilder basic() {
+ return (ApicurioRegistryEndpointBuilder) this;
+ }
+
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option is a:
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointBuilder registryClient(io.apicurio.registry.rest.client.RegistryClient registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ /**
+ * To use a pre-configured RegistryClient instance.
+ *
+ * The option will be converted to a
+ * io.apicurio.registry.rest.client.RegistryClient type.
+ *
+ * Group: advanced
+ *
+ * @param registryClient the value to set
+ * @return the dsl builder
+ */
+ default AdvancedApicurioRegistryEndpointBuilder registryClient(String registryClient) {
+ doSetProperty("registryClient", registryClient);
+ return this;
+ }
+ }
+
+ public interface ApicurioRegistryBuilders {
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * @return the dsl builder for the headers' name.
+ */
+ default ApicurioRegistryHeaderNameBuilder apicurioRegistry() {
+ return ApicurioRegistryHeaderNameBuilder.INSTANCE;
+ }
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * Syntax: apicurio-registry:groupId/artifactId
+ *
+ * Path parameter: groupId
+ * The artifact group ID
+ *
+ * Path parameter: artifactId
+ * The artifact ID
+ *
+ * @param path groupId/artifactId
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder apicurioRegistry(String path) {
+ return ApicurioRegistryEndpointBuilderFactory.endpointBuilder("apicurio-registry", path);
+ }
+ /**
+ * Apicurio Registry (camel-apicurio-registry)
+ * Manage artifacts, versions, and groups in Apicurio Registry v3.
+ *
+ * Category: cloud,api
+ * Since: 4.24
+ * Maven coordinates: org.apache.camel:camel-apicurio-registry
+ *
+ * Syntax: apicurio-registry:groupId/artifactId
+ *
+ * Path parameter: groupId
+ * The artifact group ID
+ *
+ * Path parameter: artifactId
+ * The artifact ID
+ *
+ * @param componentName to use a custom component name for the endpoint
+ * instead of the default name
+ * @param path groupId/artifactId
+ * @return the dsl builder
+ */
+ default ApicurioRegistryEndpointBuilder apicurioRegistry(String componentName, String path) {
+ return ApicurioRegistryEndpointBuilderFactory.endpointBuilder(componentName, path);
+ }
+
+ }
+ /**
+ * The builder of headers' name for the Apicurio Registry component.
+ */
+ public static class ApicurioRegistryHeaderNameBuilder {
+ /**
+ * The internal instance of the builder used to access to all the
+ * methods representing the name of headers.
+ */
+ public static final ApicurioRegistryHeaderNameBuilder INSTANCE = new ApicurioRegistryHeaderNameBuilder();
+
+ /**
+ * The operation to perform.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryOperation}.
+ */
+ public String apicurioRegistryOperation() {
+ return "CamelApicurioRegistryOperation";
+ }
+ /**
+ * The artifact group ID.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryGroupId}.
+ */
+ public String apicurioRegistryGroupId() {
+ return "CamelApicurioRegistryGroupId";
+ }
+ /**
+ * The artifact ID.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryArtifactId}.
+ */
+ public String apicurioRegistryArtifactId() {
+ return "CamelApicurioRegistryArtifactId";
+ }
+ /**
+ * The artifact type (e.g. AVRO, PROTOBUF, JSON, OPENAPI).
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryArtifactType}.
+ */
+ public String apicurioRegistryArtifactType() {
+ return "CamelApicurioRegistryArtifactType";
+ }
+ /**
+ * The artifact version expression.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryVersion}.
+ */
+ public String apicurioRegistryVersion() {
+ return "CamelApicurioRegistryVersion";
+ }
+ /**
+ * The artifact name.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryArtifactName}.
+ */
+ public String apicurioRegistryArtifactName() {
+ return "CamelApicurioRegistryArtifactName";
+ }
+ /**
+ * The artifact description.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code
+ * ApicurioRegistryArtifactDescription}.
+ */
+ public String apicurioRegistryArtifactDescription() {
+ return "CamelApicurioRegistryArtifactDescription";
+ }
+ /**
+ * Behavior when artifact already exists (FAIL, CREATE_VERSION,
+ * FIND_OR_CREATE_VERSION).
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryIfExists}.
+ */
+ public String apicurioRegistryIfExists() {
+ return "CamelApicurioRegistryIfExists";
+ }
+ /**
+ * The content type of the artifact.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code ApicurioRegistryContentType}.
+ */
+ public String apicurioRegistryContentType() {
+ return "CamelApicurioRegistryContentType";
+ }
+ /**
+ * The version global ID.
+ *
+ * The option is a: {@code Long} type.
+ *
+ * Group: consumer
+ *
+ * @return the name of the header {@code ApicurioRegistryGlobalId}.
+ */
+ public String apicurioRegistryGlobalId() {
+ return "CamelApicurioRegistryGlobalId";
+ }
+ /**
+ * The content ID.
+ *
+ * The option is a: {@code Long} type.
+ *
+ * Group: consumer
+ *
+ * @return the name of the header {@code ApicurioRegistryContentId}.
+ */
+ public String apicurioRegistryContentId() {
+ return "CamelApicurioRegistryContentId";
+ }
+ /**
+ * The version state.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: consumer
+ *
+ * @return the name of the header {@code ApicurioRegistryVersionState}.
+ */
+ public String apicurioRegistryVersionState() {
+ return "CamelApicurioRegistryVersionState";
+ }
+ /**
+ * Whether the check passed. Set by validate and testCompatibility.
+ *
+ * The option is a: {@code Boolean} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code
+ * ApicurioRegistryValidationResult}.
+ */
+ public String apicurioRegistryValidationResult() {
+ return "CamelApicurioRegistryValidationResult";
+ }
+ /**
+ * Rule violation details from validate or testCompatibility. Cleared
+ * before each check.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: common
+ *
+ * @return the name of the header {@code
+ * ApicurioRegistryValidationErrors}.
+ */
+ public String apicurioRegistryValidationErrors() {
+ return "CamelApicurioRegistryValidationErrors";
+ }
+ /**
+ * Labels to filter searchArtifacts by, as key:value pairs. Either a
+ * comma-separated String or a collection.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code ApicurioRegistryLabels}.
+ */
+ public String apicurioRegistryLabels() {
+ return "CamelApicurioRegistryLabels";
+ }
+ /**
+ * The number of results to skip in searchArtifacts. Defaults to 0.
+ *
+ * The option is a: {@code Integer} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code ApicurioRegistrySearchOffset}.
+ */
+ public String apicurioRegistrySearchOffset() {
+ return "CamelApicurioRegistrySearchOffset";
+ }
+ /**
+ * The maximum number of results returned by searchArtifacts. The
+ * registry defaults to 20.
+ *
+ * The option is a: {@code Integer} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code ApicurioRegistrySearchLimit}.
+ */
+ public String apicurioRegistrySearchLimit() {
+ return "CamelApicurioRegistrySearchLimit";
+ }
+ /**
+ * The sort order for searchArtifacts: asc or desc.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code ApicurioRegistrySearchOrder}.
+ */
+ public String apicurioRegistrySearchOrder() {
+ return "CamelApicurioRegistrySearchOrder";
+ }
+ /**
+ * The field to sort searchArtifacts by: groupId, artifactId, createdOn,
+ * modifiedOn, artifactType or name.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code ApicurioRegistrySearchOrderBy}.
+ */
+ public String apicurioRegistrySearchOrderBy() {
+ return "CamelApicurioRegistrySearchOrderBy";
+ }
+ }
+ static ApicurioRegistryEndpointBuilder endpointBuilder(String componentName, String path) {
+ class ApicurioRegistryEndpointBuilderImpl extends AbstractEndpointBuilder implements ApicurioRegistryEndpointBuilder, AdvancedApicurioRegistryEndpointBuilder {
+ public ApicurioRegistryEndpointBuilderImpl(String path) {
+ super(componentName, path);
+ }
+ }
+ return new ApicurioRegistryEndpointBuilderImpl(path);
+ }
+}
\ No newline at end of file
diff --git a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
index 46308c3243ed3..16665c3752ebe 100644
--- a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
+++ b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
@@ -31,6 +31,7 @@ org.apache.camel.component.alibaba.ots.AlibabaOtsComponent=camel:alibaba-ots
org.apache.camel.component.alibaba.sls.AlibabaSlsComponent=camel:alibaba-sls
org.apache.camel.component.alibaba.sms.SMSComponent=camel:alibaba-sms
org.apache.camel.component.amqp.AMQPComponent=camel:amqp
+org.apache.camel.component.apicurioregistry.ApicurioRegistryComponent=camel:apicurio-registry
org.apache.camel.component.arangodb.ArangoDbComponent=camel:arangodb
org.apache.camel.component.as2.AS2Component=camel:as2
org.apache.camel.component.asterisk.AsteriskComponent=camel:asterisk
diff --git a/parent/pom.xml b/parent/pom.xml
index b28e6fb41e1d3..0aa18dc4cf8f5 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -79,6 +79,7 @@
2.0.5
2.0.0.AM27
1.22.0
+ 3.3.3
7.28.0
1.13.0
9.11.1
@@ -854,6 +855,11 @@
camel-amqp
${project.version}
+
+ org.apache.camel
+ camel-apicurio-registry
+ ${project.version}
+
org.apache.camel
camel-arangodb
@@ -3417,6 +3423,13 @@
${jspecify-version}
+
+
+ io.apicurio
+ apicurio-registry-java-sdk
+ ${apicurio-registry-sdk-version}
+
+
org.slf4j
diff --git a/test-infra/camel-test-infra-all/pom.xml b/test-infra/camel-test-infra-all/pom.xml
index 4d7dc5003b4f1..548404b3fb891 100644
--- a/test-infra/camel-test-infra-all/pom.xml
+++ b/test-infra/camel-test-infra-all/pom.xml
@@ -96,6 +96,11 @@
camel-test-infra-dispatch-router
${project.version}
+
+ org.apache.camel
+ camel-test-infra-apicurio-registry
+ ${project.version}
+
org.apache.camel
camel-test-infra-arangodb
@@ -353,6 +358,12 @@
prepare-package
+
+
+ org.apache.camel
+ camel-test-infra-apicurio-registry
+
+
org.apache.camel
diff --git a/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json b/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
index 834a1750bf196..0979943c1d8d0 100644
--- a/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
+++ b/test-infra/camel-test-infra-all/src/generated/resources/META-INF/metadata.json
@@ -592,6 +592,17 @@
"version" : "4.24.0-SNAPSHOT",
"serviceVersion" : "camel",
"uiSupported" : false
+}, {
+ "service" : "org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryInfraService",
+ "description" : "Apicurio Registry is an API/Schema registry",
+ "implementation" : "org.apache.camel.test.infra.apicurio.registry.services.ApicurioRegistryLocalContainerInfraService",
+ "alias" : [ "apicurio-registry" ],
+ "aliasImplementation" : [ ],
+ "groupId" : "org.apache.camel",
+ "artifactId" : "camel-test-infra-apicurio-registry",
+ "version" : "4.24.0-SNAPSHOT",
+ "serviceVersion" : "3.3.3",
+ "uiSupported" : false
}, {
"service" : "org.apache.camel.test.infra.postgres.services.PostgresInfraService",
"description" : "PostgreSQL with pgvector extension for vector similarity search",
diff --git a/test-infra/camel-test-infra-apicurio-registry/pom.xml b/test-infra/camel-test-infra-apicurio-registry/pom.xml
new file mode 100644
index 0000000000000..2280ed1a54e7b
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ camel-test-infra-parent
+ org.apache.camel
+ ../camel-test-infra-parent/pom.xml
+ 4.24.0-SNAPSHOT
+
+
+ 4.0.0
+
+ camel-test-infra-apicurio-registry
+ Camel :: Test Infra :: Apicurio Registry
+
+
+
+ org.apache.camel
+ camel-test-infra-common
+ ${project.version}
+
+
+
+
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/common/ApicurioRegistryProperties.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/common/ApicurioRegistryProperties.java
new file mode 100644
index 0000000000000..d73943ed709d7
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/common/ApicurioRegistryProperties.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.infra.apicurio.registry.common;
+
+public final class ApicurioRegistryProperties {
+ public static final String APICURIO_REGISTRY_URL = "apicurio.registry.url";
+ public static final String APICURIO_REGISTRY_HOST = "apicurio.registry.host";
+ public static final String APICURIO_REGISTRY_PORT = "apicurio.registry.port";
+ public static final String APICURIO_REGISTRY_CONTAINER = "apicurio.registry.container";
+ public static final int DEFAULT_PORT = 8080;
+
+ private ApicurioRegistryProperties() {
+ }
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryInfraService.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryInfraService.java
new file mode 100644
index 0000000000000..7a8b8380ff79a
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryInfraService.java
@@ -0,0 +1,28 @@
+/*
+ * 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.test.infra.apicurio.registry.services;
+
+import org.apache.camel.test.infra.common.services.InfrastructureService;
+
+public interface ApicurioRegistryInfraService extends InfrastructureService {
+
+ String getApicurioRegistryUrl();
+
+ String host();
+
+ int port();
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryLocalContainerInfraService.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryLocalContainerInfraService.java
new file mode 100644
index 0000000000000..4646f86969687
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryLocalContainerInfraService.java
@@ -0,0 +1,106 @@
+/*
+ * 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.test.infra.apicurio.registry.services;
+
+import org.apache.camel.spi.annotations.InfraService;
+import org.apache.camel.test.infra.apicurio.registry.common.ApicurioRegistryProperties;
+import org.apache.camel.test.infra.common.LocalPropertyResolver;
+import org.apache.camel.test.infra.common.services.ContainerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+@InfraService(service = ApicurioRegistryInfraService.class,
+ description = "Apicurio Registry is an API/Schema registry",
+ serviceAlias = { "apicurio-registry" })
+public class ApicurioRegistryLocalContainerInfraService
+ implements ApicurioRegistryInfraService, ContainerService> {
+
+ public static final String CONTAINER_NAME = "apicurio-registry";
+
+ private static final Logger LOG = LoggerFactory.getLogger(ApicurioRegistryLocalContainerInfraService.class);
+
+ private final GenericContainer> container;
+
+ public ApicurioRegistryLocalContainerInfraService() {
+ this(LocalPropertyResolver.getProperty(
+ ApicurioRegistryLocalContainerInfraService.class,
+ ApicurioRegistryProperties.APICURIO_REGISTRY_CONTAINER));
+ }
+
+ public ApicurioRegistryLocalContainerInfraService(String containerName) {
+ container = initContainer(containerName, CONTAINER_NAME);
+ }
+
+ public ApicurioRegistryLocalContainerInfraService(GenericContainer> container) {
+ this.container = container;
+ }
+
+ @SuppressWarnings("resource")
+ protected GenericContainer> initContainer(String imageName, String containerName) {
+ return new GenericContainer<>(imageName)
+ .withNetworkAliases(containerName)
+ .withExposedPorts(ApicurioRegistryProperties.DEFAULT_PORT)
+ .withEnv("APICURIO_REST_DELETION_ARTIFACT_ENABLED", "true")
+ .waitingFor(Wait.forHttp("/apis/registry/v3/system/info")
+ .forPort(ApicurioRegistryProperties.DEFAULT_PORT).forStatusCode(200));
+ }
+
+ @Override
+ public void registerProperties() {
+ System.setProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_URL, getApicurioRegistryUrl());
+ System.setProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_HOST, host());
+ System.setProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_PORT, String.valueOf(port()));
+ }
+
+ @Override
+ public void initialize() {
+ LOG.info("Trying to start the Apicurio Registry container");
+ container.start();
+
+ registerProperties();
+ LOG.info("Apicurio Registry instance running at {}", getApicurioRegistryUrl());
+ }
+
+ @Override
+ public void shutdown() {
+ LOG.info("Stopping the Apicurio Registry container");
+ container.stop();
+ }
+
+ @Override
+ public GenericContainer> getContainer() {
+ return container;
+ }
+
+ @Override
+ public String getApicurioRegistryUrl() {
+ return String.format("http://%s:%d/apis/registry/v3", host(),
+ container.getMappedPort(ApicurioRegistryProperties.DEFAULT_PORT));
+ }
+
+ @Override
+ public String host() {
+ return container.getHost();
+ }
+
+ @Override
+ public int port() {
+ return container.getMappedPort(ApicurioRegistryProperties.DEFAULT_PORT);
+ }
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryRemoteInfraService.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryRemoteInfraService.java
new file mode 100644
index 0000000000000..694a3ef14cbee
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryRemoteInfraService.java
@@ -0,0 +1,58 @@
+/*
+ * 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.test.infra.apicurio.registry.services;
+
+import org.apache.camel.test.infra.apicurio.registry.common.ApicurioRegistryProperties;
+
+public class ApicurioRegistryRemoteInfraService implements ApicurioRegistryInfraService {
+
+ @Override
+ public void registerProperties() {
+ // NO-OP
+ }
+
+ @Override
+ public void initialize() {
+ registerProperties();
+ }
+
+ @Override
+ public void shutdown() {
+ // NO-OP
+ }
+
+ @Override
+ public String getApicurioRegistryUrl() {
+ return System.getProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_URL);
+ }
+
+ @Override
+ public String host() {
+ return System.getProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_HOST);
+ }
+
+ @Override
+ public int port() {
+ String strPort = System.getProperty(ApicurioRegistryProperties.APICURIO_REGISTRY_PORT);
+
+ if (strPort == null) {
+ return ApicurioRegistryProperties.DEFAULT_PORT;
+ }
+
+ return Integer.parseInt(strPort);
+ }
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryService.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryService.java
new file mode 100644
index 0000000000000..71969410a917e
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryService.java
@@ -0,0 +1,23 @@
+/*
+ * 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.test.infra.apicurio.registry.services;
+
+import org.apache.camel.test.infra.common.services.ContainerTestService;
+import org.apache.camel.test.infra.common.services.TestService;
+
+public interface ApicurioRegistryService extends ApicurioRegistryInfraService, TestService, ContainerTestService {
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryServiceFactory.java b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryServiceFactory.java
new file mode 100644
index 0000000000000..2a64be01f9aaa
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/java/org/apache/camel/test/infra/apicurio/registry/services/ApicurioRegistryServiceFactory.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test.infra.apicurio.registry.services;
+
+import org.apache.camel.test.infra.common.services.SimpleTestServiceBuilder;
+
+public final class ApicurioRegistryServiceFactory {
+
+ private ApicurioRegistryServiceFactory() {
+ }
+
+ public static SimpleTestServiceBuilder builder() {
+ return new SimpleTestServiceBuilder<>("apicurio-registry");
+ }
+
+ public static ApicurioRegistryService createService() {
+ return builder()
+ .addLocalMapping(ApicurioRegistryLocalContainerTestService::new)
+ .addRemoteMapping(ApicurioRegistryRemoteTestService::new)
+ .build();
+ }
+
+ public static class ApicurioRegistryLocalContainerTestService
+ extends ApicurioRegistryLocalContainerInfraService
+ implements ApicurioRegistryService {
+ }
+
+ public static class ApicurioRegistryRemoteTestService
+ extends ApicurioRegistryRemoteInfraService
+ implements ApicurioRegistryService {
+ }
+}
diff --git a/test-infra/camel-test-infra-apicurio-registry/src/main/resources/org/apache/camel/test/infra/apicurio/registry/services/container.properties b/test-infra/camel-test-infra-apicurio-registry/src/main/resources/org/apache/camel/test/infra/apicurio/registry/services/container.properties
new file mode 100644
index 0000000000000..52fe8f7536525
--- /dev/null
+++ b/test-infra/camel-test-infra-apicurio-registry/src/main/resources/org/apache/camel/test/infra/apicurio/registry/services/container.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+apicurio.registry.container=quay.io/apicurio/apicurio-registry:3.3.3
diff --git a/test-infra/pom.xml b/test-infra/pom.xml
index d67821fa64944..2ffc2452d5e65 100644
--- a/test-infra/pom.xml
+++ b/test-infra/pom.xml
@@ -59,6 +59,7 @@
camel-test-infra-dispatch-router
camel-test-infra-jdbc
camel-test-infra-arangodb
+ camel-test-infra-apicurio-registry
camel-test-infra-consul
camel-test-infra-google-pubsub
camel-test-infra-infinispan