From 3e02dc3d948dc32af5ce710cf442db2d15f0844b Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 23 Oct 2026 20:09:01 +0200 Subject: [PATCH 1/5] CAMEL-25924: kafka, ftp, dns, as2, graphql, vertx-http, github2, pdf, tika, infinispan, snmp, jdbc, jsch, micrometer - bug fixes CAMEL-25924 kafka: only a JSON array is split, for Jackson 2 and 3 CAMEL-25938 ftp, ftps, sftp, mina-sftp, azure-files, smb: the default of download is true in the catalog CAMEL-25977 dns: dig defaults to type A and needs a name; wikipedia is deprecated CAMEL-26052 as2: client/send sends bytes and files, fails on an HTTP error, requestUri default, ediMessageType deprecated, quoted-printable, logging CAMEL-26053 graphql: the request headers of the producer win and do not stay on the message; Map bodies CAMEL-26058 vertx-http: Path and Reader bodies, decoded urlencoded forms CAMEL-25983 github2: getCommitFile reads the file of the commit CAMEL-26054 pdf: pageSize values, extractText after create, line ends, no body, Path files to merge CAMEL-26057 tika: a body that is not a stream fails clearly; the charset of the output CAMEL-25976 infinispan-embedded: the configurationUri reads files and placeholders CAMEL-26055 snmp: GET without type, trap body, SnmpMessage to String, the poll consumer CAMEL-26056 jdbc: outputClass not found, generated keys, no body, docs CAMEL-26059 jsch: scp without a known hosts file; the remote directory must exist CAMEL-26061 micrometer: the docs say a failure fails the exchange Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../apache/camel/catalog/components/as2.json | 4 +- .../camel/catalog/components/azure-files.json | 2 +- .../apache/camel/catalog/components/dns.json | 8 +- .../apache/camel/catalog/components/ftp.json | 2 +- .../apache/camel/catalog/components/ftps.json | 2 +- .../camel/catalog/components/github2.json | 9 +- .../camel/catalog/components/graphql.json | 4 +- .../components/infinispan-embedded.json | 12 +- .../apache/camel/catalog/components/jdbc.json | 6 +- .../camel/catalog/components/kafka.json | 4 +- .../camel/catalog/components/micrometer.json | 4 +- .../camel/catalog/components/mina-sftp.json | 2 +- .../apache/camel/catalog/components/pdf.json | 4 +- .../apache/camel/catalog/components/sftp.json | 2 +- .../apache/camel/catalog/components/smb.json | 2 +- .../apache/camel/catalog/components/snmp.json | 2 +- .../apache/camel/catalog/components/tika.json | 4 +- .../camel/catalog/docs/as2-component.adoc | 25 +- .../camel/catalog/docs/dns-component.adoc | 6 +- .../camel/catalog/docs/github2-component.adoc | 6 +- .../camel/catalog/docs/graphql-component.adoc | 20 +- .../docs/infinispan-embedded-component.adoc | 30 ++- .../camel/catalog/docs/jdbc-component.adoc | 15 +- .../catalog/docs/micrometer-component.adoc | 22 +- .../camel/catalog/docs/pdf-component.adoc | 20 ++ .../camel/catalog/docs/scp-component.adoc | 8 + .../camel/catalog/docs/snmp-component.adoc | 15 +- .../camel/catalog/docs/tika-component.adoc | 6 + .../catalog/docs/vertx-http-component.adoc | 15 +- .../as2/api/AS2AsyncMDNServerConnection.java | 4 + .../component/as2/api/AS2ClientManager.java | 7 +- .../as2/api/AS2ServerConnection.java | 7 +- .../component/as2/api/util/EntityUtils.java | 47 +++- .../api/AS2AsyncMDNServerConnectionTest.java | 29 +- .../as2/api/AS2UnencryptedMessageTest.java | 18 ++ .../camel/component/as2/api/LogCapture.java | 70 +++++ .../as2/api/util/EntityUtilsTest.java | 33 +++ ...AS2ClientManagerEndpointConfiguration.java | 2 +- .../org/apache/camel/component/as2/as2.json | 4 +- .../src/main/docs/as2-component.adoc | 25 +- .../camel/component/as2/AS2Configuration.java | 6 +- .../camel/component/as2/AS2Endpoint.java | 32 ++- .../camel/component/as2/AS2Producer.java | 42 +++ .../component/as2/AS2ClientSendTest.java | 251 ++++++++++++++++++ .../component/file/azure/azure-files.json | 2 +- .../org/apache/camel/component/dns/dns.json | 8 +- .../src/main/docs/dns-component.adoc | 6 +- .../camel/component/dns/DnsComponent.java | 3 +- .../camel/component/dns/DnsConstants.java | 15 +- .../camel/component/dns/DnsDigProducer.java | 24 +- .../camel/component/dns/DnsEndpoint.java | 4 +- .../apache/camel/component/dns/DnsType.java | 4 + .../component/dns/DnsWikipediaProducer.java | 3 + .../component/dns/DnsDigProducerTest.java | 88 ++++++ .../file/remote/RemoteFileEndpoint.java | 9 +- .../camel/component/file/remote/ftp.json | 2 +- .../camel/component/file/remote/ftps.json | 2 +- .../camel/component/file/remote/sftp.json | 2 +- .../camel/component/github2/github2.json | 9 +- .../src/main/docs/github2-component.adoc | 6 +- .../component/github2/GitHub2Constants.java | 9 +- .../component/github2/GitHub2Endpoint.java | 2 +- .../producer/GetCommitFileProducer.java | 75 ++++-- .../producer/GetCommitFileProducerTest.java | 161 +++++++++++ .../camel/component/graphql/graphql.json | 4 +- .../src/main/docs/graphql-component.adoc | 20 +- .../component/graphql/GraphqlEndpoint.java | 5 +- .../component/graphql/GraphqlProducer.java | 75 +++++- .../graphql/GraphqlProducerHeadersTest.java | 178 +++++++++++++ .../graphql/server/GraphqlServer.java | 15 ++ .../embedded/infinispan-embedded.json | 12 +- .../docs/infinispan-embedded-component.adoc | 30 ++- .../embedded/InfinispanEmbeddedComponent.java | 3 +- .../InfinispanEmbeddedConfiguration.java | 9 + .../embedded/InfinispanEmbeddedManager.java | 25 +- .../InfinispanEmbeddedClusterView.java | 1 + ...EmbeddedComponentConfigurationUriTest.java | 92 +++++++ .../org/apache/camel/component/jdbc/jdbc.json | 6 +- .../src/main/docs/jdbc-component.adoc | 15 +- .../camel/component/jdbc/JdbcConstants.java | 4 +- .../camel/component/jdbc/JdbcEndpoint.java | 10 +- .../camel/component/jdbc/JdbcHelper.java | 4 + .../camel/component/jdbc/JdbcProducer.java | 10 +- .../JdbcGeneratedKeysOutputClassTest.java | 69 +++++ .../camel/component/jdbc/JdbcNoBodyTest.java | 62 +++++ .../jdbc/JdbcOutputClassNotFoundTest.java | 55 ++++ .../src/main/docs/scp-component.adoc | 8 + .../camel/component/scp/ScpOperations.java | 3 +- .../component/scp/ScpNoKnownHostsTest.java | 72 +++++ components/camel-kafka/pom.xml | 6 + .../apache/camel/component/kafka/kafka.json | 4 +- .../component/kafka/KafkaConfiguration.java | 3 +- .../camel/component/kafka/KafkaProducer.java | 27 +- .../component/kafka/KafkaProducerTest.java | 75 ++++++ .../MicrometerEndpointUriFactory.java | 2 +- .../component/micrometer/micrometer.json | 4 +- .../src/main/docs/micrometer-component.adoc | 22 +- .../micrometer/MicrometerConstants.java | 2 +- .../micrometer/MicrometerEndpoint.java | 3 +- .../component/file/remote/mina/mina-sftp.json | 2 +- .../pdf/converter/PdfConverterLoader.java | 16 ++ .../org/apache/camel/component/pdf/pdf.json | 4 +- .../src/main/docs/pdf-component.adoc | 20 ++ .../camel/component/pdf/PdfConfiguration.java | 56 ++-- .../component/pdf/PdfHeaderConstants.java | 5 +- .../component/pdf/PdfPageSizeConstant.java | 1 + .../camel/component/pdf/PdfProducer.java | 32 ++- .../component/pdf/converter/PdfConverter.java | 16 ++ .../LineTerminationWriterAbstractFactory.java | 39 ++- .../camel/component/pdf/PdfAppendTest.java | 6 +- .../camel/component/pdf/PdfConverterTest.java | 29 +- .../camel/component/pdf/PdfCreationTest.java | 142 +++++++--- .../camel/component/pdf/PdfMergeTest.java | 64 +++-- .../component/pdf/PdfTextExtractionTest.java | 113 +++++--- .../org/apache/camel/component/smb/smb.json | 2 +- .../camel/component/smb/SmbConfiguration.java | 9 +- .../component/snmp/SnmpConvertersLoader.java | 8 + .../org/apache/camel/component/snmp/snmp.json | 2 +- .../src/main/docs/snmp-component.adoc | 15 +- .../camel/component/snmp/SnmpConverters.java | 13 + .../camel/component/snmp/SnmpEndpoint.java | 14 +- .../camel/component/snmp/SnmpOIDPoller.java | 45 +--- .../camel/component/snmp/SnmpProducer.java | 4 +- .../component/snmp/SnmpTrapProducer.java | 2 +- .../camel/component/snmp/PollOIDIdleTest.java | 58 ++++ .../snmp/ProducerWithoutTypeTest.java | 73 +++++ .../component/snmp/TrapProducerBodyTest.java | 61 +++++ .../component/snmp/UriConfigurationTest.java | 36 +++ .../org/apache/camel/component/tika/tika.json | 4 +- .../src/main/docs/tika-component.adoc | 6 + .../component/tika/TikaConfiguration.java | 9 +- .../camel/component/tika/TikaProducer.java | 13 +- .../camel/component/tika/TikaDetectTest.java | 19 ++ .../camel/component/tika/TikaParseTest.java | 72 ++++- .../src/main/docs/vertx-http-component.adoc | 15 +- .../vertx/http/VertxHttpProducer.java | 47 +++- .../http/VertxHttpRequestBodyTypesTest.java | 98 +++++++ .../pages/camel-4x-upgrade-guide-4_24.adoc | 94 +++++++ ...nispanEmbeddedComponentBuilderFactory.java | 12 +- .../dsl/KafkaComponentBuilderFactory.java | 4 +- .../endpoint/StaticEndpointBuilders.java | 12 +- .../dsl/AS2EndpointBuilderFactory.java | 9 + .../dsl/DnsEndpointBuilderFactory.java | 15 +- .../dsl/FilesEndpointBuilderFactory.java | 4 +- .../dsl/FtpEndpointBuilderFactory.java | 4 +- .../dsl/FtpsEndpointBuilderFactory.java | 4 +- .../dsl/GitHub2EndpointBuilderFactory.java | 21 +- .../dsl/GraphqlEndpointBuilderFactory.java | 7 +- ...inispanEmbeddedEndpointBuilderFactory.java | 60 +++-- .../dsl/JdbcEndpointBuilderFactory.java | 26 +- .../dsl/KafkaEndpointBuilderFactory.java | 8 +- .../dsl/MicrometerEndpointBuilderFactory.java | 8 +- .../dsl/MinaSftpEndpointBuilderFactory.java | 4 +- .../dsl/PdfEndpointBuilderFactory.java | 8 +- .../dsl/SftpEndpointBuilderFactory.java | 4 +- .../dsl/SmbEndpointBuilderFactory.java | 4 +- .../dsl/SnmpEndpointBuilderFactory.java | 36 ++- .../dsl/TikaEndpointBuilderFactory.java | 11 +- 158 files changed, 3202 insertions(+), 530 deletions(-) create mode 100644 components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/LogCapture.java create mode 100644 components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientSendTest.java create mode 100644 components/camel-dns/src/test/java/org/apache/camel/component/dns/DnsDigProducerTest.java create mode 100644 components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java create mode 100644 components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProducerHeadersTest.java create mode 100644 components/camel-infinispan/camel-infinispan-embedded/src/test/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponentConfigurationUriTest.java create mode 100644 components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcGeneratedKeysOutputClassTest.java create mode 100644 components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcNoBodyTest.java create mode 100644 components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcOutputClassNotFoundTest.java create mode 100644 components/camel-jsch/src/test/java/org/apache/camel/component/scp/ScpNoKnownHostsTest.java create mode 100644 components/camel-snmp/src/test/java/org/apache/camel/component/snmp/PollOIDIdleTest.java create mode 100644 components/camel-snmp/src/test/java/org/apache/camel/component/snmp/ProducerWithoutTypeTest.java create mode 100644 components/camel-snmp/src/test/java/org/apache/camel/component/snmp/TrapProducerBodyTest.java create mode 100644 components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpRequestBodyTypesTest.java diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json index 989fb9a25f74f..ee51bd92337cf 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json @@ -46,7 +46,7 @@ "dispositionNotificationTo": { "index": 10, "kind": "parameter", "displayName": "Disposition Notification To", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The value of the Disposition-Notification-To header. Assigning a value to this parameter requests a message disposition notification (MDN) for the AS2 message." }, "ediMessageCharset": { "index": 11, "kind": "parameter", "displayName": "Edi Message Charset", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "us-ascii", "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The charset of the content type of EDI message." }, "ediMessageTransferEncoding": { "index": 12, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The transfer encoding of EDI message." }, - "ediMessageType": { "index": 13, "kind": "parameter", "displayName": "Edi Message Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "application\/edifact", "application\/edi-x12", "application\/edi-consent", "application\/xml" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The content type of EDI message. One of application\/edifact, application\/edi-x12, application\/edi-consent, application\/xml" }, + "ediMessageType": { "index": 13, "kind": "parameter", "displayName": "Edi Message Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "application\/edifact", "application\/edi-x12", "application\/edi-consent", "application\/xml" ], "deprecated": true, "deprecationNote": "Use ediMessageContentType instead", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The content type of EDI message. One of application\/edifact, application\/edi-x12, application\/edi-consent, application\/xml. client\/send uses this value only when ediMessageContentType is not set. Deprecation note: Use ediMessageContentType instead" }, "from": { "index": 14, "kind": "parameter", "displayName": "From", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The value of the From header of AS2 message." }, "httpConnectionPoolSize": { "index": 15, "kind": "parameter", "displayName": "Http Connection Pool Size", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The maximum size of the connection pool for http connections (client only)" }, "httpConnectionPoolTtl": { "index": 16, "kind": "parameter", "displayName": "Http Connection Pool Ttl", "group": "common", "label": "", "required": false, "type": "duration", "javaType": "java.time.Duration", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "15m", "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The time to live for connections in the connection pool (client only)" }, @@ -92,7 +92,7 @@ "server": { "consumerOnly": true, "producerOnly": false, "description": "Receives EDI Messages over HTTP", "methods": { "listen": { "description": "", "signatures": [ "void listen(String requestUriPattern, org.apache.hc.core5.http.io.HttpRequestHandler handler)" ] } } } }, "apiProperties": { - "client": { "methods": { "send": { "properties": { "accessToken": { "index": 0, "kind": "parameter", "displayName": "Access Token", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The access token that is used by the client for bearer authentication", "optional": true }, "as2From": { "index": 1, "kind": "parameter", "displayName": "As2 From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of sender", "optional": false }, "as2MessageStructure": { "index": 2, "kind": "parameter", "displayName": "As2 Message Structure", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2MessageStructure", "enum": [ "PLAIN", "SIGNED", "ENCRYPTED", "SIGNED_ENCRYPTED", "PLAIN_COMPRESSED", "COMPRESSED_SIGNED", "SIGNED_COMPRESSED", "ENCRYPTED_COMPRESSED", "ENCRYPTED_COMPRESSED_SIGNED", "ENCRYPTED_SIGNED_COMPRESSED" ], "deprecated": false, "autowired": false, "secret": false, "description": "The structure of AS2 to send; see AS2MessageStructure", "optional": false }, "as2To": { "index": 3, "kind": "parameter", "displayName": "As2 To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of recipient", "optional": false }, "attachedFileName": { "index": 4, "kind": "parameter", "displayName": "Attached File Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of the attached file or null if user doesn't want to specify it", "optional": true }, "compressionAlgorithm": { "index": 5, "kind": "parameter", "displayName": "Compression Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2CompressionAlgorithm", "enum": [ "ZLIB" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to compress the message or null if sending EDI message uncompressed", "optional": true }, "dispositionNotificationTo": { "index": 6, "kind": "parameter", "displayName": "Disposition Notification To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An RFC2822 address to request a receipt or null if no receipt requested", "optional": true }, "ediMessage": { "index": 7, "kind": "parameter", "displayName": "Edi Message", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "description": "EDI message to transport", "optional": false }, "ediMessageCharset": { "index": 8, "kind": "parameter", "displayName": "Edi Message Charset", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the EDI message", "optional": true }, "ediMessageContentType": { "index": 9, "kind": "parameter", "displayName": "Edi Message Content Type", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type of EDI message", "optional": false }, "ediMessageTransferEncoding": { "index": 10, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The transfer encoding used to transport EDI message", "optional": true }, "encryptingAlgorithm": { "index": 11, "kind": "parameter", "displayName": "Encrypting Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [ "AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM", "AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC", "CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC", "GOST28147_GCFB", "IDEA_CBC", "RC2_CBC", "RC4", "SEED_CBC" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "encryptingCertificateChain": { "index": 12, "kind": "parameter", "displayName": "Encrypting Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "from": { "index": 13, "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "RFC2822 address of sender", "optional": false }, "password": { "index": 14, "kind": "parameter", "displayName": "Password", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The password that is used by the client for basic authentication", "optional": true }, "receiptDeliveryOption": { "index": 15, "kind": "parameter", "displayName": "Receipt Delivery Option", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The return URL that the message receiver should send an asynchronous MDN to", "optional": true }, "requestUri": { "index": 16, "kind": "parameter", "displayName": "Request Uri", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Resource location to deliver message", "optional": false }, "signedReceiptMicAlgorithms": { "index": 17, "kind": "parameter", "displayName": "Signed Receipt Mic Algorithms", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The senders list of signing algorithms for signing receipt, in preferred order, or null if requesting an unsigned receipt.", "optional": true }, "signingAlgorithm": { "index": 18, "kind": "parameter", "displayName": "Signing Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [ "SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA", "MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA", "RIPEMD256WITHRSA", "SHA224WITHDSA", "SHA256WITHDSA", "SHA384WITHDSA", "SHA512WITHDSA", "SHA3_224WITHDSA", "SHA3_256WITHDSA", "SHA3_384WITHDSA", "SHA3_512WITHDSA", "SHA1WITHDSA", "SHA3_224WITHECDSA", "SHA3_256WITHECDSA", "SHA3_384WITHECDSA", "SHA3_512WITHECDSA", "SHA1WITHECDSA", "SHA224WITHECDSA", "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA", "SHA1WITHPLAIN_ECDSA", "SHA224WITHPLAIN_ECDSA", "SHA256WITHPLAIN_ECDSA", "SHA384WITHPLAIN_ECDSA", "SHA512WITHPLAIN_ECDSA", "RIPEMD160WITHPLAIN_ECDSA", "SHA1WITHRSAANDMGF1", "SHA224WITHRSAANDMGF1", "SHA256WITHRSAANDMGF1", "SHA384WITHRSAANDMGF1", "SHA512WITHRSAANDMGF1", "SHA3_224WITHRSAANDMGF1", "SHA3_256WITHRSAANDMGF1", "SHA3_384WITHRSAANDMGF1", "SHA3_512WITHRSAANDMGF1" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingCertificateChain": { "index": 19, "kind": "parameter", "displayName": "Signing Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingPrivateKey": { "index": 20, "kind": "parameter", "displayName": "Signing Private Key", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated": false, "autowired": false, "secret": false, "description": "The private key used to sign EDI message", "optional": true }, "subject": { "index": 21, "kind": "parameter", "displayName": "Subject", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Message subject", "optional": false }, "userName": { "index": 22, "kind": "parameter", "displayName": "User Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The user-name that is used for basic authentication", "optional": true } } } } }, + "client": { "methods": { "send": { "properties": { "accessToken": { "index": 0, "kind": "parameter", "displayName": "Access Token", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The access token that is used by the client for bearer authentication", "optional": true }, "as2From": { "index": 1, "kind": "parameter", "displayName": "As2 From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of sender", "optional": false }, "as2MessageStructure": { "index": 2, "kind": "parameter", "displayName": "As2 Message Structure", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2MessageStructure", "enum": [ "PLAIN", "SIGNED", "ENCRYPTED", "SIGNED_ENCRYPTED", "PLAIN_COMPRESSED", "COMPRESSED_SIGNED", "SIGNED_COMPRESSED", "ENCRYPTED_COMPRESSED", "ENCRYPTED_COMPRESSED_SIGNED", "ENCRYPTED_SIGNED_COMPRESSED" ], "deprecated": false, "autowired": false, "secret": false, "description": "The structure of AS2 to send; see AS2MessageStructure", "optional": false }, "as2To": { "index": 3, "kind": "parameter", "displayName": "As2 To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of recipient", "optional": false }, "attachedFileName": { "index": 4, "kind": "parameter", "displayName": "Attached File Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of the attached file or null if user doesn't want to specify it", "optional": true }, "compressionAlgorithm": { "index": 5, "kind": "parameter", "displayName": "Compression Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2CompressionAlgorithm", "enum": [ "ZLIB" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to compress the message or null if sending EDI message uncompressed", "optional": true }, "dispositionNotificationTo": { "index": 6, "kind": "parameter", "displayName": "Disposition Notification To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An RFC2822 address to request a receipt or null if no receipt requested", "optional": true }, "ediMessage": { "index": 7, "kind": "parameter", "displayName": "Edi Message", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "description": "EDI message to transport: a String, an InputStream or a byte array", "optional": false }, "ediMessageCharset": { "index": 8, "kind": "parameter", "displayName": "Edi Message Charset", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the EDI message", "optional": true }, "ediMessageContentType": { "index": 9, "kind": "parameter", "displayName": "Edi Message Content Type", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type of EDI message", "optional": false }, "ediMessageTransferEncoding": { "index": 10, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The transfer encoding used to transport EDI message", "optional": true }, "encryptingAlgorithm": { "index": 11, "kind": "parameter", "displayName": "Encrypting Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [ "AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM", "AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC", "CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC", "GOST28147_GCFB", "IDEA_CBC", "RC2_CBC", "RC4", "SEED_CBC" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "encryptingCertificateChain": { "index": 12, "kind": "parameter", "displayName": "Encrypting Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "from": { "index": 13, "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "RFC2822 address of sender", "optional": false }, "password": { "index": 14, "kind": "parameter", "displayName": "Password", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The password that is used by the client for basic authentication", "optional": true }, "receiptDeliveryOption": { "index": 15, "kind": "parameter", "displayName": "Receipt Delivery Option", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The return URL that the message receiver should send an asynchronous MDN to", "optional": true }, "requestUri": { "index": 16, "kind": "parameter", "displayName": "Request Uri", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Resource location to deliver message", "optional": false }, "signedReceiptMicAlgorithms": { "index": 17, "kind": "parameter", "displayName": "Signed Receipt Mic Algorithms", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The senders list of signing algorithms for signing receipt, in preferred order, or null if requesting an unsigned receipt.", "optional": true }, "signingAlgorithm": { "index": 18, "kind": "parameter", "displayName": "Signing Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [ "SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA", "MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA", "RIPEMD256WITHRSA", "SHA224WITHDSA", "SHA256WITHDSA", "SHA384WITHDSA", "SHA512WITHDSA", "SHA3_224WITHDSA", "SHA3_256WITHDSA", "SHA3_384WITHDSA", "SHA3_512WITHDSA", "SHA1WITHDSA", "SHA3_224WITHECDSA", "SHA3_256WITHECDSA", "SHA3_384WITHECDSA", "SHA3_512WITHECDSA", "SHA1WITHECDSA", "SHA224WITHECDSA", "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA", "SHA1WITHPLAIN_ECDSA", "SHA224WITHPLAIN_ECDSA", "SHA256WITHPLAIN_ECDSA", "SHA384WITHPLAIN_ECDSA", "SHA512WITHPLAIN_ECDSA", "RIPEMD160WITHPLAIN_ECDSA", "SHA1WITHRSAANDMGF1", "SHA224WITHRSAANDMGF1", "SHA256WITHRSAANDMGF1", "SHA384WITHRSAANDMGF1", "SHA512WITHRSAANDMGF1", "SHA3_224WITHRSAANDMGF1", "SHA3_256WITHRSAANDMGF1", "SHA3_384WITHRSAANDMGF1", "SHA3_512WITHRSAANDMGF1" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingCertificateChain": { "index": 19, "kind": "parameter", "displayName": "Signing Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingPrivateKey": { "index": 20, "kind": "parameter", "displayName": "Signing Private Key", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated": false, "autowired": false, "secret": false, "description": "The private key used to sign EDI message", "optional": true }, "subject": { "index": 21, "kind": "parameter", "displayName": "Subject", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Message subject", "optional": false }, "userName": { "index": 22, "kind": "parameter", "displayName": "User Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The user-name that is used for basic authentication", "optional": true } } } } }, "receipt": { "methods": { "receive": { "properties": { "requestUriPattern": { "index": 0, "kind": "parameter", "displayName": "Request Uri Pattern", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "", "optional": false } } } } }, "server": { "methods": { "listen": { "properties": { "requestUriPattern": { "index": 0, "kind": "parameter", "displayName": "Request Uri Pattern", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "", "optional": false } } } } } } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/azure-files.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/azure-files.json index 231fb1308104c..65fd7e2caa373 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/azure-files.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/azure-files.json @@ -68,7 +68,7 @@ "sendEmptyMessageWhenIdle": { "index": 17, "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." }, "streamDownload": { "index": 18, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 19, "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." }, - "download": { "index": 20, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 20, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 21, "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": 22, "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." }, "inProgressRepository": { "index": 23, "kind": "parameter", "displayName": "In Progress Repository", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable in-progress repository org.apache.camel.spi.IdempotentRepository. The in-progress repository is used to account the current in progress files being consumed. By default a memory based repository is used." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/dns.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/dns.json index 9bd010f45fe95..f6182c1142c32 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/dns.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/dns.json @@ -29,14 +29,14 @@ }, "headers": { "CamelDnsClass": { "index": 0, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DNS class of the lookup. Should match the values of org.xbill.dns.DClass. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_CLASS" }, - "CamelDnsName": { "index": 1, "kind": "header", "displayName": "", "group": "lookup", "label": "lookup", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name to lookup", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_NAME" }, + "CamelDnsName": { "index": 1, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name to lookup", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_NAME" }, "CamelDnsDomain": { "index": 2, "kind": "header", "displayName": "", "group": "ip", "label": "ip", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The domain name.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_DOMAIN" }, "CamelDnsServer": { "index": 3, "kind": "header", "displayName": "", "group": "dig", "label": "dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The server in particular for the query. If none is given, the default one specified by the OS will be used. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_SERVER" }, - "CamelDnsType": { "index": 4, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. Should match the values of org.xbill.dns.Type. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_TYPE" }, - "CamelDnsTerm": { "index": 5, "kind": "header", "displayName": "", "group": "wikipedia", "label": "wikipedia", "required": true, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The term", "constantName": "org.apache.camel.component.dns.DnsConstants#TERM" } + "CamelDnsType": { "index": 4, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. Should match the values of org.xbill.dns.Type. Optional. The dig operation uses A when none or an unknown type is given.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_TYPE" }, + "CamelDnsTerm": { "index": 5, "kind": "header", "displayName": "", "group": "wikipedia", "label": "wikipedia", "required": true, "javaType": "", "deprecated": true, "deprecationNote": "The Wikipedia over DNS service is no longer available", "autowired": false, "secret": false, "description": "The term. Deprecation note: The Wikipedia over DNS service is no longer available", "constantName": "org.apache.camel.component.dns.DnsConstants#TERM" } }, "properties": { - "dnsType": { "index": 0, "kind": "path", "displayName": "Dns Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.dns.DnsType", "enum": [ "dig", "ip", "lookup", "wikipedia" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup." }, + "dnsType": { "index": 0, "kind": "path", "displayName": "Dns Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.dns.DnsType", "enum": [ "dig", "ip", "lookup", "wikipedia" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. The wikipedia type is deprecated, as the Wikipedia over DNS service it queries is no longer available." }, "lazyStartProducer": { "index": 1, "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." } } } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json index 8d0a3be6738aa..691f642b84592 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftp.json @@ -82,7 +82,7 @@ "sendEmptyMessageWhenIdle": { "index": 23, "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." }, "streamDownload": { "index": 24, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 25, "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." }, - "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 27, "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": 28, "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." }, "handleDirectoryParserAbsoluteResult": { "index": 29, "kind": "parameter", "displayName": "Handle Directory Parser Absolute Result", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Allows you to set how the consumer will handle subfolders and files in the path if the directory parser results in with absolute paths. The reason for this is that some FTP servers may return file names with absolute paths, and if so, then the FTP component needs to handle this by converting the returned path into a relative path." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json index ba1fade434f6b..1d2a9a2cc22ad 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ftps.json @@ -83,7 +83,7 @@ "sendEmptyMessageWhenIdle": { "index": 23, "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." }, "streamDownload": { "index": 24, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 25, "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." }, - "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 27, "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": 28, "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." }, "handleDirectoryParserAbsoluteResult": { "index": 29, "kind": "parameter", "displayName": "Handle Directory Parser Absolute Result", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Allows you to set how the consumer will handle subfolders and files in the path if the directory parser results in with absolute paths. The reason for this is that some FTP servers may return file names with absolute paths, and if so, then the FTP component needs to handle this by converting the returned path into a relative path." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json index e956fb8a2d632..de592a3659697 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json @@ -39,9 +39,10 @@ "CamelGitHubIssueTitle": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The title of the issue", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_ISSUE_TITLE" }, "CamelGitHubCommitAuthor": { "index": 4, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit author", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_AUTHOR" }, "CamelGitHubCommitCommitter": { "index": 5, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The committer name", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_COMMITTER" }, - "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit sha", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" }, - "CamelGitHubCommitUrl": { "index": 7, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit URL", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" }, - "CamelGitHubEventPayload": { "index": 8, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The event payload", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" } + "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit sha. The getCommitFile producer reads the file at this commit (a commit id of 40 hexadecimal characters).", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" }, + "CamelGitHubFilePath": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path of the file in the repository for the getCommitFile producer. If not set, the body is the path.", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_FILE_PATH" }, + "CamelGitHubCommitUrl": { "index": 8, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit URL", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" }, + "CamelGitHubEventPayload": { "index": 9, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The event payload", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" } }, "properties": { "type": { "index": 0, "kind": "path", "displayName": "Type", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.github2.GitHub2Type", "enum": [ "CLOSEPULLREQUEST", "PULLREQUESTCOMMENT", "COMMIT", "PULLREQUEST", "TAG", "PULLREQUESTSTATE", "PULLREQUESTFILES", "GETCOMMITFILE", "CREATEISSUE", "EVENT" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "What git operation to execute" }, @@ -56,7 +57,7 @@ "exceptionHandler": { "index": 9, "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": 10, "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": 11, "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." }, - "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use the given encoding when getting a git commit file" }, + "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the content of the file for the getCommitFile producer (the default charset of the JVM if not set)" }, "state": { "index": 13, "kind": "parameter", "displayName": "State", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "error", "failure", "pending", "success" ], "deprecated": false, "autowired": false, "secret": false, "description": "To set git commit status state" }, "targetUrl": { "index": 14, "kind": "parameter", "displayName": "Target Url", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To set git commit status target url" }, "lazyStartProducer": { "index": 15, "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." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json index 73ef1d1a19f3c..c1a6f8bca5d22 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json @@ -37,10 +37,10 @@ "proxyHost": { "index": 3, "kind": "parameter", "displayName": "Proxy Host", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The proxy host in the format hostname:port." }, "query": { "index": 4, "kind": "parameter", "displayName": "Query", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The query text." }, "queryFile": { "index": 5, "kind": "parameter", "displayName": "Query File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The query file name located in the classpath (or use file: to load from file system)." }, - "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the GraphQL query." }, + "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the GraphQL query. The header is not sent as an HTTP header." }, "throwExceptionOnFailure": { "index": 7, "kind": "parameter", "displayName": "Throw Exception On Failure", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Option to disable throwing the HttpOperationFailedException in case of failed responses from the remote server. This allows you to get all responses regardless of the HTTP status code." }, "variables": { "index": 8, "kind": "parameter", "displayName": "Variables", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated": false, "autowired": false, "secret": false, "description": "The JsonObject instance containing the operation variables." }, - "variablesHeader": { "index": 9, "kind": "parameter", "displayName": "Variables Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing a JsonObject instance containing the operation variables." }, + "variablesHeader": { "index": 9, "kind": "parameter", "displayName": "Variables Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the operation variables, as a JsonObject or a Map. The header is not sent as an HTTP header." }, "lazyStartProducer": { "index": 10, "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." }, "httpClient": { "index": 11, "kind": "parameter", "displayName": "Http Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.hc.client5.http.classic.HttpClient", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom pre-existing Http Client. Beware that when using this, then other configurations such as proxy, access token, is not applied and all this must be pre-configured on the Http Client." }, "accessToken": { "index": 12, "kind": "parameter", "displayName": "Access Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "description": "The access token sent in the Authorization header." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/infinispan-embedded.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/infinispan-embedded.json index b884448a4a183..31fe69ab05719 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/infinispan-embedded.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/infinispan-embedded.json @@ -38,10 +38,10 @@ "operation": { "index": 11, "kind": "property", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "org.apache.camel.component.infinispan.InfinispanOperation", "enum": [ "PUT", "PUTASYNC", "PUTALL", "PUTALLASYNC", "PUTIFABSENT", "PUTIFABSENTASYNC", "GET", "GETORDEFAULT", "CONTAINSKEY", "CONTAINSVALUE", "REMOVE", "REMOVEASYNC", "REPLACE", "REPLACEASYNC", "SIZE", "CLEAR", "CLEARASYNC", "QUERY", "STATS", "COMPUTE", "COMPUTEASYNC" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "PUT", "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The operation to perform" }, "value": { "index": 12, "kind": "property", "displayName": "Value", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific value for producer operations" }, "autowiredEnabled": { "index": 13, "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." }, - "cacheContainer": { "index": 14, "kind": "property", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect" }, - "cacheContainerConfiguration": { "index": 15, "kind": "property", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined." }, + "cacheContainer": { "index": 14, "kind": "property", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, + "cacheContainerConfiguration": { "index": 15, "kind": "property", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined. Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "configurationUri": { "index": 16, "kind": "property", "displayName": "Configuration Uri", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "An implementation specific URI for the CacheManager" }, - "flags": { "index": 17, "kind": "property", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation" }, + "flags": { "index": 17, "kind": "property", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "remappingFunction": { "index": 18, "kind": "property", "displayName": "Remapping Function", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.function.BiFunction", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific remappingFunction to use in a compute operation." }, "resultHeader": { "index": 19, "kind": "property", "displayName": "Result Header", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Store the operation result in a header instead of the message body. By default, resultHeader == null and the query result is stored in the message body, any existing content in the message body is discarded. If resultHeader is set, the value is used as the name of the header to store the query result and the original message body is preserved. This value can be overridden by an in message header named: CamelInfinispanOperationResultHeader" } }, @@ -85,10 +85,10 @@ "operation": { "index": 12, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "org.apache.camel.component.infinispan.InfinispanOperation", "enum": [ "PUT", "PUTASYNC", "PUTALL", "PUTALLASYNC", "PUTIFABSENT", "PUTIFABSENTASYNC", "GET", "GETORDEFAULT", "CONTAINSKEY", "CONTAINSVALUE", "REMOVE", "REMOVEASYNC", "REPLACE", "REPLACEASYNC", "SIZE", "CLEAR", "CLEARASYNC", "QUERY", "STATS", "COMPUTE", "COMPUTEASYNC" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "PUT", "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The operation to perform" }, "value": { "index": 13, "kind": "parameter", "displayName": "Value", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific value for producer operations" }, "lazyStartProducer": { "index": 14, "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." }, - "cacheContainer": { "index": 15, "kind": "parameter", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect" }, - "cacheContainerConfiguration": { "index": 16, "kind": "parameter", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined." }, + "cacheContainer": { "index": 15, "kind": "parameter", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, + "cacheContainerConfiguration": { "index": 16, "kind": "parameter", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined. Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "configurationUri": { "index": 17, "kind": "parameter", "displayName": "Configuration Uri", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "An implementation specific URI for the CacheManager" }, - "flags": { "index": 18, "kind": "parameter", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation" }, + "flags": { "index": 18, "kind": "parameter", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "remappingFunction": { "index": 19, "kind": "parameter", "displayName": "Remapping Function", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.function.BiFunction", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific remappingFunction to use in a compute operation." }, "resultHeader": { "index": 20, "kind": "parameter", "displayName": "Result Header", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Store the operation result in a header instead of the message body. By default, resultHeader == null and the query result is stored in the message body, any existing content in the message body is discarded. If resultHeader is set, the value is used as the name of the header to store the query result and the original message body is preserved. This value can be overridden by an in message header named: CamelInfinispanOperationResultHeader" } } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json index 5109e64326886..039f9e6ad647f 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jdbc.json @@ -31,7 +31,7 @@ }, "headers": { "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is an UPDATE, query the update count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" }, - "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT, query the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, + "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT and the outputType is SelectList, the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Set", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The column names from the ResultSet as a java.util.Set type.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" }, "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A java.util.Map which has the headers to be used if useHeadersAsParameters has been enabled.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" }, "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Set its value to true to retrieve generated keys", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" }, @@ -47,8 +47,8 @@ "parameters": { "index": 4, "kind": "parameter", "displayName": "Parameters", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "statement.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "Optional parameters to the java.sql.Statement. For example to set maxRows, fetchSize etc. This is a multi-value option with prefix: statement." }, "readSize": { "index": 5, "kind": "parameter", "displayName": "Read Size", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The default maximum number of rows that can be read by a polling query. The default value is 0." }, "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": "Reset Auto Commit", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Camel will set the autoCommit on the JDBC connection to be false, commit the change after executed the statement and reset the autoCommit flag of the connection at the end, if the resetAutoCommit is true. If the JDBC connection doesn't support to reset the autoCommit flag, you can set the resetAutoCommit flag to be false, and Camel will not try to reset the autoCommit flag. When used with XA transactions you most likely need to set it to false so that the transaction manager is in charge of committing this tx." }, - "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use." }, - "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes instead of string data. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, + "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not used: the producer does not read this option", "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use. Deprecation note: Not used: the producer does not read this option" }, + "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read after the producer has closed the connection. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, "useHeadersAsParameters": { "index": 9, "kind": "parameter", "displayName": "Use Headers As Parameters", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Set this option to true to use the prepareStatementStrategy with named parameters. This allows to define queries with named placeholders, and use headers with the dynamic values for the query placeholders." }, "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", "displayName": "Use JDBC4 Column Name And Label Semantics", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get the column name where as JDBC 3.0 uses both columnName or columnLabel. Unfortunately JDBC drivers behave differently so you can use this option to work out issues around your JDBC driver if you get problem using this component This option is default true." }, "lazyStartProducer": { "index": 11, "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." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json index d1dafe51f2f12..dd09c9c37ec32 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json @@ -107,7 +107,7 @@ "retries": { "index": 80, "kind": "property", "displayName": "Retries", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Setting a value greater than zero will cause the client to resend any record that has failed to be sent due to a potentially transient error. Note that this retry is no different from if the client re-sending the record upon receiving the error. Produce requests will be failed before the number of retries has been exhausted if the timeout configured by delivery.timeout.ms expires first before successful acknowledgement. Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior. Enabling idempotence requires this config value to be greater than 0. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled. Allowing retries while setting enable.idempotence to false and max.in.flight.requests.per.connection to 1 will potentially change the ordering of records, because if two batches are sent to a single partition, and the first fails and is retried but the second succeeds; then the records in the second batch may appear first." }, "transacted": { "index": 81, "kind": "property", "displayName": "Transacted", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Indicates to create a transactional.id kafka property by using the endpoint id and route id. This property is ignored in case there is transactional.id kafka property or the transactionalId parameter." }, "transactionalId": { "index": 82, "kind": "property", "displayName": "Transactional Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Enable the kafka producer to be a transactional one by setting the transactional.id property. In case this property is used, the transacted parameter is ignored." }, - "useIterator": { "index": 83, "kind": "property", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated)." }, + "useIterator": { "index": 83, "kind": "property", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated). A Jackson JSON array node is sent as one record per element, and any other JSON node (such as an object) as one record." }, "valueSerializer": { "index": 84, "kind": "property", "displayName": "Value Serializer", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "org.apache.kafka.common.serialization.StringSerializer", "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "The serializer class for messages." }, "workerPool": { "index": 85, "kind": "property", "displayName": "Worker Pool", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "To use a custom worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing. If using this option, then you must handle the lifecycle of the thread pool to shut the pool down when no longer needed." }, "workerPoolCoreSize": { "index": 86, "kind": "property", "displayName": "Worker Pool Core Size", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Number of core threads for the worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing." }, @@ -253,7 +253,7 @@ "retries": { "index": 75, "kind": "parameter", "displayName": "Retries", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Setting a value greater than zero will cause the client to resend any record that has failed to be sent due to a potentially transient error. Note that this retry is no different from if the client re-sending the record upon receiving the error. Produce requests will be failed before the number of retries has been exhausted if the timeout configured by delivery.timeout.ms expires first before successful acknowledgement. Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior. Enabling idempotence requires this config value to be greater than 0. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled. Allowing retries while setting enable.idempotence to false and max.in.flight.requests.per.connection to 1 will potentially change the ordering of records, because if two batches are sent to a single partition, and the first fails and is retried but the second succeeds; then the records in the second batch may appear first." }, "transacted": { "index": 76, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Indicates to create a transactional.id kafka property by using the endpoint id and route id. This property is ignored in case there is transactional.id kafka property or the transactionalId parameter." }, "transactionalId": { "index": 77, "kind": "parameter", "displayName": "Transactional Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Enable the kafka producer to be a transactional one by setting the transactional.id property. In case this property is used, the transacted parameter is ignored." }, - "useIterator": { "index": 78, "kind": "parameter", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated)." }, + "useIterator": { "index": 78, "kind": "parameter", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated). A Jackson JSON array node is sent as one record per element, and any other JSON node (such as an object) as one record." }, "valueSerializer": { "index": 79, "kind": "parameter", "displayName": "Value Serializer", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "org.apache.kafka.common.serialization.StringSerializer", "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "The serializer class for messages." }, "workerPool": { "index": 80, "kind": "parameter", "displayName": "Worker Pool", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "To use a custom worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing. If using this option, then you must handle the lifecycle of the thread pool to shut the pool down when no longer needed." }, "workerPoolCoreSize": { "index": 81, "kind": "parameter", "displayName": "Worker Pool Core Size", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Number of core threads for the worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json index f7e9210de8972..89c539d9e0b37 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/micrometer.json @@ -30,7 +30,7 @@ }, "headers": { "CamelMetricsTimerAction": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.micrometer.MicrometerTimerAction", "enum": [ "start", "stop" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override timer action in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_TIMER_ACTION" }, - "CamelMetricsHistogramValue": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override histogram value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_HISTOGRAM_VALUE" }, + "CamelMetricsHistogramValue": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override histogram value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_HISTOGRAM_VALUE" }, "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override decrement value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_COUNTER_DECREMENT" }, "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override increment value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_COUNTER_INCREMENT" }, "CamelMetricsName": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override name value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_NAME" }, @@ -38,7 +38,7 @@ "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.lang.Iterable", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To augment meter tags defined as URI parameters", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_TAGS" } }, "properties": { - "metricsType": { "index": 0, "kind": "path", "displayName": "Metrics Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "io.micrometer.core.instrument.Meter.Type", "enum": [ "counter", "summary", "timer" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Type of metrics" }, + "metricsType": { "index": 0, "kind": "path", "displayName": "Metrics Type", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "io.micrometer.core.instrument.Meter.Type", "enum": [ "counter", "summary", "timer" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "counter", "description": "Type of metrics" }, "metricsName": { "index": 1, "kind": "path", "displayName": "Metrics Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Name of metrics" }, "action": { "index": 2, "kind": "parameter", "displayName": "Action", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "start", "stop" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Action expression when using timer type" }, "decrement": { "index": 3, "kind": "parameter", "displayName": "Decrement", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Decrement value expression when using counter type" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mina-sftp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mina-sftp.json index 6cae8c76d1cb1..14c37f8a89144 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mina-sftp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/mina-sftp.json @@ -72,7 +72,7 @@ "sendEmptyMessageWhenIdle": { "index": 19, "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." }, "streamDownload": { "index": 20, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.mina.MinaSftpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 21, "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." }, - "download": { "index": 22, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 22, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 23, "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": 24, "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." }, "ignoreFileNotFoundOrPermissionError": { "index": 25, "kind": "parameter", "displayName": "Ignore File Not Found Or Permission Error", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.mina.MinaSftpConfiguration", "configurationField": "configuration", "description": "Whether to ignore when (trying to list files in directories or when downloading a file), which does not exist or due to permission error. By default when a directory or file does not exist or insufficient permission, then an exception is thrown. Setting this option to true allows to ignore that instead." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pdf.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pdf.json index f2edff0482dd4..333577f9981ec 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pdf.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pdf.json @@ -31,7 +31,7 @@ "CamelPdfProtectionPolicy": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.encryption.ProtectionPolicy", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/encryption\/ProtectionPolicy.htmlProtectionPolicy. If specified then PDF document will be encrypted with it.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#PROTECTION_POLICY_HEADER_NAME" }, "CamelPdfDocument": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.PDDocument", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for append operation and ignored in all other operations. Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/PDDocument.htmlPDDocument. Stores PDF document which will be used for append operation.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#PDF_DOCUMENT_HEADER_NAME" }, "CamelPdfDecryptionMaterial": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.encryption.DecryptionMaterial", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/encryption\/DecryptionMaterial.htmlDecryptionMaterial. Mandatory header if PDF document is encrypted.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#DECRYPTION_MATERIAL_HEADER_NAME" }, - "CamelPdfFilesToMerge": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for merge operation and ignored in all other operations. The array of pdf files that will be merged.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#FILES_TO_MERGE_HEADER_NAME" } + "CamelPdfFilesToMerge": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for merge operation and ignored in all other operations. The list of pdf files that will be merged, each given as a java.io.File or a java.nio.file.Path.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#FILES_TO_MERGE_HEADER_NAME" } }, "properties": { "operation": { "index": 0, "kind": "path", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.pdf.PdfOperation", "enum": [ "create", "append", "extractText", "merge" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Operation type" }, @@ -41,7 +41,7 @@ "marginLeft": { "index": 4, "kind": "parameter", "displayName": "Margin Left", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin left in pixels" }, "marginRight": { "index": 5, "kind": "parameter", "displayName": "Margin Right", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 40, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin right in pixels" }, "marginTop": { "index": 6, "kind": "parameter", "displayName": "Margin Top", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin top in pixels" }, - "pageSize": { "index": 7, "kind": "parameter", "displayName": "Page Size", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "LETTER", "LEGAL", "A0", "A1", "A2", "A3", "A4", "A5", "A6" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "A4", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Page size" }, + "pageSize": { "index": 7, "kind": "parameter", "displayName": "Page Size", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "LETTER", "LEGAL", "A0", "A1", "A2", "A3", "A4", "A5", "A6" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "A4", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Page size: LETTER, LEGAL or A0 to A6. The older names PAGE_SIZE_A0 to PAGE_SIZE_A6 and PAGE_SIZE_LETTER are also accepted." }, "textProcessingFactory": { "index": 8, "kind": "parameter", "displayName": "Text Processing Factory", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.pdf.TextProcessingFactory", "enum": [ "autoFormatting", "lineTermination" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "lineTermination", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Text processing to use. autoFormatting: Text is getting sliced by words, then max amount of words that fits in the line will be written into pdf document. With this strategy all words that doesn't fit in the line will be moved to the new line. lineTermination: Builds set of classes for line-termination writing strategy. Text getting sliced by line termination symbol and then it will be written regardless it fits in the line or not." }, "lazyStartProducer": { "index": 9, "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." } } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json index a0471a76cb2f0..aff91a1d61774 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sftp.json @@ -84,7 +84,7 @@ "sendEmptyMessageWhenIdle": { "index": 20, "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." }, "streamDownload": { "index": 21, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 22, "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." }, - "download": { "index": 23, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 23, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 24, "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": 25, "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." }, "ignoreFileNotFoundOrPermissionError": { "index": 26, "kind": "parameter", "displayName": "Ignore File Not Found Or Permission Error", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Whether to ignore when (trying to list files in directories or when downloading a file), which does not exist or due to permission error. By default when a directory or file does not exist or insufficient permission, then an exception is thrown. Setting this option to true allows to ignore that instead." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smb.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smb.json index 89a91c6824397..1de0619cbabe2 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smb.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/smb.json @@ -69,7 +69,7 @@ "sendEmptyMessageWhenIdle": { "index": 16, "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." }, "streamDownload": { "index": 17, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route." }, "bridgeErrorHandler": { "index": 18, "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." }, - "download": { "index": 19, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Whether the SMB consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 19, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Whether the SMB consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 20, "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": 21, "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." }, "inProgressRepository": { "index": 22, "kind": "parameter", "displayName": "In Progress Repository", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable in-progress repository org.apache.camel.spi.IdempotentRepository. The in-progress repository is used to account the current in progress files being consumed. By default a memory based repository is used." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/snmp.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/snmp.json index 4dc8c5a836cd3..d2da3dc0f0c90 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/snmp.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/snmp.json @@ -41,7 +41,7 @@ "snmpContextName": { "index": 7, "kind": "parameter", "displayName": "Snmp Context Name", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the context name field of this scoped PDU." }, "snmpVersion": { "index": 8, "kind": "parameter", "displayName": "Snmp Version", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "int", "enum": [ "0", "1", "3" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Sets the snmp version for the request. The value 0 means SNMPv1, 1 means SNMPv2c, and the value 3 means SNMPv3" }, "timeout": { "index": 9, "kind": "parameter", "displayName": "Timeout", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1500, "description": "Sets the timeout value for the request in millis." }, - "type": { "index": 10, "kind": "parameter", "displayName": "Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.snmp.SnmpActionType", "enum": [ "TRAP", "POLL", "GET_NEXT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Which operation to perform such as poll, trap, etc." }, + "type": { "index": 10, "kind": "parameter", "displayName": "Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.snmp.SnmpActionType", "enum": [ "TRAP", "POLL", "GET_NEXT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Which operation to perform such as poll, trap, etc. The consumer supports TRAP (receive traps) and POLL (poll the oids). The producer supports TRAP (send the PDU of the body as a trap), POLL (a GET of the oids, also done when no type is set) and GET_NEXT (a walk of the oids, for the producer only)." }, "delay": { "index": 11, "kind": "parameter", "displayName": "Delay", "group": "consumer", "label": "consumer", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "60000", "description": "Milliseconds before the next poll." }, "sendEmptyMessageWhenIdle": { "index": 12, "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." }, "treeList": { "index": 13, "kind": "parameter", "displayName": "Tree List", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets the flag whether the scoped PDU will be displayed as the list if it has child elements in its tree" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/tika.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/tika.json index ddc7c899b1b78..80a2cf26249d9 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/tika.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/tika.json @@ -29,8 +29,8 @@ }, "properties": { "operation": { "index": 0, "kind": "path", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaOperation", "enum": [ "parse", "detect" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Operation type" }, - "tikaParseOutputEncoding": { "index": 1, "kind": "parameter", "displayName": "Tika Parse Output Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Parse Output Encoding" }, - "tikaParseOutputFormat": { "index": 2, "kind": "parameter", "displayName": "Tika Parse Output Format", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaParseOutputFormat", "enum": [ "xml", "html", "text" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "xml", "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Output Format. Supported output formats. xml: Returns Parsed Content as XML. html: Returns Parsed Content as HTML. text: Returns Parsed Content as Text. textMain: Uses the boilerpipe library to automatically extract the main content from a web page." }, + "tikaParseOutputEncoding": { "index": 1, "kind": "parameter", "displayName": "Tika Parse Output Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "The character encoding of the parsed output. Defaults to the default charset of the JVM. The parse operation sets this encoding as the charset of the exchange, so converting the output to text decodes it with this encoding." }, + "tikaParseOutputFormat": { "index": 2, "kind": "parameter", "displayName": "Tika Parse Output Format", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaParseOutputFormat", "enum": [ "xml", "html", "text" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "xml", "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Output Format. Supported output formats. xml: Returns Parsed Content as XML. html: Returns Parsed Content as HTML. text: Returns Parsed Content as Text." }, "lazyStartProducer": { "index": 3, "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." }, "tikaConfigFile": { "index": 4, "kind": "parameter", "displayName": "Tika Config File", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Path to a Tika JSON configuration file" }, "tikaLoader": { "index": 5, "kind": "parameter", "displayName": "Tika Loader", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.tika.config.loader.TikaLoader", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika loader used to configure parsers and detectors" } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc index 8b3988308f017..22692a23e9e30 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc @@ -36,8 +36,10 @@ as2://apiName/methodName apiName can be one of: -* client -* server +* client: `client/send` sends an EDI message to a trading partner +* server: `server/listen` receives EDI messages +* receipt: `receipt/receive` receives the asynchronous MDNs (receipts) that a `client/send` asked for with + `receiptDeliveryOption` // component options: START @@ -100,6 +102,25 @@ An `as2://server/listen` consumer receives AS2 messages on `serverPortNumber`, f A `SIGNED`, `ENCRYPTED` or `SIGNED_ENCRYPTED` message structure also needs the keys and certificates of the signing and encrypting options. +=== Sending with client/send + +The EDI message (`inBody: ediMessage`) can be text, a stream, a `byte[]` or a file, such as the `java.io.File` or the +file of a `file` consumer, so a route can send the files of a folder without converting them first. Text is encoded +with `ediMessageCharset` (US-ASCII when it is not set); the other types are sent as their bytes. Any other body that +the type converters can turn into bytes is sent as those bytes, and a body that cannot be converted fails the +exchange with a message that names its type. + +`requestUri` defaults to `/`. The content type of the message is set with `ediMessageContentType`; the older +`ediMessageType` option is deprecated, and it is used only when `ediMessageContentType` is not set. + +`ediMessageTransferEncoding` can be `7bit`, `8bit`, `binary`, `base64` or `quoted-printable`. + +When the trading partner answers with an HTTP status outside 2xx, such as a 500 when the route of an AS2 server fails, +the exchange fails with an exception that names the status code and reason. The exchange property +`CamelAs2.interchange` still holds the `HttpCoreContext` with the request and the response. A 2xx answer sets the body +to the receipt (MDN) when the partner returned one, and to `null` when there is none, such as a send without +`dispositionNotificationTo` or with an asynchronous receipt. + Several `as2://server/listen` consumers can share a `serverPortNumber`, each with its own `requestUriPattern`. Each consumer applies its own keys and certificates and its own asynchronous MDN settings (`mdnUserName`, `mdnPassword`, `mdnAccessToken` and `asyncMdnAllowedHosts`) to the messages received on its own pattern. diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dns-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dns-component.adoc index 63f273f75cf5e..3d51e1e7d14f1 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dns-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/dns-component.adoc @@ -20,6 +20,7 @@ The component offers the following operations: * `ip`: to resolve a domain by its ip * `lookup`: to lookup information about the domain * `dig`: to run DNS queries +* `wikipedia` (deprecated): to query the Wikipedia over DNS service, which is no longer available [NOTE] ==== @@ -177,7 +178,10 @@ YAML:: ---- ==== -The query must be provided in the header with key `"CamelDnsName"`. +The query must be provided in the header with key `"CamelDnsName"`; without it the +exchange fails with an `IllegalArgumentException`. The `"CamelDnsType"` header sets the type of +the query, such as `MX`, and defaults to `A` when it is not set or not a known type. The +`"CamelDnsClass"` header sets the DNS class, and defaults to `IN`. === Dns Activation Policy diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/github2-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/github2-component.adoc index 446143a8382c9..b2d070e78eac2 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/github2-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/github2-component.adoc @@ -153,9 +153,9 @@ Sets the commit status on a pull request. |pullRequestFiles |none | - `CamelGitHubPullRequest` (integer) (REQUIRED): Pull request number. Returns the list of files changed in a pull request. -|getCommitFile |none | - `CamelGitHubCommitSha` (String) (REQUIRED): Commit SHA. -- `CamelGitHubFilePath` (String) (REQUIRED): File path. -Returns the content of a file at a specific commit. +|getCommitFile |String (the path of the file, if the `CamelGitHubFilePath` header is not set) | - `CamelGitHubCommitSha` (String) (REQUIRED): The commit id (40 hexadecimal characters; a branch or tag name is not accepted). +- `CamelGitHubFilePath` (String): The path of the file in the repository, such as `docs/README.md`. If not set, the body is the path. +Returns the content of the file in the tree of that commit, as a `String` (decoded with the `encoding` option, or the default charset of the JVM). |======================================================================= === Example: Consuming commits diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/graphql-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/graphql-component.adoc index 3ec1d0083d546..f53906a8cfc8a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/graphql-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/graphql-component.adoc @@ -12,7 +12,7 @@ *{component-header}* -The GraphQL component is a GraphQL client that communicates over HTTP and supports queries and mutations, but not subscriptions. It uses the https://hc.apache.org/httpcomponents-client-4.5.x/index.html[Apache HttpClient] library. +The GraphQL component is a GraphQL client that communicates over HTTP and supports queries and mutations, but not subscriptions. It sends the requests with the xref:http-component.adoc[HTTP] component, which uses the Apache HttpClient 5 library. Maven users will need to add the following dependency to their `pom.xml` for this component: @@ -37,9 +37,21 @@ include::partial$component-endpoint-headers.adoc[] == Message Body -If the `variables` and `variablesHeader` parameters are not set and the IN body is a JsonObject instance, Camel will use it for the operation's variables. If the `query` and `queryFile` parameters are not set and the IN body is a String, Camel will use it as the query. Camel will store the GraphQL response from the external server on the OUT message body. -All headers from the IN message will be copied to the OUT message, so headers are preserved during routing. -Additionally, Camel will add the HTTP response headers as well to the OUT message headers. +If the `query`, `queryFile` and `queryHeader` parameters are not set, the body is the query: it must be text, bytes, a stream or a file, and another body (such as a `Map`) fails with an `InvalidPayloadException`. +Otherwise, if the `variables` and `variablesHeader` parameters are not set and the body is a `Map` (such as a `JsonObject`), Camel will use it for the operation's variables. +Camel will store the GraphQL response from the external server on the message body. + +== HTTP Headers + +The headers of the message are sent as HTTP request headers (filtered by the `headerFilterStrategy`), except: + +* the headers named by the `queryHeader` and `variablesHeader` parameters, as the query and the variables are sent in the JSON body of the request; +* a `Content-Encoding` header, as the request is not compressed. + +The producer always sends the request with `Content-Type: application/json`, `Accept: application/json` and `Accept-Encoding: gzip`, whatever the headers of the message are. + +After the call, the message keeps its own headers, and Camel adds the HTTP response headers (such as `CamelHttpResponseCode` and the `Content-Type` of the response). +The headers the producer set for the request (`CamelHttpMethod`, `Accept` and `Accept-Encoding`) do not stay on the message: a header of the message with the same name keeps its value. == Examples diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/infinispan-embedded-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/infinispan-embedded-component.adoc index 3221910ea61bc..ebf9cd695d851 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/infinispan-embedded-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/infinispan-embedded-component.adoc @@ -51,6 +51,21 @@ The consumer allows listening for events from local infinispan cache. If no cache configuration is provided, embedded cacheContainer is created directly in the component. +=== Options read on the component only + +All the endpoints of the component share one cache manager, which the component creates when it starts. So the +options that configure the cache manager are read on the component only: `configurationUri`, `cacheContainer`, +`cacheContainerConfiguration` and `flags`. Set on an endpoint, these options are not used. Configure them on the +component instead, for example in `application.properties`: + +[source,properties] +---- +camel.component.infinispan-embedded.configuration-uri = file:/etc/infinispan/infinispan.xml +---- + +The `configurationUri` is read with the resource loader of Camel, so it can be a name on the class path, a +`classpath:` or `file:` uri, or an absolute path, and it can use property placeholders. + // component options: START include::partial$component-configure-options.adoc[] include::partial$component-endpoint-options.adoc[] @@ -244,11 +259,11 @@ from("infinispan-embedded:source") from("direct:start") .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUT) // <1> .setHeader(InfinispanConstants.KEY).constant("123") // <2> - .to("infinispan-embedded:myCacheName?cacheContainer=#cacheContainer"); // <3> + .to("infinispan-embedded:myCacheName"); // <3> ---- <1> Set the operation to perform <2> Set the key used to identify the element in the cache -<3> Use the configured cache manager `cacheContainer` from the registry to put an element to the cache named `myCacheName` +<3> Put an element to the cache named `myCacheName` of the cache manager of the component -- + It is possible to configure the lifetime and/or the idle time before the entry expires and gets evicted from the cache, as example: @@ -281,7 +296,7 @@ from("direct:start") return qf.from(User.class).having("name").like("%abc%").build(); } }) - .to("infinispan-embedded:myCacheName?cacheContainer=#cacheManager") ; + .to("infinispan-embedded:myCacheName"); ---- + @@ -293,7 +308,7 @@ Java:: + [source,java] ---- -from("infinispan-embedded:myCacheName?cacheContainer=#cacheManager&customListener=#myCustomListener") +from("infinispan-embedded:myCacheName?customListener=#myCustomListener") .to("mock:result"); ---- @@ -302,7 +317,7 @@ XML:: [source,xml] ---- - + ---- @@ -315,7 +330,6 @@ YAML:: from: uri: infinispan-embedded:myCacheName parameters: - cacheContainer: "#cacheManager" customListener: "#myCustomListener" steps: - to: @@ -366,7 +380,7 @@ XML:: - + @@ -429,7 +443,7 @@ XML:: - + diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc index 04943660e73f2..d5f4442e8cf16 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/jdbc-component.adoc @@ -63,6 +63,18 @@ the result. *Note:* This component fetches `ResultSetMetaData` to be able to return the column name as the key in the `Map`. +With `outputType=SelectList` the header `CamelJdbcRowCount` holds the number of rows of the result. + +A `BLOB` or `CLOB` value of a row is a `java.sql.Blob` or `java.sql.Clob`, which can be read only while the connection is +open, and the producer closes the connection before the route continues (except with `outputType=StreamList`, +which closes it when the exchange is done). So read such a value inside the producer: set `useGetBytesForBlob=true` +to get a `BLOB` as a `byte[]`, or convert a `CLOB` to text in the SQL query with a function of the database. + +The `SQL` of the message body is mandatory: a message without a body fails with an `InvalidPayloadException`. + +The `transacted` option is not used (and deprecated). To take part in a Spring transaction, use the +xref:spring-jdbc-component.adoc[Spring JDBC Component]. + === Generated keys If you insert data using SQL INSERT, then the RDBMS may support auto @@ -72,7 +84,8 @@ return the generated keys in headers. + generated keys will be provided as headers with the keys listed in the table above. -Using generated keys does not work together with named parameters. +The generated keys in the header `CamelGeneratedKeysRows` are a `List` of `Map` objects with the key columns, +whatever the `outputClass` and `readSize` of the endpoint. === Using named parameters diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc index d63172bfad4fb..c2464b67c616b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/micrometer-component.adoc @@ -148,8 +148,10 @@ from("direct:in") will update a counter with name `new.name` instead of `name.not.used` using the tag `dynamic-key` with value `dynamic-value` in addition to the tag `key` with value `value`. -All Metrics specific headers are removed from the message once the Micrometer endpoint finishes processing of exchange. While processing exchange -Micrometer endpoint will catch all exceptions and write log entry using level `warn`. +All Metrics specific headers are removed from the message once the Micrometer endpoint finishes processing of exchange. +If the Micrometer endpoint cannot update the meter, then the exchange fails with the exception, which the +error handler of the route handles as any other failure. For example, a `value`, `increment` or `decrement` that is +not a number, or a timer `action` that is not `start` or `stop`, fails the exchange with a `TypeConversionException`. [[MicrometerComponent-counter]] @@ -412,8 +414,9 @@ micrometer:summary:metricname[?options] |value |- |Value to use in histogram |=================================== -If no `value` is not set, nothing is added to histogram and warning is -logged. +If `value` is not set, and there is no `CamelMetricsHistogramValue` header, then nothing is added to the +distribution summary, and nothing is logged. A value that is not a number fails the exchange with a +`TypeConversionException`. [tabs] ==== @@ -461,7 +464,7 @@ Java:: + [source,java] ---- -// nothing is added to simple.histogram; warning is logged +// nothing is added to simple.histogram from("direct:in") .to("micrometer:summary:simple.histogram") .to("direct:out"); @@ -543,7 +546,7 @@ the Micrometer endpoint URI. [width="100%",cols="10%,80%,10%",options="header",] |================================================================= |Name |Description |Expected type -|CamelMetricsHistogramValue |Override histogram value in URI |Long +|CamelMetricsHistogramValue |Override histogram value in URI |Double |================================================================= [tabs] @@ -609,10 +612,9 @@ micrometer:timer:metricname[?options] |action |- |start or stop |========================== -If no `action` or invalid value is provided then warning is logged -without any timer update. If action `start` is called on an already running -timer or `stop` is called on an unknown timer, nothing is updated -and warning is logged. +If no `action` is provided, then the timer is not updated, and nothing is logged. An `action` that is not +`start` or `stop` fails the exchange with a `TypeConversionException`. If action `start` is called on an +already running timer or `stop` is called on an unknown timer, nothing is updated, and nothing is logged. [tabs] ==== diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pdf-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pdf-component.adoc index e2e9dcebcb9ab..913cf95edc278 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pdf-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pdf-component.adoc @@ -95,5 +95,25 @@ YAML:: It is now possible to get the body as a PD Document by using `PDDocument doc = exchange.getIn().getBody(PDDocument.class);`, which saves the trouble of converting the byte-array to a document. +The type converter reads a PDF from a `byte[]`, a `ByteArrayOutputStream`, an `InputStream`, a `java.io.File` or a +`java.nio.file.Path`. The `create`, `append` and `merge` operations leave the PDF in a `ByteArrayOutputStream`, so +`pdf:create` can be followed by `pdf:extractText` directly. The `extractText` operation fails with an +`InvalidPayloadException` naming the type of the body when the body is missing or is not one of these types. + NOTE: this only works for unprotected PDF files. For password-protected, the files still need to be converted manually. + +=== Creating a PDF from text + +The `create` operation needs a body with the text to write; without a body it fails with an `InvalidPayloadException`. + +With the default `textProcessingFactory=lineTermination`, the text is written line by line: a line ends at `\r\n`, +`\r` or `\n`. A tab is written as four spaces, and other control characters, which the fonts cannot show, are left out. + +The `pageSize` option takes `LETTER`, `LEGAL` or `A0` to `A6` (default `A4`). The older names `PAGE_SIZE_A0` to +`PAGE_SIZE_A6` and `PAGE_SIZE_LETTER` are still accepted. An unknown value fails when the endpoint is created. + +=== Merging PDF files + +The `merge` operation reads the files to merge from the `CamelPdfFilesToMerge` header: a list of at least two +elements, each a `java.io.File` or a `java.nio.file.Path`. diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/scp-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/scp-component.adoc index 95fd93cdb3cba..5ce99b38b2951 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/scp-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/scp-component.adoc @@ -70,6 +70,14 @@ The host key is checked against a known hosts file: strictHostKeyChecking: "yes" ---- +The directory of the endpoint (`/upload` above) must exist on the remote host, as scp does not create it: +when it does not exist, each file is written to a file named like the directory, and the last one wins. +The sub directories in the `CamelFileName` header (such as `mysub/hello.txt`) are created in that directory. + +Without a known hosts file (`useUserKnownHostsFile=false` and no `knownHostsFile`), no host is known, +so the option `strictHostKeyChecking` decides: with `yes` the connection fails, with `no` (the default) +the host key is not checked. + == OpenSSH Certificate Authentication diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/snmp-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/snmp-component.adoc index 2f0eda76b8e8a..e08d2731e2f8e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/snmp-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/snmp-component.adoc @@ -39,9 +39,15 @@ and receiving traps. == Snmp Producer -It can also be used to request information using GET method. +It can also be used to request information using GET method: the producer with `type=POLL`, or without a `type`, +sends a GET request of the `oids` of the endpoint. With `type=GET_NEXT` the producer walks the `oids` +(a GET_NEXT request is for the producer only, the consumer supports the types `TRAP` and `POLL`). +With `type=TRAP` the producer sends the `org.snmp4j.PDU` of the message body as a trap. -The response body type is `org.apache.camel.component.snmp.SnmpMessage`. +The response body type is `org.apache.camel.component.snmp.SnmpMessage` (a list of them for `GET_NEXT`), and it converts +to `String` as the XML text shown in "The result of a poll" below. + +Without a port in the endpoint, the default port is 162 for `type=TRAP` and 161 otherwise. // component options: START @@ -108,7 +114,7 @@ Be prepared. ------------------------- As you may notice, default `snmpVersion` is 0, which means `version1` in the endpoint if it is not set explicitly. Make sure you explicitly set `snmpVersion` which is not default value, in a case of where you are able to query SNMP tables with different versions. -Other possible values are `version2c` and `version3`. +Other possible values are 1, which means `version2c`, and 3, which means `version3`. == Examples @@ -125,7 +131,8 @@ snmp:127.0.0.1:162?protocol=udp&type=TRAP ----------------------------------------- You can get the community of SNMP TRAP with message -header `securityName`, peer address of the SNMP TRAP with message header `peerAddress`. +header `securityName` (an `org.snmp4j.smi.OctetString`), peer address of the SNMP TRAP with message header `peerAddress` +(an `org.snmp4j.smi.Address`). Routing example in Java: (converts the SNMP PDU to XML String) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tika-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tika-component.adoc index 52c7916bdd820..5c60e4eea4c56 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tika-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/tika-component.adoc @@ -82,6 +82,12 @@ YAML:: The file should be placed in the Body. +The body of `detect` and `parse` must convert to an `InputStream`, such as a `java.io.File`, a `byte[]`, a `String` or +a stream. A missing body or a body that does not convert, such as a `Map`, fails with an `InvalidPayloadException`. + +The `parse` operation writes its output in the `tikaParseOutputEncoding` and sets that encoding as the charset of the +exchange (the `CamelCharsetName` exchange property), so converting the output to a `String` decodes it right. + [tabs] ==== Java:: diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/vertx-http-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/vertx-http-component.adoc index 87f57ffb4064c..89760b5a9bf0e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/vertx-http-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/vertx-http-component.adoc @@ -89,7 +89,7 @@ URI, or as a header with the key `Exchange.HTTP_QUERY` on the message. Camel will handle, according to the HTTP response code: -* Response code is in the range 100..299, Camel regards it as a success +* Response code is in the range of the `okStatusCodeRange` option (200-299 by default), Camel regards it as a success response. * Response code is in the range 300..399, Camel regards it as a redirection response and will throw a `HttpOperationFailedException` @@ -123,12 +123,23 @@ The following algorithm determines the HTTP method to be used: + 5. `POST` if there is data to send (body is not `null`). + 6. `GET` otherwise. +=== Request body + +The producer sends the message body as the request body: + +* a `java.io.File`, a `java.nio.file.Path` or a file of a file based component (such as `file` or `ftp`) is sent +as the content of the file (or as a multipart upload with the `multipartUpload` option); +* a `String`, or a `java.io.Reader`, is sent as its text; +* a Vert.x `Buffer`, `MultiMap` (a form), `MultipartForm` or `ReadStream` is sent as is; +* another body must convert to a Vert.x `Buffer`, such as a `byte[]` or an `InputStream`. + === HTTP form parameters You can send HTTP form parameters in one of two ways. 1. Set the `Exchange.CONTENT_TYPE` header to the value `application/x-www-form-urlencoded` and ensure the message -body is a `String` formatted as form variables. For example `param1=value1¶m2=value2`. +body is a `String` formatted as form variables. For example `param1=value1¶m2=value2`. The names and values are +URL decoded (such as `a%26b` for `a&b`) and encoded again when sent, and a repeated name sends all its values. 2. Set the message body as a https://vertx.io/docs/apidocs/io/vertx/core/MultiMap.html[MultiMap] which allows you to configure form parameter names and values. diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnection.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnection.java index dee0ffb7f4780..adc55beb99710 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnection.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnection.java @@ -33,6 +33,7 @@ import org.apache.camel.component.as2.api.io.AS2BHttpServerConnection; import org.apache.camel.util.ObjectHelper; import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ConnectionClosedException; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.impl.io.HttpService; @@ -211,6 +212,9 @@ public void run() { } this.httpService.handleRequest(this.serverConnection, context); } + } catch (final ConnectionClosedException ex) { + // the sender closed the connection after its receipt was delivered + LOG.debug("I/O exception: {}", ex.getMessage()); } catch (final IOException ex) { LOG.error("I/O exception: {}", ex.getMessage(), ex); } catch (final HttpException ex) { diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java index 155b4b354f3ea..922ec625585d3 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ClientManager.java @@ -185,7 +185,7 @@ public AS2ClientManager(AS2ClientConnection as2ClientConnection) { /** * Send ediMessage to trading partner. * - * @param ediMessage - EDI message to transport + * @param ediMessage - EDI message to transport: a String, an InputStream or a byte array * @param requestUri - resource location to deliver message * @param subject - message subject * @param from - RFC2822 address of sender @@ -290,9 +290,12 @@ public HttpCoreContext send( } else if (ediMessage instanceof String strMsg) { msgBytes = strMsg.getBytes(ct.getCharset() == null ? StandardCharsets.US_ASCII : ct.getCharset()); + } else if (ediMessage instanceof byte[] bytesMsg) { + msgBytes = bytesMsg; } else { throw new IllegalArgumentException( - "Message type not supported. Must be InputStream or String"); + "Message type " + ediMessage.getClass().getName() + + " not supported. Must be InputStream, String or byte[]"); } applicationEntity = EntityUtils.createEDIEntity(msgBytes, ct, ediMessageTransferEncoding, false, diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java index 57459d9279b22..c85167630fdc3 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java @@ -359,9 +359,10 @@ private AS2ConsumerConfiguration setupConfigurationForRequest(ClassicHttpRequest Object checkKey = context.getAttribute(AS2_DECRYPTING_PRIVATE_KEY); if (checkKey == null) { - LOG.error( - "FATAL: Decrypting Private Key failed to be read back from HttpContext immediately after injection for path: {}", - requestUriPath); + if (config.getDecryptingPrivateKey() != null) { + LOG.error("The decrypting private key could not be read back from the HttpContext for path: {}", + requestUriPath); + } } else if (!(checkKey instanceof PrivateKey)) { LOG.error("FATAL: Key in HttpContext is not a PrivateKey object! Found type: {}", checkKey.getClass().getName()); } else { diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java index 3bd4404ff917e..2c83e7825f839 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java @@ -16,7 +16,9 @@ */ package org.apache.camel.component.as2.api.util; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -98,8 +100,7 @@ public static byte[] encode(byte[] data, String encoding) throws CamelException case "base64": return Base64.encode(data); case "quoted-printable": - // TODO: implement QuotedPrintableOutputStream - return QuotedPrintableCodec.encodeQuotedPrintable(null, data); + return QuotedPrintableCodec.encodeQuotedPrintable(null, data, true); case "binary": case "7bit": case "8bit": @@ -121,9 +122,7 @@ public static OutputStream encode(OutputStream os, String encoding) throws Camel case "base64": return Base64OutputStream.builder().setOutputStream(os).setEncode(true).get(); case "quoted-printable": - // TODO: implement QuotedPrintableOutputStream - return Base64OutputStream.builder().setOutputStream(os).setEncode(true).get(); - // return new Base64OutputStream(os, true); + return new QuotedPrintableOutputStream(os); case "binary": case "7bit": case "8bit": @@ -172,8 +171,11 @@ public static InputStream decode(InputStream is, String encoding) throws CamelEx case "base64": return Base64InputStream.builder().setInputStream(is).setEncode(false).get(); case "quoted-printable": - // TODO: implement QuotedPrintableInputStream - return Base64InputStream.builder().setInputStream(is).setEncode(false).get(); + try { + return new ByteArrayInputStream(QuotedPrintableCodec.decodeQuotedPrintable(is.readAllBytes())); + } catch (IOException | DecoderException e) { + throw new CamelException("Failed to decode quoted-printable content", e); + } case "binary": case "7bit": case "8bit": @@ -287,4 +289,35 @@ public static String printEntity(HttpEntity entity) throws IOException { } } + /** + * Encodes what is written to it as quoted-printable (RFC 2045, with soft line breaks) when it is closed. + */ + private static final class QuotedPrintableOutputStream extends FilterOutputStream { + + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private boolean closed; + + QuotedPrintableOutputStream(OutputStream os) { + super(os); + } + + @Override + public void write(int b) { + buffer.write(b); + } + + @Override + public void write(byte[] b, int off, int len) { + buffer.write(b, off, len); + } + + @Override + public void close() throws IOException { + if (!closed) { + closed = true; + out.write(QuotedPrintableCodec.encodeQuotedPrintable(null, buffer.toByteArray(), true)); + super.close(); + } + } + } } diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnectionTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnectionTest.java index f8c7eb5dddddb..3994b51995720 100644 --- a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnectionTest.java +++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsyncMDNServerConnectionTest.java @@ -18,16 +18,20 @@ import java.net.HttpURLConnection; import java.net.URL; +import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.logging.log4j.Level; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.*; class AS2AsyncMDNServerConnectionTest { @@ -61,9 +65,7 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt } }); - // Wait for server to be ready - Thread.sleep(200); - + // the server socket is bound when the connection is created, so the server is ready // Send a request with a Host header that doesn't match the server's hostname // This previously caused HTTP 421 due to RequestValidateHost HttpURLConnection conn @@ -80,4 +82,25 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt // Should NOT get 421 (Misdirected Request) anymore assertNotEquals(421, responseCode, "Should not reject requests with non-matching Host header"); } + + @Test + void closedConnectionAfterAReceiptIsNotLoggedAsError() throws Exception { + connection.receive("/test-mdn", (request, response, context) -> response.setCode(200)); + + try (LogCapture log = new LogCapture(AS2AsyncMDNServerConnection.class)) { + HttpURLConnection conn + = (HttpURLConnection) new URL("http://localhost:" + port + "/test-mdn").openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Connection", "close"); + conn.setDoOutput(true); + conn.getOutputStream().write("receipt".getBytes()); + assertEquals(200, conn.getResponseCode()); + conn.disconnect(); + + // the request handler thread ends when the sender closes the connection + await().atMost(10, TimeUnit.SECONDS).until(() -> log.hasMessageContaining("Connection is closed")); + assertEquals(List.of(), log.events(Level.ERROR), "a closed connection is not an error"); + assertTrue(log.eventsContaining("Connection is closed").stream().allMatch(e -> e.getLevel() == Level.DEBUG)); + } + } } diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2UnencryptedMessageTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2UnencryptedMessageTest.java index 51901d5622a5f..028bfcb95b796 100644 --- a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2UnencryptedMessageTest.java +++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2UnencryptedMessageTest.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; +import java.util.List; import org.apache.camel.component.as2.api.entity.*; import org.apache.camel.component.as2.api.util.HttpMessageUtils; @@ -36,6 +37,7 @@ import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpCoreContext; +import org.apache.logging.log4j.Level; import org.bouncycastle.cms.jcajce.ZlibExpanderProvider; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -166,6 +168,22 @@ public void compressedMessageRequestTest() throws Exception { } + @Test + public void unencryptedMessageToAServerWithoutDecryptingKeyLogsNoError() throws Exception { + AS2ClientManager clientManager = createDefaultClientManager(); + + try (LogCapture log = new LogCapture(AS2ServerConnection.class)) { + HttpCoreContext httpContext = clientManager.send(EDI_MESSAGE, REQUEST_URI, SUBJECT, FROM, AS2_NAME, AS2_NAME, + AS2MessageStructure.PLAIN, AS2MediaType.APPLICATION_EDIFACT, null, + null, null, null, null, null, + DISPOSITION_NOTIFICATION_TO, SIGNED_RECEIPT_MIC_ALGORITHMS, null, null, + "file.txt", null, null, null, null); + + assertEquals(HttpStatus.SC_OK, httpContext.getResponse().getCode()); + assertEquals(List.of(), log.events(Level.ERROR), "the server has no decrypting key, as it needs none"); + } + } + @Test public void mdnMessageTest() throws Exception { AS2ClientManager clientManager = createDefaultClientManager(); diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/LogCapture.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/LogCapture.java new file mode 100644 index 0000000000000..14528224a54a5 --- /dev/null +++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/LogCapture.java @@ -0,0 +1,70 @@ +/* + * 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.as2.api; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; + +/** + * Captures the log events of one logger, down to DEBUG, while it is open. + */ +final class LogCapture extends AbstractAppender implements AutoCloseable { + + private final List events = new CopyOnWriteArrayList<>(); + private final Logger logger; + private final Level previousLevel; + + LogCapture(Class loggerClass) { + super("LogCapture-" + loggerClass.getSimpleName(), null, null, true, Property.EMPTY_ARRAY); + this.logger = (Logger) LogManager.getLogger(loggerClass); + this.previousLevel = logger.getLevel(); + start(); + logger.addAppender(this); + logger.setLevel(Level.DEBUG); + } + + @Override + public void append(LogEvent event) { + events.add(event.toImmutable()); + } + + List events(Level level) { + return events.stream().filter(e -> e.getLevel() == level).toList(); + } + + boolean hasMessageContaining(String text) { + return events.stream().anyMatch(e -> e.getMessage().getFormattedMessage().contains(text)); + } + + List eventsContaining(String text) { + return events.stream().filter(e -> e.getMessage().getFormattedMessage().contains(text)).toList(); + } + + @Override + public void close() { + logger.removeAppender(this); + logger.setLevel(previousLevel); + stop(); + } +} diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/EntityUtilsTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/EntityUtilsTest.java index f35e3c11470f9..14d65762a0b87 100644 --- a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/EntityUtilsTest.java +++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/util/EntityUtilsTest.java @@ -16,6 +16,10 @@ */ package org.apache.camel.component.as2.api.util; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import org.apache.camel.component.as2.api.AS2Header; @@ -70,4 +74,33 @@ public void testCreateEDIEntityContentTypeWithoutContentDisposition() throws Exc Header[] actualContentDisposition = applicationEntity.getHeaders(AS2Header.CONTENT_DISPOSITION); Assertions.assertEquals(0, actualContentDisposition.length, "no Content-Disposition headers found"); } + + @Test + public void testQuotedPrintableOutputStreamEncodesQuotedPrintable() throws Exception { + byte[] message = ("UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'\r\n" + + "FTX+AAI+++Prix = 12 \u20ac, Gr\u00fc\u00dfe, a line longer than seventy-six characters'") + .getBytes(StandardCharsets.UTF_8); + + ByteArrayOutputStream encoded = new ByteArrayOutputStream(); + try (OutputStream os = EntityUtils.encode(encoded, "quoted-printable")) { + os.write(message); + } + + String text = encoded.toString(StandardCharsets.US_ASCII); + Assertions.assertTrue(text.contains("Prix =3D 12 =E2=82=AC"), text); + for (String line : text.split("\r\n")) { + Assertions.assertTrue(line.length() <= 76, "line longer than 76 characters: " + line); + } + Assertions.assertArrayEquals(message, EntityUtils.decode(encoded.toByteArray(), "quoted-printable")); + } + + @Test + public void testQuotedPrintableInputStreamDecodesQuotedPrintable() throws Exception { + byte[] message = "FTX+AAI+++Prix = 12 \u20ac'".getBytes(StandardCharsets.UTF_8); + byte[] encoded = EntityUtils.encode(message, "quoted-printable"); + + try (InputStream is = EntityUtils.decode(new ByteArrayInputStream(encoded), "quoted-printable")) { + Assertions.assertArrayEquals(message, is.readAllBytes()); + } + } } diff --git a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfiguration.java b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfiguration.java index 24e6a57f87380..83701ebdff35a 100644 --- a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfiguration.java +++ b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfiguration.java @@ -44,7 +44,7 @@ public final class AS2ClientManagerEndpointConfiguration extends AS2Configuratio @ApiParam(optional = true, apiMethods = {@ApiMethod(methodName = "send", description="An RFC2822 address to request a receipt or null if no receipt requested")}) private String dispositionNotificationTo; @UriParam - @ApiParam(optional = false, apiMethods = {@ApiMethod(methodName = "send", description="EDI message to transport")}) + @ApiParam(optional = false, apiMethods = {@ApiMethod(methodName = "send", description="EDI message to transport: a String, an InputStream or a byte array")}) private Object ediMessage; @UriParam @ApiParam(optional = true, apiMethods = {@ApiMethod(methodName = "send", description="The charset of the EDI message")}) diff --git a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json index 989fb9a25f74f..ee51bd92337cf 100644 --- a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json +++ b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json @@ -46,7 +46,7 @@ "dispositionNotificationTo": { "index": 10, "kind": "parameter", "displayName": "Disposition Notification To", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The value of the Disposition-Notification-To header. Assigning a value to this parameter requests a message disposition notification (MDN) for the AS2 message." }, "ediMessageCharset": { "index": 11, "kind": "parameter", "displayName": "Edi Message Charset", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "us-ascii", "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The charset of the content type of EDI message." }, "ediMessageTransferEncoding": { "index": 12, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The transfer encoding of EDI message." }, - "ediMessageType": { "index": 13, "kind": "parameter", "displayName": "Edi Message Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "application\/edifact", "application\/edi-x12", "application\/edi-consent", "application\/xml" ], "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The content type of EDI message. One of application\/edifact, application\/edi-x12, application\/edi-consent, application\/xml" }, + "ediMessageType": { "index": 13, "kind": "parameter", "displayName": "Edi Message Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "application\/edifact", "application\/edi-x12", "application\/edi-consent", "application\/xml" ], "deprecated": true, "deprecationNote": "Use ediMessageContentType instead", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The content type of EDI message. One of application\/edifact, application\/edi-x12, application\/edi-consent, application\/xml. client\/send uses this value only when ediMessageContentType is not set. Deprecation note: Use ediMessageContentType instead" }, "from": { "index": 14, "kind": "parameter", "displayName": "From", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The value of the From header of AS2 message." }, "httpConnectionPoolSize": { "index": 15, "kind": "parameter", "displayName": "Http Connection Pool Size", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The maximum size of the connection pool for http connections (client only)" }, "httpConnectionPoolTtl": { "index": 16, "kind": "parameter", "displayName": "Http Connection Pool Ttl", "group": "common", "label": "", "required": false, "type": "duration", "javaType": "java.time.Duration", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "15m", "configurationClass": "org.apache.camel.component.as2.AS2Configuration", "configurationField": "configuration", "description": "The time to live for connections in the connection pool (client only)" }, @@ -92,7 +92,7 @@ "server": { "consumerOnly": true, "producerOnly": false, "description": "Receives EDI Messages over HTTP", "methods": { "listen": { "description": "", "signatures": [ "void listen(String requestUriPattern, org.apache.hc.core5.http.io.HttpRequestHandler handler)" ] } } } }, "apiProperties": { - "client": { "methods": { "send": { "properties": { "accessToken": { "index": 0, "kind": "parameter", "displayName": "Access Token", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The access token that is used by the client for bearer authentication", "optional": true }, "as2From": { "index": 1, "kind": "parameter", "displayName": "As2 From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of sender", "optional": false }, "as2MessageStructure": { "index": 2, "kind": "parameter", "displayName": "As2 Message Structure", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2MessageStructure", "enum": [ "PLAIN", "SIGNED", "ENCRYPTED", "SIGNED_ENCRYPTED", "PLAIN_COMPRESSED", "COMPRESSED_SIGNED", "SIGNED_COMPRESSED", "ENCRYPTED_COMPRESSED", "ENCRYPTED_COMPRESSED_SIGNED", "ENCRYPTED_SIGNED_COMPRESSED" ], "deprecated": false, "autowired": false, "secret": false, "description": "The structure of AS2 to send; see AS2MessageStructure", "optional": false }, "as2To": { "index": 3, "kind": "parameter", "displayName": "As2 To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of recipient", "optional": false }, "attachedFileName": { "index": 4, "kind": "parameter", "displayName": "Attached File Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of the attached file or null if user doesn't want to specify it", "optional": true }, "compressionAlgorithm": { "index": 5, "kind": "parameter", "displayName": "Compression Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2CompressionAlgorithm", "enum": [ "ZLIB" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to compress the message or null if sending EDI message uncompressed", "optional": true }, "dispositionNotificationTo": { "index": 6, "kind": "parameter", "displayName": "Disposition Notification To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An RFC2822 address to request a receipt or null if no receipt requested", "optional": true }, "ediMessage": { "index": 7, "kind": "parameter", "displayName": "Edi Message", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "description": "EDI message to transport", "optional": false }, "ediMessageCharset": { "index": 8, "kind": "parameter", "displayName": "Edi Message Charset", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the EDI message", "optional": true }, "ediMessageContentType": { "index": 9, "kind": "parameter", "displayName": "Edi Message Content Type", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type of EDI message", "optional": false }, "ediMessageTransferEncoding": { "index": 10, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The transfer encoding used to transport EDI message", "optional": true }, "encryptingAlgorithm": { "index": 11, "kind": "parameter", "displayName": "Encrypting Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [ "AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM", "AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC", "CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC", "GOST28147_GCFB", "IDEA_CBC", "RC2_CBC", "RC4", "SEED_CBC" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "encryptingCertificateChain": { "index": 12, "kind": "parameter", "displayName": "Encrypting Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "from": { "index": 13, "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "RFC2822 address of sender", "optional": false }, "password": { "index": 14, "kind": "parameter", "displayName": "Password", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The password that is used by the client for basic authentication", "optional": true }, "receiptDeliveryOption": { "index": 15, "kind": "parameter", "displayName": "Receipt Delivery Option", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The return URL that the message receiver should send an asynchronous MDN to", "optional": true }, "requestUri": { "index": 16, "kind": "parameter", "displayName": "Request Uri", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Resource location to deliver message", "optional": false }, "signedReceiptMicAlgorithms": { "index": 17, "kind": "parameter", "displayName": "Signed Receipt Mic Algorithms", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The senders list of signing algorithms for signing receipt, in preferred order, or null if requesting an unsigned receipt.", "optional": true }, "signingAlgorithm": { "index": 18, "kind": "parameter", "displayName": "Signing Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [ "SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA", "MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA", "RIPEMD256WITHRSA", "SHA224WITHDSA", "SHA256WITHDSA", "SHA384WITHDSA", "SHA512WITHDSA", "SHA3_224WITHDSA", "SHA3_256WITHDSA", "SHA3_384WITHDSA", "SHA3_512WITHDSA", "SHA1WITHDSA", "SHA3_224WITHECDSA", "SHA3_256WITHECDSA", "SHA3_384WITHECDSA", "SHA3_512WITHECDSA", "SHA1WITHECDSA", "SHA224WITHECDSA", "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA", "SHA1WITHPLAIN_ECDSA", "SHA224WITHPLAIN_ECDSA", "SHA256WITHPLAIN_ECDSA", "SHA384WITHPLAIN_ECDSA", "SHA512WITHPLAIN_ECDSA", "RIPEMD160WITHPLAIN_ECDSA", "SHA1WITHRSAANDMGF1", "SHA224WITHRSAANDMGF1", "SHA256WITHRSAANDMGF1", "SHA384WITHRSAANDMGF1", "SHA512WITHRSAANDMGF1", "SHA3_224WITHRSAANDMGF1", "SHA3_256WITHRSAANDMGF1", "SHA3_384WITHRSAANDMGF1", "SHA3_512WITHRSAANDMGF1" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingCertificateChain": { "index": 19, "kind": "parameter", "displayName": "Signing Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingPrivateKey": { "index": 20, "kind": "parameter", "displayName": "Signing Private Key", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated": false, "autowired": false, "secret": false, "description": "The private key used to sign EDI message", "optional": true }, "subject": { "index": 21, "kind": "parameter", "displayName": "Subject", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Message subject", "optional": false }, "userName": { "index": 22, "kind": "parameter", "displayName": "User Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The user-name that is used for basic authentication", "optional": true } } } } }, + "client": { "methods": { "send": { "properties": { "accessToken": { "index": 0, "kind": "parameter", "displayName": "Access Token", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The access token that is used by the client for bearer authentication", "optional": true }, "as2From": { "index": 1, "kind": "parameter", "displayName": "As2 From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of sender", "optional": false }, "as2MessageStructure": { "index": 2, "kind": "parameter", "displayName": "As2 Message Structure", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2MessageStructure", "enum": [ "PLAIN", "SIGNED", "ENCRYPTED", "SIGNED_ENCRYPTED", "PLAIN_COMPRESSED", "COMPRESSED_SIGNED", "SIGNED_COMPRESSED", "ENCRYPTED_COMPRESSED", "ENCRYPTED_COMPRESSED_SIGNED", "ENCRYPTED_SIGNED_COMPRESSED" ], "deprecated": false, "autowired": false, "secret": false, "description": "The structure of AS2 to send; see AS2MessageStructure", "optional": false }, "as2To": { "index": 3, "kind": "parameter", "displayName": "As2 To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "AS2 name of recipient", "optional": false }, "attachedFileName": { "index": 4, "kind": "parameter", "displayName": "Attached File Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of the attached file or null if user doesn't want to specify it", "optional": true }, "compressionAlgorithm": { "index": 5, "kind": "parameter", "displayName": "Compression Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2CompressionAlgorithm", "enum": [ "ZLIB" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to compress the message or null if sending EDI message uncompressed", "optional": true }, "dispositionNotificationTo": { "index": 6, "kind": "parameter", "displayName": "Disposition Notification To", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "An RFC2822 address to request a receipt or null if no receipt requested", "optional": true }, "ediMessage": { "index": 7, "kind": "parameter", "displayName": "Edi Message", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "description": "EDI message to transport: a String, an InputStream or a byte array", "optional": false }, "ediMessageCharset": { "index": 8, "kind": "parameter", "displayName": "Edi Message Charset", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the EDI message", "optional": true }, "ediMessageContentType": { "index": 9, "kind": "parameter", "displayName": "Edi Message Content Type", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The content type of EDI message", "optional": false }, "ediMessageTransferEncoding": { "index": 10, "kind": "parameter", "displayName": "Edi Message Transfer Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The transfer encoding used to transport EDI message", "optional": true }, "encryptingAlgorithm": { "index": 11, "kind": "parameter", "displayName": "Encrypting Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [ "AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM", "AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC", "CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC", "GOST28147_GCFB", "IDEA_CBC", "RC2_CBC", "RC4", "SEED_CBC" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "encryptingCertificateChain": { "index": 12, "kind": "parameter", "displayName": "Encrypting Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to encrypt the message or null if sending EDI message unencrypted", "optional": true }, "from": { "index": 13, "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "RFC2822 address of sender", "optional": false }, "password": { "index": 14, "kind": "parameter", "displayName": "Password", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The password that is used by the client for basic authentication", "optional": true }, "receiptDeliveryOption": { "index": 15, "kind": "parameter", "displayName": "Receipt Delivery Option", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The return URL that the message receiver should send an asynchronous MDN to", "optional": true }, "requestUri": { "index": 16, "kind": "parameter", "displayName": "Request Uri", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Resource location to deliver message", "optional": false }, "signedReceiptMicAlgorithms": { "index": 17, "kind": "parameter", "displayName": "Signed Receipt Mic Algorithms", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The senders list of signing algorithms for signing receipt, in preferred order, or null if requesting an unsigned receipt.", "optional": true }, "signingAlgorithm": { "index": 18, "kind": "parameter", "displayName": "Signing Algorithm", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [ "SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA", "MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA", "SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA", "RIPEMD256WITHRSA", "SHA224WITHDSA", "SHA256WITHDSA", "SHA384WITHDSA", "SHA512WITHDSA", "SHA3_224WITHDSA", "SHA3_256WITHDSA", "SHA3_384WITHDSA", "SHA3_512WITHDSA", "SHA1WITHDSA", "SHA3_224WITHECDSA", "SHA3_256WITHECDSA", "SHA3_384WITHECDSA", "SHA3_512WITHECDSA", "SHA1WITHECDSA", "SHA224WITHECDSA", "SHA256WITHECDSA", "SHA384WITHECDSA", "SHA512WITHECDSA", "SHA1WITHPLAIN_ECDSA", "SHA224WITHPLAIN_ECDSA", "SHA256WITHPLAIN_ECDSA", "SHA384WITHPLAIN_ECDSA", "SHA512WITHPLAIN_ECDSA", "RIPEMD160WITHPLAIN_ECDSA", "SHA1WITHRSAANDMGF1", "SHA224WITHRSAANDMGF1", "SHA256WITHRSAANDMGF1", "SHA384WITHRSAANDMGF1", "SHA512WITHRSAANDMGF1", "SHA3_224WITHRSAANDMGF1", "SHA3_256WITHRSAANDMGF1", "SHA3_384WITHRSAANDMGF1", "SHA3_512WITHRSAANDMGF1" ], "deprecated": false, "autowired": false, "secret": false, "description": "The algorithm used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingCertificateChain": { "index": 19, "kind": "parameter", "displayName": "Signing Certificate Chain", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.cert.Certificate[]", "deprecated": false, "autowired": false, "secret": false, "description": "The chain of certificates used to sign the message or null if sending EDI message unsigned", "optional": true }, "signingPrivateKey": { "index": 20, "kind": "parameter", "displayName": "Signing Private Key", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated": false, "autowired": false, "secret": false, "description": "The private key used to sign EDI message", "optional": true }, "subject": { "index": 21, "kind": "parameter", "displayName": "Subject", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Message subject", "optional": false }, "userName": { "index": 22, "kind": "parameter", "displayName": "User Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The user-name that is used for basic authentication", "optional": true } } } } }, "receipt": { "methods": { "receive": { "properties": { "requestUriPattern": { "index": 0, "kind": "parameter", "displayName": "Request Uri Pattern", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "", "optional": false } } } } }, "server": { "methods": { "listen": { "properties": { "requestUriPattern": { "index": 0, "kind": "parameter", "displayName": "Request Uri Pattern", "group": "consumer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "", "optional": false } } } } } } diff --git a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc index 8b3988308f017..22692a23e9e30 100644 --- a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc +++ b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc @@ -36,8 +36,10 @@ as2://apiName/methodName apiName can be one of: -* client -* server +* client: `client/send` sends an EDI message to a trading partner +* server: `server/listen` receives EDI messages +* receipt: `receipt/receive` receives the asynchronous MDNs (receipts) that a `client/send` asked for with + `receiptDeliveryOption` // component options: START @@ -100,6 +102,25 @@ An `as2://server/listen` consumer receives AS2 messages on `serverPortNumber`, f A `SIGNED`, `ENCRYPTED` or `SIGNED_ENCRYPTED` message structure also needs the keys and certificates of the signing and encrypting options. +=== Sending with client/send + +The EDI message (`inBody: ediMessage`) can be text, a stream, a `byte[]` or a file, such as the `java.io.File` or the +file of a `file` consumer, so a route can send the files of a folder without converting them first. Text is encoded +with `ediMessageCharset` (US-ASCII when it is not set); the other types are sent as their bytes. Any other body that +the type converters can turn into bytes is sent as those bytes, and a body that cannot be converted fails the +exchange with a message that names its type. + +`requestUri` defaults to `/`. The content type of the message is set with `ediMessageContentType`; the older +`ediMessageType` option is deprecated, and it is used only when `ediMessageContentType` is not set. + +`ediMessageTransferEncoding` can be `7bit`, `8bit`, `binary`, `base64` or `quoted-printable`. + +When the trading partner answers with an HTTP status outside 2xx, such as a 500 when the route of an AS2 server fails, +the exchange fails with an exception that names the status code and reason. The exchange property +`CamelAs2.interchange` still holds the `HttpCoreContext` with the request and the response. A 2xx answer sets the body +to the receipt (MDN) when the partner returned one, and to `null` when there is none, such as a send without +`dispositionNotificationTo` or with an asynchronous receipt. + Several `as2://server/listen` consumers can share a `serverPortNumber`, each with its own `requestUriPattern`. Each consumer applies its own keys and certificates and its own asynchronous MDN settings (`mdnUserName`, `mdnPassword`, `mdnAccessToken` and `asyncMdnAllowedHosts`) to the messages received on its own pattern. diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java index 3bc6aec167045..b13a8e460c592 100644 --- a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java +++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java @@ -66,7 +66,9 @@ public class AS2Configuration { private Integer serverPortNumber; @UriParam(defaultValue = "/") private String requestUri = "/"; + @Deprecated @UriParam(enums = "application/edifact,application/edi-x12,application/edi-consent,application/xml") + @Metadata(deprecationNote = "Use ediMessageContentType instead") private String ediMessageType; @UriParam(defaultValue = "us-ascii") private String ediMessageCharset; @@ -277,14 +279,16 @@ public void setRequestUri(String requestUri) { this.requestUri = requestUri; } + @Deprecated public String getEdiMessageType() { return ediMessageType; } /** * The content type of EDI message. One of application/edifact, application/edi-x12, application/edi-consent, - * application/xml + * application/xml. client/send uses this value only when ediMessageContentType is not set. */ + @Deprecated public void setEdiMessageType(String ediMessageType) { this.ediMessageType = ediMessageType; } diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java index 14b4b8dbba472..a3a5907c7f677 100644 --- a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java +++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Endpoint.java @@ -62,6 +62,10 @@ + ",greedy,scheduler,schedulerProperties,scheduledExecutorService,backoffMultiplier,backoffIdleThreshold,backoffErrorThreshold,repeatCount,bridgeErrorHandler") public class AS2Endpoint extends AbstractApiEndpoint { + private static final String REQUEST_URI = "requestUri"; + private static final String DEFAULT_REQUEST_URI = "/"; + private static final String EDI_MESSAGE_CONTENT_TYPE = "ediMessageContentType"; + @UriParam private AS2Configuration configuration; @@ -166,14 +170,27 @@ public void setAs2MessageStructure(AS2MessageStructure as2MessageStructure) { configuration.setAs2MessageStructure(as2MessageStructure); } + @Deprecated public String getEdiMessageType() { return configuration.getEdiMessageType(); } - public void setEdiMessageContentType(String ediMessageType) { + @Deprecated + public void setEdiMessageType(String ediMessageType) { configuration.setEdiMessageType(ediMessageType); } + public String getEdiMessageContentType() { + return configuration instanceof AS2ClientManagerEndpointConfiguration client + ? client.getEdiMessageContentType() : null; + } + + public void setEdiMessageContentType(String ediMessageContentType) { + if (configuration instanceof AS2ClientManagerEndpointConfiguration client) { + client.setEdiMessageContentType(ediMessageContentType); + } + } + public String getEdiMessageTransferEncoding() { return configuration.getEdiMessageTransferEncoding(); } @@ -278,6 +295,19 @@ public void setSslContext(SSLContext sslContext) { configuration.setSslContext(sslContext); } + @Override + @SuppressWarnings("deprecation") + public void interceptProperties(Map properties) { + if (apiName == AS2ApiName.CLIENT) { + // the generated configuration of client/send has its own requestUri without the default + properties.putIfAbsent(REQUEST_URI, DEFAULT_REQUEST_URI); + // the deprecated ediMessageType is the content type when ediMessageContentType is not set + if (configuration.getEdiMessageType() != null) { + properties.putIfAbsent(EDI_MESSAGE_CONTENT_TYPE, configuration.getEdiMessageType()); + } + } + } + @Override protected ApiMethodPropertiesHelper getPropertiesHelper() { return AS2PropertiesHelper.getHelper(getCamelContext()); diff --git a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java index b3c389ba8c968..89c7ee5d4403b 100644 --- a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java +++ b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Producer.java @@ -16,13 +16,18 @@ */ package org.apache.camel.component.as2; +import java.io.InputStream; +import java.util.Map; + import org.apache.camel.Exchange; +import org.apache.camel.RuntimeCamelException; import org.apache.camel.component.as2.api.entity.DispositionNotificationMultipartReportEntity; import org.apache.camel.component.as2.api.entity.MultipartSignedEntity; import org.apache.camel.component.as2.internal.AS2ApiName; import org.apache.camel.component.as2.internal.AS2Constants; import org.apache.camel.component.as2.internal.AS2PropertiesHelper; import org.apache.camel.support.component.AbstractApiProducer; +import org.apache.camel.support.component.ApiMethod; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpResponse; @@ -33,15 +38,52 @@ */ public class AS2Producer extends AbstractApiProducer { + private static final String EDI_MESSAGE = "ediMessage"; + public AS2Producer(AS2Endpoint endpoint) { super(endpoint, AS2PropertiesHelper.getHelper(endpoint.getCamelContext())); } + @Override + protected ApiMethod findMethod(Exchange exchange, Map properties) { + ApiMethod method = super.findMethod(exchange, properties); + if (method != null && !convertEdiMessage(exchange, properties)) { + return null; + } + return method; + } + + /** + * The client sends text and a stream as they are, and any other EDI message, such as bytes or a file, as its bytes. + */ + private boolean convertEdiMessage(Exchange exchange, Map properties) { + Object ediMessage = properties.get(EDI_MESSAGE); + if (ediMessage == null || ediMessage instanceof String || ediMessage instanceof InputStream) { + return true; + } + try { + properties.put(EDI_MESSAGE, + exchange.getContext().getTypeConverter().mandatoryConvertTo(byte[].class, exchange, ediMessage)); + return true; + } catch (Exception e) { + exchange.setException(new RuntimeCamelException( + String.format("Cannot send an EDI message of type %s: send text, a stream, bytes or a file", + ediMessage.getClass().getName()), + e)); + return false; + } + } + @Override public void interceptResult(Object methodResult, Exchange resultExchange) { HttpCoreContext context = (HttpCoreContext) methodResult; resultExchange.setProperty(AS2Constants.AS2_INTERCHANGE, context); HttpResponse response = context.getResponse(); + if (response != null && (response.getCode() < 200 || response.getCode() >= 300)) { + String reason = response.getReasonPhrase(); + throw new RuntimeCamelException( + "The AS2 partner answered with HTTP status " + response.getCode() + (reason != null ? " " + reason : "")); + } if (response instanceof ClassicHttpResponse classicResponse) { HttpEntity entity = classicResponse.getEntity(); if (entity instanceof DispositionNotificationMultipartReportEntity || entity instanceof MultipartSignedEntity) { diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientSendTest.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientSendTest.java new file mode 100644 index 0000000000000..e2fff84e0f199 --- /dev/null +++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ClientSendTest.java @@ -0,0 +1,251 @@ +/* + * 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.as2; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.as2.api.AS2MediaType; +import org.apache.camel.component.as2.api.AS2MessageStructure; +import org.apache.camel.component.as2.api.AS2ServerConnection; +import org.apache.camel.component.as2.api.AS2ServerManager; +import org.apache.camel.component.as2.api.AS2SignatureAlgorithm; +import org.apache.camel.component.as2.api.entity.ApplicationEntity; +import org.apache.camel.component.as2.api.util.HttpMessageUtils; +import org.apache.camel.component.as2.internal.AS2Constants; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.hc.core5.http.HttpStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The body types client/send accepts, the default of requestUri, the deprecated ediMessageType option, the + * quoted-printable transfer encoding and a partner that answers with an HTTP error (CAMEL-26052). + */ +public class AS2ClientSendTest extends AbstractAS2ITSupport { + + private static final String EDI_MESSAGE + = "UNB+UNOA:1+005435656:1+006415160:1+060515:1434+00000000000778'UNZ+1+00000000000778'"; + + @TempDir + static Path inbox; + + private final List received = new CopyOnWriteArrayList<>(); + private final List receivedPaths = new CopyOnWriteArrayList<>(); + private AS2ServerConnection serverConnection; + private int targetPort; + + @Override + public void setupResources() throws Exception { + serverConnection = new AS2ServerConnection( + "1.1", "AS2ClientSendTest Server", "server.example.com", 0, + AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null, "TBD", null, null, + null, null, null, "localhost"); + targetPort = serverConnection.getLocalPort(); + serverConnection.listen("/fail", (request, response, context) -> response.setCode(HttpStatus.SC_SERVER_ERROR)); + serverConnection.listen("*", (request, response, context) -> { + context.setAttribute(AS2ServerManager.FROM, "mrAS@example.org"); + context.setAttribute(AS2ServerManager.SUBJECT, "MDN Response"); + ApplicationEntity entity = HttpMessageUtils.extractEdiPayload(request, + new HttpMessageUtils.DecrpytingAndSigningInfo(null, null)); + receivedPaths.add(request.getPath()); + received.add(asText(entity.getEdiMessage())); + }); + } + + private String asText(Object ediMessage) { + return context.getTypeConverter().convertTo(String.class, ediMessage); + } + + @Override + public void cleanupResources() { + if (serverConnection != null) { + serverConnection.close(); + } + } + + @BeforeEach + public void clearReceived() { + received.clear(); + receivedPaths.clear(); + } + + @Test + public void sendText() { + Object body = requestBodyAndHeaders("direct://SEND", EDI_MESSAGE, as2Headers()); + + assertNull(body); + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void sendBytes() { + requestBodyAndHeaders("direct://SEND", EDI_MESSAGE.getBytes(StandardCharsets.US_ASCII), as2Headers()); + + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void sendFile() throws Exception { + File file = Files.writeString(inbox.resolve("message.edi"), EDI_MESSAGE).toFile(); + + requestBodyAndHeaders("direct://SEND", file, as2Headers()); + + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void sendTheFilesOfAFolder() throws Exception { + MockEndpoint sent = getMockEndpoint("mock:sent"); + sent.expectedMessageCount(1); + + Files.writeString(inbox.resolve("order.edi"), EDI_MESSAGE); + context.getRouteController().startRoute("files"); + + sent.assertIsSatisfied(); + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void sendABodyThatDoesNotConvert() { + Map notAnEdiMessage = Map.of("order", "1"); + + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> requestBodyAndHeaders("direct://SEND", notAnEdiMessage, as2Headers())); + + assertTrue(e.getCause().getMessage().contains(notAnEdiMessage.getClass().getName()), e.getCause().getMessage()); + assertTrue(received.isEmpty()); + } + + @Test + public void requestUriDefaultsToSlash() { + Map headers = as2Headers(); + headers.remove("CamelAs2.requestUri"); + + // an as2 component without a component configuration, as with camel run + requestBodyAndHeaders("direct://SEND_WITHOUT_COMPONENT_CONFIGURATION", EDI_MESSAGE, headers); + + assertEquals(List.of("/"), receivedPaths); + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void ediMessageTypeIsUsedAsContentType() { + Map headers = as2Headers(); + headers.remove("CamelAs2.ediMessageContentType"); + + requestBodyAndHeaders("direct://SEND_EDI_MESSAGE_TYPE", EDI_MESSAGE, headers); + + assertEquals(List.of(EDI_MESSAGE), received); + } + + @Test + public void sendQuotedPrintable() { + // a long line, an equal sign and characters outside US-ASCII all need encoding + String message = EDI_MESSAGE + EDI_MESSAGE + "FTX+AAI+++Prix = 12 €, Grüße'"; + Map headers = as2Headers(); + headers.put("CamelAs2.ediMessageTransferEncoding", "quoted-printable"); + headers.put("CamelAs2.ediMessageCharset", "utf-8"); + + requestBodyAndHeaders("direct://SEND", message, headers); + + assertEquals(List.of(message), received); + } + + @Test + public void partnerErrorFailsTheExchange() { + Map headers = as2Headers(); + headers.put("CamelAs2.requestUri", "/fail"); + + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> requestBodyAndHeaders("direct://SEND", EDI_MESSAGE, headers)); + + assertTrue(e.getCause().getMessage().contains("500"), e.getCause().getMessage()); + // the interchange is still there to look at + assertNotNull(e.getExchange().getProperty(AS2Constants.AS2_INTERCHANGE)); + } + + private Map as2Headers() { + Map headers = new HashMap<>(); + headers.put("CamelAs2.requestUri", "/"); + headers.put("CamelAs2.subject", "Test Case"); + headers.put("CamelAs2.from", "mrAS@example.org"); + headers.put("CamelAs2.as2From", "878051556"); + headers.put("CamelAs2.as2To", "878051556"); + headers.put("CamelAs2.as2MessageStructure", AS2MessageStructure.PLAIN); + headers.put("CamelAs2.ediMessageContentType", AS2MediaType.APPLICATION_EDIFACT); + headers.put("CamelAs2.ediMessageTransferEncoding", "7bit"); + return headers; + } + + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext camelContext = super.createCamelContext(); + camelContext.addComponent("as2-plain", new AS2Component(camelContext)); + return camelContext; + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct://SEND") + .to("as2://client/send?inBody=ediMessage"); + + from("direct://SEND_WITHOUT_COMPONENT_CONFIGURATION") + .to("as2-plain://client/send?inBody=ediMessage&targetHostname=localhost&targetPortNumber=" + + targetPort); + + from("direct://SEND_EDI_MESSAGE_TYPE") + .to("as2://client/send?inBody=ediMessage&ediMessageType=application/edifact"); + + from("file:" + inbox + "?include=.*\\.edi&noop=true&initialDelay=0&delay=10").routeId("files") + .autoStartup(false) + .setHeader("CamelAs2.requestUri", constant("/")) + .setHeader("CamelAs2.subject", constant("Test Case")) + .setHeader("CamelAs2.from", constant("mrAS@example.org")) + .setHeader("CamelAs2.as2From", constant("878051556")) + .setHeader("CamelAs2.as2To", constant("878051556")) + .setHeader("CamelAs2.as2MessageStructure", constant(AS2MessageStructure.PLAIN)) + .setHeader("CamelAs2.ediMessageContentType", constant(AS2MediaType.APPLICATION_EDIFACT)) + .to("as2://client/send?inBody=ediMessage") + .to("mock:sent"); + } + }; + } + + @Override + protected void customizeConfiguration(AS2Configuration configuration) { + configuration.setTargetPortNumber(targetPort); + } +} diff --git a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/org/apache/camel/component/file/azure/azure-files.json b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/org/apache/camel/component/file/azure/azure-files.json index 231fb1308104c..65fd7e2caa373 100644 --- a/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/org/apache/camel/component/file/azure/azure-files.json +++ b/components/camel-azure/camel-azure-files/src/generated/resources/META-INF/org/apache/camel/component/file/azure/azure-files.json @@ -68,7 +68,7 @@ "sendEmptyMessageWhenIdle": { "index": 17, "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." }, "streamDownload": { "index": 18, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.azure.FilesConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 19, "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." }, - "download": { "index": 20, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 20, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 21, "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": 22, "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." }, "inProgressRepository": { "index": 23, "kind": "parameter", "displayName": "In Progress Repository", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable in-progress repository org.apache.camel.spi.IdempotentRepository. The in-progress repository is used to account the current in progress files being consumed. By default a memory based repository is used." }, diff --git a/components/camel-dns/src/generated/resources/META-INF/org/apache/camel/component/dns/dns.json b/components/camel-dns/src/generated/resources/META-INF/org/apache/camel/component/dns/dns.json index 9bd010f45fe95..f6182c1142c32 100644 --- a/components/camel-dns/src/generated/resources/META-INF/org/apache/camel/component/dns/dns.json +++ b/components/camel-dns/src/generated/resources/META-INF/org/apache/camel/component/dns/dns.json @@ -29,14 +29,14 @@ }, "headers": { "CamelDnsClass": { "index": 0, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The DNS class of the lookup. Should match the values of org.xbill.dns.DClass. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_CLASS" }, - "CamelDnsName": { "index": 1, "kind": "header", "displayName": "", "group": "lookup", "label": "lookup", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name to lookup", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_NAME" }, + "CamelDnsName": { "index": 1, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name to lookup", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_NAME" }, "CamelDnsDomain": { "index": 2, "kind": "header", "displayName": "", "group": "ip", "label": "ip", "required": true, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The domain name.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_DOMAIN" }, "CamelDnsServer": { "index": 3, "kind": "header", "displayName": "", "group": "dig", "label": "dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The server in particular for the query. If none is given, the default one specified by the OS will be used. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_SERVER" }, - "CamelDnsType": { "index": 4, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. Should match the values of org.xbill.dns.Type. Optional.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_TYPE" }, - "CamelDnsTerm": { "index": 5, "kind": "header", "displayName": "", "group": "wikipedia", "label": "wikipedia", "required": true, "javaType": "", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The term", "constantName": "org.apache.camel.component.dns.DnsConstants#TERM" } + "CamelDnsType": { "index": 4, "kind": "header", "displayName": "", "group": "lookup dig", "label": "lookup dig", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. Should match the values of org.xbill.dns.Type. Optional. The dig operation uses A when none or an unknown type is given.", "constantName": "org.apache.camel.component.dns.DnsConstants#DNS_TYPE" }, + "CamelDnsTerm": { "index": 5, "kind": "header", "displayName": "", "group": "wikipedia", "label": "wikipedia", "required": true, "javaType": "", "deprecated": true, "deprecationNote": "The Wikipedia over DNS service is no longer available", "autowired": false, "secret": false, "description": "The term. Deprecation note: The Wikipedia over DNS service is no longer available", "constantName": "org.apache.camel.component.dns.DnsConstants#TERM" } }, "properties": { - "dnsType": { "index": 0, "kind": "path", "displayName": "Dns Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.dns.DnsType", "enum": [ "dig", "ip", "lookup", "wikipedia" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup." }, + "dnsType": { "index": 0, "kind": "path", "displayName": "Dns Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.dns.DnsType", "enum": [ "dig", "ip", "lookup", "wikipedia" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of the lookup. The wikipedia type is deprecated, as the Wikipedia over DNS service it queries is no longer available." }, "lazyStartProducer": { "index": 1, "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." } } } diff --git a/components/camel-dns/src/main/docs/dns-component.adoc b/components/camel-dns/src/main/docs/dns-component.adoc index 63f273f75cf5e..3d51e1e7d14f1 100644 --- a/components/camel-dns/src/main/docs/dns-component.adoc +++ b/components/camel-dns/src/main/docs/dns-component.adoc @@ -20,6 +20,7 @@ The component offers the following operations: * `ip`: to resolve a domain by its ip * `lookup`: to lookup information about the domain * `dig`: to run DNS queries +* `wikipedia` (deprecated): to query the Wikipedia over DNS service, which is no longer available [NOTE] ==== @@ -177,7 +178,10 @@ YAML:: ---- ==== -The query must be provided in the header with key `"CamelDnsName"`. +The query must be provided in the header with key `"CamelDnsName"`; without it the +exchange fails with an `IllegalArgumentException`. The `"CamelDnsType"` header sets the type of +the query, such as `MX`, and defaults to `A` when it is not set or not a known type. The +`"CamelDnsClass"` header sets the DNS class, and defaults to `IN`. === Dns Activation Policy diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsComponent.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsComponent.java index c74542bd46f60..5db0f7be2a52a 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsComponent.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsComponent.java @@ -57,7 +57,8 @@ *

*

*

- * dns:///wikipedia This endpoint takes one paramter : + * dns:///wikipedia (deprecated, the Wikipedia over DNS service is no longer available) This endpoint takes one + * parameter : *

    *
  • CamelDnsTerm: the search term on wikipedia
  • *
diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsConstants.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsConstants.java index 0c5bce4457a9c..b38b4782757ac 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsConstants.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsConstants.java @@ -23,13 +23,17 @@ public class DnsConstants { public static final String OPERATION_DIG = DnsType.dig.name(); public static final String OPERATION_IP = DnsType.ip.name(); public static final String OPERATION_LOOKUP = DnsType.lookup.name(); + /** + * @deprecated the Wikipedia over DNS service is no longer available + */ + @Deprecated public static final String OPERATION_WIKIPEDIA = DnsType.wikipedia.name(); @Metadata(label = "lookup dig", description = "The DNS class of the lookup. Should match the values of\n" + "`org.xbill.dns.DClass`. Optional.", javaType = "String") public static final String DNS_CLASS = "CamelDnsClass"; - @Metadata(label = "lookup", description = "The name to lookup", javaType = "String", required = true) + @Metadata(label = "lookup dig", description = "The name to lookup", javaType = "String", required = true) public static final String DNS_NAME = "CamelDnsName"; @Metadata(label = "ip", description = "The domain name.", javaType = "String", required = true) public static final String DNS_DOMAIN = "CamelDnsDomain"; @@ -38,10 +42,15 @@ public class DnsConstants { javaType = "String") public static final String DNS_SERVER = "CamelDnsServer"; @Metadata(label = "lookup dig", description = "The type of the lookup. Should match the values of `org.xbill.dns.Type`.\n" + - "Optional.", + "Optional. The dig operation uses A when none or an unknown type is given.", javaType = "String") public static final String DNS_TYPE = "CamelDnsType"; - @Metadata(label = "wikipedia", description = "The term", required = true) + /** + * @deprecated the Wikipedia over DNS service is no longer available + */ + @Deprecated + @Metadata(label = "wikipedia", description = "The term", required = true, + deprecationNote = "The Wikipedia over DNS service is no longer available") public static final String TERM = "CamelDnsTerm"; protected DnsConstants() { diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsDigProducer.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsDigProducer.java index 8659427b2c68c..0395eb90b1c00 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsDigProducer.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsDigProducer.java @@ -19,6 +19,7 @@ import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.support.DefaultProducer; +import org.apache.camel.util.StringHelper; import org.xbill.DNS.DClass; import org.xbill.DNS.Message; import org.xbill.DNS.Name; @@ -42,9 +43,22 @@ public void process(Exchange exchange) throws Exception { String server = exchange.getIn().getHeader(DnsConstants.DNS_SERVER, String.class); SimpleResolver resolver = new SimpleResolver(server); - int type = Type.value(exchange.getIn().getHeader(DnsConstants.DNS_TYPE, String.class)); + Message query = createQuery(exchange); + Message response = resolver.send(query); + exchange.getIn().setBody(response); + } + + /** + * Creates the query from the CamelDnsName, CamelDnsType and CamelDnsClass headers. + */ + Message createQuery(Exchange exchange) throws Exception { + String dnsName = exchange.getIn().getHeader(DnsConstants.DNS_NAME, String.class); + StringHelper.notEmpty(dnsName, "Header " + DnsConstants.DNS_NAME); + + String typeValue = exchange.getIn().getHeader(DnsConstants.DNS_TYPE, String.class); + int type = typeValue != null ? Type.value(typeValue) : -1; if (type == -1) { - // default: if unparsable value given, use A. + // default: if no or an unparsable value given, use A. type = Type.A; } @@ -59,10 +73,8 @@ public void process(Exchange exchange) throws Exception { dclass = DClass.IN; } - Name name = Name.fromString(exchange.getIn().getHeader(DnsConstants.DNS_NAME, String.class), Name.root); + Name name = Name.fromString(dnsName, Name.root); Record rec = Record.newRecord(name, type, dclass); - Message query = Message.newQuery(rec); - Message response = resolver.send(query); - exchange.getIn().setBody(response); + return Message.newQuery(rec); } } diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsEndpoint.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsEndpoint.java index 05b5fba1824aa..160283a4b7739 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsEndpoint.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsEndpoint.java @@ -42,6 +42,7 @@ public DnsEndpoint(String endpointUri, Component component) { } @Override + @SuppressWarnings("deprecation") public Producer createProducer() throws Exception { if (DnsType.dig == dnsType) { return new DnsDigProducer(this); @@ -66,7 +67,8 @@ public DnsType getDnsType() { } /** - * The type of the lookup. + * The type of the lookup. The wikipedia type is deprecated, as the Wikipedia over DNS service it queries is no + * longer available. */ public void setDnsType(DnsType dnsType) { this.dnsType = dnsType; diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsType.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsType.java index 0eecac32593a2..a81843070ab7e 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsType.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsType.java @@ -21,6 +21,10 @@ public enum DnsType { dig, ip, lookup, + /** + * @deprecated the Wikipedia over DNS service that this type queries is no longer available + */ + @Deprecated wikipedia } diff --git a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsWikipediaProducer.java b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsWikipediaProducer.java index 2cf0d2bacf386..ed59012419b1e 100644 --- a/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsWikipediaProducer.java +++ b/components/camel-dns/src/main/java/org/apache/camel/component/dns/DnsWikipediaProducer.java @@ -35,7 +35,10 @@ * See here for a reference: http://www.commandlinefu.com/commands/view/2829/query-wikipedia-via-console-over-dns *

* This endpoint accepts the following header: CamelDnsTerm: a simple term to use to query wikipedia. + * + * @deprecated the Wikipedia over DNS service that this producer queries is no longer available */ +@Deprecated public class DnsWikipediaProducer extends DefaultProducer { public DnsWikipediaProducer(Endpoint endpoint) { diff --git a/components/camel-dns/src/test/java/org/apache/camel/component/dns/DnsDigProducerTest.java b/components/camel-dns/src/test/java/org/apache/camel/component/dns/DnsDigProducerTest.java new file mode 100644 index 0000000000000..e1115ac4de2f9 --- /dev/null +++ b/components/camel-dns/src/test/java/org/apache/camel/component/dns/DnsDigProducerTest.java @@ -0,0 +1,88 @@ +/* + * 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.dns; + +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.support.DefaultExchange; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xbill.DNS.DClass; +import org.xbill.DNS.Message; +import org.xbill.DNS.Record; +import org.xbill.DNS.Type; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests the query that the dig producer builds from the headers, without sending it. + */ +public class DnsDigProducerTest { + + private CamelContext context; + private DnsDigProducer producer; + + @BeforeEach + public void setUp() throws Exception { + context = new DefaultCamelContext(); + context.start(); + producer = new DnsDigProducer(context.getEndpoint("dns:dig")); + } + + @AfterEach + public void tearDown() { + context.stop(); + } + + @Test + public void typeAndClassDefaultToAAndIn() throws Exception { + Exchange exchange = new DefaultExchange(context); + exchange.getIn().setHeader(DnsConstants.DNS_NAME, "camel.apache.org"); + + Record question = producer.createQuery(exchange).getQuestion(); + + assertEquals("camel.apache.org.", question.getName().toString()); + assertEquals(Type.A, question.getType()); + assertEquals(DClass.IN, question.getDClass()); + } + + @Test + public void typeAndClassFromHeaders() throws Exception { + Exchange exchange = new DefaultExchange(context); + exchange.getIn().setHeader(DnsConstants.DNS_NAME, "camel.apache.org"); + exchange.getIn().setHeader(DnsConstants.DNS_TYPE, "MX"); + exchange.getIn().setHeader(DnsConstants.DNS_CLASS, "CH"); + + Message query = producer.createQuery(exchange); + + assertEquals(Type.MX, query.getQuestion().getType()); + assertEquals(DClass.CH, query.getQuestion().getDClass()); + } + + @Test + public void missingNameFails() { + Exchange exchange = new DefaultExchange(context); + exchange.getIn().setHeader(DnsConstants.DNS_TYPE, "A"); + + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> producer.createQuery(exchange)); + assertTrue(e.getMessage().contains(DnsConstants.DNS_NAME), e.getMessage()); + } +} diff --git a/components/camel-ftp-common/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java b/components/camel-ftp-common/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java index 678012a536c2a..cc9195ca19c4d 100644 --- a/components/camel-ftp-common/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java +++ b/components/camel-ftp-common/src/main/java/org/apache/camel/component/file/remote/RemoteFileEndpoint.java @@ -62,10 +62,11 @@ public abstract class RemoteFileEndpoint extends GenericFileEndpoint imple + "to update file information or not. This can be used to speed up the process if the FTP server has a lot " + "of files.") private boolean fastExistsCheck; - @UriParam(label = "consumer,advanced", description = "Whether the FTP consumer should download the file. If this " - + "option is set to false, then the message body will be null, but the consumer will still trigger a Camel " - + "Exchange that has details about the file such as file name, file size, etc. It's just that the file will " - + "not be downloaded.") + @UriParam(label = "consumer,advanced", defaultValue = "true", + description = "Whether the FTP consumer should download the file. If this " + + "option is set to false, then the message body will be null, but the consumer will still trigger a Camel " + + "Exchange that has details about the file such as file name, file size, etc. It's just that the file will " + + "not be downloaded.") private boolean download = true; protected RemoteFileEndpoint() { diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json index 8d0a3be6738aa..691f642b84592 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftp.json @@ -82,7 +82,7 @@ "sendEmptyMessageWhenIdle": { "index": 23, "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." }, "streamDownload": { "index": 24, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 25, "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." }, - "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 27, "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": 28, "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." }, "handleDirectoryParserAbsoluteResult": { "index": 29, "kind": "parameter", "displayName": "Handle Directory Parser Absolute Result", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpConfiguration", "configurationField": "configuration", "description": "Allows you to set how the consumer will handle subfolders and files in the path if the directory parser results in with absolute paths. The reason for this is that some FTP servers may return file names with absolute paths, and if so, then the FTP component needs to handle this by converting the returned path into a relative path." }, diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json index ba1fade434f6b..1d2a9a2cc22ad 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/ftps.json @@ -83,7 +83,7 @@ "sendEmptyMessageWhenIdle": { "index": 23, "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." }, "streamDownload": { "index": 24, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 25, "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." }, - "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 26, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 27, "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": 28, "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." }, "handleDirectoryParserAbsoluteResult": { "index": 29, "kind": "parameter", "displayName": "Handle Directory Parser Absolute Result", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.FtpsConfiguration", "configurationField": "configuration", "description": "Allows you to set how the consumer will handle subfolders and files in the path if the directory parser results in with absolute paths. The reason for this is that some FTP servers may return file names with absolute paths, and if so, then the FTP component needs to handle this by converting the returned path into a relative path." }, diff --git a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json index a0471a76cb2f0..aff91a1d61774 100644 --- a/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json +++ b/components/camel-ftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/sftp.json @@ -84,7 +84,7 @@ "sendEmptyMessageWhenIdle": { "index": 20, "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." }, "streamDownload": { "index": 21, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 22, "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." }, - "download": { "index": 23, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 23, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 24, "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": 25, "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." }, "ignoreFileNotFoundOrPermissionError": { "index": 26, "kind": "parameter", "displayName": "Ignore File Not Found Or Permission Error", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.SftpConfiguration", "configurationField": "configuration", "description": "Whether to ignore when (trying to list files in directories or when downloading a file), which does not exist or due to permission error. By default when a directory or file does not exist or insufficient permission, then an exception is thrown. Setting this option to true allows to ignore that instead." }, diff --git a/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json b/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json index e956fb8a2d632..de592a3659697 100644 --- a/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json +++ b/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json @@ -39,9 +39,10 @@ "CamelGitHubIssueTitle": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The title of the issue", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_ISSUE_TITLE" }, "CamelGitHubCommitAuthor": { "index": 4, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit author", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_AUTHOR" }, "CamelGitHubCommitCommitter": { "index": 5, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The committer name", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_COMMITTER" }, - "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit sha", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" }, - "CamelGitHubCommitUrl": { "index": 7, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit URL", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" }, - "CamelGitHubEventPayload": { "index": 8, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The event payload", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" } + "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit sha. The getCommitFile producer reads the file at this commit (a commit id of 40 hexadecimal characters).", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" }, + "CamelGitHubFilePath": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The path of the file in the repository for the getCommitFile producer. If not set, the body is the path.", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_FILE_PATH" }, + "CamelGitHubCommitUrl": { "index": 8, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The commit URL", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" }, + "CamelGitHubEventPayload": { "index": 9, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The event payload", "constantName": "org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" } }, "properties": { "type": { "index": 0, "kind": "path", "displayName": "Type", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.github2.GitHub2Type", "enum": [ "CLOSEPULLREQUEST", "PULLREQUESTCOMMENT", "COMMIT", "PULLREQUEST", "TAG", "PULLREQUESTSTATE", "PULLREQUESTFILES", "GETCOMMITFILE", "CREATEISSUE", "EVENT" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "What git operation to execute" }, @@ -56,7 +57,7 @@ "exceptionHandler": { "index": 9, "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": 10, "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": 11, "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." }, - "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use the given encoding when getting a git commit file" }, + "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The charset of the content of the file for the getCommitFile producer (the default charset of the JVM if not set)" }, "state": { "index": 13, "kind": "parameter", "displayName": "State", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "error", "failure", "pending", "success" ], "deprecated": false, "autowired": false, "secret": false, "description": "To set git commit status state" }, "targetUrl": { "index": 14, "kind": "parameter", "displayName": "Target Url", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To set git commit status target url" }, "lazyStartProducer": { "index": 15, "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." }, diff --git a/components/camel-github2/src/main/docs/github2-component.adoc b/components/camel-github2/src/main/docs/github2-component.adoc index 446143a8382c9..b2d070e78eac2 100644 --- a/components/camel-github2/src/main/docs/github2-component.adoc +++ b/components/camel-github2/src/main/docs/github2-component.adoc @@ -153,9 +153,9 @@ Sets the commit status on a pull request. |pullRequestFiles |none | - `CamelGitHubPullRequest` (integer) (REQUIRED): Pull request number. Returns the list of files changed in a pull request. -|getCommitFile |none | - `CamelGitHubCommitSha` (String) (REQUIRED): Commit SHA. -- `CamelGitHubFilePath` (String) (REQUIRED): File path. -Returns the content of a file at a specific commit. +|getCommitFile |String (the path of the file, if the `CamelGitHubFilePath` header is not set) | - `CamelGitHubCommitSha` (String) (REQUIRED): The commit id (40 hexadecimal characters; a branch or tag name is not accepted). +- `CamelGitHubFilePath` (String): The path of the file in the repository, such as `docs/README.md`. If not set, the body is the path. +Returns the content of the file in the tree of that commit, as a `String` (decoded with the `encoding` option, or the default charset of the JVM). |======================================================================= === Example: Consuming commits diff --git a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java index 140b48118a239..7560874cd6556 100644 --- a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java +++ b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java @@ -35,8 +35,15 @@ public interface GitHub2Constants { String GITHUB_COMMIT_AUTHOR = "CamelGitHubCommitAuthor"; @Metadata(label = "consumer", description = "The committer name", javaType = "String") String GITHUB_COMMIT_COMMITTER = "CamelGitHubCommitCommitter"; - @Metadata(label = "consumer", description = "The commit sha", javaType = "String") + @Metadata(description = "The commit sha. The getCommitFile producer reads the file at this commit (a commit id of" + + " 40 hexadecimal characters).", + javaType = "String") String GITHUB_COMMIT_SHA = "CamelGitHubCommitSha"; + @Metadata(label = "producer", + description = "The path of the file in the repository for the getCommitFile producer. If not set, the body" + + " is the path.", + javaType = "String") + String GITHUB_FILE_PATH = "CamelGitHubFilePath"; @Metadata(label = "consumer", description = "The commit URL", javaType = "String") String GITHUB_COMMIT_URL = "CamelGitHubCommitUrl"; @Metadata(label = "consumer", description = "The event payload", diff --git a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java index 23d56fc73413f..452eb653a0a1d 100644 --- a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java +++ b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java @@ -252,7 +252,7 @@ public String getEncoding() { } /** - * To use the given encoding when getting a git commit file + * The charset of the content of the file for the getCommitFile producer (the default charset of the JVM if not set) */ public void setEncoding(String encoding) { this.encoding = encoding; diff --git a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java index 6de27c410dadb..57563852708af 100644 --- a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java +++ b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java @@ -16,22 +16,28 @@ */ package org.apache.camel.component.github2.producer; +import java.io.IOException; +import java.io.InputStream; import java.nio.charset.Charset; import java.util.Base64; +import java.util.regex.Pattern; import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.component.github2.GitHub2Constants; import org.apache.camel.component.github2.GitHub2Endpoint; import org.apache.camel.util.ObjectHelper; -import org.kohsuke.github.GHBlob; -import org.kohsuke.github.GHCommit; +import org.kohsuke.github.GHContent; /** - * Producer endpoint that gets a file from a commit. The endpoint requires the "CamelGitHubCommitSha" header, - * identifying the commit SHA. The message body should contain the file path within the repository. + * Producer endpoint that gets the content of a file at a commit. The "CamelGitHubCommitSha" header is the commit id (40 + * hexadecimal characters), and the "CamelGitHubFilePath" header, or else the message body, is the path of the file in + * the repository. The file is read from the tree of that commit, and the body is set to its content as text. */ public class GetCommitFileProducer extends AbstractGitHub2Producer { + private static final Pattern COMMIT_ID = Pattern.compile("[0-9a-fA-F]{40}"); + private final String encoding; public GetCommitFileProducer(GitHub2Endpoint endpoint) { @@ -43,35 +49,58 @@ public GetCommitFileProducer(GitHub2Endpoint endpoint) { public void process(Exchange exchange) throws Exception { String sha = exchange.getIn().getHeader(GitHub2Constants.GITHUB_COMMIT_SHA, String.class); if (ObjectHelper.isEmpty(sha)) { - sha = exchange.getIn().getBody(String.class); + throw new IllegalArgumentException( + "The getCommitFile operation needs the " + GitHub2Constants.GITHUB_COMMIT_SHA + + " header with the commit id"); } - - if (ObjectHelper.isEmpty(sha)) { - throw new IllegalArgumentException("Commit SHA must be specified"); + if (!COMMIT_ID.matcher(sha).matches()) { + throw new IllegalArgumentException( + "The " + GitHub2Constants.GITHUB_COMMIT_SHA + + " header must be a commit id of 40 hexadecimal characters, but was: " + + sha); } - GHCommit commit = getRepository().getCommit(sha); - GHBlob blob = null; + String path = exchange.getIn().getHeader(GitHub2Constants.GITHUB_FILE_PATH, String.class); + if (ObjectHelper.isEmpty(path)) { + path = exchange.getIn().getBody(String.class); + } + if (ObjectHelper.isEmpty(path)) { + throw new InvalidPayloadException( + exchange, String.class, exchange.getIn(), + "set the " + GitHub2Constants.GITHUB_FILE_PATH + " header or the body to the path of the file"); + } - // Get the tree and find the file - // For simplicity, we get the blob SHA from the exchange body if it's a file path - String filePath = exchange.getIn().getBody(String.class); - if (filePath != null && !filePath.equals(sha)) { - // Try to get the file content from the commit - blob = getRepository().getBlob(sha); - } else { - blob = getRepository().getBlob(sha); + for (String segment : path.split("/")) { + if (".".equals(segment) || "..".equals(segment)) { + throw new IllegalArgumentException( + "The path of the file must be a path in the tree of the commit, without . or .. segments, but was: " + + path); + } } - String content = null; - if (blob != null) { - String encodedContent = blob.getContent(); - Charset charset = encoding != null ? Charset.forName(encoding) : Charset.defaultCharset(); - content = new String(Base64.getDecoder().decode(encodedContent), charset); + // the file at its path in the tree of the commit + GHContent file = getRepository().getFileContent(path, sha); + if (!file.isFile()) { + throw new IllegalArgumentException( + "The path " + path + " at commit " + sha + " is not a file but a " + file.getType()); } + Charset charset = encoding != null ? Charset.forName(encoding) : Charset.defaultCharset(); + String content = new String(readContent(file), charset); + // copy the header of in message to the out message exchange.getMessage().copyFrom(exchange.getIn()); exchange.getMessage().setBody(content); } + + private byte[] readContent(GHContent file) throws IOException { + if ("base64".equals(file.getEncoding())) { + // GitHub wraps the lines of the base64 content + return Base64.getMimeDecoder().decode(file.getEncodedContent()); + } + // the contents API gives no content for a file of more than 1 MB: read its blob + try (InputStream is = getRepository().getBlob(file.getSha()).read()) { + return is.readAllBytes(); + } + } } diff --git a/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java b/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java new file mode 100644 index 0000000000000..30c5145286443 --- /dev/null +++ b/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java @@ -0,0 +1,161 @@ +/* + * 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.github2.producer; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import org.apache.camel.BindToRegistry; +import org.apache.camel.CamelExecutionException; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.github2.GitHub2Constants; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.junit.jupiter.api.Test; +import org.kohsuke.github.GHBlob; +import org.kohsuke.github.GHContent; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GitHub; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * The getCommitFile producer reads the file at its path in the tree of the commit. + */ +class GetCommitFileProducerTest extends CamelTestSupport { + + private static final String SHA = "0123456789abcdef0123456789abcdef01234567"; + private static final String PATH = "docs/readme.md"; + // longer than the 76 characters of a line of MIME base64, so the encoded content has line breaks + private static final String TEXT = "The Open Source Integration Framework, with more than 300 connectors: æøå\n"; + + private final GHRepository repository = mock(GHRepository.class); + + @BindToRegistry(GitHub2Constants.GITHUB_CLIENT) + private final GitHub github = createGitHub(); + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:start") + .to("github2:getCommitFile?repoOwner=acme&repoName=demo&oauthToken=not-used&encoding=UTF-8"); + } + }; + } + + @Test + void fileFromPathHeader() throws Exception { + GHContent content + = fileContent("base64", Base64.getMimeEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8))); + when(repository.getFileContent(PATH, SHA)).thenReturn(content); + + String body = template.requestBodyAndHeaders("direct:start", "ignored", + Map.of(GitHub2Constants.GITHUB_COMMIT_SHA, SHA, GitHub2Constants.GITHUB_FILE_PATH, PATH), String.class); + + assertEquals(TEXT, body); + } + + @Test + void fileFromPathInBody() throws Exception { + GHContent content + = fileContent("base64", Base64.getMimeEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8))); + when(repository.getFileContent(PATH, SHA)).thenReturn(content); + + String body = template.requestBodyAndHeader("direct:start", PATH, GitHub2Constants.GITHUB_COMMIT_SHA, SHA, + String.class); + + assertEquals(TEXT, body); + } + + @Test + void largeFileIsReadAsBlob() throws Exception { + // the contents API gives no content for a file of more than 1 MB + GHContent content = fileContent("none", ""); + when(content.getSha()).thenReturn("fedcba9876543210fedcba9876543210fedcba98"); + when(repository.getFileContent(PATH, SHA)).thenReturn(content); + GHBlob blob = mock(GHBlob.class); + when(blob.read()).thenReturn(new ByteArrayInputStream(TEXT.getBytes(StandardCharsets.UTF_8))); + when(repository.getBlob("fedcba9876543210fedcba9876543210fedcba98")).thenReturn(blob); + + String body = template.requestBodyAndHeader("direct:start", PATH, GitHub2Constants.GITHUB_COMMIT_SHA, SHA, + String.class); + + assertEquals(TEXT, body); + } + + @Test + void shaMustBeCommitId() throws Exception { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBodyAndHeader("direct:start", PATH, GitHub2Constants.GITHUB_COMMIT_SHA, "main")); + + IllegalArgumentException cause = assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertTrue(cause.getMessage().contains("40 hexadecimal characters"), cause.getMessage()); + verify(repository, never()).getFileContent(anyString(), anyString()); + } + + @Test + void pathMustBeInTreeOfCommit() throws Exception { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBodyAndHeader("direct:start", "docs/../readme.md", GitHub2Constants.GITHUB_COMMIT_SHA, + SHA)); + + IllegalArgumentException cause = assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertTrue(cause.getMessage().contains("path in the tree of the commit"), cause.getMessage()); + verify(repository, never()).getFileContent(anyString(), anyString()); + } + + @Test + void shaHeaderIsRequired() throws Exception { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBody("direct:start", SHA)); + + IllegalArgumentException cause = assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertTrue(cause.getMessage().contains(GitHub2Constants.GITHUB_COMMIT_SHA), cause.getMessage()); + verify(repository, never()).getFileContent(anyString(), anyString()); + } + + private static GHContent fileContent(String encoding, String encodedContent) throws IOException { + GHContent content = mock(GHContent.class); + when(content.isFile()).thenReturn(true); + when(content.getType()).thenReturn("file"); + when(content.getEncoding()).thenReturn(encoding); + when(content.getEncodedContent()).thenReturn(encodedContent); + return content; + } + + private GitHub createGitHub() { + GitHub client = mock(GitHub.class); + try { + when(client.getRepository("acme/demo")).thenReturn(repository); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return client; + } +} diff --git a/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json b/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json index 73ef1d1a19f3c..c1a6f8bca5d22 100644 --- a/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json +++ b/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json @@ -37,10 +37,10 @@ "proxyHost": { "index": 3, "kind": "parameter", "displayName": "Proxy Host", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The proxy host in the format hostname:port." }, "query": { "index": 4, "kind": "parameter", "displayName": "Query", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The query text." }, "queryFile": { "index": 5, "kind": "parameter", "displayName": "Query File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The query file name located in the classpath (or use file: to load from file system)." }, - "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the GraphQL query." }, + "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the GraphQL query. The header is not sent as an HTTP header." }, "throwExceptionOnFailure": { "index": 7, "kind": "parameter", "displayName": "Throw Exception On Failure", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Option to disable throwing the HttpOperationFailedException in case of failed responses from the remote server. This allows you to get all responses regardless of the HTTP status code." }, "variables": { "index": 8, "kind": "parameter", "displayName": "Variables", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated": false, "autowired": false, "secret": false, "description": "The JsonObject instance containing the operation variables." }, - "variablesHeader": { "index": 9, "kind": "parameter", "displayName": "Variables Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing a JsonObject instance containing the operation variables." }, + "variablesHeader": { "index": 9, "kind": "parameter", "displayName": "Variables Header", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a header containing the operation variables, as a JsonObject or a Map. The header is not sent as an HTTP header." }, "lazyStartProducer": { "index": 10, "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." }, "httpClient": { "index": 11, "kind": "parameter", "displayName": "Http Client", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.hc.client5.http.classic.HttpClient", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom pre-existing Http Client. Beware that when using this, then other configurations such as proxy, access token, is not applied and all this must be pre-configured on the Http Client." }, "accessToken": { "index": 12, "kind": "parameter", "displayName": "Access Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "description": "The access token sent in the Authorization header." }, diff --git a/components/camel-graphql/src/main/docs/graphql-component.adoc b/components/camel-graphql/src/main/docs/graphql-component.adoc index 3ec1d0083d546..f53906a8cfc8a 100644 --- a/components/camel-graphql/src/main/docs/graphql-component.adoc +++ b/components/camel-graphql/src/main/docs/graphql-component.adoc @@ -12,7 +12,7 @@ *{component-header}* -The GraphQL component is a GraphQL client that communicates over HTTP and supports queries and mutations, but not subscriptions. It uses the https://hc.apache.org/httpcomponents-client-4.5.x/index.html[Apache HttpClient] library. +The GraphQL component is a GraphQL client that communicates over HTTP and supports queries and mutations, but not subscriptions. It sends the requests with the xref:http-component.adoc[HTTP] component, which uses the Apache HttpClient 5 library. Maven users will need to add the following dependency to their `pom.xml` for this component: @@ -37,9 +37,21 @@ include::partial$component-endpoint-headers.adoc[] == Message Body -If the `variables` and `variablesHeader` parameters are not set and the IN body is a JsonObject instance, Camel will use it for the operation's variables. If the `query` and `queryFile` parameters are not set and the IN body is a String, Camel will use it as the query. Camel will store the GraphQL response from the external server on the OUT message body. -All headers from the IN message will be copied to the OUT message, so headers are preserved during routing. -Additionally, Camel will add the HTTP response headers as well to the OUT message headers. +If the `query`, `queryFile` and `queryHeader` parameters are not set, the body is the query: it must be text, bytes, a stream or a file, and another body (such as a `Map`) fails with an `InvalidPayloadException`. +Otherwise, if the `variables` and `variablesHeader` parameters are not set and the body is a `Map` (such as a `JsonObject`), Camel will use it for the operation's variables. +Camel will store the GraphQL response from the external server on the message body. + +== HTTP Headers + +The headers of the message are sent as HTTP request headers (filtered by the `headerFilterStrategy`), except: + +* the headers named by the `queryHeader` and `variablesHeader` parameters, as the query and the variables are sent in the JSON body of the request; +* a `Content-Encoding` header, as the request is not compressed. + +The producer always sends the request with `Content-Type: application/json`, `Accept: application/json` and `Accept-Encoding: gzip`, whatever the headers of the message are. + +After the call, the message keeps its own headers, and Camel adds the HTTP response headers (such as `CamelHttpResponseCode` and the `Content-Type` of the response). +The headers the producer set for the request (`CamelHttpMethod`, `Accept` and `Accept-Encoding`) do not stay on the message: a header of the message with the same name keeps its value. == Examples diff --git a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java index 1a0fb874023c1..eb0156708c59a 100644 --- a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java +++ b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java @@ -358,7 +358,8 @@ public String getVariablesHeader() { } /** - * The name of a header containing a JsonObject instance containing the operation variables. + * The name of a header containing the operation variables, as a JsonObject or a Map. The header is not sent as an + * HTTP header. */ public void setVariablesHeader(String variablesHeader) { this.variablesHeader = variablesHeader; @@ -369,7 +370,7 @@ public String getQueryHeader() { } /** - * The name of a header containing the GraphQL query. + * The name of a header containing the GraphQL query. The header is not sent as an HTTP header. */ public void setQueryHeader(String queryHeader) { this.queryHeader = queryHeader; diff --git a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java index 1dff0d91e6880..35ee762d00dfa 100644 --- a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java +++ b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java @@ -16,11 +16,19 @@ */ package org.apache.camel.component.graphql; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.nio.file.Path; +import java.util.Map; + import org.apache.camel.AsyncCallback; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.InvalidPayloadException; +import org.apache.camel.Message; import org.apache.camel.Producer; +import org.apache.camel.WrappedFile; import org.apache.camel.component.http.HttpConstants; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.Registry; @@ -32,6 +40,9 @@ public class GraphqlProducer extends DefaultAsyncProducer { + private static final String ACCEPT = "Accept"; + private static final String ACCEPT_ENCODING = "Accept-Encoding"; + private Producer http; public GraphqlProducer(GraphqlEndpoint endpoint) { @@ -91,17 +102,28 @@ public GraphqlEndpoint getEndpoint() { public boolean process(Exchange exchange, AsyncCallback callback) { Exchange httpExchange = getEndpoint().createExchange(); + Message request = httpExchange.getIn(); + MessageHelper.copyHeaders(exchange.getMessage(), request, true); + // the query and the variables are sent in the JSON body, not as HTTP headers + if (getEndpoint().getQueryHeader() != null) { + request.removeHeader(getEndpoint().getQueryHeader()); + } + if (getEndpoint().getVariablesHeader() != null) { + request.removeHeader(getEndpoint().getVariablesHeader()); + } + // camel-http would compress the JSON body for a Content-Encoding header of the message + request.removeHeader(Exchange.CONTENT_ENCODING); + // the headers of the producer win over the headers of the message // not read by camel-http (skipControlHeaders=true): the httpMethod option of the internal endpoint decides - httpExchange.getIn().setHeader(HttpConstants.HTTP_METHOD, "post"); - httpExchange.getIn().setHeader("Content-Type", "application/json"); - httpExchange.getIn().setHeader("Accept", "application/json"); - httpExchange.getIn().setHeader("Accept-Encoding", "gzip"); - MessageHelper.copyHeaders(exchange.getMessage(), httpExchange.getIn(), true); + request.setHeader(HttpConstants.HTTP_METHOD, "post"); + request.setHeader(Exchange.CONTENT_TYPE, "application/json"); + request.setHeader(ACCEPT, "application/json"); + request.setHeader(ACCEPT_ENCODING, "gzip"); try { String requestBody = buildRequestBody(getQuery(exchange), getEndpoint().getOperationName(), getVariables(exchange)); - httpExchange.getIn().setBody(requestBody); + request.setBody(requestBody); try { http.process(httpExchange); String data = httpExchange.getMessage().getBody(String.class); @@ -109,7 +131,17 @@ public boolean process(Exchange exchange, AsyncCallback callback) { } catch (Exception e) { exchange.setException(e); } - MessageHelper.copyHeaders(httpExchange.getMessage(), exchange.getIn(), true); + Message response = httpExchange.getMessage(); + // the headers the producer set for the request do not stay on the message (a header of the message + // with the same name keeps its value); the Content-Type of the response of the server is kept + response.removeHeader(HttpConstants.HTTP_METHOD); + response.removeHeader(ACCEPT); + response.removeHeader(ACCEPT_ENCODING); + if (response == request) { + // no response message (a failed call): the Content-Type is the one of the request + response.removeHeader(Exchange.CONTENT_TYPE); + } + MessageHelper.copyHeaders(response, exchange.getIn(), true); } catch (Exception e) { exchange.setException(e); } @@ -133,20 +165,43 @@ private String getQuery(Exchange exchange) throws InvalidPayloadException { } else if (getEndpoint().getQueryHeader() != null) { query = exchange.getIn().getHeader(getEndpoint().getQueryHeader(), String.class); } else { + Object body = exchange.getIn().getBody(); + if (body != null && !isQueryBody(body)) { + throw new InvalidPayloadException( + exchange, String.class, exchange.getIn(), + "the body is the GraphQL query when the query, queryFile and queryHeader options are not set," + + " and must be text, bytes, a stream or a file"); + } query = exchange.getIn().getMandatoryBody(String.class); } return query; } + private static boolean isQueryBody(Object body) { + return body instanceof CharSequence || body instanceof byte[] || body instanceof InputStream + || body instanceof Reader || body instanceof File || body instanceof Path + || body instanceof WrappedFile; + } + private JsonObject getVariables(Exchange exchange) { JsonObject variables = null; if (getEndpoint().getVariables() != null) { variables = getEndpoint().getVariables(); } else if (getEndpoint().getVariablesHeader() != null) { - variables = exchange.getIn().getHeader(getEndpoint().getVariablesHeader(), JsonObject.class); - } else if (exchange.getIn().getBody() instanceof JsonObject) { - variables = exchange.getIn().getBody(JsonObject.class); + Object value = exchange.getIn().getHeader(getEndpoint().getVariablesHeader()); + if (value instanceof Map map) { + variables = toJsonObject(map); + } else { + variables = exchange.getIn().getHeader(getEndpoint().getVariablesHeader(), JsonObject.class); + } + } else if (exchange.getIn().getBody() instanceof Map map) { + variables = toJsonObject(map); } return variables; } + + @SuppressWarnings("unchecked") + private static JsonObject toJsonObject(Map map) { + return map instanceof JsonObject json ? json : new JsonObject((Map) map); + } } diff --git a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProducerHeadersTest.java b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProducerHeadersTest.java new file mode 100644 index 0000000000000..f831c2c0490ab --- /dev/null +++ b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlProducerHeadersTest.java @@ -0,0 +1,178 @@ +/* + * 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.graphql; + +import java.io.IOException; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.Message; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.graphql.server.GraphqlServer; +import org.apache.camel.component.http.HttpConstants; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.json.JsonObject; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The headers of the producer for the HTTP request, and the headers of the message after the call. + */ +public class GraphqlProducerHeadersTest extends CamelTestSupport { + + private static final String BOOK_BY_ID_QUERY + = "query BookById($id: ID) { bookById(id: $id) { id name author { name } } }"; + + private static String booksQueryResult; + private static String bookByIdQueryResult; + private static GraphqlServer server; + + @BeforeAll + public static void setUpBeforeClass() throws Exception { + booksQueryResult = readJsonFile("booksQueryResult.json"); + bookByIdQueryResult = readJsonFile("bookByIdQueryResult.json"); + + server = new GraphqlServer(); + server.start(); + } + + private static String readJsonFile(String name) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(objectMapper.readValue( + IOHelper.loadText(ObjectHelper.loadResourceAsStream(name)), Object.class)); + } + + @AfterAll + public static void tearDownAfterClass() { + server.shutdown(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:static") + .to("graphql://http://localhost:" + server.getPort() + "/graphql?query={books{id name}}"); + from("direct:headers") + .to("graphql://http://localhost:" + server.getPort() + + "/graphql?queryHeader=myQuery&variablesHeader=myVariables"); + from("direct:body") + .to("graphql://http://localhost:" + server.getPort() + "/graphql"); + from("direct:variablesBody") + .to("graphql://http://localhost:" + server.getPort() + + "/graphql?queryFile=multipleQueries.graphql&operationName=BookById"); + } + }; + } + + @Test + public void requestHeadersOfProducerWinOverMessageHeaders() { + Exchange exchange = template.request("direct:static", e -> { + e.getIn().setHeader("Content-Type", "text/plain"); + e.getIn().setHeader("Accept", "text/html"); + e.getIn().setHeader("Content-Encoding", "gzip"); + }); + + assertNull(exchange.getException()); + assertEquals(booksQueryResult, exchange.getMessage().getBody(String.class)); + + Map sent = server.getLastRequestHeaders(); + assertEquals("application/json", sent.get("Content-Type")); + assertEquals("application/json", sent.get("Accept")); + assertFalse(sent.containsKey("Content-Encoding")); + } + + @Test + public void queryAndVariablesHeadersAreNotSentAsHttpHeaders() { + JsonObject variables = new JsonObject(); + variables.put("id", "book-1"); + + Exchange exchange = template.request("direct:headers", e -> { + e.getIn().setHeader("myQuery", BOOK_BY_ID_QUERY); + e.getIn().setHeader("myVariables", variables); + }); + + assertNull(exchange.getException()); + assertEquals(bookByIdQueryResult, exchange.getMessage().getBody(String.class)); + + Map sent = server.getLastRequestHeaders(); + assertFalse(sent.containsKey("myQuery")); + assertFalse(sent.containsKey("myVariables")); + + // the headers stay on the message + assertEquals(BOOK_BY_ID_QUERY, exchange.getMessage().getHeader("myQuery")); + assertNotNull(exchange.getMessage().getHeader("myVariables")); + } + + @Test + public void requestHeadersOfProducerDoNotStayOnMessage() { + Exchange exchange = template.request("direct:static", e -> e.getIn().setBody("")); + + assertNull(exchange.getException()); + Message message = exchange.getMessage(); + assertNull(message.getHeader(HttpConstants.HTTP_METHOD)); + assertNull(message.getHeader("Accept")); + assertNull(message.getHeader("Accept-Encoding")); + // the Content-Type of the response of the server + assertEquals("application/json; charset=UTF-8", message.getHeader("Content-Type")); + assertEquals(200, message.getHeader(Exchange.HTTP_RESPONSE_CODE)); + } + + @Test + public void requestHeadersOfMessageAreKeptAfterCall() { + Exchange exchange = template.request("direct:static", e -> { + e.getIn().setHeader("Accept", "text/html"); + e.getIn().setHeader("Accept-Encoding", "identity"); + }); + + assertNull(exchange.getException()); + Message message = exchange.getMessage(); + assertEquals("text/html", message.getHeader("Accept")); + assertEquals("identity", message.getHeader("Accept-Encoding")); + assertNull(message.getHeader(HttpConstants.HTTP_METHOD)); + } + + @Test + public void mapBodyIsNotSentAsQuery() { + Exchange exchange = template.request("direct:body", e -> e.getIn().setBody(Map.of("id", "1"))); + + InvalidPayloadException e = assertInstanceOf(InvalidPayloadException.class, exchange.getException()); + assertTrue(e.getMessage().contains("queryHeader"), e.getMessage()); + } + + @Test + public void mapBodyIsUsedAsVariables() { + Exchange exchange = template.request("direct:variablesBody", e -> e.getIn().setBody(Map.of("id", "book-1"))); + + assertNull(exchange.getException()); + assertEquals(bookByIdQueryResult, exchange.getMessage().getBody(String.class)); + } + +} diff --git a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java index 65f71c622482d..ebc0444a4064a 100644 --- a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java +++ b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.Map; +import java.util.TreeMap; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -40,6 +41,7 @@ public class GraphqlServer { private final GraphQL graphql; private final HttpServer server; + private volatile Map lastRequestHeaders = Map.of(); public GraphqlServer() { this.graphql = GraphqlFactory.newGraphQL(); @@ -61,6 +63,13 @@ public int getPort() { return server.getLocalPort(); } + /** + * The headers of the last request received by the server, with case-insensitive names. + */ + public Map getLastRequestHeaders() { + return lastRequestHeaders; + } + class GraphqlHandler implements HttpRequestHandler { private final ObjectMapper objectMapper = new ObjectMapper(); @@ -70,6 +79,12 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt throws HttpException, IOException { HttpEntity entity = request.getEntity(); + Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (Header header : request.getHeaders()) { + headers.put(header.getName(), header.getValue()); + } + lastRequestHeaders = headers; + Header h = request.getHeader("kaboom"); if (h != null) { response.setCode(500); diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/generated/resources/META-INF/org/apache/camel/component/infinispan/embedded/infinispan-embedded.json b/components/camel-infinispan/camel-infinispan-embedded/src/generated/resources/META-INF/org/apache/camel/component/infinispan/embedded/infinispan-embedded.json index b884448a4a183..31fe69ab05719 100644 --- a/components/camel-infinispan/camel-infinispan-embedded/src/generated/resources/META-INF/org/apache/camel/component/infinispan/embedded/infinispan-embedded.json +++ b/components/camel-infinispan/camel-infinispan-embedded/src/generated/resources/META-INF/org/apache/camel/component/infinispan/embedded/infinispan-embedded.json @@ -38,10 +38,10 @@ "operation": { "index": 11, "kind": "property", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "org.apache.camel.component.infinispan.InfinispanOperation", "enum": [ "PUT", "PUTASYNC", "PUTALL", "PUTALLASYNC", "PUTIFABSENT", "PUTIFABSENTASYNC", "GET", "GETORDEFAULT", "CONTAINSKEY", "CONTAINSVALUE", "REMOVE", "REMOVEASYNC", "REPLACE", "REPLACEASYNC", "SIZE", "CLEAR", "CLEARASYNC", "QUERY", "STATS", "COMPUTE", "COMPUTEASYNC" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "PUT", "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The operation to perform" }, "value": { "index": 12, "kind": "property", "displayName": "Value", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific value for producer operations" }, "autowiredEnabled": { "index": 13, "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." }, - "cacheContainer": { "index": 14, "kind": "property", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect" }, - "cacheContainerConfiguration": { "index": 15, "kind": "property", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined." }, + "cacheContainer": { "index": 14, "kind": "property", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, + "cacheContainerConfiguration": { "index": 15, "kind": "property", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined. Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "configurationUri": { "index": 16, "kind": "property", "displayName": "Configuration Uri", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "An implementation specific URI for the CacheManager" }, - "flags": { "index": 17, "kind": "property", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation" }, + "flags": { "index": 17, "kind": "property", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "remappingFunction": { "index": 18, "kind": "property", "displayName": "Remapping Function", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.function.BiFunction", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific remappingFunction to use in a compute operation." }, "resultHeader": { "index": 19, "kind": "property", "displayName": "Result Header", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Store the operation result in a header instead of the message body. By default, resultHeader == null and the query result is stored in the message body, any existing content in the message body is discarded. If resultHeader is set, the value is used as the name of the header to store the query result and the original message body is preserved. This value can be overridden by an in message header named: CamelInfinispanOperationResultHeader" } }, @@ -85,10 +85,10 @@ "operation": { "index": 12, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "producer", "required": false, "type": "enum", "javaType": "org.apache.camel.component.infinispan.InfinispanOperation", "enum": [ "PUT", "PUTASYNC", "PUTALL", "PUTALLASYNC", "PUTIFABSENT", "PUTIFABSENTASYNC", "GET", "GETORDEFAULT", "CONTAINSKEY", "CONTAINSVALUE", "REMOVE", "REMOVEASYNC", "REPLACE", "REPLACEASYNC", "SIZE", "CLEAR", "CLEARASYNC", "QUERY", "STATS", "COMPUTE", "COMPUTEASYNC" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "PUT", "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The operation to perform" }, "value": { "index": 13, "kind": "parameter", "displayName": "Value", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.lang.Object", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific value for producer operations" }, "lazyStartProducer": { "index": 14, "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." }, - "cacheContainer": { "index": 15, "kind": "parameter", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect" }, - "cacheContainerConfiguration": { "index": 16, "kind": "parameter", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined." }, + "cacheContainer": { "index": 15, "kind": "parameter", "displayName": "Cache Container", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.manager.EmbeddedCacheManager", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Specifies the cache Container to connect Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, + "cacheContainerConfiguration": { "index": 16, "kind": "parameter", "displayName": "Cache Container Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.infinispan.configuration.cache.Configuration", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "The CacheContainer configuration. Used if the cacheContainer is not defined. Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "configurationUri": { "index": 17, "kind": "parameter", "displayName": "Configuration Uri", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportFileReference": true, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "An implementation specific URI for the CacheManager" }, - "flags": { "index": 18, "kind": "parameter", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation" }, + "flags": { "index": 18, "kind": "parameter", "displayName": "Flags", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an endpoint, this option is not used." }, "remappingFunction": { "index": 19, "kind": "parameter", "displayName": "Remapping Function", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.function.BiFunction", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Set a specific remappingFunction to use in a compute operation." }, "resultHeader": { "index": 20, "kind": "parameter", "displayName": "Result Header", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.infinispan.embedded.InfinispanEmbeddedConfiguration", "configurationField": "configuration", "description": "Store the operation result in a header instead of the message body. By default, resultHeader == null and the query result is stored in the message body, any existing content in the message body is discarded. If resultHeader is set, the value is used as the name of the header to store the query result and the original message body is preserved. This value can be overridden by an in message header named: CamelInfinispanOperationResultHeader" } } diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/main/docs/infinispan-embedded-component.adoc b/components/camel-infinispan/camel-infinispan-embedded/src/main/docs/infinispan-embedded-component.adoc index 3221910ea61bc..ebf9cd695d851 100644 --- a/components/camel-infinispan/camel-infinispan-embedded/src/main/docs/infinispan-embedded-component.adoc +++ b/components/camel-infinispan/camel-infinispan-embedded/src/main/docs/infinispan-embedded-component.adoc @@ -51,6 +51,21 @@ The consumer allows listening for events from local infinispan cache. If no cache configuration is provided, embedded cacheContainer is created directly in the component. +=== Options read on the component only + +All the endpoints of the component share one cache manager, which the component creates when it starts. So the +options that configure the cache manager are read on the component only: `configurationUri`, `cacheContainer`, +`cacheContainerConfiguration` and `flags`. Set on an endpoint, these options are not used. Configure them on the +component instead, for example in `application.properties`: + +[source,properties] +---- +camel.component.infinispan-embedded.configuration-uri = file:/etc/infinispan/infinispan.xml +---- + +The `configurationUri` is read with the resource loader of Camel, so it can be a name on the class path, a +`classpath:` or `file:` uri, or an absolute path, and it can use property placeholders. + // component options: START include::partial$component-configure-options.adoc[] include::partial$component-endpoint-options.adoc[] @@ -244,11 +259,11 @@ from("infinispan-embedded:source") from("direct:start") .setHeader(InfinispanConstants.OPERATION).constant(InfinispanOperation.PUT) // <1> .setHeader(InfinispanConstants.KEY).constant("123") // <2> - .to("infinispan-embedded:myCacheName?cacheContainer=#cacheContainer"); // <3> + .to("infinispan-embedded:myCacheName"); // <3> ---- <1> Set the operation to perform <2> Set the key used to identify the element in the cache -<3> Use the configured cache manager `cacheContainer` from the registry to put an element to the cache named `myCacheName` +<3> Put an element to the cache named `myCacheName` of the cache manager of the component -- + It is possible to configure the lifetime and/or the idle time before the entry expires and gets evicted from the cache, as example: @@ -281,7 +296,7 @@ from("direct:start") return qf.from(User.class).having("name").like("%abc%").build(); } }) - .to("infinispan-embedded:myCacheName?cacheContainer=#cacheManager") ; + .to("infinispan-embedded:myCacheName"); ---- + @@ -293,7 +308,7 @@ Java:: + [source,java] ---- -from("infinispan-embedded:myCacheName?cacheContainer=#cacheManager&customListener=#myCustomListener") +from("infinispan-embedded:myCacheName?customListener=#myCustomListener") .to("mock:result"); ---- @@ -302,7 +317,7 @@ XML:: [source,xml] ---- - + ---- @@ -315,7 +330,6 @@ YAML:: from: uri: infinispan-embedded:myCacheName parameters: - cacheContainer: "#cacheManager" customListener: "#myCustomListener" steps: - to: @@ -366,7 +380,7 @@ XML:: - + @@ -429,7 +443,7 @@ XML:: - + diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponent.java b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponent.java index 3bc6c54e895c1..74fb73b7e5a1e 100644 --- a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponent.java +++ b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponent.java @@ -53,7 +53,8 @@ protected Endpoint createEndpoint(String uri, String remaining, Map + * Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an + * endpoint, this option is not used. */ public EmbeddedCacheManager getCacheContainer() { return cacheContainer; @@ -62,6 +65,9 @@ public Configuration getCacheContainerConfiguration() { /** * The CacheContainer configuration. Used if the cacheContainer is not defined. + *

+ * Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an + * endpoint, this option is not used. */ public void setCacheContainerConfiguration(Configuration cacheContainerConfiguration) { this.cacheContainerConfiguration = cacheContainerConfiguration; @@ -126,6 +132,9 @@ public Flag[] getFlags() { /** * A comma separated list of org.infinispan.context.Flag to be applied by default on each cache invocation + *

+ * Read on the component only, as the cache manager is shared by all the endpoints of the component: set on an + * endpoint, this option is not used. */ public void setFlags(String flagsAsString) { String[] flagsArray = flagsAsString.split(","); diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedManager.java b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedManager.java index 6df87f4e5dcbc..b89bae420ea13 100644 --- a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedManager.java +++ b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedManager.java @@ -16,6 +16,9 @@ */ package org.apache.camel.component.infinispan.embedded; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.Set; import org.apache.camel.CamelContext; @@ -23,6 +26,7 @@ import org.apache.camel.component.infinispan.InfinispanConstants; import org.apache.camel.component.infinispan.InfinispanManager; import org.apache.camel.component.infinispan.InfinispanUtil; +import org.apache.camel.support.ResourceHelper; import org.apache.camel.support.service.ServiceSupport; import org.apache.camel.util.ObjectHelper; import org.infinispan.Cache; @@ -82,8 +86,9 @@ public void doStart() throws Exception { cacheContainer.defineConfiguration("default", containerConf); } else { if (ObjectHelper.isNotEmpty(configuration.getConfigurationUri())) { - cacheContainer = new DefaultCacheManager( - InfinispanUtil.openInputStream(camelContext, configuration.getConfigurationUri())); + try (InputStream is = openConfiguration(configuration.getConfigurationUri())) { + cacheContainer = new DefaultCacheManager(is); + } } else { ConfigurationBuilderHolder holder = new ConfigurationBuilderHolder(); holder.getGlobalConfigurationBuilder().defaultCacheName("default"); @@ -96,6 +101,22 @@ public void doStart() throws Exception { } } + private InputStream openConfiguration(String uri) throws Exception { + if (camelContext != null) { + uri = camelContext.resolvePropertyPlaceholders(uri); + if (!ResourceHelper.hasScheme(uri) && new File(uri).isAbsolute()) { + // an absolute path is a file, not a name on the class path + uri = "file:" + uri; + } + return ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, uri); + } + InputStream is = InfinispanUtil.openInputStream(null, uri); + if (is == null) { + throw new FileNotFoundException("Cannot find the configurationUri " + uri + " on the class path"); + } + return is; + } + @Override public void doStop() throws Exception { if (isManagedCacheContainer) { diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/cluster/InfinispanEmbeddedClusterView.java b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/cluster/InfinispanEmbeddedClusterView.java index fe6cd0b47352f..a0db91d60c352 100644 --- a/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/cluster/InfinispanEmbeddedClusterView.java +++ b/components/camel-infinispan/camel-infinispan-embedded/src/main/java/org/apache/camel/component/infinispan/embedded/cluster/InfinispanEmbeddedClusterView.java @@ -68,6 +68,7 @@ protected InfinispanEmbeddedClusterView( public void doStart() throws Exception { super.doStart(); + manager.setCamelContext(getCamelContext()); ServiceHelper.startService(manager); this.cache = manager.getCache(getNamespace(), Cache.class); diff --git a/components/camel-infinispan/camel-infinispan-embedded/src/test/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponentConfigurationUriTest.java b/components/camel-infinispan/camel-infinispan-embedded/src/test/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponentConfigurationUriTest.java new file mode 100644 index 0000000000000..0b6cc116fbc91 --- /dev/null +++ b/components/camel-infinispan/camel-infinispan-embedded/src/test/java/org/apache/camel/component/infinispan/embedded/InfinispanEmbeddedComponentConfigurationUriTest.java @@ -0,0 +1,92 @@ +/* + * 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.infinispan.embedded; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +import org.apache.camel.CamelContext; +import org.apache.camel.impl.DefaultCamelContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * The configurationUri of the component is read with the resource loader of Camel. + */ +public class InfinispanEmbeddedComponentConfigurationUriTest { + + private static final String CONFIG + = "org/apache/camel/component/infinispan/spring/SpringInfinispanEmbeddedIdempotentRepositoryConfiguration.xml"; + + @TempDir + Path tempDir; + + private Path configFile; + + @BeforeEach + void copyConfig() throws Exception { + configFile = tempDir.resolve("infinispan.xml"); + try (InputStream is = getClass().getClassLoader().getResourceAsStream(CONFIG)) { + Files.copy(is, configFile, StandardCopyOption.REPLACE_EXISTING); + } + } + + @Test + public void testClasspathName() throws Exception { + assertConfigurationLoaded(CONFIG); + } + + @Test + public void testClasspathScheme() throws Exception { + assertConfigurationLoaded("classpath:" + CONFIG); + } + + @Test + public void testFileScheme() throws Exception { + assertConfigurationLoaded("file:" + configFile.toAbsolutePath()); + } + + @Test + public void testAbsolutePath() throws Exception { + assertConfigurationLoaded(configFile.toAbsolutePath().toString()); + } + + @Test + public void testPlaceholder() throws Exception { + assertConfigurationLoaded("{{infinispan.config}}"); + } + + private void assertConfigurationLoaded(String configurationUri) throws Exception { + try (CamelContext context = new DefaultCamelContext()) { + context.getPropertiesComponent().addInitialProperty("infinispan.config", "file:" + configFile.toAbsolutePath()); + + InfinispanEmbeddedComponent component = new InfinispanEmbeddedComponent(); + component.getConfiguration().setConfigurationUri(configurationUri); + context.addComponent(InfinispanEmbeddedComponent.SCHEME, component); + context.start(); + + // the cache idempotent is only defined in the configuration file + assertNotNull(component.getManager().getCacheContainer().getCacheConfiguration("idempotent"), + "The configuration " + configurationUri + " is not loaded"); + } + } +} diff --git a/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json b/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json index 5109e64326886..039f9e6ad647f 100644 --- a/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json +++ b/components/camel-jdbc/src/generated/resources/META-INF/org/apache/camel/component/jdbc/jdbc.json @@ -31,7 +31,7 @@ }, "headers": { "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is an UPDATE, query the update count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" }, - "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT, query the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, + "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT and the outputType is SelectList, the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Set", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The column names from the ResultSet as a java.util.Set type.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" }, "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A java.util.Map which has the headers to be used if useHeadersAsParameters has been enabled.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" }, "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Set its value to true to retrieve generated keys", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" }, @@ -47,8 +47,8 @@ "parameters": { "index": 4, "kind": "parameter", "displayName": "Parameters", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "statement.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "Optional parameters to the java.sql.Statement. For example to set maxRows, fetchSize etc. This is a multi-value option with prefix: statement." }, "readSize": { "index": 5, "kind": "parameter", "displayName": "Read Size", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The default maximum number of rows that can be read by a polling query. The default value is 0." }, "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": "Reset Auto Commit", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Camel will set the autoCommit on the JDBC connection to be false, commit the change after executed the statement and reset the autoCommit flag of the connection at the end, if the resetAutoCommit is true. If the JDBC connection doesn't support to reset the autoCommit flag, you can set the resetAutoCommit flag to be false, and Camel will not try to reset the autoCommit flag. When used with XA transactions you most likely need to set it to false so that the transaction manager is in charge of committing this tx." }, - "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use." }, - "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes instead of string data. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, + "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not used: the producer does not read this option", "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use. Deprecation note: Not used: the producer does not read this option" }, + "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read after the producer has closed the connection. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, "useHeadersAsParameters": { "index": 9, "kind": "parameter", "displayName": "Use Headers As Parameters", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Set this option to true to use the prepareStatementStrategy with named parameters. This allows to define queries with named placeholders, and use headers with the dynamic values for the query placeholders." }, "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", "displayName": "Use JDBC4 Column Name And Label Semantics", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get the column name where as JDBC 3.0 uses both columnName or columnLabel. Unfortunately JDBC drivers behave differently so you can use this option to work out issues around your JDBC driver if you get problem using this component This option is default true." }, "lazyStartProducer": { "index": 11, "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." }, diff --git a/components/camel-jdbc/src/main/docs/jdbc-component.adoc b/components/camel-jdbc/src/main/docs/jdbc-component.adoc index 04943660e73f2..d5f4442e8cf16 100644 --- a/components/camel-jdbc/src/main/docs/jdbc-component.adoc +++ b/components/camel-jdbc/src/main/docs/jdbc-component.adoc @@ -63,6 +63,18 @@ the result. *Note:* This component fetches `ResultSetMetaData` to be able to return the column name as the key in the `Map`. +With `outputType=SelectList` the header `CamelJdbcRowCount` holds the number of rows of the result. + +A `BLOB` or `CLOB` value of a row is a `java.sql.Blob` or `java.sql.Clob`, which can be read only while the connection is +open, and the producer closes the connection before the route continues (except with `outputType=StreamList`, +which closes it when the exchange is done). So read such a value inside the producer: set `useGetBytesForBlob=true` +to get a `BLOB` as a `byte[]`, or convert a `CLOB` to text in the SQL query with a function of the database. + +The `SQL` of the message body is mandatory: a message without a body fails with an `InvalidPayloadException`. + +The `transacted` option is not used (and deprecated). To take part in a Spring transaction, use the +xref:spring-jdbc-component.adoc[Spring JDBC Component]. + === Generated keys If you insert data using SQL INSERT, then the RDBMS may support auto @@ -72,7 +84,8 @@ return the generated keys in headers. + generated keys will be provided as headers with the keys listed in the table above. -Using generated keys does not work together with named parameters. +The generated keys in the header `CamelGeneratedKeysRows` are a `List` of `Map` objects with the key columns, +whatever the `outputClass` and `readSize` of the endpoint. === Using named parameters diff --git a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java index 3dcd18c945114..786c9f1c76065 100644 --- a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java +++ b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcConstants.java @@ -27,8 +27,8 @@ public final class JdbcConstants { "OUT header.", javaType = "int") public static final String JDBC_UPDATE_COUNT = "CamelJdbcUpdateCount"; - @Metadata(label = "producer", description = "If the query is a `SELECT`, query the row count is returned in this OUT\n" + - "header.", + @Metadata(label = "producer", description = "If the query is a `SELECT` and the outputType is `SelectList`, the row\n" + + "count is returned in this OUT header.", javaType = "int") public static final String JDBC_ROW_COUNT = "CamelJdbcRowCount"; @Metadata(label = "producer", description = "The column names from the ResultSet as a `java.util.Set`\n" + diff --git a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java index 927733d122b8e..c7357ce022bfb 100644 --- a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java +++ b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcEndpoint.java @@ -46,6 +46,8 @@ public class JdbcEndpoint extends DefaultEndpoint { @UriParam private int readSize; @UriParam + @Metadata(deprecationNote = "Not used: the producer does not read this option") + @Deprecated private boolean transacted; @UriParam(defaultValue = "true") private boolean resetAutoCommit = true; @@ -112,13 +114,18 @@ public void setReadSize(int readSize) { this.readSize = readSize; } + @Deprecated public boolean isTransacted() { return transacted; } /** * Whether transactions are in use. + * + * @deprecated not used: the producer does not read this option. The producer takes part in a Spring transaction + * through the connection of the data source with the Spring JDBC component. */ + @Deprecated public void setTransacted(boolean transacted) { this.transacted = transacted; } @@ -258,7 +265,8 @@ public boolean isUseGetBytesForBlob() { } /** - * To read BLOB columns as bytes instead of string data. + * To read BLOB columns as bytes (a byte[]) instead of a java.sql.Blob, which cannot be read after the producer has + * closed the connection. *

* This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes. */ diff --git a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcHelper.java b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcHelper.java index d6f4d40de0d9b..227b01a7381fe 100644 --- a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcHelper.java +++ b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcHelper.java @@ -33,6 +33,10 @@ public static Object newBeanInstance( BeanRowMapper beanRowMapper, Map row) throws SQLException { Class clazz = camelContext.getClassResolver().resolveClass(outputClass); + if (clazz == null) { + throw new IllegalArgumentException( + "Cannot find the outputClass " + outputClass + ": set outputClass to the name of a class on the classpath"); + } Object answer = camelContext.getInjector().newInstance(clazz); Map properties = new LinkedHashMap<>(); diff --git a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcProducer.java b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcProducer.java index f56bb7588cfe4..f2f8ff5507e8b 100644 --- a/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcProducer.java +++ b/components/camel-jdbc/src/main/java/org/apache/camel/component/jdbc/JdbcProducer.java @@ -74,7 +74,7 @@ public void process(Exchange exchange) throws Exception { } private void processingSqlBySettingAutoCommit(Exchange exchange) throws Exception { - String sql = exchange.getIn().getBody(String.class); + String sql = exchange.getIn().getMandatoryBody(String.class); Connection conn = null; boolean autoCommit = false; boolean shouldCloseResources = true; @@ -122,7 +122,7 @@ public int getOrder() { } private void processingSqlWithoutSettingAutoCommit(Exchange exchange) throws Exception { - String sql = exchange.getIn().getBody(String.class); + String sql = exchange.getIn().getMandatoryBody(String.class); Connection conn = null; boolean shouldCloseResources = true; @@ -372,7 +372,11 @@ protected void setGeneratedKeys(Exchange exchange, Connection conn, ResultSet ge ResultSetIterator iterator = new ResultSetIterator( conn, generatedKeys, getEndpoint().isUseJDBC4ColumnNameAndLabelSemantics(), getEndpoint().isUseGetBytesForBlob()); - List> data = extractRows(iterator); + // the generated keys are the key columns of the statement, not rows of the outputClass + List> data = new ArrayList<>(); + while (iterator.hasNext()) { + data.add(iterator.next()); + } exchange.getMessage().setHeader(JdbcConstants.JDBC_GENERATED_KEYS_ROW_COUNT, data.size()); exchange.getMessage().setHeader(JdbcConstants.JDBC_GENERATED_KEYS_DATA, data); diff --git a/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcGeneratedKeysOutputClassTest.java b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcGeneratedKeysOutputClassTest.java new file mode 100644 index 0000000000000..70968d285b4ad --- /dev/null +++ b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcGeneratedKeysOutputClassTest.java @@ -0,0 +1,69 @@ +/* + * 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.jdbc; + +import java.util.List; +import java.util.Map; + +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +/** + * The generated keys are maps of the key columns, whatever the outputClass and readSize of the endpoint. + */ +public class JdbcGeneratedKeysOutputClassTest extends AbstractJdbcTestSupport { + + @Test + public void testGeneratedKeysAreMaps() { + Exchange out = template.request("direct:insert", e -> { + e.getIn().setBody("insert into tableWithAutoIncr (content) values ('value2')"); + e.getIn().setHeader(JdbcConstants.JDBC_RETRIEVE_GENERATED_KEYS, true); + }); + + List keys = out.getMessage().getHeader(JdbcConstants.JDBC_GENERATED_KEYS_DATA, List.class); + assertEquals(1, keys.size()); + Map row = assertInstanceOf(Map.class, keys.get(0)); + assertEquals(2, ((Number) row.get("ID")).intValue()); + assertEquals(1, out.getMessage().getHeader(JdbcConstants.JDBC_GENERATED_KEYS_ROW_COUNT)); + } + + @Test + public void testAllGeneratedKeysWithReadSize() { + Exchange out = template.request("direct:insert", e -> { + e.getIn().setBody("insert into tableWithAutoIncr (content) values ('value2'), ('value3')"); + e.getIn().setHeader(JdbcConstants.JDBC_RETRIEVE_GENERATED_KEYS, true); + }); + + List keys = out.getMessage().getHeader(JdbcConstants.JDBC_GENERATED_KEYS_DATA, List.class); + assertEquals(2, keys.size()); + assertEquals(2, out.getMessage().getHeader(JdbcConstants.JDBC_GENERATED_KEYS_ROW_COUNT)); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:insert") + .to("jdbc:testdb?readSize=1&outputClass=org.apache.camel.component.jdbc.CustomerModel"); + } + }; + } +} diff --git a/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcNoBodyTest.java b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcNoBodyTest.java new file mode 100644 index 0000000000000..008f63d3404d2 --- /dev/null +++ b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcNoBodyTest.java @@ -0,0 +1,62 @@ +/* + * 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.jdbc; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * The SQL of the body is mandatory. + */ +public class JdbcNoBodyTest extends AbstractJdbcTestSupport { + + @Test + public void testNoBody() { + assertNoBody("direct:plain"); + } + + @Test + public void testNoBodyWithHeadersAsParameters() { + assertNoBody("direct:headers"); + } + + @Test + public void testNoBodyWithoutResetAutoCommit() { + assertNoBody("direct:noReset"); + } + + private void assertNoBody(String uri) { + CamelExecutionException e = assertThrows(CamelExecutionException.class, () -> template.sendBody(uri, null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:plain").to("jdbc:testdb"); + from("direct:headers").to("jdbc:testdb?useHeadersAsParameters=true"); + from("direct:noReset").to("jdbc:testdb?resetAutoCommit=false"); + } + }; + } +} diff --git a/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcOutputClassNotFoundTest.java b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcOutputClassNotFoundTest.java new file mode 100644 index 0000000000000..b30bd311a0ec6 --- /dev/null +++ b/components/camel-jdbc/src/test/java/org/apache/camel/component/jdbc/JdbcOutputClassNotFoundTest.java @@ -0,0 +1,55 @@ +/* + * 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.jdbc; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JdbcOutputClassNotFoundTest extends AbstractJdbcTestSupport { + + @Test + public void testSelectListOutputClassNotFound() { + assertOutputClassNotFound("direct:list"); + } + + @Test + public void testSelectOneOutputClassNotFound() { + assertOutputClassNotFound("direct:one"); + } + + private void assertOutputClassNotFound(String uri) { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.sendBody(uri, "select * from customer where ID = 'cust1'")); + IllegalArgumentException cause = assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertTrue(cause.getMessage().contains("org.example.NoSuchItem"), cause.getMessage()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:list").to("jdbc:testdb?outputType=SelectList&outputClass=org.example.NoSuchItem"); + from("direct:one").to("jdbc:testdb?outputType=SelectOne&outputClass=org.example.NoSuchItem"); + } + }; + } +} diff --git a/components/camel-jsch/src/main/docs/scp-component.adoc b/components/camel-jsch/src/main/docs/scp-component.adoc index 95fd93cdb3cba..5ce99b38b2951 100644 --- a/components/camel-jsch/src/main/docs/scp-component.adoc +++ b/components/camel-jsch/src/main/docs/scp-component.adoc @@ -70,6 +70,14 @@ The host key is checked against a known hosts file: strictHostKeyChecking: "yes" ---- +The directory of the endpoint (`/upload` above) must exist on the remote host, as scp does not create it: +when it does not exist, each file is written to a file named like the directory, and the last one wins. +The sub directories in the `CamelFileName` header (such as `mysub/hello.txt`) are created in that directory. + +Without a known hosts file (`useUserKnownHostsFile=false` and no `knownHostsFile`), no host is known, +so the option `strictHostKeyChecking` decides: with `yes` the connection fails, with `no` (the default) +the host key is not checked. + == OpenSSH Certificate Authentication diff --git a/components/camel-jsch/src/main/java/org/apache/camel/component/scp/ScpOperations.java b/components/camel-jsch/src/main/java/org/apache/camel/component/scp/ScpOperations.java index fc0d9fd9d387f..f853b13e299e2 100644 --- a/components/camel-jsch/src/main/java/org/apache/camel/component/scp/ScpOperations.java +++ b/components/camel-jsch/src/main/java/org/apache/camel/component/scp/ScpOperations.java @@ -380,9 +380,8 @@ private Session createSession(ScpConfiguration config) { } catch (Exception e) { throw new GenericFileOperationFailedException("Cannot load known host file: " + knownHostsFile, e); } - } else { - jsch.setKnownHosts((String) null); } + // without a known hosts file no host is known, and strictHostKeyChecking decides whether to connect session = jsch.getSession(config.getUsername(), config.getHost(), config.getPort()); session.setTimeout(config.getTimeout()); session.setUserInfo(new SessionUserInfo(config)); diff --git a/components/camel-jsch/src/test/java/org/apache/camel/component/scp/ScpNoKnownHostsTest.java b/components/camel-jsch/src/test/java/org/apache/camel/component/scp/ScpNoKnownHostsTest.java new file mode 100644 index 0000000000000..8a9badb5947bf --- /dev/null +++ b/components/camel-jsch/src/test/java/org/apache/camel/component/scp/ScpNoKnownHostsTest.java @@ -0,0 +1,72 @@ +/* + * 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.scp; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.file.GenericFileOperationFailedException; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * Without a known hosts file (useUserKnownHostsFile=false and no knownHostsFile), strictHostKeyChecking decides. + */ +public class ScpNoKnownHostsTest extends ScpServerTestSupport { + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("file:" + getScpPath() + "?recursive=true&delete=true") + .convertBodyTo(String.class) + .to("mock:result"); + } + }; + } + + @Test + public void testNoKnownHostsWithoutStrictHostKeyChecking() throws Exception { + assumeTrue(this.isSetupComplete()); + + getMockEndpoint("mock:result").expectedBodiesReceived("Hello World"); + + String uri = getScpUri() + "?username=admin&password=admin&useUserKnownHostsFile=false"; + template.sendBodyAndHeader(uri, "Hello World", Exchange.FILE_NAME, "hello.txt"); + + MockEndpoint.assertIsSatisfied(context); + } + + @Test + public void testNoKnownHostsWithStrictHostKeyChecking() { + assumeTrue(this.isSetupComplete()); + + String uri = getScpUri() + "?username=admin&password=admin&useUserKnownHostsFile=false" + + "&strictHostKeyChecking=yes"; + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.sendBodyAndHeader(uri, "Hello World", Exchange.FILE_NAME, "hello.txt")); + // the host key is rejected as the host is not known, so the producer cannot connect + GenericFileOperationFailedException cause = assertInstanceOf(GenericFileOperationFailedException.class, e.getCause()); + assertTrue(cause.getMessage().startsWith("Failed to connect to"), cause.getMessage()); + } +} diff --git a/components/camel-kafka/pom.xml b/components/camel-kafka/pom.xml index 2d47e67fb7397..1698b9b5c52f1 100644 --- a/components/camel-kafka/pom.xml +++ b/components/camel-kafka/pom.xml @@ -59,6 +59,12 @@ ${hamcrest-version} test + + tools.jackson.core + jackson-databind + ${jackson3-version} + test + org.springframework spring-jdbc diff --git a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json index d1dafe51f2f12..dd09c9c37ec32 100644 --- a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json +++ b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json @@ -107,7 +107,7 @@ "retries": { "index": 80, "kind": "property", "displayName": "Retries", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Setting a value greater than zero will cause the client to resend any record that has failed to be sent due to a potentially transient error. Note that this retry is no different from if the client re-sending the record upon receiving the error. Produce requests will be failed before the number of retries has been exhausted if the timeout configured by delivery.timeout.ms expires first before successful acknowledgement. Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior. Enabling idempotence requires this config value to be greater than 0. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled. Allowing retries while setting enable.idempotence to false and max.in.flight.requests.per.connection to 1 will potentially change the ordering of records, because if two batches are sent to a single partition, and the first fails and is retried but the second succeeds; then the records in the second batch may appear first." }, "transacted": { "index": 81, "kind": "property", "displayName": "Transacted", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Indicates to create a transactional.id kafka property by using the endpoint id and route id. This property is ignored in case there is transactional.id kafka property or the transactionalId parameter." }, "transactionalId": { "index": 82, "kind": "property", "displayName": "Transactional Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Enable the kafka producer to be a transactional one by setting the transactional.id property. In case this property is used, the transacted parameter is ignored." }, - "useIterator": { "index": 83, "kind": "property", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated)." }, + "useIterator": { "index": 83, "kind": "property", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated). A Jackson JSON array node is sent as one record per element, and any other JSON node (such as an object) as one record." }, "valueSerializer": { "index": 84, "kind": "property", "displayName": "Value Serializer", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "org.apache.kafka.common.serialization.StringSerializer", "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "The serializer class for messages." }, "workerPool": { "index": 85, "kind": "property", "displayName": "Worker Pool", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "To use a custom worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing. If using this option, then you must handle the lifecycle of the thread pool to shut the pool down when no longer needed." }, "workerPoolCoreSize": { "index": 86, "kind": "property", "displayName": "Worker Pool Core Size", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Number of core threads for the worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing." }, @@ -253,7 +253,7 @@ "retries": { "index": 75, "kind": "parameter", "displayName": "Retries", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Setting a value greater than zero will cause the client to resend any record that has failed to be sent due to a potentially transient error. Note that this retry is no different from if the client re-sending the record upon receiving the error. Produce requests will be failed before the number of retries has been exhausted if the timeout configured by delivery.timeout.ms expires first before successful acknowledgement. Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior. Enabling idempotence requires this config value to be greater than 0. If conflicting configurations are set and idempotence is not explicitly enabled, idempotence is disabled. Allowing retries while setting enable.idempotence to false and max.in.flight.requests.per.connection to 1 will potentially change the ordering of records, because if two batches are sent to a single partition, and the first fails and is retried but the second succeeds; then the records in the second batch may appear first." }, "transacted": { "index": 76, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Indicates to create a transactional.id kafka property by using the endpoint id and route id. This property is ignored in case there is transactional.id kafka property or the transactionalId parameter." }, "transactionalId": { "index": 77, "kind": "parameter", "displayName": "Transactional Id", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Enable the kafka producer to be a transactional one by setting the transactional.id property. In case this property is used, the transacted parameter is ignored." }, - "useIterator": { "index": 78, "kind": "parameter", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated)." }, + "useIterator": { "index": 78, "kind": "parameter", "displayName": "Use Iterator", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to send multiple records to kafka (if the message body can be iterated). A Jackson JSON array node is sent as one record per element, and any other JSON node (such as an object) as one record." }, "valueSerializer": { "index": 79, "kind": "parameter", "displayName": "Value Serializer", "group": "producer", "label": "producer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "org.apache.kafka.common.serialization.StringSerializer", "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "The serializer class for messages." }, "workerPool": { "index": 80, "kind": "parameter", "displayName": "Worker Pool", "group": "producer", "label": "producer", "required": false, "type": "object", "javaType": "java.util.concurrent.ExecutorService", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "To use a custom worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing. If using this option, then you must handle the lifecycle of the thread pool to shut the pool down when no longer needed." }, "workerPoolCoreSize": { "index": 81, "kind": "parameter", "displayName": "Worker Pool Core Size", "group": "producer", "label": "producer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 10, "configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": "configuration", "description": "Number of core threads for the worker pool for continue routing Exchange after kafka server has acknowledged the message that was sent to it from KafkaProducer using asynchronous non-blocking processing." }, diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java index be3d6ac461f16..946fd0f53d9e3 100755 --- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java +++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java @@ -632,7 +632,8 @@ public boolean isUseIterator() { /** * Sets whether sending to kafka should send the message body as a single record, or use a java.util.Iterator to - * send multiple records to kafka (if the message body can be iterated). + * send multiple records to kafka (if the message body can be iterated). A Jackson JSON array node is sent as one + * record per element, and any other JSON node (such as an object) as one record. */ public void setUseIterator(boolean useIterator) { this.useIterator = useIterator; diff --git a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java index 8bb4129de392d..f7f16f471e5bb 100755 --- a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java +++ b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaProducer.java @@ -64,6 +64,8 @@ public class KafkaProducer extends DefaultAsyncProducer implements RouteIdAware { private static final Logger LOG = LoggerFactory.getLogger(KafkaProducer.class); + private static final String JACKSON3_JSON_NODE = "tools.jackson.databind.JsonNode"; + private static final String JACKSON3_ARRAY_NODE = "tools.jackson.databind.node.ArrayNode"; @SuppressWarnings("rawtypes") private Producer kafkaProducer; @@ -314,14 +316,33 @@ private String evaluateTopic(Message message) { } private boolean isIterable(Object body) { + // a JSON node is an Iterable of its elements, but only an array is split: an object node (whose elements + // are its field values) and a scalar node are sent as one record if (body instanceof JsonNode node) { - return node.isContainerNode(); + return node.isArray(); } - if (body instanceof Iterable || body instanceof Iterator) { - return true; + if (body instanceof Iterable) { + return !isJackson3NonArrayNode(body.getClass()); } + return body instanceof Iterator; + } + + /** + * Whether the class is a Jackson 3 node other than an array node. Jackson 3 is recognized by class name, as + * camel-kafka does not depend on it. + */ + private static boolean isJackson3NonArrayNode(Class type) { + for (Class c = type; c != null && c != Object.class; c = c.getSuperclass()) { + String name = c.getName(); + if (JACKSON3_ARRAY_NODE.equals(name)) { + return false; + } + if (JACKSON3_JSON_NODE.equals(name)) { + return true; + } + } return false; } diff --git a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java index e842694e5eddb..968a5431cd631 100755 --- a/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java +++ b/components/camel-kafka/src/test/java/org/apache/camel/component/kafka/KafkaProducerTest.java @@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.camel.AggregationStrategy; import org.apache.camel.AsyncCallback; import org.apache.camel.CamelContext; @@ -55,6 +56,7 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import tools.jackson.databind.JsonNode; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -618,6 +620,79 @@ public void processSendsMessagesWithJsonArrayNode() throws Exception { verifySendMessages(Arrays.asList("sometopic", "sometopic"), null); } + @Test + public void processSendsMessageWithJsonObjectNode() throws Exception { + // CAMEL-25924: an object node is one record, not one record per field value + endpoint.getConfiguration().setTopic("sometopic"); + Mockito.when(exchange.getIn()).thenReturn(in); + Mockito.when(exchange.getMessage()).thenReturn(in); + + ObjectNode node = JsonNodeFactory.instance.objectNode(); + node.put("orderId", 42); + node.put("customer", "acme"); + node.put("total", 9.5); + + in.setBody(node); + producer.process(exchange); + + verifySendMessage("sometopic"); + verifySentValues(node); + } + + @Test + public void processSendsMessagesWithJackson3ArrayNode() throws Exception { + endpoint.getConfiguration().setTopic("sometopic"); + Mockito.when(exchange.getIn()).thenReturn(in); + Mockito.when(exchange.getMessage()).thenReturn(in); + + JsonNode first = tools.jackson.databind.node.JsonNodeFactory.instance.numberNode(1); + JsonNode second = tools.jackson.databind.node.JsonNodeFactory.instance.numberNode(2); + in.setBody(tools.jackson.databind.node.JsonNodeFactory.instance.arrayNode().add(first).add(second)); + producer.process(exchange); + + verifySendMessages(Arrays.asList("sometopic", "sometopic"), null); + verifySentValues(first, second); + } + + @Test + public void processSendsMessageWithJackson3ObjectNode() throws Exception { + // CAMEL-25924: a Jackson 3 object node is one record, not one record per field value + endpoint.getConfiguration().setTopic("sometopic"); + Mockito.when(exchange.getIn()).thenReturn(in); + Mockito.when(exchange.getMessage()).thenReturn(in); + + JsonNode node + = tools.jackson.databind.node.JsonNodeFactory.instance.objectNode().put("orderId", 42).put("customer", "acme"); + in.setBody(node); + producer.process(exchange); + + verifySendMessage("sometopic"); + verifySentValues(node); + } + + @Test + public void processSendsMessageWithJackson3ScalarNode() throws Exception { + // CAMEL-25924: a Jackson 3 scalar node has no elements, and must be sent as one record and not dropped + endpoint.getConfiguration().setTopic("sometopic"); + Mockito.when(exchange.getIn()).thenReturn(in); + Mockito.when(exchange.getMessage()).thenReturn(in); + + JsonNode node = tools.jackson.databind.node.JsonNodeFactory.instance.numberNode(42); + in.setBody(node); + producer.process(exchange); + + verifySendMessage("sometopic"); + verifySentValues(node); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void verifySentValues(Object... expectedValues) { + ArgumentCaptor captor = ArgumentCaptor.forClass(ProducerRecord.class); + Mockito.verify(producer.getKafkaProducer(), Mockito.atLeastOnce()).send(captor.capture()); + List actualValues = captor.getAllValues().stream().map(ProducerRecord::value).collect(Collectors.toList()); + assertEquals(Arrays.asList(expectedValues), actualValues); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected void verifySendMessage(Integer partitionKey, String topic, String messageKey) { ArgumentCaptor captor = ArgumentCaptor.forClass(ProducerRecord.class); diff --git a/components/camel-micrometer/src/generated/java/org/apache/camel/component/micrometer/MicrometerEndpointUriFactory.java b/components/camel-micrometer/src/generated/java/org/apache/camel/component/micrometer/MicrometerEndpointUriFactory.java index e33776f8d1478..9ece8bc5d4a4b 100644 --- a/components/camel-micrometer/src/generated/java/org/apache/camel/component/micrometer/MicrometerEndpointUriFactory.java +++ b/components/camel-micrometer/src/generated/java/org/apache/camel/component/micrometer/MicrometerEndpointUriFactory.java @@ -54,7 +54,7 @@ public String buildUri(String scheme, Map properties, boolean en Map copy = copyParameters(properties); - uri = buildPathParameter(syntax, uri, "metricsType", null, true, copy); + uri = buildPathParameter(syntax, uri, "metricsType", "counter", false, copy); uri = buildPathParameter(syntax, uri, "metricsName", null, true, copy); uri = buildQueryParameters(uri, copy, encode); return uri; diff --git a/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json b/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json index f7e9210de8972..89c539d9e0b37 100644 --- a/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json +++ b/components/camel-micrometer/src/generated/resources/META-INF/org/apache/camel/component/micrometer/micrometer.json @@ -30,7 +30,7 @@ }, "headers": { "CamelMetricsTimerAction": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.micrometer.MicrometerTimerAction", "enum": [ "start", "stop" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override timer action in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_TIMER_ACTION" }, - "CamelMetricsHistogramValue": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override histogram value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_HISTOGRAM_VALUE" }, + "CamelMetricsHistogramValue": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override histogram value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_HISTOGRAM_VALUE" }, "CamelMetricsCounterDecrement": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override decrement value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_COUNTER_DECREMENT" }, "CamelMetricsCounterIncrement": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override increment value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_COUNTER_INCREMENT" }, "CamelMetricsName": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Override name value in URI", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_NAME" }, @@ -38,7 +38,7 @@ "CamelMetricsTags": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.lang.Iterable", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "To augment meter tags defined as URI parameters", "constantName": "org.apache.camel.component.micrometer.MicrometerConstants#HEADER_METRIC_TAGS" } }, "properties": { - "metricsType": { "index": 0, "kind": "path", "displayName": "Metrics Type", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "io.micrometer.core.instrument.Meter.Type", "enum": [ "counter", "summary", "timer" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Type of metrics" }, + "metricsType": { "index": 0, "kind": "path", "displayName": "Metrics Type", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "io.micrometer.core.instrument.Meter.Type", "enum": [ "counter", "summary", "timer" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "counter", "description": "Type of metrics" }, "metricsName": { "index": 1, "kind": "path", "displayName": "Metrics Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Name of metrics" }, "action": { "index": 2, "kind": "parameter", "displayName": "Action", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "start", "stop" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Action expression when using timer type" }, "decrement": { "index": 3, "kind": "parameter", "displayName": "Decrement", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "supportSimpleExpression": true, "description": "Decrement value expression when using counter type" }, diff --git a/components/camel-micrometer/src/main/docs/micrometer-component.adoc b/components/camel-micrometer/src/main/docs/micrometer-component.adoc index d63172bfad4fb..c2464b67c616b 100644 --- a/components/camel-micrometer/src/main/docs/micrometer-component.adoc +++ b/components/camel-micrometer/src/main/docs/micrometer-component.adoc @@ -148,8 +148,10 @@ from("direct:in") will update a counter with name `new.name` instead of `name.not.used` using the tag `dynamic-key` with value `dynamic-value` in addition to the tag `key` with value `value`. -All Metrics specific headers are removed from the message once the Micrometer endpoint finishes processing of exchange. While processing exchange -Micrometer endpoint will catch all exceptions and write log entry using level `warn`. +All Metrics specific headers are removed from the message once the Micrometer endpoint finishes processing of exchange. +If the Micrometer endpoint cannot update the meter, then the exchange fails with the exception, which the +error handler of the route handles as any other failure. For example, a `value`, `increment` or `decrement` that is +not a number, or a timer `action` that is not `start` or `stop`, fails the exchange with a `TypeConversionException`. [[MicrometerComponent-counter]] @@ -412,8 +414,9 @@ micrometer:summary:metricname[?options] |value |- |Value to use in histogram |=================================== -If no `value` is not set, nothing is added to histogram and warning is -logged. +If `value` is not set, and there is no `CamelMetricsHistogramValue` header, then nothing is added to the +distribution summary, and nothing is logged. A value that is not a number fails the exchange with a +`TypeConversionException`. [tabs] ==== @@ -461,7 +464,7 @@ Java:: + [source,java] ---- -// nothing is added to simple.histogram; warning is logged +// nothing is added to simple.histogram from("direct:in") .to("micrometer:summary:simple.histogram") .to("direct:out"); @@ -543,7 +546,7 @@ the Micrometer endpoint URI. [width="100%",cols="10%,80%,10%",options="header",] |================================================================= |Name |Description |Expected type -|CamelMetricsHistogramValue |Override histogram value in URI |Long +|CamelMetricsHistogramValue |Override histogram value in URI |Double |================================================================= [tabs] @@ -609,10 +612,9 @@ micrometer:timer:metricname[?options] |action |- |start or stop |========================== -If no `action` or invalid value is provided then warning is logged -without any timer update. If action `start` is called on an already running -timer or `stop` is called on an unknown timer, nothing is updated -and warning is logged. +If no `action` is provided, then the timer is not updated, and nothing is logged. An `action` that is not +`start` or `stop` fails the exchange with a `TypeConversionException`. If action `start` is called on an +already running timer or `stop` is called on an unknown timer, nothing is updated, and nothing is logged. [tabs] ==== diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java index 9979681874651..c2f2c4e1fcaf5 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerConstants.java @@ -27,7 +27,7 @@ public final class MicrometerConstants { @Metadata(description = "Override timer action in URI", javaType = "org.apache.camel.component.micrometer.MicrometerTimerAction") public static final String HEADER_TIMER_ACTION = HEADER_PREFIX + "TimerAction"; - @Metadata(description = "Override histogram value in URI", javaType = "long") + @Metadata(description = "Override histogram value in URI", javaType = "Double") public static final String HEADER_HISTOGRAM_VALUE = HEADER_PREFIX + "HistogramValue"; @Metadata(description = "Override decrement value in URI", javaType = "Double") public static final String HEADER_COUNTER_DECREMENT = HEADER_PREFIX + "CounterDecrement"; diff --git a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java index 90fb0911a1f92..bbddf39b3c40a 100644 --- a/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java +++ b/components/camel-micrometer/src/main/java/org/apache/camel/component/micrometer/MicrometerEndpoint.java @@ -45,8 +45,7 @@ public class MicrometerEndpoint extends DefaultEndpoint { protected MeterRegistry registry; - @UriPath(description = "Type of metrics", enums = "counter,summary,timer") - @Metadata(required = true) + @UriPath(description = "Type of metrics", enums = "counter,summary,timer", defaultValue = "counter") protected final Meter.Type metricsType; @UriPath(description = "Name of metrics") @Metadata(required = true, supportSimpleExpression = true) diff --git a/components/camel-mina-sftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/mina/mina-sftp.json b/components/camel-mina-sftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/mina/mina-sftp.json index 6cae8c76d1cb1..14c37f8a89144 100644 --- a/components/camel-mina-sftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/mina/mina-sftp.json +++ b/components/camel-mina-sftp/src/generated/resources/META-INF/org/apache/camel/component/file/remote/mina/mina-sftp.json @@ -72,7 +72,7 @@ "sendEmptyMessageWhenIdle": { "index": 19, "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." }, "streamDownload": { "index": 20, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.mina.MinaSftpConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route. If enabling this option then you must set stepwise=false as both cannot be enabled at the same time." }, "bridgeErrorHandler": { "index": 21, "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." }, - "download": { "index": 22, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 22, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether the FTP consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 23, "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": 24, "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." }, "ignoreFileNotFoundOrPermissionError": { "index": 25, "kind": "parameter", "displayName": "Ignore File Not Found Or Permission Error", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.file.remote.mina.MinaSftpConfiguration", "configurationField": "configuration", "description": "Whether to ignore when (trying to list files in directories or when downloading a file), which does not exist or due to permission error. By default when a directory or file does not exist or insufficient permission, then an exception is thrown. Setting this option to true allows to ignore that instead." }, diff --git a/components/camel-pdf/src/generated/java/org/apache/camel/component/pdf/converter/PdfConverterLoader.java b/components/camel-pdf/src/generated/java/org/apache/camel/component/pdf/converter/PdfConverterLoader.java index 313981c469a17..276c82eefc781 100644 --- a/components/camel-pdf/src/generated/java/org/apache/camel/component/pdf/converter/PdfConverterLoader.java +++ b/components/camel-pdf/src/generated/java/org/apache/camel/component/pdf/converter/PdfConverterLoader.java @@ -52,6 +52,14 @@ private void registerConverters(TypeConverterRegistry registry) { } return answer; }); + addTypeConverter(registry, org.apache.pdfbox.pdmodel.PDDocument.class, java.io.ByteArrayOutputStream.class, false, + (type, exchange, value) -> { + Object answer = getPdfConverter().convertToPDF((java.io.ByteArrayOutputStream) value); + if (false && answer == null) { + answer = Void.class; + } + return answer; + }); addTypeConverter(registry, org.apache.pdfbox.pdmodel.PDDocument.class, java.io.File.class, false, (type, exchange, value) -> { Object answer = getPdfConverter().convertToPDF((java.io.File) value); @@ -68,6 +76,14 @@ private void registerConverters(TypeConverterRegistry registry) { } return answer; }); + addTypeConverter(registry, org.apache.pdfbox.pdmodel.PDDocument.class, java.nio.file.Path.class, false, + (type, exchange, value) -> { + Object answer = getPdfConverter().convertToPDF((java.nio.file.Path) value); + if (false && answer == null) { + answer = Void.class; + } + return answer; + }); } private static void addTypeConverter(TypeConverterRegistry registry, Class toType, Class fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) { diff --git a/components/camel-pdf/src/generated/resources/META-INF/org/apache/camel/component/pdf/pdf.json b/components/camel-pdf/src/generated/resources/META-INF/org/apache/camel/component/pdf/pdf.json index f2edff0482dd4..333577f9981ec 100644 --- a/components/camel-pdf/src/generated/resources/META-INF/org/apache/camel/component/pdf/pdf.json +++ b/components/camel-pdf/src/generated/resources/META-INF/org/apache/camel/component/pdf/pdf.json @@ -31,7 +31,7 @@ "CamelPdfProtectionPolicy": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.encryption.ProtectionPolicy", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/encryption\/ProtectionPolicy.htmlProtectionPolicy. If specified then PDF document will be encrypted with it.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#PROTECTION_POLICY_HEADER_NAME" }, "CamelPdfDocument": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.PDDocument", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for append operation and ignored in all other operations. Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/PDDocument.htmlPDDocument. Stores PDF document which will be used for append operation.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#PDF_DOCUMENT_HEADER_NAME" }, "CamelPdfDecryptionMaterial": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.pdfbox.pdmodel.encryption.DecryptionMaterial", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Expected type is https:\/\/pdfbox.apache.org\/docs\/2.0.13\/javadocs\/org\/apache\/pdfbox\/pdmodel\/encryption\/DecryptionMaterial.htmlDecryptionMaterial. Mandatory header if PDF document is encrypted.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#DECRYPTION_MATERIAL_HEADER_NAME" }, - "CamelPdfFilesToMerge": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for merge operation and ignored in all other operations. The array of pdf files that will be merged.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#FILES_TO_MERGE_HEADER_NAME" } + "CamelPdfFilesToMerge": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Mandatory header for merge operation and ignored in all other operations. The list of pdf files that will be merged, each given as a java.io.File or a java.nio.file.Path.", "constantName": "org.apache.camel.component.pdf.PdfHeaderConstants#FILES_TO_MERGE_HEADER_NAME" } }, "properties": { "operation": { "index": 0, "kind": "path", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.pdf.PdfOperation", "enum": [ "create", "append", "extractText", "merge" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Operation type" }, @@ -41,7 +41,7 @@ "marginLeft": { "index": 4, "kind": "parameter", "displayName": "Margin Left", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin left in pixels" }, "marginRight": { "index": 5, "kind": "parameter", "displayName": "Margin Right", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 40, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin right in pixels" }, "marginTop": { "index": 6, "kind": "parameter", "displayName": "Margin Top", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 20, "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Margin top in pixels" }, - "pageSize": { "index": 7, "kind": "parameter", "displayName": "Page Size", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "LETTER", "LEGAL", "A0", "A1", "A2", "A3", "A4", "A5", "A6" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "A4", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Page size" }, + "pageSize": { "index": 7, "kind": "parameter", "displayName": "Page Size", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "java.lang.String", "enum": [ "LETTER", "LEGAL", "A0", "A1", "A2", "A3", "A4", "A5", "A6" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "A4", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Page size: LETTER, LEGAL or A0 to A6. The older names PAGE_SIZE_A0 to PAGE_SIZE_A6 and PAGE_SIZE_LETTER are also accepted." }, "textProcessingFactory": { "index": 8, "kind": "parameter", "displayName": "Text Processing Factory", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.pdf.TextProcessingFactory", "enum": [ "autoFormatting", "lineTermination" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "lineTermination", "configurationClass": "org.apache.camel.component.pdf.PdfConfiguration", "configurationField": "pdfConfiguration", "description": "Text processing to use. autoFormatting: Text is getting sliced by words, then max amount of words that fits in the line will be written into pdf document. With this strategy all words that doesn't fit in the line will be moved to the new line. lineTermination: Builds set of classes for line-termination writing strategy. Text getting sliced by line termination symbol and then it will be written regardless it fits in the line or not." }, "lazyStartProducer": { "index": 9, "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." } } diff --git a/components/camel-pdf/src/main/docs/pdf-component.adoc b/components/camel-pdf/src/main/docs/pdf-component.adoc index e2e9dcebcb9ab..913cf95edc278 100644 --- a/components/camel-pdf/src/main/docs/pdf-component.adoc +++ b/components/camel-pdf/src/main/docs/pdf-component.adoc @@ -95,5 +95,25 @@ YAML:: It is now possible to get the body as a PD Document by using `PDDocument doc = exchange.getIn().getBody(PDDocument.class);`, which saves the trouble of converting the byte-array to a document. +The type converter reads a PDF from a `byte[]`, a `ByteArrayOutputStream`, an `InputStream`, a `java.io.File` or a +`java.nio.file.Path`. The `create`, `append` and `merge` operations leave the PDF in a `ByteArrayOutputStream`, so +`pdf:create` can be followed by `pdf:extractText` directly. The `extractText` operation fails with an +`InvalidPayloadException` naming the type of the body when the body is missing or is not one of these types. + NOTE: this only works for unprotected PDF files. For password-protected, the files still need to be converted manually. + +=== Creating a PDF from text + +The `create` operation needs a body with the text to write; without a body it fails with an `InvalidPayloadException`. + +With the default `textProcessingFactory=lineTermination`, the text is written line by line: a line ends at `\r\n`, +`\r` or `\n`. A tab is written as four spaces, and other control characters, which the fonts cannot show, are left out. + +The `pageSize` option takes `LETTER`, `LEGAL` or `A0` to `A6` (default `A4`). The older names `PAGE_SIZE_A0` to +`PAGE_SIZE_A6` and `PAGE_SIZE_LETTER` are still accepted. An unknown value fails when the endpoint is created. + +=== Merging PDF files + +The `merge` operation reads the files to merge from the `CamelPdfFilesToMerge` header: a list of at least two +elements, each a `java.io.File` or a `java.nio.file.Path`. diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfConfiguration.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfConfiguration.java index 82237ea31146c..57c0523ac96c6 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfConfiguration.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfConfiguration.java @@ -16,7 +16,8 @@ */ package org.apache.camel.component.pdf; -import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; import org.apache.camel.spi.Metadata; @@ -25,33 +26,28 @@ import org.apache.camel.spi.UriPath; import org.apache.pdfbox.pdmodel.common.PDRectangle; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A0; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A1; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A2; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A3; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A4; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A5; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_A6; -import static org.apache.camel.component.pdf.PdfPageSizeConstant.PAGE_SIZE_LETTER; - /** * Handles pdf component configuration values. */ @UriParams public class PdfConfiguration { - private static final Map PAGE_MAP = new HashMap<>(); + private static final Map PAGE_MAP = new LinkedHashMap<>(); static { - PAGE_MAP.put(PAGE_SIZE_A0, PDRectangle.A0); - PAGE_MAP.put(PAGE_SIZE_A1, PDRectangle.A1); - PAGE_MAP.put(PAGE_SIZE_A2, PDRectangle.A2); - PAGE_MAP.put(PAGE_SIZE_A3, PDRectangle.A3); - PAGE_MAP.put(PAGE_SIZE_A4, PDRectangle.A4); - PAGE_MAP.put(PAGE_SIZE_A5, PDRectangle.A5); - PAGE_MAP.put(PAGE_SIZE_A6, PDRectangle.A6); - PAGE_MAP.put(PAGE_SIZE_LETTER, PDRectangle.LETTER); + PAGE_MAP.put("LETTER", PDRectangle.LETTER); + PAGE_MAP.put("LEGAL", PDRectangle.LEGAL); + PAGE_MAP.put("A0", PDRectangle.A0); + PAGE_MAP.put("A1", PDRectangle.A1); + PAGE_MAP.put("A2", PDRectangle.A2); + PAGE_MAP.put("A3", PDRectangle.A3); + PAGE_MAP.put("A4", PDRectangle.A4); + PAGE_MAP.put("A5", PDRectangle.A5); + PAGE_MAP.put("A6", PDRectangle.A6); } + // the names before the catalog values, such as PAGE_SIZE_A4, which are still accepted + private static final String OLD_PAGE_SIZE_PREFIX = "PAGE_SIZE_"; + @UriPath(description = "Operation type") @Metadata(required = true) private PdfOperation operation; @@ -66,7 +62,7 @@ public class PdfConfiguration { @UriParam(defaultValue = "14") private float fontSize = 14; @UriParam(defaultValue = "A4", enums = "LETTER,LEGAL,A0,A1,A2,A3,A4,A5,A6") - private String pageSize = PAGE_SIZE_A4; + private String pageSize = "A4"; @UriParam(defaultValue = "HELVETICA", enums = "COURIER,COURIER_BOLD,COURIER_OBLIQUE,COURIER_BOLD_OBLIQUE," + "HELVETICA,HELVETICA_BOLD,HELVETICA_OBLIQUE,HELVETICA_BOLD_OBLIQUE," + "TIMES_ROMAN,TIMES_BOLD,TIMES_ITALIC,TIMES_BOLD_ITALIC," @@ -143,16 +139,32 @@ public void setFontSize(float fontSize) { } public PDRectangle getPageSize() { - return PAGE_MAP.get(pageSize); + return toPageSize(pageSize); } /** - * Page size + * Page size: LETTER, LEGAL or A0 to A6. The older names PAGE_SIZE_A0 to PAGE_SIZE_A6 and PAGE_SIZE_LETTER are also + * accepted. */ public void setPageSize(String pageSize) { + // fail on an unknown value when the endpoint is configured, rather than when the first page is made + toPageSize(pageSize); this.pageSize = pageSize; } + private static PDRectangle toPageSize(String pageSize) { + String name = pageSize != null ? pageSize.trim().toUpperCase(Locale.ROOT) : ""; + if (name.startsWith(OLD_PAGE_SIZE_PREFIX)) { + name = name.substring(OLD_PAGE_SIZE_PREFIX.length()); + } + PDRectangle answer = PAGE_MAP.get(name); + if (answer == null) { + throw new IllegalArgumentException( + String.format("Unknown pageSize %s: use one of %s", pageSize, String.join(", ", PAGE_MAP.keySet()))); + } + return answer; + } + public String getFont() { return font; } diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfHeaderConstants.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfHeaderConstants.java index a74241c7ac86c..51c4c6d9dbbff 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfHeaderConstants.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfHeaderConstants.java @@ -42,9 +42,10 @@ public final class PdfHeaderConstants { @Metadata(description = """ *Mandatory* header for `merge` operation and ignored in all other operations. - The array of pdf files that will be merged. + The list of pdf files that will be merged, each given as a `java.io.File` + or a `java.nio.file.Path`. """, - javaType = "java.util.List") + javaType = "java.util.List") public static final String FILES_TO_MERGE_HEADER_NAME = "CamelPdfFilesToMerge"; private PdfHeaderConstants() { diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfPageSizeConstant.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfPageSizeConstant.java index 43a74e639a631..c07f23bb4303c 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfPageSizeConstant.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfPageSizeConstant.java @@ -26,6 +26,7 @@ public final class PdfPageSizeConstant { public static final String PAGE_SIZE_A5 = "PAGE_SIZE_A5"; public static final String PAGE_SIZE_A6 = "PAGE_SIZE_A6"; public static final String PAGE_SIZE_LETTER = "PAGE_SIZE_LETTER"; + public static final String PAGE_SIZE_LEGAL = "PAGE_SIZE_LEGAL"; private PdfPageSizeConstant() { } diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfProducer.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfProducer.java index 3b8efe3811064..2b99b90dac61d 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfProducer.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/PdfProducer.java @@ -16,11 +16,16 @@ */ package org.apache.camel.component.pdf; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Path; import java.util.Collection; import java.util.List; import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.NoSuchHeaderException; import org.apache.camel.component.pdf.text.AutoFormattedWriterAbstractFactory; import org.apache.camel.component.pdf.text.LineBuilderStrategy; @@ -88,18 +93,29 @@ private OutputStream doMerge(Exchange exchange) throws IOException, NoSuchHeader LOG.debug("Got {} operation, going to merge multiple files into a single pdf document.", pdfConfiguration.getOperation()); PDFMergerUtility mergerUtility = new PDFMergerUtility(); - List files = ExchangeHelper.getMandatoryHeader(exchange, FILES_TO_MERGE_HEADER_NAME, List.class); + List files = ExchangeHelper.getMandatoryHeader(exchange, FILES_TO_MERGE_HEADER_NAME, List.class); if (files.size() < 2) { throw new IllegalArgumentException("Must provide at least 2 files to merge"); } - for (File file : files) { - mergerUtility.addSource(file); + for (Object file : files) { + mergerUtility.addSource(toFile(file)); } mergerUtility.setDestinationStream(new ByteArrayOutputStream()); mergerUtility.mergeDocuments(RandomAccessStreamCacheImpl::new); return mergerUtility.getDestinationStream(); } + private static File toFile(Object file) { + if (file instanceof File f) { + return f; + } else if (file instanceof Path p) { + return p.toFile(); + } + throw new IllegalArgumentException( + String.format("The %s header must hold java.io.File or java.nio.file.Path elements, but has %s", + FILES_TO_MERGE_HEADER_NAME, file != null ? file.getClass().getName() : "null")); + } + private Object doAppend(Exchange exchange) throws IOException { LOG.debug("Got {} operation, going to append text to provided pdf.", pdfConfiguration.getOperation()); String body = exchange.getIn().getBody(String.class); @@ -124,18 +140,18 @@ private Object doAppend(Exchange exchange) throws IOException { } } - private String doExtractText(Exchange exchange) throws IOException { + private String doExtractText(Exchange exchange) throws IOException, InvalidPayloadException { LOG.debug("Got {} operation, going to extract text from provided pdf.", pdfConfiguration.getOperation()); - try (PDDocument document = exchange.getIn().getBody(PDDocument.class)) { + try (PDDocument document = exchange.getIn().getMandatoryBody(PDDocument.class)) { PDFTextStripper pdfTextStripper = new PDFTextStripper(); return pdfTextStripper.getText(document); } } - private OutputStream doCreate(Exchange exchange) throws IOException { + private OutputStream doCreate(Exchange exchange) throws IOException, InvalidPayloadException { LOG.debug("Got {} operation, going to create and write provided string to pdf document.", pdfConfiguration.getOperation()); - String body = exchange.getIn().getBody(String.class); + String body = exchange.getIn().getMandatoryBody(String.class); try (PDDocument document = new PDDocument()) { StandardProtectionPolicy protectionPolicy = exchange.getIn().getHeader( PROTECTION_POLICY_HEADER_NAME, StandardProtectionPolicy.class); diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/converter/PdfConverter.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/converter/PdfConverter.java index ef267794147ec..49ef9b7671d95 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/converter/PdfConverter.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/converter/PdfConverter.java @@ -17,9 +17,12 @@ package org.apache.camel.component.pdf.converter; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.camel.Converter; import org.apache.pdfbox.Loader; @@ -45,4 +48,17 @@ public PDDocument convertToPDF(File file) throws Exception { return toPDDocument(is); } } + + /** + * Converts the output of the pdf create, append and merge operations. + */ + @Converter + public PDDocument convertToPDF(ByteArrayOutputStream bos) throws Exception { + return convertToPDF(bos.toByteArray()); + } + + @Converter + public PDDocument convertToPDF(Path path) throws Exception { + return convertToPDF(Files.readAllBytes(path)); + } } diff --git a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/text/LineTerminationWriterAbstractFactory.java b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/text/LineTerminationWriterAbstractFactory.java index 3d95f9330165c..f7552a5bc4536 100644 --- a/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/text/LineTerminationWriterAbstractFactory.java +++ b/components/camel-pdf/src/main/java/org/apache/camel/component/pdf/text/LineTerminationWriterAbstractFactory.java @@ -17,16 +17,22 @@ package org.apache.camel.component.pdf.text; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.apache.camel.component.pdf.PdfConfiguration; /** - * Builds set of classes for line-termination writing strategy. Text getting sliced by line termination symbol (\n) and - * then it will be written regardless it fits in the line or not. + * Builds set of classes for line-termination writing strategy. Text getting sliced by line termination symbol (\r\n, \r + * or \n) and then it will be written regardless it fits in the line or not. A tab is written as spaces, and other + * control characters, which the fonts cannot show, are left out. */ public class LineTerminationWriterAbstractFactory implements TextProcessingAbstractFactory { + private static final String LINE_END = "\\r\\n|\\r|\\n"; + private static final String TAB_SPACES = " "; + private final PdfConfiguration pdfConfiguration; public LineTerminationWriterAbstractFactory(PdfConfiguration pdfConfiguration) { @@ -40,17 +46,40 @@ public WriteStrategy createWriteStrategy() { @Override public SplitStrategy createSplitStrategy() { - return new PatternSplitStrategy("\n"); + return new PatternSplitStrategy(LINE_END); } @Override public LineBuilderStrategy createLineBuilderStrategy() { - // Do not format when LineByLineWriter is used. + // Do not format when LineByLineWriter is used, only leave out what the font cannot show. return new LineBuilderStrategy() { @Override public Collection buildLines(Collection splittedText) throws IOException { - return splittedText; + List lines = new ArrayList<>(splittedText.size()); + for (String line : splittedText) { + lines.add(toPrintable(line)); + } + return lines; } }; } + + static String toPrintable(String line) { + StringBuilder sb = null; + for (int i = 0; i < line.length(); i++) { + char ch = line.charAt(i); + if (Character.isISOControl(ch)) { + if (sb == null) { + sb = new StringBuilder(line.length() + TAB_SPACES.length()); + sb.append(line, 0, i); + } + if (ch == '\t') { + sb.append(TAB_SPACES); + } + } else if (sb != null) { + sb.append(ch); + } + } + return sb != null ? sb.toString() : line; + } } diff --git a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfAppendTest.java b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfAppendTest.java index 075947a4ef9fc..d5f0b76d17839 100644 --- a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfAppendTest.java +++ b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfAppendTest.java @@ -55,8 +55,6 @@ public void testAppend() throws Exception { PDDocument document = PDFUtil.textToPDF(originalText); - template.sendBodyAndHeader("direct:start", textToAppend, PdfHeaderConstants.PDF_DOCUMENT_HEADER_NAME, document); - resultEndpoint.setExpectedMessageCount(1); resultEndpoint.expectedMessagesMatches(new Predicate() { @Override @@ -77,6 +75,7 @@ public boolean matches(Exchange exchange) { return true; } }); + template.sendBodyAndHeader("direct:start", textToAppend, PdfHeaderConstants.PDF_DOCUMENT_HEADER_NAME, document); resultEndpoint.assertIsSatisfied(); } @@ -107,8 +106,6 @@ public void testAppendEncrypted() throws Exception { headers.put(PdfHeaderConstants.PDF_DOCUMENT_HEADER_NAME, encryptedDocument); headers.put(PdfHeaderConstants.DECRYPTION_MATERIAL_HEADER_NAME, new StandardDecryptionMaterial(userPass)); - template.sendBodyAndHeaders("direct:start", textToAppend, headers); - resultEndpoint.setExpectedMessageCount(1); resultEndpoint.expectedMessagesMatches(new Predicate() { @Override @@ -130,6 +127,7 @@ public boolean matches(Exchange exchange) { return true; } }); + template.sendBodyAndHeaders("direct:start", textToAppend, headers); resultEndpoint.assertIsSatisfied(); } diff --git a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfConverterTest.java b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfConverterTest.java index 20c986700c8b1..847b49e551ca7 100644 --- a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfConverterTest.java +++ b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfConverterTest.java @@ -17,21 +17,19 @@ package org.apache.camel.component.pdf; import java.io.ByteArrayOutputStream; -import java.io.IOException; import org.apache.camel.EndpointInject; -import org.apache.camel.RuntimeCamelException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit6.CamelTestSupport; import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.text.PDFTextStripper; import org.junit.jupiter.api.Test; import static org.apache.camel.component.pdf.PDFUtil.textToPDF; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class PdfConverterTest extends CamelTestSupport { @@ -39,34 +37,27 @@ public class PdfConverterTest extends CamelTestSupport { protected MockEndpoint resultEndpoint; @Test - public void testAppend() throws Exception { + public void testConvertByteArray() throws Exception { final String originalText = "Test"; - final PDDocument document = textToPDF(originalText); byte[] array; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + try (PDDocument document = textToPDF(originalText); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { document.save(baos); - array = baos.toByteArray(); - template.sendBodyAndHeader("direct:start", array, PdfHeaderConstants.PDF_DOCUMENT_HEADER_NAME, document); + } + + try (PDDocument doc = context.getTypeConverter().convertTo(PDDocument.class, array)) { + assertNotNull(doc); + assertEquals(1, doc.getNumberOfPages()); } resultEndpoint.setExpectedMessageCount(1); resultEndpoint.expectedMessagesMatches(exchange -> { - PDDocument doc = exchange.getIn().getBody(PDDocument.class); - - try { - PDFTextStripper pdfTextStripper = new PDFTextStripper(); - String text = pdfTextStripper.getText(doc); - assertEquals(2, doc.getNumberOfPages()); - assertThat(text, containsString(originalText)); - } catch (IOException e) { - throw new RuntimeCamelException(e); - } + assertThat(exchange.getIn().getBody(String.class), containsString(originalText)); return true; }); + template.sendBody("direct:start", array); resultEndpoint.assertIsSatisfied(); - } @Override diff --git a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfCreationTest.java b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfCreationTest.java index a3c2be8f181b8..72c6a4e64407a 100644 --- a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfCreationTest.java +++ b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfCreationTest.java @@ -16,20 +16,20 @@ */ package org.apache.camel.component.pdf; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; -import org.apache.camel.Exchange; -import org.apache.camel.Predicate; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.ResolveEndpointFailedException; import org.apache.camel.RuntimeCamelException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit6.CamelTestSupport; import org.apache.pdfbox.Loader; -import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.apache.pdfbox.text.PDFTextStripper; @@ -40,6 +40,8 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class PdfCreationTest extends CamelTestSupport { @@ -50,26 +52,21 @@ public class PdfCreationTest extends CamelTestSupport { @Test public void testPdfCreation() throws Exception { final String expectedText = "expectedText"; - template.sendBody("direct:start", expectedText); resultEndpoint.setExpectedMessageCount(1); - resultEndpoint.expectedMessagesMatches(new Predicate() { - @Override - public boolean matches(Exchange exchange) { - Object body = exchange.getIn().getBody(); - assertThat(body, instanceOf(ByteArrayOutputStream.class)); - try { - PDDocument doc = Loader.loadPDF( - new RandomAccessReadBuffer(new ByteArrayInputStream(((ByteArrayOutputStream) body).toByteArray()))); - PDFTextStripper pdfTextStripper = new PDFTextStripper(); - String text = pdfTextStripper.getText(doc); - assertEquals(1, doc.getNumberOfPages()); - assertThat(text, containsString(expectedText)); - } catch (IOException e) { - throw new RuntimeCamelException(e); - } - return true; + resultEndpoint.expectedMessagesMatches(exchange -> { + Object body = exchange.getIn().getBody(); + assertThat(body, instanceOf(ByteArrayOutputStream.class)); + try (PDDocument doc = Loader.loadPDF(((ByteArrayOutputStream) body).toByteArray())) { + String text = new PDFTextStripper().getText(doc); + assertEquals(1, doc.getNumberOfPages()); + assertThat(text, containsString(expectedText)); + } catch (IOException e) { + throw new RuntimeCamelException(e); } + return true; }); + + template.sendBody("direct:start", expectedText); resultEndpoint.assertIsSatisfied(); } @@ -82,36 +79,88 @@ public void testPdfCreationWithEncryption() throws Exception { accessPermission.setCanPrint(false); StandardProtectionPolicy protectionPolicy = new StandardProtectionPolicy(ownerPass, userPass, accessPermission); protectionPolicy.setEncryptionKeyLength(128); - template.sendBodyAndHeader("direct:start", - expectedText, - PdfHeaderConstants.PROTECTION_POLICY_HEADER_NAME, - protectionPolicy); resultEndpoint.setExpectedMessageCount(1); - resultEndpoint.expectedMessagesMatches(new Predicate() { - @Override - public boolean matches(Exchange exchange) { - Object body = exchange.getIn().getBody(); - assertThat(body, instanceOf(ByteArrayOutputStream.class)); - try { - PDDocument doc - = Loader.loadPDF(new RandomAccessReadBuffer( - new ByteArrayInputStream(((ByteArrayOutputStream) body).toByteArray())), userPass); - assertTrue(doc.isEncrypted(), "Expected encrypted document"); - assertFalse(doc.getCurrentAccessPermission().canPrint(), "Printing should not be permitted"); - PDFTextStripper pdfTextStripper = new PDFTextStripper(); - String text = pdfTextStripper.getText(doc); - assertEquals(1, doc.getNumberOfPages()); - assertThat(text, containsString(expectedText)); - } catch (Exception e) { - throw new RuntimeCamelException(e); - } - return true; + resultEndpoint.expectedMessagesMatches(exchange -> { + Object body = exchange.getIn().getBody(); + assertThat(body, instanceOf(ByteArrayOutputStream.class)); + try (PDDocument doc = Loader.loadPDF(((ByteArrayOutputStream) body).toByteArray(), userPass)) { + assertTrue(doc.isEncrypted(), "Expected encrypted document"); + assertFalse(doc.getCurrentAccessPermission().canPrint(), "Printing should not be permitted"); + String text = new PDFTextStripper().getText(doc); + assertEquals(1, doc.getNumberOfPages()); + assertThat(text, containsString(expectedText)); + } catch (IOException e) { + throw new RuntimeCamelException(e); } + return true; }); + + template.sendBodyAndHeader("direct:start", + expectedText, + PdfHeaderConstants.PROTECTION_POLICY_HEADER_NAME, + protectionPolicy); resultEndpoint.assertIsSatisfied(); } + @Test + public void testPageSizeOfCatalog() throws Exception { + assertMediaBox(PDRectangle.A4, "pdf:create"); + assertMediaBox(PDRectangle.A4, "pdf:create?pageSize=A4"); + assertMediaBox(PDRectangle.A5, "pdf:create?pageSize=A5"); + assertMediaBox(PDRectangle.LETTER, "pdf:create?pageSize=LETTER"); + assertMediaBox(PDRectangle.LEGAL, "pdf:create?pageSize=LEGAL"); + assertMediaBox(PDRectangle.A4, "pdf:create?pageSize=A4&textProcessingFactory=autoFormatting"); + } + + @Test + public void testPageSizeOfOldName() throws Exception { + assertMediaBox(PDRectangle.A1, "pdf:create?pageSize=PAGE_SIZE_A1"); + assertMediaBox(PDRectangle.LETTER, "pdf:create?pageSize=PAGE_SIZE_LETTER"); + } + + @Test + public void testUnknownPageSize() { + Exception e = assertThrows(ResolveEndpointFailedException.class, + () -> context.getEndpoint("pdf:create?pageSize=B5")); + assertThat(e.getCause().getMessage(), containsString("B5")); + assertThat(e.getCause().getMessage(), containsString("LETTER, LEGAL, A0, A1, A2, A3, A4, A5, A6")); + } + + @Test + public void testCreateWithoutBody() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + @Test + public void testCreateWithLineEndsAndTab() throws Exception { + Object body = template.requestBody("direct:create", "line one\r\nline two\rline\tthree\nline four\u0007"); + try (PDDocument doc = Loader.loadPDF(((ByteArrayOutputStream) body).toByteArray())) { + String text = new PDFTextStripper().getText(doc); + assertThat(text, containsString("line one")); + assertThat(text, containsString("line two")); + assertThat(text, containsString("line three")); + assertThat(text, containsString("line four")); + } + } + + @Test + public void testCreateThenExtractText() { + String text = template.requestBody("direct:createExtract", "Hello from create", String.class); + assertThat(text, containsString("Hello from create")); + } + + private void assertMediaBox(PDRectangle expected, String uri) throws IOException { + Object body = template.requestBody(uri, "Some text"); + try (PDDocument doc = Loader.loadPDF(((ByteArrayOutputStream) body).toByteArray())) { + PDRectangle mediaBox = doc.getPage(0).getMediaBox(); + assertEquals(expected.getWidth(), mediaBox.getWidth(), 0.01, uri); + assertEquals(expected.getHeight(), mediaBox.getHeight(), 0.01, uri); + } + } + @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -120,6 +169,11 @@ public void configure() { from("direct:start") .to("pdf:create?fontSize=6&font=COURIER&pageSize=PAGE_SIZE_A1") .to("mock:result"); + from("direct:create") + .to("pdf:create"); + from("direct:createExtract") + .to("pdf:create") + .to("pdf:extractText"); } }; } diff --git a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfMergeTest.java b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfMergeTest.java index 13e3f8fcfd249..815bfdda684d4 100644 --- a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfMergeTest.java +++ b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfMergeTest.java @@ -16,56 +16,86 @@ */ package org.apache.camel.component.pdf; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.List; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; import org.apache.camel.RuntimeCamelException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit6.CamelTestSupport; import org.apache.pdfbox.Loader; -import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; public class PdfMergeTest extends CamelTestSupport { @EndpointInject("mock:result") protected MockEndpoint resultEndpoint; + @TempDir + File tempDir; + + private File pdfFile1; + private File pdfFile2; + + @BeforeEach + void createPdfFiles() throws IOException { + pdfFile1 = new File(tempDir, "pdf1.pdf"); + try (PDDocument document1 = PDFUtil.textToPDF("PDF 1 text")) { + document1.save(pdfFile1); + } + pdfFile2 = new File(tempDir, "pdf2.pdf"); + try (PDDocument document2 = PDFUtil.textToPDF("PDF 2 text")) { + document2.save(pdfFile2); + } + } + @Test public void testMerge() throws Exception { - final String pdf1 = "PDF 1 text"; - PDDocument document1 = PDFUtil.textToPDF(pdf1); - File pdfFile1 = File.createTempFile("pdf1", "pdf"); - document1.save(pdfFile1); - final String pdf2 = "PDF 2 text"; - PDDocument document2 = PDFUtil.textToPDF(pdf2); - File pdfFile2 = File.createTempFile("pdf2", "pdf"); - document2.save(pdfFile2); - + expectMerged(); template.sendBodyAndHeader("direct:start", "", PdfHeaderConstants.FILES_TO_MERGE_HEADER_NAME, List.of(pdfFile1, pdfFile2)); + resultEndpoint.assertIsSatisfied(); + } + @Test + public void testMergePath() throws Exception { + expectMerged(); + template.sendBodyAndHeader("direct:start", "", PdfHeaderConstants.FILES_TO_MERGE_HEADER_NAME, + List.of(pdfFile1.toPath(), pdfFile2.toPath())); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testMergeUnknownElement() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBodyAndHeader("direct:start", "", PdfHeaderConstants.FILES_TO_MERGE_HEADER_NAME, + List.of(pdfFile1, pdfFile2.getAbsolutePath()))); + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertThat(e.getCause().getMessage(), containsString("java.lang.String")); + } + + private void expectMerged() { resultEndpoint.setExpectedMessageCount(1); resultEndpoint.expectedMessagesMatches(exchange -> { Object body = exchange.getIn().getBody(); assertThat(body, instanceOf(ByteArrayOutputStream.class)); - try { - PDDocument doc = Loader.loadPDF( - new RandomAccessReadBuffer(new ByteArrayInputStream(((ByteArrayOutputStream) body).toByteArray()))); - PDFTextStripper pdfTextStripper = new PDFTextStripper(); - String text = pdfTextStripper.getText(doc); + try (PDDocument doc = Loader.loadPDF(((ByteArrayOutputStream) body).toByteArray())) { + String text = new PDFTextStripper().getText(doc); assertEquals(2, doc.getNumberOfPages()); assertThat(text, containsString("PDF 1 text")); assertThat(text, containsString("PDF 2 text")); @@ -74,8 +104,6 @@ public void testMerge() throws Exception { } return true; }); - resultEndpoint.assertIsSatisfied(); - } @Override diff --git a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfTextExtractionTest.java b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfTextExtractionTest.java index 7fe5e6aacc023..040bc75aabf6c 100644 --- a/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfTextExtractionTest.java +++ b/components/camel-pdf/src/test/java/org/apache/camel/component/pdf/PdfTextExtractionTest.java @@ -18,47 +18,44 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; -import org.apache.camel.Exchange; -import org.apache.camel.Predicate; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.junit6.CamelTestSupport; import org.apache.pdfbox.Loader; -import org.apache.pdfbox.io.RandomAccessReadBuffer; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial; import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; public class PdfTextExtractionTest extends CamelTestSupport { + private static final String EXPECTED_TEXT = "Test string"; + @EndpointInject("mock:result") protected MockEndpoint resultEndpoint; + @TempDir + File tempDir; + @Test public void testExtractText() throws Exception { - final String expectedText = "Test string"; - PDDocument document = PDFUtil.textToPDF(expectedText); + PDDocument document = PDFUtil.textToPDF(EXPECTED_TEXT); + expectText(); template.sendBody("direct:start", document); - - resultEndpoint.setExpectedMessageCount(1); - resultEndpoint.expectedMessagesMatches(new Predicate() { - @Override - public boolean matches(Exchange exchange) { - Object body = exchange.getIn().getBody(); - assertThat(body, instanceOf(String.class)); - assertThat((String) body, containsString(expectedText)); - return true; - } - }); resultEndpoint.assertIsSatisfied(); } @@ -71,8 +68,7 @@ public void testExtractTextFromEncrypted() throws Exception { StandardProtectionPolicy protectionPolicy = new StandardProtectionPolicy(ownerPass, userPass, accessPermission); protectionPolicy.setEncryptionKeyLength(128); - final String expectedText = "Test string"; - PDDocument document = PDFUtil.textToPDF(expectedText); + PDDocument document = PDFUtil.textToPDF(EXPECTED_TEXT); document.protect(protectionPolicy); @@ -80,26 +76,83 @@ public void testExtractTextFromEncrypted() throws Exception { document.save(output); // Encryption happens after saving. - PDDocument encryptedDocument - = Loader.loadPDF(new RandomAccessReadBuffer(new ByteArrayInputStream(output.toByteArray())), userPass); + PDDocument encryptedDocument = Loader.loadPDF(output.toByteArray(), userPass); + expectText(); template.sendBodyAndHeader("direct:start", encryptedDocument, PdfHeaderConstants.DECRYPTION_MATERIAL_HEADER_NAME, new StandardDecryptionMaterial(userPass)); + resultEndpoint.assertIsSatisfied(); + } + @Test + public void testExtractTextFromBytes() throws Exception { + expectText(); + template.sendBody("direct:start", pdfBytes()); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testExtractTextFromByteArrayOutputStream() throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(pdfBytes()); + + expectText(); + template.sendBody("direct:start", bos); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testExtractTextFromInputStream() throws Exception { + expectText(); + template.sendBody("direct:start", new ByteArrayInputStream(pdfBytes())); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testExtractTextFromFile() throws Exception { + File file = new File(tempDir, "test.pdf"); + try (PDDocument document = PDFUtil.textToPDF(EXPECTED_TEXT)) { + document.save(file); + } + + expectText(); + template.sendBody("direct:start", file); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testExtractTextFromText() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", "not a pdf")); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + assertThat(e.getCause().getMessage(), containsString("java.lang.String")); + } + + @Test + public void testExtractTextWithoutBody() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + private void expectText() { resultEndpoint.setExpectedMessageCount(1); - resultEndpoint.expectedMessagesMatches(new Predicate() { - @Override - public boolean matches(Exchange exchange) { - Object body = exchange.getIn().getBody(); - assertThat(body, instanceOf(String.class)); - assertThat((String) body, containsString(expectedText)); - return true; - } + resultEndpoint.expectedMessagesMatches(exchange -> { + Object body = exchange.getIn().getBody(); + assertThat(body, instanceOf(String.class)); + assertThat((String) body, containsString(EXPECTED_TEXT)); + return true; }); - resultEndpoint.assertIsSatisfied(); - document.isEncrypted(); + } + + private static byte[] pdfBytes() throws Exception { + try (PDDocument document = PDFUtil.textToPDF(EXPECTED_TEXT)) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + document.save(bos); + return bos.toByteArray(); + } } @Override diff --git a/components/camel-smb/src/generated/resources/META-INF/org/apache/camel/component/smb/smb.json b/components/camel-smb/src/generated/resources/META-INF/org/apache/camel/component/smb/smb.json index 89a91c6824397..1de0619cbabe2 100644 --- a/components/camel-smb/src/generated/resources/META-INF/org/apache/camel/component/smb/smb.json +++ b/components/camel-smb/src/generated/resources/META-INF/org/apache/camel/component/smb/smb.json @@ -69,7 +69,7 @@ "sendEmptyMessageWhenIdle": { "index": 16, "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." }, "streamDownload": { "index": 17, "kind": "parameter", "displayName": "Stream Download", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Sets the download method to use when not using a local working directory. If set to true, the remote files are streamed to the route as they are read. When set to false, the remote files are loaded into memory before being sent into the route." }, "bridgeErrorHandler": { "index": 18, "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." }, - "download": { "index": 19, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Whether the SMB consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, + "download": { "index": 19, "kind": "parameter", "displayName": "Download", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "configurationClass": "org.apache.camel.component.smb.SmbConfiguration", "configurationField": "configuration", "description": "Whether the SMB consumer should download the file. If this option is set to false, then the message body will be null, but the consumer will still trigger a Camel Exchange that has details about the file such as file name, file size, etc. It's just that the file will not be downloaded." }, "exceptionHandler": { "index": 20, "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": 21, "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." }, "inProgressRepository": { "index": 22, "kind": "parameter", "displayName": "In Progress Repository", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.IdempotentRepository", "deprecated": false, "autowired": false, "secret": false, "description": "A pluggable in-progress repository org.apache.camel.spi.IdempotentRepository. The in-progress repository is used to account the current in progress files being consumed. By default a memory based repository is used." }, diff --git a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConfiguration.java b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConfiguration.java index 060158eac504a..f11dd27dbd578 100644 --- a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConfiguration.java +++ b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbConfiguration.java @@ -61,10 +61,11 @@ public class SmbConfiguration extends GenericFileConfiguration { + " false, the remote files are loaded into memory before being sent into the route.") @Metadata private boolean streamDownload; - @UriParam(label = "consumer,advanced", description = "Whether the SMB consumer should download the file. If this " - + "option is set to false, then the message body will be null, but the consumer will still trigger a Camel " - + "Exchange that has details about the file such as file name, file size, etc. It's just that the file will " - + "not be downloaded.") + @UriParam(label = "consumer,advanced", defaultValue = "true", + description = "Whether the SMB consumer should download the file. If this " + + "option is set to false, then the message body will be null, but the consumer will still trigger a Camel " + + "Exchange that has details about the file such as file name, file size, etc. It's just that the file will " + + "not be downloaded.") private boolean download = true; @UriParam(label = "common", description = "Whether or not to disconnect from remote SMB share right after use. " + "Disconnect will only disconnect the current connection to the SMB share. If you have a consumer which " diff --git a/components/camel-snmp/src/generated/java/org/apache/camel/component/snmp/SnmpConvertersLoader.java b/components/camel-snmp/src/generated/java/org/apache/camel/component/snmp/SnmpConvertersLoader.java index 9463c98b40483..62c7456f65ca3 100644 --- a/components/camel-snmp/src/generated/java/org/apache/camel/component/snmp/SnmpConvertersLoader.java +++ b/components/camel-snmp/src/generated/java/org/apache/camel/component/snmp/SnmpConvertersLoader.java @@ -44,6 +44,14 @@ public void load(TypeConverterRegistry registry) throws TypeConverterLoaderExcep } private void registerConverters(TypeConverterRegistry registry) { + addTypeConverter(registry, java.lang.String.class, org.apache.camel.component.snmp.SnmpMessage.class, false, + (type, exchange, value) -> { + Object answer = org.apache.camel.component.snmp.SnmpConverters.toString((org.apache.camel.component.snmp.SnmpMessage) value); + if (false && answer == null) { + answer = Void.class; + } + return answer; + }); addTypeConverter(registry, java.lang.String.class, org.snmp4j.PDU.class, false, (type, exchange, value) -> { Object answer = org.apache.camel.component.snmp.SnmpConverters.toString((org.snmp4j.PDU) value); diff --git a/components/camel-snmp/src/generated/resources/META-INF/org/apache/camel/component/snmp/snmp.json b/components/camel-snmp/src/generated/resources/META-INF/org/apache/camel/component/snmp/snmp.json index 4dc8c5a836cd3..d2da3dc0f0c90 100644 --- a/components/camel-snmp/src/generated/resources/META-INF/org/apache/camel/component/snmp/snmp.json +++ b/components/camel-snmp/src/generated/resources/META-INF/org/apache/camel/component/snmp/snmp.json @@ -41,7 +41,7 @@ "snmpContextName": { "index": 7, "kind": "parameter", "displayName": "Snmp Context Name", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the context name field of this scoped PDU." }, "snmpVersion": { "index": 8, "kind": "parameter", "displayName": "Snmp Version", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "int", "enum": [ "0", "1", "3" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, "description": "Sets the snmp version for the request. The value 0 means SNMPv1, 1 means SNMPv2c, and the value 3 means SNMPv3" }, "timeout": { "index": 9, "kind": "parameter", "displayName": "Timeout", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1500, "description": "Sets the timeout value for the request in millis." }, - "type": { "index": 10, "kind": "parameter", "displayName": "Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.snmp.SnmpActionType", "enum": [ "TRAP", "POLL", "GET_NEXT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Which operation to perform such as poll, trap, etc." }, + "type": { "index": 10, "kind": "parameter", "displayName": "Type", "group": "common", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.snmp.SnmpActionType", "enum": [ "TRAP", "POLL", "GET_NEXT" ], "deprecated": false, "autowired": false, "secret": false, "description": "Which operation to perform such as poll, trap, etc. The consumer supports TRAP (receive traps) and POLL (poll the oids). The producer supports TRAP (send the PDU of the body as a trap), POLL (a GET of the oids, also done when no type is set) and GET_NEXT (a walk of the oids, for the producer only)." }, "delay": { "index": 11, "kind": "parameter", "displayName": "Delay", "group": "consumer", "label": "consumer", "required": false, "type": "duration", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "60000", "description": "Milliseconds before the next poll." }, "sendEmptyMessageWhenIdle": { "index": 12, "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." }, "treeList": { "index": 13, "kind": "parameter", "displayName": "Tree List", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets the flag whether the scoped PDU will be displayed as the list if it has child elements in its tree" }, diff --git a/components/camel-snmp/src/main/docs/snmp-component.adoc b/components/camel-snmp/src/main/docs/snmp-component.adoc index 2f0eda76b8e8a..e08d2731e2f8e 100644 --- a/components/camel-snmp/src/main/docs/snmp-component.adoc +++ b/components/camel-snmp/src/main/docs/snmp-component.adoc @@ -39,9 +39,15 @@ and receiving traps. == Snmp Producer -It can also be used to request information using GET method. +It can also be used to request information using GET method: the producer with `type=POLL`, or without a `type`, +sends a GET request of the `oids` of the endpoint. With `type=GET_NEXT` the producer walks the `oids` +(a GET_NEXT request is for the producer only, the consumer supports the types `TRAP` and `POLL`). +With `type=TRAP` the producer sends the `org.snmp4j.PDU` of the message body as a trap. -The response body type is `org.apache.camel.component.snmp.SnmpMessage`. +The response body type is `org.apache.camel.component.snmp.SnmpMessage` (a list of them for `GET_NEXT`), and it converts +to `String` as the XML text shown in "The result of a poll" below. + +Without a port in the endpoint, the default port is 162 for `type=TRAP` and 161 otherwise. // component options: START @@ -108,7 +114,7 @@ Be prepared. ------------------------- As you may notice, default `snmpVersion` is 0, which means `version1` in the endpoint if it is not set explicitly. Make sure you explicitly set `snmpVersion` which is not default value, in a case of where you are able to query SNMP tables with different versions. -Other possible values are `version2c` and `version3`. +Other possible values are 1, which means `version2c`, and 3, which means `version3`. == Examples @@ -125,7 +131,8 @@ snmp:127.0.0.1:162?protocol=udp&type=TRAP ----------------------------------------- You can get the community of SNMP TRAP with message -header `securityName`, peer address of the SNMP TRAP with message header `peerAddress`. +header `securityName` (an `org.snmp4j.smi.OctetString`), peer address of the SNMP TRAP with message header `peerAddress` +(an `org.snmp4j.smi.Address`). Routing example in Java: (converts the SNMP PDU to XML String) diff --git a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpConverters.java b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpConverters.java index 48eb2d7a58afb..793b2db8eb40a 100644 --- a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpConverters.java +++ b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpConverters.java @@ -85,6 +85,19 @@ private static void entryAppend(StringBuilder sb, String tag, String value) { sb.append(ENTRY_TAG_CLOSE); } + /** + * Converts the given snmp message, such as the body of the producer, to the XML text of its pdu, as the body of the + * consumer. + * + * @param message the snmp message + * @return the text content, or null if the message has no pdu + */ + @Converter + public static String toString(SnmpMessage message) { + PDU pdu = message.getSnmpMessage(); + return pdu != null ? toString(pdu) : null; + } + /** * Converts the given snmp pdu to a String body. * diff --git a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpEndpoint.java b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpEndpoint.java index 81bf37263a510..fd3ec93106e23 100644 --- a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpEndpoint.java +++ b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpEndpoint.java @@ -128,7 +128,9 @@ public Consumer createConsumer(Processor processor) throws Exception { configureConsumer(answer); return answer; } else { - throw new IllegalArgumentException("The type '" + this.type + "' is not valid!"); + throw new IllegalArgumentException( + "The snmp consumer needs the type option set to TRAP or POLL, not " + this.type + + " (GET_NEXT is for the producer)"); } } @@ -170,7 +172,9 @@ public SnmpActionType getType() { } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer supports TRAP (receive traps) and POLL (poll the + * oids). The producer supports TRAP (send the PDU of the body as a trap), POLL (a GET of the oids, also done when + * no type is set) and GET_NEXT (a walk of the oids, for the producer only). */ public void setType(SnmpActionType type) { this.type = type; @@ -265,10 +269,10 @@ protected void doInit() throws Exception { host = "127.0.0.1"; } if (port == -1) { - if (getType() == SnmpActionType.POLL) { - port = 161; // default snmp poll port - } else { + if (getType() == SnmpActionType.TRAP) { port = 162; // default trap port + } else { + port = 161; // default snmp port of GET and GET_NEXT requests } } diff --git a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpOIDPoller.java b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpOIDPoller.java index 71a24ebe25acc..748496a03e83e 100644 --- a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpOIDPoller.java +++ b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpOIDPoller.java @@ -28,7 +28,6 @@ import org.snmp4j.Target; import org.snmp4j.TransportMapping; import org.snmp4j.event.ResponseEvent; -import org.snmp4j.event.ResponseListener; import org.snmp4j.smi.Address; import org.snmp4j.smi.GenericAddress; import org.snmp4j.smi.OID; @@ -39,7 +38,7 @@ import org.snmp4j.util.TreeEvent; import org.snmp4j.util.TreeUtils; -public class SnmpOIDPoller extends ScheduledPollConsumer implements ResponseListener { +public class SnmpOIDPoller extends ScheduledPollConsumer { private static final Logger LOG = LoggerFactory.getLogger(SnmpOIDPoller.class); @@ -48,7 +47,6 @@ public class SnmpOIDPoller extends ScheduledPollConsumer implements ResponseList private Snmp snmp; private Target target; - private PDU pdu; private final SnmpEndpoint endpoint; public SnmpOIDPoller(SnmpEndpoint endpoint, Processor processor) { @@ -73,7 +71,6 @@ protected void doStart() throws Exception { this.snmp = new Snmp(this.transport); SnmpHelper.createAndSetUSM(endpoint); - this.pdu = SnmpHelper.createPDU(endpoint); this.target = SnmpHelper.createTarget(endpoint); // listen to the transport @@ -100,16 +97,14 @@ protected void doStop() throws Exception { @Override protected int poll() throws Exception { - this.pdu.clear(); - - int type = this.getPduType(this.endpoint.getType()); - - this.pdu.setType(type); + // a new request PDU for each poll, as SNMP4J keeps the PDU of a request to send it again on a retry + PDU pdu = SnmpHelper.createPDU(endpoint); + pdu.setType(this.getPduType(this.endpoint.getType())); if (!endpoint.isTreeList()) { // prepare the request items for (OID oid : this.endpoint.getOids()) { - this.pdu.add(new VariableBinding(oid)); + pdu.add(new VariableBinding(oid)); } } else { TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory()); @@ -132,34 +127,22 @@ protected int poll() throws Exception { if (varBinding == null) { continue; } - this.pdu.add(varBinding); + pdu.add(varBinding); } } } } - // send the request - snmp.send(pdu, target, null, this); - - return 1; - } - - @Override - public void onResponse(ResponseEvent event) { - // Always cancel async request when response has been received - // otherwise a memory leak is created! Not canceling a request - // immediately can be useful when sending a request to a broadcast address. - ((Snmp) event.getSource()).cancel(event.getRequest(), this); - - // check for valid response - if (event.getRequest() == null || event.getResponse() == null) { - // ignore null requests/responses - LOG.debug("Received invalid SNMP event. Request: {} / Response: {}", event.getRequest(), event.getResponse()); - return; + // send the request and wait for the response, so the poll returns the number of messages it polled + ResponseEvent event = snmp.send(pdu, target); + if (event == null || event.getResponse() == null) { + // no response within the timeout and retries of the endpoint + LOG.debug("Received no SNMP response from {}", endpoint.getServerAddress()); + return 0; } - PDU pdu = event.getResponse(); - processPDU(pdu); + processPDU(event.getResponse()); + return 1; } /** diff --git a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpProducer.java b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpProducer.java index 89a0de8ab2f3e..9696c73c5e124 100644 --- a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpProducer.java +++ b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpProducer.java @@ -79,8 +79,8 @@ protected void doStart() throws Exception { private PDU createPdu() { PDU pdu = SnmpHelper.createPDU(endpoint); - // in here,only POLL do set the oids - if (this.actionType == SnmpActionType.POLL) { + // a GET (type POLL or no type) asks for the oids; a walk adds its oid for each request + if (this.actionType != SnmpActionType.GET_NEXT) { for (OID oid : this.endpoint.getOids()) { pdu.add(new VariableBinding(oid)); } diff --git a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpTrapProducer.java b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpTrapProducer.java index b55ba2fd204b2..3ec2b15a732c3 100644 --- a/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpTrapProducer.java +++ b/components/camel-snmp/src/main/java/org/apache/camel/component/snmp/SnmpTrapProducer.java @@ -98,7 +98,7 @@ public void process(final Exchange exchange) throws Exception { snmp = new Snmp(transport); LOG.debug("SnmpTrap: getting pdu from body"); - PDU trap = exchange.getIn().getBody(PDU.class); + PDU trap = exchange.getIn().getMandatoryBody(PDU.class); trap.setErrorIndex(0); trap.setErrorStatus(0); diff --git a/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/PollOIDIdleTest.java b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/PollOIDIdleTest.java new file mode 100644 index 0000000000000..8cc891f8febab --- /dev/null +++ b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/PollOIDIdleTest.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.component.snmp; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.AvailablePortFinder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.snmp4j.mp.SnmpConstants; + +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * A poll without an answer polls no message, so sendEmptyMessageWhenIdle sends an empty message. + */ +public class PollOIDIdleTest extends SnmpTestSupport { + + @RegisterExtension + AvailablePortFinder.Port port = AvailablePortFinder.find(); + + @Test + public void testNoAnswerIsIdle() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:idle"); + mock.expectedMinimumMessageCount(1); + mock.assertIsSatisfied(); + + assertNull(mock.getReceivedExchanges().get(0).getIn().getBody()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // no agent listens on the port + from(String.format( + "snmp:127.0.0.1:%d?protocol=udp&type=POLL&oids=%s&delay=100&timeout=100&retries=0" + + "&sendEmptyMessageWhenIdle=true", + port.getPort(), SnmpConstants.sysName)) + .to("mock:idle"); + } + }; + } +} diff --git a/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/ProducerWithoutTypeTest.java b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/ProducerWithoutTypeTest.java new file mode 100644 index 0000000000000..cef7259c3bb20 --- /dev/null +++ b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/ProducerWithoutTypeTest.java @@ -0,0 +1,73 @@ +/* + * 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.snmp; + +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.snmp4j.PDU; +import org.snmp4j.smi.OID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * A producer without type sends a GET with the oids of the endpoint, and its body converts to the XML text. + */ +public class ProducerWithoutTypeTest extends SnmpRespondTestSupport { + + private static final String OID = "1.3.6.1.2.1.2.2.1.2"; + + @ParameterizedTest + @MethodSource("supportedVersions") + public void testGetWithoutType(int version) { + Object body = template.requestBody("direct:inV" + version, ""); + + SnmpMessage snmpMessage = assertInstanceOf(SnmpMessage.class, body); + // the test agent answers the next oid of the requested one (and gives the request back) + PDU response = snmpMessage.getSnmpMessage(); + assertNotNull(response.getVariable(new OID(OID + ".1"))); + assertEquals("ether1", response.getVariable(new OID(OID + ".1")).toString()); + } + + @ParameterizedTest + @MethodSource("supportedVersions") + public void testConvertBodyToString(int version) { + String body = template.requestBody("direct:textV" + version, "", String.class); + + assertTrue(body.startsWith(""), body); + assertTrue(body.contains("" + OID + ".1ether1"), body); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:inV0").to("snmp://" + getListeningAddress() + "?oids=" + OID); + from("direct:inV1").to("snmp://" + getListeningAddress() + "?snmpVersion=1&oids=" + OID); + from("direct:inV3").to("snmp://" + getListeningAddress() + + "?securityName=test&securityLevel=1&snmpVersion=3&oids=" + OID); + + from("direct:textV0").to("direct:inV0").convertBodyTo(String.class); + from("direct:textV1").to("direct:inV1").convertBodyTo(String.class); + from("direct:textV3").to("direct:inV3").convertBodyTo(String.class); + } + }; + } +} diff --git a/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/TrapProducerBodyTest.java b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/TrapProducerBodyTest.java new file mode 100644 index 0000000000000..513b00e713394 --- /dev/null +++ b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/TrapProducerBodyTest.java @@ -0,0 +1,61 @@ +/* + * 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.snmp; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.InvalidPayloadException; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.AvailablePortFinder; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The trap producer needs a PDU body. + */ +public class TrapProducerBodyTest extends SnmpTestSupport { + + @RegisterExtension + AvailablePortFinder.Port port = AvailablePortFinder.find(); + + @Test + public void testTextBody() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:trap", "Hello")); + InvalidPayloadException cause = assertInstanceOf(InvalidPayloadException.class, e.getCause()); + assertTrue(cause.getMessage().contains("org.snmp4j.PDU"), cause.getMessage()); + } + + @Test + public void testNoBody() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:trap", null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:trap").to("snmp:127.0.0.1:" + port.getPort() + "?protocol=udp&type=TRAP&snmpVersion=1"); + } + }; + } +} diff --git a/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/UriConfigurationTest.java b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/UriConfigurationTest.java index 6a9c9b9ce9265..0715dca19a1bd 100644 --- a/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/UriConfigurationTest.java +++ b/components/camel-snmp/src/test/java/org/apache/camel/component/snmp/UriConfigurationTest.java @@ -22,6 +22,8 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class UriConfigurationTest { @@ -65,4 +67,38 @@ public void testOidPollerConfiguration() { assertEquals("1.3.6.1.2.1.7.5.1", snmpEndpoint.getOids().get(0).toString()); assertEquals("udp:127.0.0.1/1662", snmpEndpoint.getServerAddress()); } + + @Test + public void testProducerWithoutTypeAndPortConfiguration() { + context.start(); + + SnmpEndpoint snmpEndpoint = context.getEndpoint("snmp:127.0.0.1?oids=1.3.6.1.2.1.1.1.0", SnmpEndpoint.class); + + assertNull(snmpEndpoint.getType()); + assertEquals("udp:127.0.0.1/161", snmpEndpoint.getServerAddress()); + } + + @Test + public void testGetNextWithoutPortConfiguration() { + context.start(); + + SnmpEndpoint snmpEndpoint + = context.getEndpoint("snmp:127.0.0.1?type=GET_NEXT&oids=1.3.6.1.2.1.1.1.0", SnmpEndpoint.class); + + assertEquals(SnmpActionType.GET_NEXT, snmpEndpoint.getType()); + assertEquals("udp:127.0.0.1/161", snmpEndpoint.getServerAddress()); + } + + @Test + public void testConsumerWithGetNextFails() { + context.start(); + + SnmpEndpoint snmpEndpoint + = context.getEndpoint("snmp:127.0.0.1?type=GET_NEXT&oids=1.3.6.1.2.1.1.1.0", SnmpEndpoint.class); + + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> snmpEndpoint.createConsumer(exchange -> { + })); + assertTrue(e.getMessage().contains("TRAP or POLL"), e.getMessage()); + } } diff --git a/components/camel-tika/src/generated/resources/META-INF/org/apache/camel/component/tika/tika.json b/components/camel-tika/src/generated/resources/META-INF/org/apache/camel/component/tika/tika.json index ddc7c899b1b78..80a2cf26249d9 100644 --- a/components/camel-tika/src/generated/resources/META-INF/org/apache/camel/component/tika/tika.json +++ b/components/camel-tika/src/generated/resources/META-INF/org/apache/camel/component/tika/tika.json @@ -29,8 +29,8 @@ }, "properties": { "operation": { "index": 0, "kind": "path", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaOperation", "enum": [ "parse", "detect" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Operation type" }, - "tikaParseOutputEncoding": { "index": 1, "kind": "parameter", "displayName": "Tika Parse Output Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Parse Output Encoding" }, - "tikaParseOutputFormat": { "index": 2, "kind": "parameter", "displayName": "Tika Parse Output Format", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaParseOutputFormat", "enum": [ "xml", "html", "text" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "xml", "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Output Format. Supported output formats. xml: Returns Parsed Content as XML. html: Returns Parsed Content as HTML. text: Returns Parsed Content as Text. textMain: Uses the boilerpipe library to automatically extract the main content from a web page." }, + "tikaParseOutputEncoding": { "index": 1, "kind": "parameter", "displayName": "Tika Parse Output Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "The character encoding of the parsed output. Defaults to the default charset of the JVM. The parse operation sets this encoding as the charset of the exchange, so converting the output to text decodes it with this encoding." }, + "tikaParseOutputFormat": { "index": 2, "kind": "parameter", "displayName": "Tika Parse Output Format", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.tika.TikaParseOutputFormat", "enum": [ "xml", "html", "text" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "xml", "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika Output Format. Supported output formats. xml: Returns Parsed Content as XML. html: Returns Parsed Content as HTML. text: Returns Parsed Content as Text." }, "lazyStartProducer": { "index": 3, "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." }, "tikaConfigFile": { "index": 4, "kind": "parameter", "displayName": "Tika Config File", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Path to a Tika JSON configuration file" }, "tikaLoader": { "index": 5, "kind": "parameter", "displayName": "Tika Loader", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.tika.config.loader.TikaLoader", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.tika.TikaConfiguration", "configurationField": "tikaConfiguration", "description": "Tika loader used to configure parsers and detectors" } diff --git a/components/camel-tika/src/main/docs/tika-component.adoc b/components/camel-tika/src/main/docs/tika-component.adoc index 52c7916bdd820..5c60e4eea4c56 100644 --- a/components/camel-tika/src/main/docs/tika-component.adoc +++ b/components/camel-tika/src/main/docs/tika-component.adoc @@ -82,6 +82,12 @@ YAML:: The file should be placed in the Body. +The body of `detect` and `parse` must convert to an `InputStream`, such as a `java.io.File`, a `byte[]`, a `String` or +a stream. A missing body or a body that does not convert, such as a `Map`, fails with an `InvalidPayloadException`. + +The `parse` operation writes its output in the `tikaParseOutputEncoding` and sets that encoding as the charset of the +exchange (the `CamelCharsetName` exchange property), so converting the output to a `String` decodes it right. + [tabs] ==== Java:: diff --git a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaConfiguration.java b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaConfiguration.java index 9f2dee1dd0ab8..2f9a9636492be 100644 --- a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaConfiguration.java +++ b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaConfiguration.java @@ -32,7 +32,9 @@ public class TikaConfiguration { private TikaOperation operation; @UriParam(defaultValue = "xml") private TikaParseOutputFormat tikaParseOutputFormat = TikaParseOutputFormat.xml; - @UriParam(description = "Tika Parse Output Encoding") + @UriParam(description = "The character encoding of the parsed output. Defaults to the default charset of the JVM." + + " The parse operation sets this encoding as the charset of the exchange, so converting" + + " the output to text decodes it with this encoding.") private String tikaParseOutputEncoding = Charset.defaultCharset().name(); @UriParam(label = "advanced", description = "Tika loader used to configure parsers and detectors") private TikaLoader tikaLoader; @@ -64,8 +66,6 @@ public TikaParseOutputFormat getTikaParseOutputFormat() { *
  • xml: Returns Parsed Content as XML.
  • *
  • html: Returns Parsed Content as HTML.
  • *
  • text: Returns Parsed Content as Text.
  • - *
  • textMain: Uses the boilerpipe library to automatically - * extract the main content from a web page.
  • * */ public void setTikaParseOutputFormat(TikaParseOutputFormat tikaParseOutputFormat) { @@ -78,7 +78,8 @@ public String getTikaParseOutputEncoding() { /** * Tika Parse Output Encoding - Used to specify the character encoding of the parsed output. Defaults to - * Charset.defaultCharset(). + * Charset.defaultCharset(). The parse operation sets this encoding as the charset of the exchange, so converting + * the output to text decodes it with this encoding. */ public void setTikaParseOutputEncoding(String tikaParseOutputEncoding) { this.tikaParseOutputEncoding = tikaParseOutputEncoding; diff --git a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java index 99cba88703987..7ccca28fd4534 100644 --- a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java +++ b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java @@ -36,6 +36,8 @@ import org.xml.sax.SAXException; import org.apache.camel.Exchange; +import org.apache.camel.ExchangePropertyKey; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.RuntimeCamelException; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.support.DefaultHeaderFilterStrategy; @@ -125,11 +127,11 @@ public void process(Exchange exchange) throws Exception { exchange.getMessage().setBody(result); } - private Object doDetect(Exchange exchange) throws IOException, TikaConfigException { + private Object doDetect(Exchange exchange) throws IOException, TikaConfigException, InvalidPayloadException { MediaType result; Metadata metadata = new Metadata(); ParseContext context = tikaLoader.loadParseContext(); - try (InputStream stream = exchange.getIn().getBody(InputStream.class); + try (InputStream stream = exchange.getIn().getMandatoryBody(InputStream.class); TikaInputStream inputStream = TikaInputStream.get(stream, metadata)) { result = this.detector.detect(inputStream, metadata, context); convertMetadataToHeaders(metadata, exchange); @@ -138,11 +140,12 @@ private Object doDetect(Exchange exchange) throws IOException, TikaConfigExcepti } private Object doParse(Exchange exchange) - throws TikaException, TikaConfigException, IOException, SAXException, TransformerConfigurationException { + throws TikaException, TikaConfigException, IOException, SAXException, TransformerConfigurationException, + InvalidPayloadException { OutputStream result = new ByteArrayOutputStream(); Metadata metadata = new Metadata(); - try (InputStream stream = exchange.getIn().getBody(InputStream.class); + try (InputStream stream = exchange.getIn().getMandatoryBody(InputStream.class); TikaInputStream inputStream = TikaInputStream.get(stream, metadata)) { ContentHandler contentHandler = getContentHandler(this.tikaConfiguration, result); ParseContext context = tikaLoader.loadParseContext(); @@ -150,6 +153,8 @@ private Object doParse(Exchange exchange) this.parser.parse(inputStream, contentHandler, metadata, context); convertMetadataToHeaders(metadata, exchange); } + // the output is written in the output encoding, so converting it to text must decode it with that charset + exchange.setProperty(ExchangePropertyKey.CHARSET_NAME, this.encoding); return result; } diff --git a/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaDetectTest.java b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaDetectTest.java index ab7b684d9b127..af9f78dbf145a 100644 --- a/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaDetectTest.java +++ b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaDetectTest.java @@ -17,9 +17,12 @@ package org.apache.camel.component.tika; import java.io.File; +import java.util.List; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.Predicate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; @@ -29,6 +32,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; public class TikaDetectTest extends CamelTestSupport { @@ -71,6 +76,20 @@ public boolean matches(Exchange exchange) { resultEndpoint.assertIsSatisfied(); } + @Test + public void testDetectBodyNotStream() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", List.of("a", "b"))); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + @Test + public void testDetectWithoutBody() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { diff --git a/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaParseTest.java b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaParseTest.java index 4670aa529a4cf..d4cdad6ed7a37 100644 --- a/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaParseTest.java +++ b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaParseTest.java @@ -23,8 +23,10 @@ import java.nio.charset.StandardCharsets; import java.util.Map; +import org.apache.camel.CamelExecutionException; import org.apache.camel.EndpointInject; import org.apache.camel.Exchange; +import org.apache.camel.InvalidPayloadException; import org.apache.camel.Predicate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; @@ -41,11 +43,19 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; public class TikaParseTest extends CamelTestSupport { + private static final String HTML_TEXT = "Gr\u00fc\u00dfe aus K\u00f8benhavn"; + private static final byte[] HTML + = ("Test

    " + HTML_TEXT + "

    ") + .getBytes(StandardCharsets.UTF_8); + @EndpointInject("mock:result") protected MockEndpoint resultEndpoint; @@ -133,23 +143,58 @@ public boolean matches(Exchange exchange) { @Test public void testEmptyConfigDocumentParse() throws Exception { resultEndpoint.setExpectedMessageCount(1); - resultEndpoint.expectedMessagesMatches(new Predicate() { - @Override - public boolean matches(Exchange exchange) { - Object body = exchange.getIn().getBody(String.class); - Map headerMap = exchange.getIn().getHeaders(); - assertThat(body, instanceOf(String.class)); - assertThat((String) body, containsString("")); - assertThat(headerMap.get(Exchange.CONTENT_TYPE), equalTo("application/x-tika-msoffice")); - return true; - } + resultEndpoint.expectedMessagesMatches(exchange -> { + String body = exchange.getIn().getBody(String.class); + // the empty parser of the config file gives no text, which the default parser gives + assertThat(body, containsString("")); + assertThat(body, not(containsString(HTML_TEXT))); + return true; }); - File document = new File("src/test/resources/test.doc"); - template.sendBody("direct:start3", document); + template.sendBody("direct:start2", HTML); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testDefaultConfigDocumentParse() throws Exception { + resultEndpoint.setExpectedMessageCount(1); + resultEndpoint.expectedMessagesMatches(exchange -> { + assertThat(exchange.getIn().getBody(String.class), containsString(HTML_TEXT)); + return true; + }); + + template.sendBody("direct:start", HTML); resultEndpoint.assertIsSatisfied(); } + @Test + public void testTextParseWithEncoding() throws Exception { + resultEndpoint.setExpectedMessageCount(1); + resultEndpoint.expectedMessagesMatches(exchange -> { + // the exchange carries the output encoding, so the text decodes right + assertThat(exchange.getIn().getBody(String.class), containsString(HTML_TEXT)); + assertThat(exchange.getProperty(Exchange.CHARSET_NAME), equalTo(StandardCharsets.UTF_16.name())); + return true; + }); + + template.sendBody("direct:start5", HTML); + resultEndpoint.assertIsSatisfied(); + } + + @Test + public void testParseBodyNotStream() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", Map.of("key", "value"))); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + + @Test + public void testParseWithoutBody() { + Exception e = assertThrows(CamelExecutionException.class, + () -> template.sendBody("direct:start", null)); + assertInstanceOf(InvalidPayloadException.class, e.getCause()); + } + @Test public void testRegistryConfigDocumentParse() throws Exception { resultEndpoint.setExpectedMessageCount(1); @@ -181,6 +226,9 @@ public void configure() { from("direct:start3").to("tika:parse?tikaLoader=#testLoader").to("mock:result"); from("direct:start4").to("tika:parse?tikaParseOutputEncoding=" + StandardCharsets.UTF_16.name()) .to("mock:result"); + from("direct:start5") + .to("tika:parse?tikaParseOutputFormat=text&tikaParseOutputEncoding=" + StandardCharsets.UTF_16.name()) + .to("mock:result"); } }; } diff --git a/components/camel-vertx/camel-vertx-http/src/main/docs/vertx-http-component.adoc b/components/camel-vertx/camel-vertx-http/src/main/docs/vertx-http-component.adoc index 87f57ffb4064c..89760b5a9bf0e 100644 --- a/components/camel-vertx/camel-vertx-http/src/main/docs/vertx-http-component.adoc +++ b/components/camel-vertx/camel-vertx-http/src/main/docs/vertx-http-component.adoc @@ -89,7 +89,7 @@ URI, or as a header with the key `Exchange.HTTP_QUERY` on the message. Camel will handle, according to the HTTP response code: -* Response code is in the range 100..299, Camel regards it as a success +* Response code is in the range of the `okStatusCodeRange` option (200-299 by default), Camel regards it as a success response. * Response code is in the range 300..399, Camel regards it as a redirection response and will throw a `HttpOperationFailedException` @@ -123,12 +123,23 @@ The following algorithm determines the HTTP method to be used: + 5. `POST` if there is data to send (body is not `null`). + 6. `GET` otherwise. +=== Request body + +The producer sends the message body as the request body: + +* a `java.io.File`, a `java.nio.file.Path` or a file of a file based component (such as `file` or `ftp`) is sent +as the content of the file (or as a multipart upload with the `multipartUpload` option); +* a `String`, or a `java.io.Reader`, is sent as its text; +* a Vert.x `Buffer`, `MultiMap` (a form), `MultipartForm` or `ReadStream` is sent as is; +* another body must convert to a Vert.x `Buffer`, such as a `byte[]` or an `InputStream`. + === HTTP form parameters You can send HTTP form parameters in one of two ways. 1. Set the `Exchange.CONTENT_TYPE` header to the value `application/x-www-form-urlencoded` and ensure the message -body is a `String` formatted as form variables. For example `param1=value1¶m2=value2`. +body is a `String` formatted as form variables. For example `param1=value1¶m2=value2`. The names and values are +URL decoded (such as `a%26b` for `a&b`) and encoded again when sent, and a repeated name sends all its values. 2. Set the message body as a https://vertx.io/docs/apidocs/io/vertx/core/MultiMap.html[MultiMap] which allows you to configure form parameter names and values. diff --git a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpProducer.java b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpProducer.java index 1bce789e634a0..2a9c7d572b965 100644 --- a/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpProducer.java +++ b/components/camel-vertx/camel-vertx-http/src/main/java/org/apache/camel/component/vertx/http/VertxHttpProducer.java @@ -20,8 +20,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; +import java.io.Reader; import java.io.Serializable; -import java.util.Map; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.Callable; import io.vertx.core.AsyncResult; @@ -41,7 +45,6 @@ import org.apache.camel.support.DefaultAsyncProducer; import org.apache.camel.support.MessageHelper; import org.apache.camel.util.MimeTypeHelper; -import org.apache.camel.util.URISupport; import static org.apache.camel.component.vertx.http.VertxHttpConstants.CONTENT_TYPE_FORM_URLENCODED; import static org.apache.camel.component.vertx.http.VertxHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT; @@ -95,12 +98,23 @@ public boolean process(Exchange exchange, AsyncCallback callback) { return false; } - if (body instanceof File || body instanceof WrappedFile) { + if (body instanceof Reader) { + // send the text of the reader, as a String body + body = message.getMandatoryBody(String.class); + } + + if (body instanceof File || body instanceof Path || body instanceof WrappedFile) { // file based (could potentially also be a FTP file etc) - File file = message.getBody(File.class); + File file = body instanceof Path ? null : message.getBody(File.class); Buffer buf; String fileName; - if (file != null) { + if (body instanceof Path path) { + try (InputStream is = Files.newInputStream(path)) { + buf = VertxBufferConverter.toBuffer(is); + } + Path name = path.getFileName(); + fileName = name != null ? name.toString() : multipartName; + } else if (file != null) { try (InputStream is = new FileInputStream(file)) { buf = VertxBufferConverter.toBuffer(is); } @@ -124,10 +138,7 @@ public boolean process(Exchange exchange, AsyncCallback callback) { } else if (body instanceof String str) { // Try to extract URL encoded form data from the message body if (CONTENT_TYPE_FORM_URLENCODED.equals(contentType)) { - MultiMap map = MultiMap.caseInsensitiveMultiMap(); - Map formParams = URISupport.parseQuery(str); - formParams.forEach((key, o) -> map.add(key, String.valueOf(o))); - request.sendForm(map).onComplete(resultHandler); + request.sendForm(parseForm(str)).onComplete(resultHandler); } else { // Fallback to send as Buffer Buffer buffer = VertxBufferConverter.toBuffer(str, exchange); @@ -161,6 +172,24 @@ public boolean process(Exchange exchange, AsyncCallback callback) { return false; } + /** + * Parses a URL encoded form, with its values decoded (Vert.x encodes them again) and all the values of a repeated + * name. + */ + private static MultiMap parseForm(String form) { + MultiMap map = MultiMap.caseInsensitiveMultiMap(); + for (String pair : form.split("&")) { + if (pair.isEmpty()) { + continue; + } + int pos = pair.indexOf('='); + String name = pos < 0 ? pair : pair.substring(0, pos); + String value = pos < 0 ? "" : pair.substring(pos + 1); + map.add(URLDecoder.decode(name, StandardCharsets.UTF_8), URLDecoder.decode(value, StandardCharsets.UTF_8)); + } + return map; + } + private Handler>> createResultHandler(Exchange exchange, AsyncCallback callback) { // Process the response on a thread from the Vert.x worker pool since there may be blocking code: // - If a custom VertxHttpBinding is in use diff --git a/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpRequestBodyTypesTest.java b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpRequestBodyTypesTest.java new file mode 100644 index 0000000000000..745bd2393b010 --- /dev/null +++ b/components/camel-vertx/camel-vertx-http/src/test/java/org/apache/camel/component/vertx/http/VertxHttpRequestBodyTypesTest.java @@ -0,0 +1,98 @@ +/* + * 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.vertx.http; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.Exchange; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.apache.camel.component.vertx.http.VertxHttpConstants.CONTENT_TYPE_FORM_URLENCODED; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * The request bodies the producer reads: a Path, a Reader and a URL encoded form. + */ +public class VertxHttpRequestBodyTypesTest extends VertxHttpTestSupport { + + private static final String BODY_PAYLOAD = "Some Body"; + + @TempDir + Path tempDir; + + @Test + public void testPostBodyAsPath() throws IOException { + Path file = tempDir.resolve("body.txt"); + Files.writeString(file, BODY_PAYLOAD, StandardCharsets.UTF_8); + + String result = template.requestBody(getProducerUri(), file, String.class); + assertEquals("Got body: " + BODY_PAYLOAD, result); + } + + @Test + public void testPostBodyAsReader() { + String result = template.requestBody(getProducerUri(), new StringReader(BODY_PAYLOAD), String.class); + assertEquals("Got body: " + BODY_PAYLOAD, result); + } + + @Test + public void testPostFormUrlEncodedValuesAreDecoded() throws Exception { + MockEndpoint mockEndpoint = getMockEndpoint("mock:form"); + mockEndpoint.expectedHeaderReceived("q", "a&b"); + mockEndpoint.expectedHeaderReceived("name", "Camel Rider"); + + template.sendBodyAndHeader(getProducerUri() + "/form", "q=a%26b&name=Camel+Rider", Exchange.CONTENT_TYPE, + CONTENT_TYPE_FORM_URLENCODED); + + mockEndpoint.assertIsSatisfied(); + } + + @Test + public void testPostFormUrlEncodedRepeatedName() throws Exception { + MockEndpoint mockEndpoint = getMockEndpoint("mock:form"); + mockEndpoint.expectedHeaderReceived("project", new ArrayList<>(List.of("Camel", "Kamelets"))); + + template.sendBodyAndHeader(getProducerUri() + "/form", "project=Camel&project=Kamelets", Exchange.CONTENT_TYPE, + CONTENT_TYPE_FORM_URLENCODED); + + mockEndpoint.assertIsSatisfied(); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from(getTestServerUri()) + .setBody(simple("Got body: ${body}")); + + from(getTestServerUri() + "/form") + .to("mock:form"); + } + }; + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index ceb34e9b59065..6bc9945133034 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -2636,3 +2636,97 @@ Node producer creates it, from the options of the Edge Node endpoint (`deviceIds Device route that starts before the Edge Node route, or while it is stopped, therefore no longer fails with a `NullPointerException` when the Device URI has no `deviceIds`. Sending to a Device endpoint while no producer of its Edge Node endpoint is started now fails the exchange with an `IllegalStateException`; before, the data was dropped. + +=== camel-kafka - only a JSON array is sent as one record per element + +With `useIterator=true` (the default), the producer now splits a Jackson JSON node only when it is an array, for +Jackson 2 and Jackson 3. An object node is now sent as one record; previously it was sent as one record per field +value, without the field names, as the 4.22 upgrade guide said. A Jackson 3 scalar node is now sent as one record, +where nothing was sent before. + +=== camel-dns - dig without a type, and the wikipedia type + +`dns:dig` without the `CamelDnsType` header now asks for type A, and without the `CamelDnsName` header fails with an +`IllegalArgumentException` that names the header; both failed with a `NullPointerException` before. The `wikipedia` +type and the `CamelDnsTerm` header are deprecated, as the service behind them is gone. + +=== camel-as2 - client/send + +* A partner answer with an HTTP status outside 2xx, such as a 500 when the route of the partner fails, now fails the + exchange. Previously the exchange succeeded with a null body. The `CamelAs2.interchange` exchange property is still + set. +* The EDI message can now be a `byte[]`, a `java.io.File`, the file of a file consumer, or anything that converts to + bytes, as well as text and a stream. +* The `requestUri` option now defaults to `/` for client/send, as the catalog says. +* The `ediMessageType` option is deprecated: use `ediMessageContentType`. When only `ediMessageType` is set, + client/send uses it as the content type. +* `ediMessageTransferEncoding=quoted-printable` now encodes the message as quoted-printable. Previously it was + encoded as Base64 but labelled quoted-printable, which the receiver rejected. + +=== camel-graphql - the HTTP headers of the request + +The `Content-Type`, `Accept` and `Accept-Encoding` headers of the request are now always the ones of the producer +(JSON), whatever headers the message has, and a `Content-Encoding` header of the message is no longer sent. The +headers named by the `queryHeader` and `variablesHeader` options are no longer sent as HTTP headers. After the call +the `CamelHttpMethod`, `Accept` and `Accept-Encoding` headers that the producer set no longer stay on the message, so +a later http producer no longer sends a POST because of them. When the body is the query, a body that is not text, +bytes, a stream or a file, such as a Map, now fails instead of being sent as its `toString()`, and a Map body (or a +Map in the variables header) is used as the variables. + +=== camel-vertx-http - Path and Reader bodies, and urlencoded forms + +A `java.nio.file.Path` body is now sent as a file and a `Reader` body as its text; both failed before. The values of +an `application/x-www-form-urlencoded` String body are now decoded before Vert.x encodes the form, so an escape such as +`%26` is no longer sent encoded twice, and a name that is repeated sends all its values. `RAW(...)` values are no +longer recognized in such a form body. + +=== camel-github2 - getCommitFile + +getCommitFile now takes the commit from the `CamelGitHubCommitSha` header, which must be a commit id of 40 +hexadecimal characters, and the path of the file from the new `CamelGitHubFilePath` header, or else the body, and gives +the content of the file at that commit. Previously the body was taken as the commit when the header was not set, and +the operation could not give the content of a file. + +=== camel-pdf - page sizes, extractText and line ends + +* The `pageSize` values of the catalog (`LETTER`, `LEGAL`, `A0` to `A6`) now work: `pageSize=A4` gave a Letter page + before. The former names such as `PAGE_SIZE_A4` still work. An unknown value now fails when the endpoint is + created. +* extractText now reads a `byte[]`, a `ByteArrayOutputStream` (the output of create and merge), a stream, a file or a + Path, so create followed by extractText works. Another body, or none, fails with an `InvalidPayloadException`. +* create without a body fails with an `InvalidPayloadException`. With the default `lineTermination`, a CR LF or a CR + now ends a line, a tab is written as spaces and other control characters are left out; they failed before. +* The `CamelPdfFilesToMerge` header of merge can also hold `java.nio.file.Path` elements. + +=== camel-tika - the body and the charset of the output + +detect and parse fail with an `InvalidPayloadException` for a body that does not convert to a stream, or no body, +instead of a `NullPointerException`. parse now sets the charset of the exchange to `tikaParseOutputEncoding`, so the +output converts to text in that charset. + +=== camel-infinispan-embedded - the configurationUri + +The `configurationUri` of the component now reads a `file:` URI, an absolute path and property placeholders, as well +as a name on the class path. The `configurationUri`, `cacheContainer`, `cacheContainerConfiguration` and `flags` +options are read on the component only: set on an endpoint they are not used. + +=== camel-snmp - the producer and the poll consumer + +* A producer without `type` now sends a GET with the `oids` of the endpoint, where it sent a GET without them, and + without a port in the URI a producer that is not of type TRAP now uses port 161 instead of 162. +* The trap producer fails with an `InvalidPayloadException` for a body that is not an `org.snmp4j.PDU`. +* The `SnmpMessage` body of the producer converts to the XML text of its PDU, as the body of the consumer is. +* The POLL consumer now waits for the answer of each poll, and a poll without an answer counts as an empty poll, so + `sendEmptyMessageWhenIdle` and the backoff options work. `SnmpOIDPoller` no longer implements `ResponseListener`. + +=== camel-jdbc - generated keys, outputClass and the body + +The `CamelGeneratedKeysRows` header is now always a List of Maps of the key columns: the `outputClass` and `readSize` +options no longer apply to it. An `outputClass` that does not resolve fails with an `IllegalArgumentException` that +names the class, and a message without a body fails with an `InvalidPayloadException`. The `transacted` option is +deprecated, as it is not used. + +=== camel-jsch - scp without a known hosts file + +With `useUserKnownHostsFile=false` and no `knownHostsFile`, scp no longer fails with a `NullPointerException`: no host +is known, and `strictHostKeyChecking` decides whether to connect. diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/InfinispanEmbeddedComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/InfinispanEmbeddedComponentBuilderFactory.java index 7ca53573a997b..5ce617e9940e0 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/InfinispanEmbeddedComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/InfinispanEmbeddedComponentBuilderFactory.java @@ -310,7 +310,9 @@ default InfinispanEmbeddedComponentBuilder autowiredEnabled(boolean autowiredEna } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: * <code>org.infinispan.manager.EmbeddedCacheManager</code> @@ -328,7 +330,9 @@ default InfinispanEmbeddedComponentBuilder cacheContainer(org.infinispan.manager /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option is a: * <code>org.infinispan.configuration.cache.Configuration</code> type. @@ -363,7 +367,9 @@ default InfinispanEmbeddedComponentBuilder configurationUri(java.lang.String con /** * A comma separated list of org.infinispan.context.Flag to be applied - * by default on each cache invocation. + * by default on each cache invocation Read on the component only, as + * the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: <code>java.lang.String</code> type. * diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java index b004f39c1f527..2314cb62af567 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java @@ -1781,7 +1781,9 @@ default KafkaComponentBuilder transactionalId(java.lang.String transactionalId) /** * Sets whether sending to kafka should send the message body as a * single record, or use a java.util.Iterator to send multiple records - * to kafka (if the message body can be iterated). + * to kafka (if the message body can be iterated). A Jackson JSON array + * node is sent as one record per element, and any other JSON node (such + * as an object) as one record. * * The option is a: <code>boolean</code> type. * 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..38390fde9d803 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 @@ -4990,7 +4990,8 @@ public static DJLEndpointBuilderFactory.DJLEndpointBuilder djl(String componentN * Syntax: dns:dnsType * * Path parameter: dnsType (required) - * The type of the lookup. + * The type of the lookup. The wikipedia type is deprecated, as the + * Wikipedia over DNS service it queries is no longer available. * There are 4 enums and the value can be one of: dig, ip, lookup, wikipedia * * @param path dnsType @@ -5010,7 +5011,8 @@ public static DnsEndpointBuilderFactory.DnsEndpointBuilder dns(String path) { * Syntax: dns:dnsType * * Path parameter: dnsType (required) - * The type of the lookup. + * The type of the lookup. The wikipedia type is deprecated, as the + * Wikipedia over DNS service it queries is no longer available. * There are 4 enums and the value can be one of: dig, ip, lookup, wikipedia * * @param componentName to use a custom component name for the endpoint @@ -11703,8 +11705,9 @@ public static MetricsEndpointBuilderFactory.MetricsEndpointBuilder metrics(Strin * * Syntax: micrometer:metricsType:metricsName * - * Path parameter: metricsType (required) + * Path parameter: metricsType * Type of metrics + * Default value: counter * There are 3 enums and the value can be one of: counter, summary, timer * * Path parameter: metricsName (required) @@ -11727,8 +11730,9 @@ public static MicrometerEndpointBuilderFactory.MicrometerEndpointBuilder microme * * Syntax: micrometer:metricsType:metricsName * - * Path parameter: metricsType (required) + * Path parameter: metricsType * Type of metrics + * Default value: counter * There are 3 enums and the value can be one of: counter, summary, timer * * Path parameter: metricsName (required) diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java index 97e5a5dc260e4..5b828f55290ad 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java @@ -259,6 +259,8 @@ default AS2EndpointConsumerBuilder ediMessageTransferEncoding(String ediMessageT /** * The content type of EDI message. One of application/edifact, * application/edi-x12, application/edi-consent, application/xml. + * client/send uses this value only when ediMessageContentType is not + * set. Deprecation note: Use ediMessageContentType instead. * * The option is a: java.lang.String type. * @@ -267,6 +269,7 @@ default AS2EndpointConsumerBuilder ediMessageTransferEncoding(String ediMessageT * @param ediMessageType the value to set * @return the dsl builder */ + @Deprecated default AS2EndpointConsumerBuilder ediMessageType(String ediMessageType) { doSetProperty("ediMessageType", ediMessageType); return this; @@ -1361,6 +1364,8 @@ default AS2EndpointProducerBuilder ediMessageTransferEncoding(String ediMessageT /** * The content type of EDI message. One of application/edifact, * application/edi-x12, application/edi-consent, application/xml. + * client/send uses this value only when ediMessageContentType is not + * set. Deprecation note: Use ediMessageContentType instead. * * The option is a: java.lang.String type. * @@ -1369,6 +1374,7 @@ default AS2EndpointProducerBuilder ediMessageTransferEncoding(String ediMessageT * @param ediMessageType the value to set * @return the dsl builder */ + @Deprecated default AS2EndpointProducerBuilder ediMessageType(String ediMessageType) { doSetProperty("ediMessageType", ediMessageType); return this; @@ -2484,6 +2490,8 @@ default AS2EndpointBuilder ediMessageTransferEncoding(String ediMessageTransferE /** * The content type of EDI message. One of application/edifact, * application/edi-x12, application/edi-consent, application/xml. + * client/send uses this value only when ediMessageContentType is not + * set. Deprecation note: Use ediMessageContentType instead. * * The option is a: java.lang.String type. * @@ -2492,6 +2500,7 @@ default AS2EndpointBuilder ediMessageTransferEncoding(String ediMessageTransferE * @param ediMessageType the value to set * @return the dsl builder */ + @Deprecated default AS2EndpointBuilder ediMessageType(String ediMessageType) { doSetProperty("ediMessageType", ediMessageType); return this; diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DnsEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DnsEndpointBuilderFactory.java index ef8e4237fb677..c7e0d1e7ba974 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DnsEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DnsEndpointBuilderFactory.java @@ -129,7 +129,8 @@ default DnsHeaderNameBuilder dns() { * Syntax: dns:dnsType * * Path parameter: dnsType (required) - * The type of the lookup. + * The type of the lookup. The wikipedia type is deprecated, as the + * Wikipedia over DNS service it queries is no longer available. * There are 4 enums and the value can be one of: dig, ip, lookup, * wikipedia * @@ -150,7 +151,8 @@ default DnsEndpointBuilder dns(String path) { * Syntax: dns:dnsType * * Path parameter: dnsType (required) - * The type of the lookup. + * The type of the lookup. The wikipedia type is deprecated, as the + * Wikipedia over DNS service it queries is no longer available. * There are 4 enums and the value can be one of: dig, ip, lookup, * wikipedia * @@ -193,7 +195,7 @@ public String dnsClass() { * The option is a: {@code String} type. * * Required: true - * Group: lookup + * Group: lookup dig * * @return the name of the header {@code DnsName}. */ @@ -228,7 +230,8 @@ public String dnsServer() { } /** * The type of the lookup. Should match the values of - * org.xbill.dns.Type. Optional. + * org.xbill.dns.Type. Optional. The dig operation uses A when none or + * an unknown type is given. * * The option is a: {@code String} type. * @@ -240,7 +243,8 @@ public String dnsType() { return "CamelDnsType"; } /** - * The term. + * The term. Deprecation note: The Wikipedia over DNS service is no + * longer available. * * The option is a: {@code } type. * @@ -249,6 +253,7 @@ public String dnsType() { * * @return the name of the header {@code DnsTerm}. */ + @Deprecated public String dnsTerm() { return "CamelDnsTerm"; } diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FilesEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FilesEndpointBuilderFactory.java index b1471cdd89c66..30974eea3e6d4 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FilesEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FilesEndpointBuilderFactory.java @@ -2216,7 +2216,7 @@ default AdvancedFilesEndpointConsumerBuilder bridgeErrorHandler(String bridgeErr * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2235,7 +2235,7 @@ default AdvancedFilesEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpEndpointBuilderFactory.java index 456115c0569d3..f4c8e4a8f1e35 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpEndpointBuilderFactory.java @@ -2347,7 +2347,7 @@ default AdvancedFtpEndpointConsumerBuilder bridgeErrorHandler(String bridgeError * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2366,7 +2366,7 @@ default AdvancedFtpEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java index 965ea58aede75..640eff3542506 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/FtpsEndpointBuilderFactory.java @@ -2625,7 +2625,7 @@ default AdvancedFtpsEndpointConsumerBuilder bridgeErrorHandler(String bridgeErro * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2644,7 +2644,7 @@ default AdvancedFtpsEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java index 27b59b6735a8a..3fd618141e9e2 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java @@ -880,7 +880,8 @@ default GitHub2EndpointProducerBuilder repoOwner(String repoOwner) { return this; } /** - * To use the given encoding when getting a git commit file. + * The charset of the content of the file for the getCommitFile producer + * (the default charset of the JVM if not set). * * The option is a: java.lang.String type. * @@ -1246,17 +1247,31 @@ public String gitHubCommitCommitter() { return "CamelGitHubCommitCommitter"; } /** - * The commit sha. + * The commit sha. The getCommitFile producer reads the file at this + * commit (a commit id of 40 hexadecimal characters). * * The option is a: {@code String} type. * - * Group: consumer + * Group: common * * @return the name of the header {@code GitHubCommitSha}. */ public String gitHubCommitSha() { return "CamelGitHubCommitSha"; } + /** + * The path of the file in the repository for the getCommitFile + * producer. If not set, the body is the path. + * + * The option is a: {@code String} type. + * + * Group: producer + * + * @return the name of the header {@code GitHubFilePath}. + */ + public String gitHubFilePath() { + return "CamelGitHubFilePath"; + } /** * The commit URL. * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java index 055e6853409bc..e71e19d9e776d 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java @@ -102,7 +102,8 @@ default GraphqlEndpointBuilder queryFile(String queryFile) { return this; } /** - * The name of a header containing the GraphQL query. + * The name of a header containing the GraphQL query. The header is not + * sent as an HTTP header. * * The option is a: java.lang.String type. * @@ -180,8 +181,8 @@ default GraphqlEndpointBuilder variables(String variables) { return this; } /** - * The name of a header containing a JsonObject instance containing the - * operation variables. + * The name of a header containing the operation variables, as a + * JsonObject or a Map. The header is not sent as an HTTP header. * * The option is a: java.lang.String type. * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/InfinispanEmbeddedEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/InfinispanEmbeddedEndpointBuilderFactory.java index 25d9022d1cafa..c6466e035cef1 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/InfinispanEmbeddedEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/InfinispanEmbeddedEndpointBuilderFactory.java @@ -313,7 +313,9 @@ default AdvancedInfinispanEmbeddedEndpointConsumerBuilder exchangePattern(String return this; } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: * org.infinispan.manager.EmbeddedCacheManager type. @@ -328,7 +330,9 @@ default AdvancedInfinispanEmbeddedEndpointConsumerBuilder cacheContainer(org.inf return this; } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option will be converted to a * org.infinispan.manager.EmbeddedCacheManager type. @@ -344,7 +348,9 @@ default AdvancedInfinispanEmbeddedEndpointConsumerBuilder cacheContainer(String } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option is a: * org.infinispan.configuration.cache.Configuration type. @@ -360,7 +366,9 @@ default AdvancedInfinispanEmbeddedEndpointConsumerBuilder cacheContainerConfigur } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option will be converted to a * org.infinispan.configuration.cache.Configuration type. @@ -393,7 +401,9 @@ default AdvancedInfinispanEmbeddedEndpointConsumerBuilder configurationUri(Strin } /** * A comma separated list of org.infinispan.context.Flag to be applied - * by default on each cache invocation. + * by default on each cache invocation Read on the component only, as + * the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: java.lang.String type. * @@ -700,7 +710,9 @@ default AdvancedInfinispanEmbeddedEndpointProducerBuilder lazyStartProducer(Stri return this; } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: * org.infinispan.manager.EmbeddedCacheManager type. @@ -715,7 +727,9 @@ default AdvancedInfinispanEmbeddedEndpointProducerBuilder cacheContainer(org.inf return this; } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option will be converted to a * org.infinispan.manager.EmbeddedCacheManager type. @@ -731,7 +745,9 @@ default AdvancedInfinispanEmbeddedEndpointProducerBuilder cacheContainer(String } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option is a: * org.infinispan.configuration.cache.Configuration type. @@ -747,7 +763,9 @@ default AdvancedInfinispanEmbeddedEndpointProducerBuilder cacheContainerConfigur } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option will be converted to a * org.infinispan.configuration.cache.Configuration type. @@ -780,7 +798,9 @@ default AdvancedInfinispanEmbeddedEndpointProducerBuilder configurationUri(Strin } /** * A comma separated list of org.infinispan.context.Flag to be applied - * by default on each cache invocation. + * by default on each cache invocation Read on the component only, as + * the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: java.lang.String type. * @@ -899,7 +919,9 @@ default InfinispanEmbeddedEndpointBuilder basic() { } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: * org.infinispan.manager.EmbeddedCacheManager type. @@ -914,7 +936,9 @@ default AdvancedInfinispanEmbeddedEndpointBuilder cacheContainer(org.infinispan. return this; } /** - * Specifies the cache Container to connect. + * Specifies the cache Container to connect Read on the component only, + * as the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option will be converted to a * org.infinispan.manager.EmbeddedCacheManager type. @@ -930,7 +954,9 @@ default AdvancedInfinispanEmbeddedEndpointBuilder cacheContainer(String cacheCon } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option is a: * org.infinispan.configuration.cache.Configuration type. @@ -946,7 +972,9 @@ default AdvancedInfinispanEmbeddedEndpointBuilder cacheContainerConfiguration(or } /** * The CacheContainer configuration. Used if the cacheContainer is not - * defined. + * defined. Read on the component only, as the cache manager is shared + * by all the endpoints of the component: set on an endpoint, this + * option is not used. * * The option will be converted to a * org.infinispan.configuration.cache.Configuration type. @@ -979,7 +1007,9 @@ default AdvancedInfinispanEmbeddedEndpointBuilder configurationUri(String config } /** * A comma separated list of org.infinispan.context.Flag to be applied - * by default on each cache invocation. + * by default on each cache invocation Read on the component only, as + * the cache manager is shared by all the endpoints of the component: + * set on an endpoint, this option is not used. * * The option is a: java.lang.String type. * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java index 7efa3b32b80fe..d70b960c88c72 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JdbcEndpointBuilderFactory.java @@ -237,7 +237,8 @@ default JdbcEndpointBuilder resetAutoCommit(String resetAutoCommit) { return this; } /** - * Whether transactions are in use. + * Whether transactions are in use. Deprecation note: Not used: the + * producer does not read this option. * * The option is a: boolean type. * @@ -247,12 +248,14 @@ default JdbcEndpointBuilder resetAutoCommit(String resetAutoCommit) { * @param transacted the value to set * @return the dsl builder */ + @Deprecated default JdbcEndpointBuilder transacted(boolean transacted) { doSetProperty("transacted", transacted); return this; } /** - * Whether transactions are in use. + * Whether transactions are in use. Deprecation note: Not used: the + * producer does not read this option. * * The option will be converted to a boolean type. * @@ -262,14 +265,16 @@ default JdbcEndpointBuilder transacted(boolean transacted) { * @param transacted the value to set * @return the dsl builder */ + @Deprecated default JdbcEndpointBuilder transacted(String transacted) { doSetProperty("transacted", transacted); return this; } /** - * To read BLOB columns as bytes instead of string data. This may be - * needed for certain databases such as Oracle where you must read BLOB - * columns as bytes. + * To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, + * which cannot be read after the producer has closed the connection. + * This may be needed for certain databases such as Oracle where you + * must read BLOB columns as bytes. * * The option is a: boolean type. * @@ -284,9 +289,10 @@ default JdbcEndpointBuilder useGetBytesForBlob(boolean useGetBytesForBlob) { return this; } /** - * To read BLOB columns as bytes instead of string data. This may be - * needed for certain databases such as Oracle where you must read BLOB - * columns as bytes. + * To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, + * which cannot be read after the producer has closed the connection. + * This may be needed for certain databases such as Oracle where you + * must read BLOB columns as bytes. * * The option will be converted to a boolean type. * @@ -628,8 +634,8 @@ public String jdbcUpdateCount() { return "CamelJdbcUpdateCount"; } /** - * If the query is a SELECT, query the row count is returned in this OUT - * header. + * If the query is a SELECT and the outputType is SelectList, the row + * count is returned in this OUT header. * * The option is a: {@code int} type. * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java index 1f0932959ee3c..304801bb65c6d 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java @@ -3923,7 +3923,9 @@ default KafkaEndpointProducerBuilder transactionalId(String transactionalId) { /** * Sets whether sending to kafka should send the message body as a * single record, or use a java.util.Iterator to send multiple records - * to kafka (if the message body can be iterated). + * to kafka (if the message body can be iterated). A Jackson JSON array + * node is sent as one record per element, and any other JSON node (such + * as an object) as one record. * * The option is a: boolean type. * @@ -3940,7 +3942,9 @@ default KafkaEndpointProducerBuilder useIterator(boolean useIterator) { /** * Sets whether sending to kafka should send the message body as a * single record, or use a java.util.Iterator to send multiple records - * to kafka (if the message body can be iterated). + * to kafka (if the message body can be iterated). A Jackson JSON array + * node is sent as one record per element, and any other JSON node (such + * as an object) as one record. * * The option will be converted to a boolean type. * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java index 8eaeb80ca7fc3..4b88b09c84544 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MicrometerEndpointBuilderFactory.java @@ -238,8 +238,9 @@ default MicrometerHeaderNameBuilder micrometer() { * * Syntax: micrometer:metricsType:metricsName * - * Path parameter: metricsType (required) + * Path parameter: metricsType * Type of metrics + * Default value: counter * There are 3 enums and the value can be one of: counter, summary, * timer * @@ -263,8 +264,9 @@ default MicrometerEndpointBuilder micrometer(String path) { * * Syntax: micrometer:metricsType:metricsName * - * Path parameter: metricsType (required) + * Path parameter: metricsType * Type of metrics + * Default value: counter * There are 3 enums and the value can be one of: counter, summary, * timer * @@ -307,7 +309,7 @@ public String metricsTimerAction() { /** * Override histogram value in URI. * - * The option is a: {@code long} type. + * The option is a: {@code Double} type. * * Group: producer * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MinaSftpEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MinaSftpEndpointBuilderFactory.java index 1bd0d66229e6e..ae4ffd68fbe63 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MinaSftpEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/MinaSftpEndpointBuilderFactory.java @@ -2781,7 +2781,7 @@ default AdvancedMinaSftpEndpointConsumerBuilder bridgeErrorHandler(String bridge * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2800,7 +2800,7 @@ default AdvancedMinaSftpEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PdfEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PdfEndpointBuilderFactory.java index 7b5eb484f46ac..3f0d4de7b6b5f 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PdfEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/PdfEndpointBuilderFactory.java @@ -210,7 +210,8 @@ default PdfEndpointBuilder marginTop(String marginTop) { return this; } /** - * Page size. + * Page size: LETTER, LEGAL or A0 to A6. The older names PAGE_SIZE_A0 to + * PAGE_SIZE_A6 and PAGE_SIZE_LETTER are also accepted. * * The option is a: java.lang.String type. * @@ -444,9 +445,10 @@ public String pdfDecryptionMaterial() { } /** * Mandatory header for merge operation and ignored in all other - * operations. The array of pdf files that will be merged. + * operations. The list of pdf files that will be merged, each given as + * a java.io.File or a java.nio.file.Path. * - * The option is a: {@code java.util.List} type. + * The option is a: {@code java.util.List} type. * * Group: producer * diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SftpEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SftpEndpointBuilderFactory.java index 3d2a3a036a593..60cee4e14f9b2 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SftpEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SftpEndpointBuilderFactory.java @@ -2795,7 +2795,7 @@ default AdvancedSftpEndpointConsumerBuilder bridgeErrorHandler(String bridgeErro * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2814,7 +2814,7 @@ default AdvancedSftpEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SmbEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SmbEndpointBuilderFactory.java index e37521490e4a9..a0f351fb3237d 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SmbEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SmbEndpointBuilderFactory.java @@ -2055,7 +2055,7 @@ default AdvancedSmbEndpointConsumerBuilder bridgeErrorHandler(String bridgeError * * The option is a: boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set @@ -2074,7 +2074,7 @@ default AdvancedSmbEndpointConsumerBuilder download(boolean download) { * * The option will be converted to a boolean type. * - * Default: false + * Default: true * Group: consumer (advanced) * * @param download the value to set diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SnmpEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SnmpEndpointBuilderFactory.java index 7fac365239eb8..5805e8d7aefb8 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SnmpEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SnmpEndpointBuilderFactory.java @@ -232,7 +232,11 @@ default SnmpEndpointConsumerBuilder timeout(String timeout) { return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option is a: * org.apache.camel.component.snmp.SnmpActionType type. @@ -247,7 +251,11 @@ default SnmpEndpointConsumerBuilder type(org.apache.camel.component.snmp.SnmpAct return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option will be converted to a * org.apache.camel.component.snmp.SnmpActionType type. @@ -1276,7 +1284,11 @@ default SnmpEndpointProducerBuilder timeout(String timeout) { return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option is a: * org.apache.camel.component.snmp.SnmpActionType type. @@ -1291,7 +1303,11 @@ default SnmpEndpointProducerBuilder type(org.apache.camel.component.snmp.SnmpAct return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option will be converted to a * org.apache.camel.component.snmp.SnmpActionType type. @@ -1692,7 +1708,11 @@ default SnmpEndpointBuilder timeout(String timeout) { return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option is a: * org.apache.camel.component.snmp.SnmpActionType type. @@ -1707,7 +1727,11 @@ default SnmpEndpointBuilder type(org.apache.camel.component.snmp.SnmpActionType return this; } /** - * Which operation to perform such as poll, trap, etc. + * Which operation to perform such as poll, trap, etc. The consumer + * supports TRAP (receive traps) and POLL (poll the oids). The producer + * supports TRAP (send the PDU of the body as a trap), POLL (a GET of + * the oids, also done when no type is set) and GET_NEXT (a walk of the + * oids, for the producer only). * * The option will be converted to a * org.apache.camel.component.snmp.SnmpActionType type. diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TikaEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TikaEndpointBuilderFactory.java index 45184959aa7eb..221644104e78c 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TikaEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/TikaEndpointBuilderFactory.java @@ -45,7 +45,10 @@ default AdvancedTikaEndpointBuilder advanced() { } /** - * Tika Parse Output Encoding. + * The character encoding of the parsed output. Defaults to the default + * charset of the JVM. The parse operation sets this encoding as the + * charset of the exchange, so converting the output to text decodes it + * with this encoding. * * The option is a: java.lang.String type. * @@ -61,8 +64,7 @@ default TikaEndpointBuilder tikaParseOutputEncoding(String tikaParseOutputEncodi /** * Tika Output Format. Supported output formats. xml: Returns Parsed * Content as XML. html: Returns Parsed Content as HTML. text: Returns - * Parsed Content as Text. textMain: Uses the boilerpipe library to - * automatically extract the main content from a web page. + * Parsed Content as Text. * * The option is a: * org.apache.camel.component.tika.TikaParseOutputFormat @@ -81,8 +83,7 @@ default TikaEndpointBuilder tikaParseOutputFormat(org.apache.camel.component.tik /** * Tika Output Format. Supported output formats. xml: Returns Parsed * Content as XML. html: Returns Parsed Content as HTML. text: Returns - * Parsed Content as Text. textMain: Uses the boilerpipe library to - * automatically extract the main content from a web page. + * Parsed Content as Text. * * The option will be converted to a * org.apache.camel.component.tika.TikaParseOutputFormat From bd7b65a50e7083e9b3e3a305119ca892d55f3afa Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 23 Oct 2026 22:53:36 +0200 Subject: [PATCH 2/5] CAMEL-25983: camel-github2 - upgrade notes for 4.18.6 and 4.22.3 Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc | 7 +++++++ .../modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc index 8491caa579137..c73149809cd31 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc @@ -436,6 +436,13 @@ earlier in the route. Signatures created by earlier versions may not verify and need to be created again. +=== camel-github2 - getCommitFile + +getCommitFile now takes the commit from the `CamelGitHubCommitSha` header, which must be a commit id of 40 +hexadecimal characters, and the path of the file from the new `CamelGitHubFilePath` header, or else the body, and gives +the content of the file at that commit. Previously the body was taken as the commit when the header was not set, and +the operation could not give the content of a file. + == Upgrading from 4.18.4 to 4.18.5 === camel-azure-eventhubs diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc index 8b4172b4e0439..ac26b12e3d840 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc @@ -236,6 +236,13 @@ earlier in the route. Signatures created by earlier versions over a `File`, `Path`, `GenericFile` or other non-`byte[]`/`InputStream` body may not verify and need to be created again. +=== camel-github2 - getCommitFile + +getCommitFile now takes the commit from the `CamelGitHubCommitSha` header, which must be a commit id of 40 +hexadecimal characters, and the path of the file from the new `CamelGitHubFilePath` header, or else the body, and gives +the content of the file at that commit. Previously the body was taken as the commit when the header was not set, and +the operation could not give the content of a file. + == Upgrading from 4.22.1 to 4.22.2 === camel-lra - only an LRA hosted by the configured coordinator is joined From 48bb5fb81cfa123cd8b97d14a6dda1b87ce287bb Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 23 Oct 2026 23:30:45 +0200 Subject: [PATCH 3/5] CAMEL-26052: camel-as2, camel-graphql - review fixes The quoted-printable stream closes the underlying stream also when the write fails, and the GraphQL variables of a Map copy its keys as text instead of an unchecked cast. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../apache/camel/component/as2/api/util/EntityUtils.java | 7 +++++-- .../apache/camel/component/graphql/GraphqlProducer.java | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java index 2c83e7825f839..3b7e4e4464342 100644 --- a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java +++ b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/util/EntityUtils.java @@ -315,8 +315,11 @@ public void write(byte[] b, int off, int len) { public void close() throws IOException { if (!closed) { closed = true; - out.write(QuotedPrintableCodec.encodeQuotedPrintable(null, buffer.toByteArray(), true)); - super.close(); + try { + out.write(QuotedPrintableCodec.encodeQuotedPrintable(null, buffer.toByteArray(), true)); + } finally { + super.close(); + } } } } diff --git a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java index 35ee762d00dfa..92c39ced58243 100644 --- a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java +++ b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java @@ -200,8 +200,13 @@ private JsonObject getVariables(Exchange exchange) { return variables; } - @SuppressWarnings("unchecked") private static JsonObject toJsonObject(Map map) { - return map instanceof JsonObject json ? json : new JsonObject((Map) map); + if (map instanceof JsonObject json) { + return json; + } + // the variable names of GraphQL are text, so a key that is not a String is turned into one + JsonObject json = new JsonObject(); + map.forEach((key, value) -> json.put(String.valueOf(key), value)); + return json; } } From 358745893d027fa0a83ece5d64803b2ba72f2717 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Fri, 23 Oct 2026 22:20:35 +0000 Subject: [PATCH 4/5] Regen --- .../camel/catalog/components/spring-jdbc.json | 6 ++--- .../component/spring/jdbc/spring-jdbc.json | 6 ++--- .../dsl/SpringJdbcEndpointBuilderFactory.java | 24 +++++++++++-------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json index 3e5e9fbd46690..7cec3ebb2d7f1 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-jdbc.json @@ -31,7 +31,7 @@ }, "headers": { "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is an UPDATE, query the update count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" }, - "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT, query the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, + "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT and the outputType is SelectList, the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Set", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The column names from the ResultSet as a java.util.Set type.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" }, "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A java.util.Map which has the headers to be used if useHeadersAsParameters has been enabled.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" }, "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Set its value to true to retrieve generated keys", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" }, @@ -47,8 +47,8 @@ "parameters": { "index": 4, "kind": "parameter", "displayName": "Parameters", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "statement.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "This is a multi-value option with prefix: statement." }, "readSize": { "index": 5, "kind": "parameter", "displayName": "Read Size", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The default maximum number of rows that can be read by a polling query. The default value is 0." }, "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": "Reset Auto Commit", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Camel will set the autoCommit on the JDBC connection to be false, commit the change after executed the statement and reset the autoCommit flag of the connection at the end, if the resetAutoCommit is true. If the JDBC connection doesn't support to reset the autoCommit flag, you can set the resetAutoCommit flag to be false, and Camel will not try to reset the autoCommit flag. When used with XA transactions you most likely need to set it to false so that the transaction manager is in charge of committing this tx." }, - "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use." }, - "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes instead of string data. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, + "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not used: the producer does not read this option", "autowired": false, "secret": false, "defaultValue": false, "description": "Deprecation note: Not used: the producer does not read this option" }, + "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read after the producer has closed the connection. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, "useHeadersAsParameters": { "index": 9, "kind": "parameter", "displayName": "Use Headers As Parameters", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Set this option to true to use the prepareStatementStrategy with named parameters. This allows to define queries with named placeholders, and use headers with the dynamic values for the query placeholders." }, "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", "displayName": "Use JDBC4 Column Name And Label Semantics", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get the column name where as JDBC 3.0 uses both columnName or columnLabel. Unfortunately JDBC drivers behave differently so you can use this option to work out issues around your JDBC driver if you get problem using this component This option is default true." }, "lazyStartProducer": { "index": 11, "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." }, diff --git a/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json b/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json index 3e5e9fbd46690..7cec3ebb2d7f1 100644 --- a/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json +++ b/components/camel-spring-parent/camel-spring-jdbc/src/generated/resources/META-INF/org/apache/camel/component/spring/jdbc/spring-jdbc.json @@ -31,7 +31,7 @@ }, "headers": { "CamelJdbcUpdateCount": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is an UPDATE, query the update count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_UPDATE_COUNT" }, - "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT, query the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, + "CamelJdbcRowCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "If the query is a SELECT and the outputType is SelectList, the row count is returned in this OUT header.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_ROW_COUNT" }, "CamelJdbcColumnNames": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Set", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The column names from the ResultSet as a java.util.Set type.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_COLUMN_NAMES" }, "CamelJdbcParameters": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Map", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "A java.util.Map which has the headers to be used if useHeadersAsParameters has been enabled.", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_PARAMETERS" }, "CamelRetrieveGeneratedKeys": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Set its value to true to retrieve generated keys", "constantName": "org.apache.camel.component.jdbc.JdbcConstants#JDBC_RETRIEVE_GENERATED_KEYS" }, @@ -47,8 +47,8 @@ "parameters": { "index": 4, "kind": "parameter", "displayName": "Parameters", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "java.util.Map", "prefix": "statement.", "multiValue": true, "deprecated": false, "autowired": false, "secret": false, "description": "This is a multi-value option with prefix: statement." }, "readSize": { "index": 5, "kind": "parameter", "displayName": "Read Size", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "description": "The default maximum number of rows that can be read by a polling query. The default value is 0." }, "resetAutoCommit": { "index": 6, "kind": "parameter", "displayName": "Reset Auto Commit", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Camel will set the autoCommit on the JDBC connection to be false, commit the change after executed the statement and reset the autoCommit flag of the connection at the end, if the resetAutoCommit is true. If the JDBC connection doesn't support to reset the autoCommit flag, you can set the resetAutoCommit flag to be false, and Camel will not try to reset the autoCommit flag. When used with XA transactions you most likely need to set it to false so that the transaction manager is in charge of committing this tx." }, - "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether transactions are in use." }, - "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes instead of string data. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, + "transacted": { "index": 7, "kind": "parameter", "displayName": "Transacted", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": true, "deprecationNote": "Not used: the producer does not read this option", "autowired": false, "secret": false, "defaultValue": false, "description": "Deprecation note: Not used: the producer does not read this option" }, + "useGetBytesForBlob": { "index": 8, "kind": "parameter", "displayName": "Use Get Bytes For Blob", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, which cannot be read after the producer has closed the connection. This may be needed for certain databases such as Oracle where you must read BLOB columns as bytes." }, "useHeadersAsParameters": { "index": 9, "kind": "parameter", "displayName": "Use Headers As Parameters", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Set this option to true to use the prepareStatementStrategy with named parameters. This allows to define queries with named placeholders, and use headers with the dynamic values for the query placeholders." }, "useJDBC4ColumnNameAndLabelSemantics": { "index": 10, "kind": "parameter", "displayName": "Use JDBC4 Column Name And Label Semantics", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Sets whether to use JDBC 4 or JDBC 3.0 or older semantic when retrieving column name. JDBC 4.0 uses columnLabel to get the column name where as JDBC 3.0 uses both columnName or columnLabel. Unfortunately JDBC drivers behave differently so you can use this option to work out issues around your JDBC driver if you get problem using this component This option is default true." }, "lazyStartProducer": { "index": 11, "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." }, diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java index b5b9e57ba4fc8..8a05397e4f0c1 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/SpringJdbcEndpointBuilderFactory.java @@ -233,7 +233,7 @@ default SpringJdbcEndpointBuilder resetAutoCommit(String resetAutoCommit) { return this; } /** - * Whether transactions are in use. + * Deprecation note: Not used: the producer does not read this option. * * The option is a: boolean type. * @@ -243,12 +243,13 @@ default SpringJdbcEndpointBuilder resetAutoCommit(String resetAutoCommit) { * @param transacted the value to set * @return the dsl builder */ + @Deprecated default SpringJdbcEndpointBuilder transacted(boolean transacted) { doSetProperty("transacted", transacted); return this; } /** - * Whether transactions are in use. + * Deprecation note: Not used: the producer does not read this option. * * The option will be converted to a boolean type. * @@ -258,14 +259,16 @@ default SpringJdbcEndpointBuilder transacted(boolean transacted) { * @param transacted the value to set * @return the dsl builder */ + @Deprecated default SpringJdbcEndpointBuilder transacted(String transacted) { doSetProperty("transacted", transacted); return this; } /** - * To read BLOB columns as bytes instead of string data. This may be - * needed for certain databases such as Oracle where you must read BLOB - * columns as bytes. + * To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, + * which cannot be read after the producer has closed the connection. + * This may be needed for certain databases such as Oracle where you + * must read BLOB columns as bytes. * * The option is a: boolean type. * @@ -280,9 +283,10 @@ default SpringJdbcEndpointBuilder useGetBytesForBlob(boolean useGetBytesForBlob) return this; } /** - * To read BLOB columns as bytes instead of string data. This may be - * needed for certain databases such as Oracle where you must read BLOB - * columns as bytes. + * To read BLOB columns as bytes (a byte) instead of a java.sql.Blob, + * which cannot be read after the producer has closed the connection. + * This may be needed for certain databases such as Oracle where you + * must read BLOB columns as bytes. * * The option will be converted to a boolean type. * @@ -627,8 +631,8 @@ public String jdbcUpdateCount() { return "CamelJdbcUpdateCount"; } /** - * If the query is a SELECT, query the row count is returned in this OUT - * header. + * If the query is a SELECT and the outputType is SelectList, the row + * count is returned in this OUT header. * * The option is a: {@code int} type. * From 3856e7e394316bd8a3a3335704515cae839edd5f Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Sat, 24 Oct 2026 08:17:00 +0200 Subject: [PATCH 5/5] CAMEL-25983: camel-github2 - getCommitFile uses the deprecated getEncodedContent on purpose Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claus Ibsen --- .../camel/component/github2/producer/GetCommitFileProducer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java index 57563852708af..7ecf6bd20a1ce 100644 --- a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java +++ b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java @@ -93,6 +93,7 @@ public void process(Exchange exchange) throws Exception { exchange.getMessage().setBody(content); } + @SuppressWarnings("deprecation") private byte[] readContent(GHContent file) throws IOException { if ("base64".equals(file.getEncoding())) { // GitHub wraps the lines of the base64 content