[[TypeConverter-TypeConverter]]
= Type Converter

Converting body payloads from one type to another is common when routing messages between endpoints.
Conversions regularly occur between the following types:

* `File`
* `String`
* `byte[]` and `ByteBuffer`
* `InputStream` and `OutputStream`
* `Reader` and `Writer`
* `Document` and `Source`

To convert the body of a message to another type:

[source,java]
----
Message message = exchange.getIn();
Document document = message.getBody(Document.class); // <1>
----
<1> where https://www.javadoc.io/doc/org.apache.camel/camel-api/current/org/apache/camel/Message.html#getBody-java.lang.Class-[`Message#getBody(Class)`] performs the conversion.

[[TypeConverter-HowTypeConversionworks]]
== How Type Conversion works

The type conversion strategy is defined by the
https://www.javadoc.io/doc/org.apache.camel/camel-api/current/org/apache/camel/TypeConverter.html[TypeConverter]
interface that can be customized on a
https://www.javadoc.io/doc/org.apache.camel/camel-api/current/org/apache/camel/CamelContext.html[CamelContext].

Camel provides a default type converter registry.
From Camel 3 onwards, the type converter registry implementation generates source code using the Camel Maven
Package Plugin, allowing Camel, at runtime, to load and invoke these type converters via
quick Java method invocations. In Camel 2, type converters are registered using annotation based discovery, invoking the type converters via the https://docs.oracle.com/javase/tutorial/reflect/[Java Reflection API].


[[TypeConverter-TypeConverterRegistry]]
== TypeConverterRegistry

Add a `TypeConverter` to the
https://www.javadoc.io/doc/org.apache.camel/camel-api/current/org/apache/camel/spi/TypeConverterRegistry.html[`TypeConverterRegistry`]
at runtime using the xref:camelcontext.adoc[CamelContext]:

[source,java]
----
CamelContext context = ...;
context.getTypeConverterRegistry();
----

NOTE: This is useful on platforms where the automatic type converters discovery fails due to classloading issues.

[[TypeConverter-TypeConverterRegistryutilizationstatistics]]
=== TypeConverterRegistry utilization statistics

Camel can gather utilization statistics of the runtime usage of type
converters. These statistics are available in JMX as well as
from https://www.javadoc.io/doc/org.apache.camel/camel-api/latest/org/apache/camel/spi/TypeConverterRegistry.html#getStatistics--[`TypeConverterRegistry#getStatistics()`].

These statistics are turned off by
default as there is some performance overhead under very high concurrent
load.

Enabling statistics in Java:

[source,java]
----
CamelContext context = ...;
context.setTypeConverterStatisticsEnabled(true);
----

Enabling statistics in XML DSL:

[source,xml]
----
<camelContext xmlns="http://camel.apache.org/schema/spring" typeConverterStatisticsEnabled="true">
    ...
</camelContext>
----

[[TypeConverter-Addtypeconverterclassesatruntime]]
=== Add type converter classes at runtime

Classes implementing https://www.javadoc.io/doc/org.apache.camel/camel-api/latest/org/apache/camel/TypeConverters.html[`TypeConverters`] are added to the type converter registry
at runtime. Use https://javadoc.io/doc/org.apache.camel/camel-api/latest/org/apache/camel/Converter.html[`@Converter`] to mark each type converter.

[source,java]
----
private class MyOrderTypeConverters implements TypeConverters {
    @Converter
    public MyOrder toMyOrder(String orderId) {
        MyOrder order = new MyOrder();
        order.setId(Integer.parseInt(orderId));
        return order;
    }
}
----

Then, add these converters to the registry:

[source,xml]
----
MyOrderTypeConverters myClass = ...;
context.getTypeConverterRegistry().addTypeConverters(myClass);
----

If you are using Spring or Blueprint, then you can just declare a `<bean>`
then `CamelContext` will automatic discover and add the converters.

[source,xml]
----
<bean id="myOrderTypeConverters" class="..."/>
    <camelContext ...>
        ...
    </camelContext>
----

You can declare multiple `<bean>`s if you have more classes.

[[TypeConverter-DiscoveringTypeConverters]]
== Discovering Type Converters

Camel automatically discovers and loads the type converters from all JARs on the classpath at startup.

Camel searches the classpath for a file called
`META-INF/services/org/apache/camel/TypeConverterLoader` which lists
all type converter loader classes. These are automatically generated by the Camel Maven Package Plugin.
These _loader_ classes will load the type converters into the Camel type converter registry
and invoke them in a _fast way_ using standard Java method calls.

=== Discovering Camel 2.x based type converters (not loader)

Camel will not perform additional package scanning for type converters which have no source code generated for loader classes.

To enable additional package scanning in Java:

[source,java]
----
camelContext.setLoadTypeConverters(true);
----

In XML DSL:

[source,xml]
----
<camelContext loadTypeConverters="true">
...
</camelContext>
----

In Spring Boot `application.properties`:

[source,properties]
----
camel.springboot.load-type-converters=true
----

Camel will discover Camel 2.x compatible type converters by
searching the classpath for a file called `META-INF/services/org/apache/camel/TypeConverter`
which lists all type converter classes. These classes are automatically registered in the type converter
registry. However, invoking these type converters does **not** happen in a _fast way_ and uses the Java Reflection API.
It is therefore recommended to upgrade any type converters to use the faster way. See below for more details.

TIP: You can turn off the fallback of discovering Camel 2.x compatible type converters by
setting `CamelContext.setLoadTypeConverters(false)`.

`@Converter` must appear at the class and method level for each type converter.

.Register a type converter from `File` to `InputStream`

[source,java]
----
@Converter
public class IOConverter {
    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }
}
----

=== Discovering Type Converters in the fast way

To enable the fast type converter way, you should enable `generateLoader = true`
on the class level annotation as shown:

[source,java]
----
@Converter(generateLoader = true)
public class IOConverter {
    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }
}
----

And then you should have the Camel Maven Package Plugin in as build plugin when compiling the project.
Also add the build helper plugin which ensures the generated source code in `src/generated` will be included in the source path.

When using Maven you add:

[source,xml]
----
      <plugin>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-package-maven-plugin</artifactId>
        <version>${camel-version}</version>
        <executions>
          <execution>
            <id>generate</id>
            <goals>
              <goal>generate-component</goal>
            </goals>
            <phase>process-classes</phase>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <executions>
          <execution>
            <phase>initialize</phase>
            <goals>
              <goal>add-source</goal>
              <goal>add-resource</goal>
            </goals>
            <configuration>
              <sources>
                <source>src/generated/java</source>
              </sources>
              <resources>
                <resource>
                  <directory>src/generated/resources</directory>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
----

[[TypeConverter-Returningnullvalues]]
=== Returning null values

By default when using a method in a POJO annotation with @Converter
returning null is not a valid response. If null is returned, then Camel
will regard that type converter as a _miss_, and prevent from using it
in the future. If null should be allowed as a valid response, then from
*Camel 2.11.2/2.12* onwards you can specify this in the annotation as
shown:

[source,java]
----
@Converter(allowNull = true)
public static InputStream toInputStream(File file) throws IOException {
    if (file.exist()) {
        return new BufferedInputStream(new FileInputStream(file));
    } else {
        return null;
    }
}
----

=== Discovering Type Converters in the fastest way

*Available in Camel 3.7*

In Camel 3.7 we optimized the type converter system for optimal performance when using the built-in converters.
This was done by bulking together all the converters in the same Maven module into a single class. The class has a single
`convert` method where all the supported converters are available and discovered in a fast way using Java primitives.

To enable this then set `generateBulkLoader=true` in the class `@Converter` annotation. You should do this for all the converter
classes within the same Maven artifact. Then they will be bulked together into a single class.

[source,java]
----
@Converter(generateBulkLoader = true)
public class IOConverter {
    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }
}
----

There is a few limitations:

- fallback converters is not supported
- the order of the `@Converter` methods matters. If you have multiple `@Converter` methods that accept as _from_ type
  types which are from the same class hierachy then put the methods first that are the most concrete.

For example in `camel-xml-jaxp` we have in the `XmlConverter` multiple `@Converter` methods which can convert to `DomSource`.
We had to put the method that takes `org.w3c.dom.Document` before the method that takes `org.w3c.dom.Node` as `Document` extends `Node`.

The following code shows snippet of the source code generated bulk class. As you can see we have the `Document` method before the `Node` method below:

[source,java]
----
        } else if (to == javax.xml.transform.dom.DOMSource.class) {
            if (value instanceof org.w3c.dom.Document) {
                return getXmlConverter().toDOMSource((org.w3c.dom.Document) value);
            }
            if (value instanceof org.w3c.dom.Node) {
                return getXmlConverter().toDOMSource((org.w3c.dom.Node) value);
            }
----

[[TypeConverter-DiscoveringFallbackTypeConverters]]
== Discovering Fallback Type Converters

*Available in Camel 2.0*

The
https://www.javadoc.io/doc/org.apache.camel/camel-base/current/org/apache/camel/impl/converter/AnnotationTypeConverterLoader.html[AnnotationTypeConverterLoader]
has been enhanced to also look for methods defined with a
`@FallbackConverter` annotation, and register it as a fallback type
converter.

Fallback type converters are used as a last resort for converting a
given value to another type. Its used when the regular type converters
give up.
The fallback converters is also meant for a broader scope, so its method
signature is a bit different:

[source,java]
----
@FallbackConverter
public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry)
----

Or you can use the non generic signature.

[source,java]
----
@FallbackConverter
public static Object convertTo(Class type, Exchange exchange, Object value, TypeConverterRegistry registry)
----

And the method name can be anything (`convertTo` is not required as a
name), so it can be named `convertMySpecialTypes` if you like. +
The `Exchange` parameter is optional, just as its with the regular
`@Converter` methods.

The purpose with this broad scope method signature is allowing you to
control if you can convert the given type or not. The `type` parameter
holds the type we want the `value` converted to. Its used internally in
Camel for wrapper objects so we can delegate the type convertions to the
body that is wrapped.

For instance in the method below we will handle all type conversions
that is based on the wrapper class `GenericFile` and we let Camel do the
type conversions on its body instead.

[source,java]
----
@FallbackConverter
public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry) {
    // use a fallback type converter so we can convert the embedded body
    // if the value is GenericFile
    if (GenericFile.class.isAssignableFrom(value.getClass())) {
        GenericFile file = (GenericFile) value;
        Class from = file.getBody().getClass();
        TypeConverter tc = registry.lookup(type, from);
        if (tc != null) {
            Object body = file.getBody();
            return tc.convertTo(type, exchange, body);
        }
    }
    return null;
}
----

[[TypeConverter-WritingyourownTypeConverters]]
== Writing your own Type Converters

You are welcome to write your own converters. Remember to use the
`@Converter` annotations on the classes and methods you wish to use.
And on the top-level class add `Converter(loader = true)` to support the _fast way_
of using type converters.

* static methods are encouraged to reduce caching, but instance methods
are fine, particularly if you want to allow optional dependency
injection to customize the converter
* converter methods should be thread safe and reentrant

[[TypeConverter-Exchangeparameter]]
== Exchange parameter

The type converter accepts the `Exchange` as an optional 2nd parameter.
This is usable if the type converter for instance needs information from
the current exchange. For instance combined with the encoding support
its possible for type converters to convert with the configured
encoding. An example from camel-core for the `byte[]` -> `String`
converter:

[source,java]
----
@Converter
public static String toString(byte[] data, Exchange exchange) {
    String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
    if (charsetName != null) {
        try {
            return new String(data, charsetName);
        } catch (UnsupportedEncodingException e) {
            // ignore
        }
        return new String(data);
    }
}
----
