Chapter 36. Type Converters Abstract Apache Camel has a built-in type conversion mechanism, which is used to convert message bodies and message headers to different types. This chapter explains how to extend the type conversion mechanism by adding your own custom converter methods. 36.1. Type Converter Architecture Overview This section describes the overall architecture of the type converter mechanism, which you must understand, if you want to write custom type converters. If you only need to use the built-in type converters, see Chapter 34, Understanding Message Formats . Type converter interface Example 36.1, "TypeConverter Interface" shows the definition of the org.apache.camel.TypeConverter interface, which all type converters must implement. Example 36.1. TypeConverter Interface Controller type converter The Apache Camel type converter mechanism follows a controller/worker pattern. There are many worker type converters, which are each capable of performing a limited number of type conversions, and a single controller type converter, which aggregates the type conversions performed by the workers. The controller type converter acts as a front-end for the worker type converters. When you request the controller to perform a type conversion, it selects the appropriate worker and delegates the conversion task to that worker. For users of the type conversion mechanism, the controller type converter is the most important because it provides the entry point for accessing the conversion mechanism. During start up, Apache Camel automatically associates a controller type converter instance with the CamelContext object. To obtain a reference to the controller type converter, you call the CamelContext.getTypeConverter() method. For example, if you have an exchange object, exchange , you can obtain a reference to the controller type converter as shown in Example 36.2, "Getting a Controller Type Converter" . Example 36.2. Getting a Controller Type Converter Type converter loader The controller type converter uses a type converter loader to populate the registry of worker type converters. A type converter loader is any class that implements the TypeConverterLoader interface. Apache Camel currently uses only one kind of type converter loader - the annotation type converter loader (of AnnotationTypeConverterLoader type). Type conversion process Figure 36.1, "Type Conversion Process" gives an overview of the type conversion process, showing the steps involved in converting a given data value, value , to a specified type, toType . Figure 36.1. Type Conversion Process The type conversion mechanism proceeds as follows: The CamelContext object holds a reference to the controller TypeConverter instance. The first step in the conversion process is to retrieve the controller type converter by calling CamelContext.getTypeConverter() . Type conversion is initiated by calling the convertTo() method on the controller type converter. This method instructs the type converter to convert the data object, value , from its original type to the type specified by the toType argument. Because the controller type converter is a front end for many different worker type converters, it looks up the appropriate worker type converter by checking a registry of type mappings The registry of type converters is keyed by a type mapping pair ( toType , fromType ) . If a suitable type converter is found in the registry, the controller type converter calls the worker's convertTo() method and returns the result. If a suitable type converter cannot be found in the registry, the controller type converter loads a new type converter, using the type converter loader. The type converter loader searches the available JAR libraries on the classpath to find a suitable type converter. Currently, the loader strategy that is used is implemented by the annotation type converter loader, which attempts to load a class annotated by the org.apache.camel.Converter annotation. See the section called "Create a TypeConverter file" . If the type converter loader is successful, a new worker type converter is loaded and entered into the type converter registry. This type converter is then used to convert the value argument to the toType type. If the data is successfully converted, the converted data value is returned. If the conversion does not succeed, null is returned. 36.2. Handling Duplicate Type Converters You can configure what must happen if a duplicate type converter is added. In the TypeConverterRegistry (See Section 36.3, "Implementing Type Converter Using Annotations" ) you can set the action to Override , Ignore or Fail using the following code: Override in this code can be replaced by Ignore or Fail , depending on your requirements. TypeConverterExists Class The TypeConverterExists class consists of the following commands: 36.3. Implementing Type Converter Using Annotations Overview The type conversion mechanism can easily be customized by adding a new worker type converter. This section describes how to implement a worker type converter and how to integrate it with Apache Camel, so that it is automatically loaded by the annotation type converter loader. How to implement a type converter To implement a custom type converter, perform the following steps: the section called "Implement an annotated converter class" . the section called "Create a TypeConverter file" . the section called "Package the type converter" . Implement an annotated converter class You can implement a custom type converter class using the @Converter annotation. You must annotate the class itself and each of the static methods intended to perform type conversion. Each converter method takes an argument that defines the from type, optionally takes a second Exchange argument, and has a non-void return value that defines the to type. The type converter loader uses Java reflection to find the annotated methods and integrate them into the type converter mechanism. Example 36.3, "Example of an Annotated Converter Class" shows an example of an annotated converter class that defines a converter method for converting from java.io.File to java.io.InputStream and another converter method (with an Exchange argument) for converting from byte[] to String . Example 36.3. Example of an Annotated Converter Class The toInputStream() method is responsible for performing the conversion from the File type to the InputStream type and the toString() method is responsible for performing the conversion from the byte[] type to the String type. Note The method name is unimportant, and can be anything you choose. What is important are the argument type, the return type, and the presence of the @Converter annotation. Create a TypeConverter file To enable the discovery mechanism (which is implemented by the annotation type converter loader ) for your custom converter, create a TypeConverter file at the following location: The TypeConverter file must contain a comma-separated list of Fully Qualified Names (FQN) of type converter classes. For example, if you want the type converter loader to search the YourPackageName . YourClassName package for annotated converter classes, the TypeConverter file would have the following contents: An alternative method of enabling the discovery mechanism is to add just package names to the TypeConverter file. For example, the TypeConverter file would have the following contents: This would cause the package scanner to scan through the packages for the @Converter tag. Using the FQN method is faster and is the preferred method. Package the type converter The type converter is packaged as a JAR file containing the compiled classes of your custom type converters and the META-INF directory. Put this JAR file on your classpath to make it available to your Apache Camel application. Fallback converter method In addition to defining regular converter methods using the @Converter annotation, you can optionally define a fallback converter method using the @FallbackConverter annotation. The fallback converter method will only be tried, if the controller type converter fails to find a regular converter method in the type registry. The essential difference between a regular converter method and a fallback converter method is that whereas a regular converter is defined to perform conversion between a specific pair of types (for example, from byte[] to String ), a fallback converter can potentially perform conversion between any pair of types. It is up to the code in the body of the fallback converter method to figure out which conversions it is able to perform. At run time, if a conversion cannot be performed by a regular converter, the controller type converter iterates through every available fallback converter until it finds one that can perform the conversion. The method signature of a fallback converter can have either of the following forms: Where MethodName is an arbitrary method name for the fallback converter. For example, the following code extract (taken from the implementation of the File component) shows a fallback converter that can convert the body of a GenericFile object, exploiting the type converters already available in the type converter registry: 36.4. Implementing a Type Converter Directly Overview Generally, the recommended way to implement a type converter is to use an annotated class, as described in the section, Section 36.3, "Implementing Type Converter Using Annotations" . But if you want to have complete control over the registration of your type converter, you can implement a custom worker type converter and add it directly to the type converter registry, as described here. Implement the TypeConverter interface To implement your own type converter class, define a class that implements the TypeConverter interface. For example, the following MyOrderTypeConverter class converts an integer value to a MyOrder object, where the integer value is used to initialize the order ID in the MyOrder object. Add the type converter to the registry You can add the custom type converter directly to the type converter registry using code like the following: Where context is the current org.apache.camel.CamelContext instance. The addTypeConverter() method registers the MyOrderTypeConverter class against the specific type conversion, from String.class to MyOrder.class . You can add custom type converters to your Camel applications without having to use the META-INF file. If you are using Spring or Blueprint , then you can just declare a <bean>. CamelContext discovers the bean automatically and adds the converters. You can declare multiple <bean>s if you have more classes.

Commands:
package org.apache.camel; public interface TypeConverter { <T> T convertTo(Class<T> type, Object value); }
org.apache.camel.TypeConverter tc = exchange.getContext().getTypeConverter();
typeconverterregistry = camelContext.getTypeConverter() // Define the behaviour if the TypeConverter already exists typeconverterregistry.setTypeConverterExists(TypeConverterExists.Override);
package org.apache.camel; import javax.xml.bind.annotation.XmlEnum; /** * What to do if attempting to add a duplicate type converter * * @version */ @XmlEnum public enum TypeConverterExists { Override, Ignore, Fail }
package com. YourDomain . YourPackageName ; import org.apache.camel. Converter ; import java.io.*; @Converter public class IOConverter { private IOConverter() { } @Converter public static InputStream toInputStream(File file) throws FileNotFoundException { return new BufferedInputStream(new FileInputStream(file)); } @Converter public static String toString(byte[] data, Exchange exchange) { if (exchange != null) { String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class); if (charsetName != null) { try { return new String(data, charsetName); } catch (UnsupportedEncodingException e) { LOG.warn("Can't convert the byte to String with the charset " + charsetName, e); } } } return new String(data); } }
META-INF/services/org/apache/camel/TypeConverter
com. PackageName . FooClass
com. PackageName
// 1. Non-generic form of signature @FallbackConverter public static Object MethodName ( Class type, Exchange exchange, Object value, TypeConverterRegistry registry ) // 2. Templating form of signature @FallbackConverter public static <T> T MethodName ( Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry )
package org.apache.camel.component.file; import org.apache.camel. Converter ; import org.apache.camel. FallbackConverter ; import org.apache.camel.Exchange; import org.apache.camel.TypeConverter; import org.apache.camel.spi.TypeConverterRegistry; @Converter public final class GenericFileConverter { private GenericFileConverter() { // Helper Class } @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; } }
import org.apache.camel.TypeConverter private class MyOrderTypeConverter implements TypeConverter { public <T> T convertTo(Class<T> type, Object value) { // converter from value to the MyOrder bean MyOrder order = new MyOrder(); order.setId(Integer.parseInt(value.toString())); return (T) order; } public <T> T convertTo(Class<T> type, Exchange exchange, Object value) { // this method with the Exchange parameter will be preferd by Camel to invoke // this allows you to fetch information from the exchange during convertions // such as an encoding parameter or the likes return convertTo(type, value); } public <T> T mandatoryConvertTo(Class<T> type, Object value) { return convertTo(type, value); } public <T> T mandatoryConvertTo(Class<T> type, Exchange exchange, Object value) { return convertTo(type, value); } }
// Add the custom type converter to the type converter registry context.getTypeConverterRegistry().addTypeConverter(MyOrder.class, String.class, new MyOrderTypeConverter());
<bean id="myOrderTypeConverters" class="..."/> <camelContext> </camelContext>