Dozer
Since Camel 2.15
Only producer is supported
The Dozer component provides the ability to map between Java beans using the Dozer mapping framework. Camel also supports the ability to trigger Dozer mappings as a type converter. The primary differences between using a Dozer endpoint and a Dozer converter are:
-
The ability to manage Dozer mapping configuration on a per-endpoint basis vs. global configuration via the converter registry.
-
A Dozer endpoint can be configured to marshal/unmarshal input and output data using Camel data formats to support a single, any-to-any transformation endpoint
-
The Dozer component allows for fine-grained integration and extension of Dozer to support additional functionality (e.g. mapping literal values, using expressions for mappings, etc.).
In order to use the Dozer component, Maven users will need to add the
following dependency to their pom.xml
:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-dozer</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
URI format
The Dozer component only supports producer endpoints.
dozer:endpointId[?options]
Where endpointId is a name used to uniquely identify the Dozer endpoint configuration.
An example Dozer endpoint URI:
from("direct:orderInput").
to("dozer:transformOrder?mappingFile=orderMapping.xml&targetModel=example.XYZOrder").
to("direct:orderOutput");
Configuring Options
Camel components are configured on two separate levels:
-
component level
-
endpoint level
Configuring Component Options
The component level is the highest level which holds general and common configurations that are inherited by the endpoints. For example a component may have security settings, credentials for authentication, urls for network connection and so forth.
Some components only have a few options, and others may have many. Because components typically have pre configured defaults that are commonly used, then you may often only need to configure a few options on a component; or none at all.
Configuring components can be done with the Component DSL, in a configuration file (application.properties|yaml), or directly with Java code.
Configuring Endpoint Options
Where you find yourself configuring the most is on endpoints, as endpoints often have many options, which allows you to configure what you need the endpoint to do. The options are also categorized into whether the endpoint is used as consumer (from) or as a producer (to), or used for both.
Configuring endpoints is most often done directly in the endpoint URI as path and query parameters. You can also use the Endpoint DSL as a type safe way of configuring endpoints.
A good practice when configuring options is to use Property Placeholders, which allows to not hardcode urls, port numbers, sensitive information, and other settings. In other words placeholders allows to externalize the configuration from your code, and gives more flexibility and reuse.
The following two sections lists all the options, firstly for the component followed by the endpoint.
Component Options
The Dozer component supports 2 options, which are listed below.
Name | Description | Default | Type |
---|---|---|---|
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. |
false |
boolean |
|
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. |
true |
boolean |
Endpoint Options
The Dozer endpoint is configured using URI syntax:
dozer:name
with the following path and query parameters:
Query Parameters (7 parameters)
Name | Description | Default | Type |
---|---|---|---|
The name of a DozerBeanMapperConfiguration bean in the Camel registry which should be used for configuring the Dozer mapping. This is an alternative to the mappingFile option that can be used for fine-grained control over how Dozer is configured. Remember to use a # prefix in the value to indicate that the bean is in the Camel registry (e.g. #myDozerConfig). |
DozerBeanMapperConfiguration |
||
The location of a Dozer configuration file. The file is loaded from the classpath by default, but you can use file:, classpath:, or http: to load the configuration from a specific location. |
dozerBeanMapping.xml |
String |
|
The id of a dataFormat defined within the Camel Context to use for marshalling the mapping output to a non-Java type. |
String |
||
Fully-qualified class name for the source type used in the mapping. If specified, the input to the mapping is converted to the specified type before being mapped with Dozer. |
String |
||
Required Fully-qualified class name for the target type used in the mapping. |
String |
||
The id of a dataFormat defined within the Camel Context to use for unmarshalling the mapping input from a non-Java type. |
String |
||
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. |
false |
boolean |
Using Data Formats with Dozer
Dozer does not support non-Java sources and targets for mappings, so it cannot, for example, map an XML document to a Java object on its own. Luckily, Camel has extensive support for marshalling between Java and a wide variety of formats using data formats. The Dozer component takes advantage of this support by allowing you to specify that input and output data should be passed through a data format prior to processing via Dozer. You can always do this on your own outside the call to Dozer, but supporting it directly in the Dozer component allows you to use a single endpoints to configure any-to-any transformation within Camel.
As an example, let’s say you wanted to map between an XML data structure and a JSON data structure using the Dozer component. If you had the following data formats defined in a Camel Context:
<dataFormats>
<json library="Jackson" id="myjson"/>
<jaxb contextPath="org.example" id="myjaxb"/>
</dataFormats>
You could then configure a Dozer endpoint to unmarshal the input XML using a JAXB data format and marshal the mapping output using Jackson.
<endpoint uri="dozer:xml2json?marshalId=myjson&unmarshalId=myjaxb&targetModel=org.example.Order"/>
Configuring Dozer
All Dozer endpoints require a Dozer mapping configuration file which
defines mappings between source and target objects. The component will
default to a location of META-INF/dozerBeanMapping.xml if the
mappingFile or mappingConfiguration options are not specified on an
endpoint. If you need to supply multiple mapping configuration files
for a single endpoint or specify additional configuration options (e.g.
event listeners, custom converters, etc.), then you can use an instance
of org.apache.camel.converter.dozer.DozerBeanMapperConfiguration
.
<bean id="mapper" class="org.apache.camel.converter.dozer.DozerBeanMapperConfiguration">
<property name="mappingFiles">
<list>
<value>mapping1.xml</value>
<value>mapping2.xml</value>
</list>
</property>
</bean>
Mapping Extensions
The Dozer component implements a number of extensions to the Dozer mapping framework as custom converters. These converters implement mapping functions that are not supported directly by Dozer itself.
Variable Mappings
Variable mappings allow you to map the value of a variable definition within a Dozer configuration into a target field instead of using the value of a source field. This is equivalent to constant mapping in other mapping frameworks, where can you assign a literal value to a target field. To use a variable mapping, simply define a variable within your mapping configuration and then map from the VariableMapper class into your target field of choice:
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
<configuration>
<variables>
<variable name="CUST_ID">ACME-SALES</variable>
</variables>
</configuration>
<mapping>
<class-a>org.apache.camel.component.dozer.VariableMapper</class-a>
<class-b>org.example.Order</class-b>
<field custom-converter-id="_variableMapping" custom-converter-param="${CUST_ID}">
<a>literal</a>
<b>custId</b>
</field>
</mapping>
</mappings>
Custom Mappings
Custom mappings allow you to define your own logic for how a source field is mapped to a target field. They are similar in function to Dozer customer converters, with two notable differences:
-
You can have multiple converter methods in a single class with custom mappings.
-
There is no requirement to implement a Dozer-specific interface with custom mappings.
A custom mapping is declared by using the built-in '_customMapping' converter in your mapping configuration. The parameter to this converter has the following syntax:
[class-name][,method-name]
Method name is optional - the Dozer component will search for a method that matches the input and output types required for a mapping. An example custom mapping and configuration are provided below.
public class CustomMapper {
// All customer ids must be wrapped in "[ ]"
public Object mapCustomer(String customerId) {
return "[" + customerId + "]";
}
}
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
<mapping>
<class-a>org.example.A</class-a>
<class-b>org.example.B</class-b>
<field custom-converter-id="_customMapping"
custom-converter-param="org.example.CustomMapper,mapCustomer">
<a>header.customerNum</a>
<b>custId</b>
</field>
</mapping>
</mappings>
Expression Mappings
Expression mappings allow you to use the powerful language capabilities of Camel to evaluate an expression and assign the result to a target field in a mapping. Any language that Camel supports can be used in an expression mapping. Basic examples of expressions include the ability to map a Camel message header or exchange property to a target field or to concatenate multiple source fields into a target field. The syntax of a mapping expression is:
[language]:[expression]
An example of mapping a message header into a target field:
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
<mapping>
<class-a>org.apache.camel.component.dozer.ExpressionMapper</class-a>
<class-b>org.example.B</class-b>
<field custom-converter-id="_expressionMapping" custom-converter-param="simple:${header.customerNumber}">
<a>expression</a>
<b>custId</b>
</field>
</mapping>
</mappings>
Note that any properties within your expression must be escaped with "\" to prevent an error when Dozer attempts to resolve variable values defined using the EL.
Dozer Type Conversion
The dozer type conversion is deprecated. Do not use. |
Dozer is a fast and flexible framework for mapping back and forth between Java Beans. Coupled with Camel’s automatic type conversion, it’s a formidable tool for dealing object to object mapping headaches that crop up in enterprise integration projects.
To explain how Dozer can be used within Camel we’ll use the following example of a simple Customer Support Service. The initial version of the Service defined a 'Customer' object used with a very flat structure.
Legacy Customer Service Class
public class Customer {
private String firstName;
private String lastName;
private String street;
private String zip;
public Customer() {}
public Customer(String firstName, String lastName, String zip, String street) {
this.firstName = firstName;
this.lastName = lastName;
this.zip = zip;
this.street = street;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
... getters and setters for each field
In the next version it was decided to structure the data better in the model by moving the address data into its own type, with the resulting domain object ending up looking like the below.
Next Gen Customer object
public class Customer {
private String firstName;
private String lastName;
private Address address;
public Customer() {}
public Customer(String firstName, String lastName, Address address) {
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
}
....
public class Address {
private String zipCode;
private String streetName;
public Address() {}
public Address(String zipCode, String streetName) {
this.zipCode = zipCode;
this.streetName = streetName;
}
Much nicer! But as often occurs, the previous version of the service, with the old flat 'Customer' object, was in production with a client and the project must support the legacy interface. To support both versions, we must add a mechanism to convert between the old Customer service type and the new Customer domain type and back again. It would be a simple matter to write a custom converter class to map between them, but this may not be the only service/domain inconsistency and these tedious and error prone custom mappings could quickly start to add up, and bugs with them.
To a large extent the two objects share an identical structure, with only the address representation being different. It would be very helpful if there were a practical way to automate this kind of mapping, such that the similar properties could get mapped automatically and only the inconsistencies requiring custom mapping.
This is where Dozer comes in; it uses reflection to map data between two bean types using a set of simple mapping rules. Where no rule is specified, Dozer will attempt to map between them by using matching properties of two beans. In this way, focus can be given to the inconsistencies between the beans i.e. the address properties, knowing that Dozer will automatically match and convert the others.
Configuring Dozer Type Converter
Dozer’s configuration is extremely flexible and many mapping scenarios are covered here. For our simple example, the configuration looks like the following.
mapping.xml
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
<mapping>
<class-a>org.apache.camel.converter.dozer.service.Customer</class-a>
<class-b>org.apache.camel.converter.dozer.model.Customer</class-b>
<field>
<a>street</a>
<b>address.streetName</b>
</field>
<field>
<a>zip</a>
<b>address.zipCode</b>
</field>
</mapping>
</mappings>
Support for Dozer in Camel Type Converter
Camel provides a simple mechanism to integrate Dozer Mappers with it’s
own powerful Type Conversion framework.
Its configured by creating an instance of DozerTypeConverterLoader
providing it the camel context and an optional Dozer mapper.
If no mapper is supplied, Camel’s registry will be searched for suitable instances. The loader will query the Dozer Mapper for the types it converts and register them with Camel’s type conversion framework to be handled by the mapper.
Limitation The Camel Dozer type converter does not support having the same type conversion pairs in different mapping ids (eg map-id) in Dozer. |
In Java it can be configured as follows:
Mapper mapper = DozerBeanMapperBuilder.create()
.withMappingFiles("mapping.xml")
.build();
new DozerTypeConverterLoader(camelContext, mapper);
Or in Spring XML:
<bean id="dozerConverterLoader" class="org.apache.camel.converter.dozer.DozerTypeConverterLoader">
<argument index="0" ref="myCamel"/>
<argument index="1" ref="mapperConfiguration"/>
</bean>
<bean id="mapperConfiguration" class="org.apache.camel.converter.dozer.DozerBeanMapperConfiguration">
<property name="mappingFiles">
<list>
<value>mapping.xml</value>
</list>
</property>
</bean>
<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">
...
</camelContext>
Now, where necessary, Camel will use Dozer to do conversions; in our case between the new domain and legacy Customer types e.g.
// given the following route
from("direct:legacy-service-in").bean(new CustomerProcessor());
// and a processor
public class CustomerProcessor {
public Customer processCustomer(org.apache.camel.converter.dozer.model.Customer customer) {
...
}
}
// service objects can be sent to the processor and automagically converted by Camel & Dozer
template.sendBody("direct:legacy-service-in", new org.apache.camel.converter.dozer.service.Customer("Bob", "Roberts", "12345", "1 Main st."));
Spring Boot Auto-Configuration
When using dozer with Spring Boot make sure to use the following Maven dependency to have support for auto configuration:
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-dozer-starter</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
The component supports 3 options, which are listed below.
Name | Description | Default | Type |
---|---|---|---|
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. |
true |
Boolean |
|
Whether to enable auto configuration of the dozer component. This is enabled by default. |
Boolean |
||
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. |
false |
Boolean |