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

cdeppisch pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-website.git


The following commit(s) were added to refs/heads/main by this push:
     new 224de070 Add blog post about Camel 4 data types
224de070 is described below

commit 224de070c7f4e90d205372fa0bca4c0bb7503ac4
Author: Christoph Deppisch <[email protected]>
AuthorDate: Mon Dec 4 16:52:13 2023 +0100

    Add blog post about Camel 4 data types
---
 content/blog/2023/12/camel-data-types/featured.png | Bin 0 -> 447535 bytes
 content/blog/2023/12/camel-data-types/index.md     | 492 +++++++++++++++++++++
 2 files changed, 492 insertions(+)

diff --git a/content/blog/2023/12/camel-data-types/featured.png 
b/content/blog/2023/12/camel-data-types/featured.png
new file mode 100644
index 00000000..1c764889
Binary files /dev/null and b/content/blog/2023/12/camel-data-types/featured.png 
differ
diff --git a/content/blog/2023/12/camel-data-types/index.md 
b/content/blog/2023/12/camel-data-types/index.md
new file mode 100644
index 00000000..196081bf
--- /dev/null
+++ b/content/blog/2023/12/camel-data-types/index.md
@@ -0,0 +1,492 @@
+---
+title: "Camel 4 Data Types"
+date: 2023-12-04
+draft: false
+authors: [christophd]
+categories: ["Howtos", "Transformation"]
+preview: "Boosting the interoperability of Camel routes and Kamelets with data 
type transformations"
+---
+
+Since Camel 4, users are able to apply data types to their individual Camel 
routes in order to transform the processed message content in a declarative way.
+The data type functionality has been added on top of the well-known 
[Transformer EIP](/manual/transformer.html) that is a part of Apache Camel 
since the beginning.
+
+This post gives a short introduction to the concept of data types and 
continues with several examples that show how to use those data types in Camel 
for instance as a form of Camel route contracts.
+The post closes with the usage of data types in Pipe definitions to specify 
the input/output behavior of Kamelets.
+
+# How to use data types
+
+With the Camel version 4, users can now declare data types and do the 
following data type specific transformations in a route:
+
+```java
+public class DemoRoute extends EndpointRouteBuilder {
+    @Override
+    public void configure() throws Exception {
+        transformer().name("base64") // (1)
+                .withDataFormat(dataFormat().base64().end());
+        DataType base64 = new DataType("base64"); // (2)
+
+
+        from(timer("tick"))
+                .setBody()
+                .constant("Camel rocks!")
+                .transform(base64) // (3)
+                .to(log("info"));
+    }
+}
+```
+
+1. Transformer declaration with a name and an implementation (e.g. data format 
based, endpoint uri based, custom Java class, …)
+2. Data Type declaration matching a transformer name
+3. Transform EIP using the Data Type
+
+The example route above forms a simple timer-to-log route with a constant 
plain text message body saying _"Camel rocks!"_.
+
+The route also declares a new DataType named _"base64"_ in combination with a 
respective transformer implementation that uses the existing Apache Camel 
_base64_ data format.
+Users of the transformer EIP can now use the data type as a target type in 
order to transform the message content to base64 encoding.
+
+This example should give you a good first impression on how to use data types 
in Camel.
+The next section adds custom data types to the picture.
+
+# Custom data types
+
+The previous example has been using an existing Camel data format 
implementation to perform the data transformation.
+Of course, you can also add custom transformer implementations and use them in 
combination with a data type.
+
+```java
+public class DemoRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       transformer().name("uppercase")
+                 .withJava(UppercaseTransformer.class); // (1)
+
+
+       from(timer("tick"))
+           .setBody()
+               .constant("Camel rocks!")
+           .transform(new DataType("uppercase")) // (2)
+           .to(log("info"));
+   }
+}
+```
+
+1. Directly reference the custom transformer implementation Java type
+2. Reference the data type by its name in a transform EIP
+
+The custom UppercaseTransformer Java class extends the Transformer SPI and is 
able to access the Message object that is being processed by the route.
+Also, it gets access to the data type from/to in case you want to apply very 
specific transformations from one data type into another.
+
+```java
+@DataTypeTransformer(name = "uppercase")
+public class UppercaseTransformer extends Transformer {
+   @Override
+   public void transform(Message message,
+                         DataType from,
+                         DataType to) throws Exception {
+      message.setBody(message.getBody(String.class)
+                             .toUpperCase());
+   }
+}
+```
+
+This way users are able to provide their custom transformer implementations in 
combination with data types.
+
+As an alternative to specifying the transformer Java class implementation you 
can also perform a classpath scan which loads all available transformer 
implementations from a given package.
+
+```java
+public class DemoRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       transformer().scan("org.apache.camel.demo.transform"); // (1)
+
+
+       from(timer("tick"))
+           .setBody()
+               .constant("Camel rocks!")
+           .transform(new DataType("uppercase")) // (2)
+           .to(log("info"));
+   }
+}
+```
+
+1. Scan package "org.apache.camel.demo.transform" for available transformer 
implementations
+2. Reference the data type by its name in a transform EIP
+
+The transformer classpath discovery mechanism is based on using the 
`@DataTypeTransformer` annotation.
+The particular annotation defines the transformer "name" and gives optional 
_"fromType"_ and _"toType"_ filters in order to apply this specific transformer 
only when the type information matches.
+
+```java
+@DataTypeTransformer(name = "uppercase")
+public class UppercaseTransformer extends Transformer {
+   // ...
+}
+```
+
+Last but not least, the transformer may also use the service locator pattern 
in order to do lazy loading via service locator lookup.
+You may add the transformer service locator to the 
_"META-INF/services/org/apache/camel/datatype/transformer"_ folder in your 
project.
+Just add a file with the transformer name pointing to the implementing class.
+
+_META-INF/services/org/apache/camel/datatype/transformer/uppercase_
+```properties
+class=org.apache.camel.demo.transformer.UppercaseTransformer
+```
+
+This is how you can use custom transformer implementations and use them as 
part of a data type transformation in your Camel route.
+In the next section we move on to using predefined transformers provided by 
Camel components.
+
+# Default data type transformers
+
+Camel 4 also introduces a set of default data type transformer implementations 
that you can directly use in your route.
+
+The default data type transformers in Camel can be of generic nature but may 
also relate to a very specific Camel component.
+This way each Camel component is able to provide additional data types for 
input and output data transformations.
+These Camel component specific transformers usually use the component scheme 
as part of the name (e.g. _aws2-s3:application-cloudevents_).
+
+This is a list of available data type transformers provided by Camel 4.
+
+| Data Type Name                             |      Component       |
+|--------------------------------------------|:--------------------:|
+| text-plain                                 | camel-core-processor |
+| application-octet-stream                   | camel-core-processor |
+| http:application-cloudevents               |  camel-cloudevents   |
+| application-cloudevents+json               |  camel-cloudevents   |
+| aws2-s3:application-cloudevents            |    camel-aws2-s3     |
+| aws2-sqs:application-cloudevents           |    camel-aws2-sqs    |
+| aws2-ddb:application-json                  |    camel-aws2-ddb    |
+| azure-storage-blob:application-cloudevents | camel-azure-storage  |
+| google-sheets:application-x-struct         | camel-google-sheets  |
+| google-storage:application-cloudevents     | camel-google-storage |
+| application-json                           |    camel-jackson     |
+| application-x-struct                       |    camel-jackson     |
+| application-x-java-object                  |    camel-jackson     |
+| avro-binary                                |  camel-jackson-avro  |
+| avro-x-struct                              |  camel-jackson-avro  |
+| avro-x-java-object                         |  camel-jackson-avro  |
+
+The combination of Camel component specific data types can be very powerful 
when it comes to processing the data coming from one component as an input to 
another component.
+
+As an example the next Camel route uses the _"http:application-cloudevents"_ 
data type that is provided by the _"camel-cloudevents"_ component.
+The data type reads data from the processed message and the Camel exchange and 
sets proper values according to the CloudEvents specification (e.g. _ce-id_, 
_ce-source_, _ce-type_).
+
+```java
+public class DemoRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       from(timer("tick"))
+           .setBody()
+               .constant("Camel rocks!")
+           .transform(
+               new DataType("http:application-cloudevents"))
+           .to(log("info"));
+   }
+}
+```
+
+The route now uses the CloudEvents specific Http headers as you can see in the 
log output:
+
+```text
+Exchange[
+  Headers: {
+      CamelMessageTimestamp=1697562611364,
+      ce-id=7C4BE86A0A14A5E-0000000000000013,
+      ce-source=org.apache.camel, ce-specversion=1.0,
+      ce-time=2023-10-17T17:10:11.364Z,
+      ce-type=org.apache.camel.event,
+      Content-Type=application/json,
+      firedTime=Tue Oct 17 19:10:11 CEST 2023
+  }
+  BodyType: String
+  Body: Camel rocks!
+]
+```
+
+We can change this to the Json binding of the CloudEvents specification just 
by using the data type _"application-cloudevents+json"_.
+The output then looks like this:
+
+```text
+Exchange[
+  Headers: {
+      CamelMessageTimestamp=1697562914886,
+      Content-Type=application/cloudevents+json,
+      firedTime=Tue Oct 17 19:15:14 CEST 2023}
+  BodyType: String
+  Body: {
+      "datacontenttype":"application/json",
+      "data":"Camel rocks!",
+      "specversion":"1.0",
+      "id":"4667EE9FC3E7889-0000000000000004",
+      "source":"org.apache.camel",
+      "time":"2023-10-17T17:15:14.886Z",
+      "type":"org.apache.camel.event"
+  }
+]
+```
+
+Each Camel component knows best about its individual domain model that may be 
required as an input or gets produced as an output.
+Therefore, adding data types to the Camel components that enable us to 
automatically transform the data into its individual domain model makes much 
sense because it adds more flexibility in using the components.
+
+For instance the sample above has been using a generic Http CloudEvents data 
type which is awesome already as it sets reasonable values according to the 
CloudEvents specification.
+In addition to that, individual Camel components may add component specific 
data types for CloudEvents, too.
+Just like the AWS S3 component adds a CloudEvent specific output data type 
which is able to set proper S3 bucket values as CloudEvent fields (e.g. setting 
the _s3-bucketname_ as the _ce-source_ property).
+
+Also, as another example the Google Sheets component adds an input data type 
that is able to transform a simple Json structure into a proper ValueRange 
(_com.google.api.services.sheets.v4.model_ API) domain model object in order to 
write data into a spreadsheet.
+
+This data type simplifies the interaction with the component a lot because the 
user does not even need to know about the individual Google domain model object 
structure at all.
+Instead, users just provide an arbitrary Json object as an input in order to 
write data to a spreadsheet.
+
+The following route shows the interaction with the Google Sheets Camel 
component without data types.
+The user needs to provide a proper ValueRange domain model object and needs to 
set this in a specific message header.
+
+```java
+public class ModelToGoogleSheetsRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       from(timer("tick").period(5000))
+           .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId")
+               .simple("{{sheets.spreadsheetId}}") // (1)
+           .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "range")
+               .simple("{{sheets.range}}")
+           .process(new CreateValueRangeProcessor()) // (2)
+           .to(googleSheets("data/append") // (3)
+                   .clientId("{{sheets.clientId}}")
+                   .accessToken("{{sheets.accessToken}}")
+                   .refreshToken("{{sheets.refreshToken}}")
+                   .clientSecret("{{sheets.clientSecret}}"));
+   }
+
+
+   private static class CreateValueRangeProcessor implements Processor {
+       @Override
+       public void process(Exchange exchange) throws Exception {
+           ValueRange valueRange = new ValueRange();
+
+
+           List<List<Object>> values = new ArrayList<>();
+
+
+           values.add(Arrays.asList("java-route", "Pineapple", 100)); // (4)
+
+
+           valueRange.setMajorDimension("ROWS");
+           valueRange.setValues(values);
+
+
+           exchange.getMessage().setBody(valueRange);
+
+
+           exchange.getMessage()
+               .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX +
+                                      "valueInputOption","USER_ENTERED");
+           exchange.getMessage()
+               .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX +
+                                      "values", valueRange); // (5)
+       }
+   }
+}
+```
+
+1. Start the route and set some Google Sheets specific headers such as the 
spreadsheet id
+2. Use a custom processor implementation that creates a proper Google Sheets 
ValueRange object
+3. Call the Google Sheets Camel component to write data to the spreadsheet
+4. Construct a ValueRange domain model object that is required by the Google 
Sheets component. Sets the values to write into the spreadsheet (client, 
product and amount)
+5. Set the domain model object as a specific header on the message
+
+Using the Google Sheets component requires a lot of knowledge of the component 
internals and the Google Sheets domain model.
+Instead, you can use the Json struct data type that is provided by the Google 
Sheets component since Camel version 4.
+It automatically transforms an arbitrary Json object with custom fields into a 
ValueRange object.
+
+```java
+public class JsonToSheetsRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       from(timer("tick"))
+           .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "columnNames")
+               .constant("client,product,amount") // (1)
+           .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "spreadsheetId")
+               .simple("{{sheets.spreadsheetId}}")
+           .setHeader(GoogleSheetsConstants.PROPERTY_PREFIX + "range")
+               .simple("{{sheets.range}}")
+           .setBody().simple("""
+             {"client": "java-route","product": "Pineapple","amount": 100}
+           """) // (2)
+           .transform(new DataType("google-sheets:application-x-struct")) // 
(3)
+           .to(googleSheets("data/append")
+                   .clientId("{{sheets.clientId}}")
+                   .accessToken("{{sheets.accessToken}}")
+                   .refreshToken("{{sheets.refreshToken}}")
+                   .clientSecret("{{sheets.clientSecret}}"));
+   }
+}
+```
+
+1. Start the route and set some Google Sheets related headers such as the 
spreadsheet id. Also set the custom column names that the custom Json object 
uses
+2. Use arbitrary Json object as a message body representing the values to 
write into the spreadsheet (client, product and amount)
+3. Define _"google-sheets:application-x-struct"_ as a data type. The data type 
will transform the arbitrary Json object into a proper Google Sheets ValueRange 
domain model so that the Camel component is able to interact with the Sheets API
+
+This is a good example how data types are keen to simplify the usage of Camel 
components and increase the interoperability of components adapting the 
input/output data to a specific context in a declarative way (e.g. CloudEvents, 
Kafka, Http, JsonNode, …).
+There is no need to add a custom processor any more that deals with the 
component internals.
+Instead, the component itself provides a data type that is able to do the 
transformation.
+
+The next section uses this concept to create contracts for Camel routes where 
the input/output data of a route is well declared and all processed data gets 
automatically transformed based on that contract.
+
+# Camel route contracts
+
+Up to now the code samples always used the transform EIP with a data type in 
the route.
+As an alternative to that you may also specify the data type as an 
input/output type for the whole Camel route.
+
+```java
+public class DemoRoute extends EndpointRouteBuilder {
+   @Override
+   public void configure() throws Exception {
+       from(timer("tick"))
+           .setBody()
+               .constant("Camel rocks!")
+           .to(direct("ce-outbound")); // (1)
+
+
+       from(direct("ce-outbound"))
+           .inputType("application-cloudevents+json") // (2)
+           .to(log("info"));
+   }
+}
+```
+
+1. Call another Camel route via direct endpoint
+2. Route declaring its input type as _"application-cloudevents+json"_
+
+In this sample the _"ce-outbound"_ route defines an inputType with the value 
_"application-cloudevents+json"_.
+This means that all calls to this route automatically perform a data type 
transformation to this specific input type.
+
+With input and output types you are able to define contracts between Camel 
routes leveraging the automatic data type transformations.
+
+As data types are declarative their usage gets even more important when it 
comes to using Kamelets and Pipes in Camel.
+
+# Kamelet data types
+
+The declarative nature of data types is very important for Kamelets and Pipes 
because here you may not have the opportunity to add a custom processor with 
some lines of code to do the data transformations.
+Instead, we use the predefined data types to apply transformations as part of 
a Kamelet or Pipe.
+
+The feature brings huge improvements in terms of interoperability of Kamelets 
where the output of Kamelet _A_ is used as an input for Kamelet _B_, for 
instance when binding sources and sinks in a Pipe.
+
+## Expose data type information
+
+First of all, Kamelets may expose the supported input/output data types as 
part of the Kamelet specification.
+
+```yaml
+apiVersion: camel.apache.org/v1
+kind: Kamelet
+metadata:
+ name: aws-s3-source
+spec:
+ properties:
+   # ...
+ dataTypes:
+   out:
+     default: binary
+     types:
+       binary:
+         format: "application-octet-stream"
+         description: |-
+           Default binary representation of the source.
+         mediaType: application/octet-stream
+       cloudevents:
+         format: "aws2-s3:application-cloudevents"
+         description: |-
+            Data type sets CloudEvent headers on the message.
+# ...
+```
+
+The sample above exposes two output data types _"binary"_ and _"cloudevents"_.
+This means that the Kamelet is able to produce these outputs and users may 
choose from these when referencing the Kamelet in a Pipe.
+
+```yaml
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+ name: aws-s3-to-http
+spec:
+ integration:
+   dependencies:
+   - "camel:cloudevents"
+ source:
+   ref:
+     kind: Kamelet
+     apiVersion: camel.apache.org/v1
+     name: aws-s3-source
+   properties:
+     bucketNameOrArn: "{{aws.s3.bucketNameOrArn}}"
+     accessKey: "{{aws.s3.accessKey}}"
+     secretKey: "{{aws.s3.secretKey}}"
+   dataTypes:
+     out:
+       format: aws2-s3:application-cloudevents # (1)
+ sink:
+   ref:
+     kind: Kamelet
+     apiVersion: camel.apache.org/v1
+     name: http-sink
+   dataTypes:
+     in:
+       format: http:application-cloudevents  # (2)
+   properties:
+     url: "{{http.sink.url}}"
+```
+
+1. Use aws2-s3 specific CloudEvents output which sets specific headers
+2. Use Http binding of CloudEvents to produce a proper Http request following 
the CloudEvents specification
+
+The Pipe is able to choose input and output data types when referencing 
Kamelets as sources and sinks.
+This way you can adapt the Kamelets behavior and the combination of Kamelets 
becomes much more robust as the Kamelets are able to work hand in hand with 
increased interoperability.
+
+Another example shows that Kamelet data types also help to transform the 
message as part of a Pipe.
+There are various Kamelets available to transform JsonNode based workloads 
such as extract-field-action, insert-field-action, hoist-field-action.
+All these action Kamelets operate on a generic JsonNode model object.
+The data types help to transform the Kamelet source output into such a generic 
JsonNode by using the _"application-x-struct"_ data type.
+
+```yaml
+apiVersion: camel.apache.org/v1
+kind: Pipe
+metadata:
+ name: slack-to-kafka
+spec:
+ source:
+   ref:
+     kind: Kamelet
+     apiVersion: camel.apache.org/v1
+     name: slack-source
+   properties:
+     channel: "{{slack.channel}}"
+     token: "{{slack.bot.token}}"
+   dataTypes:
+     out:
+       format: application-x-struct # (1)
+ steps:
+   - ref:
+       kind: Kamelet
+       apiVersion: camel.apache.org/v1
+       name: extract-field-action
+     properties:
+       field: text # (2)
+ sink:
+   ref:
+     kind: KafkaTopic
+     apiVersion: kafka.strimzi.io/v1beta2
+     name: "{{kafka.topic}}"
+   dataTypes:
+     in:
+       format: plain-text # (3)
+   properties:
+     brokers: "{{kafka.bootstrapServers}}"
+```
+
+1. Use "application-x-struct" data type that transforms the Slack Json into a 
generic JsonNode using the Jackson library
+2. Extract the "text" field (user input on the Slack channel) from the 
JsonNode and set as a new message body
+3. Transform the message to "plain-text" before sending the data to Kafka
+
+This example Pipe shows that data types tremendously improve the interaction 
between Kamelets because it is very easy to transform the message content into 
different data formats as part of the Pipe processing by just referencing some 
data types.
+
+The input/output data types on Kamelets perfectly complement the declarative 
nature of Kamelet and Pipes.
+
+# What’s next!?
+
+The data types feature is available since Camel 4 and the next step is to 
provide more data type transformer implementations for different types of Camel 
components.
+So please do not hesitate to provide feedback and get involved by adding more 
data types that everybody can use.

Reply via email to