| [[TypeConverter-TypeConverter]] |
| === Type Converter |
| |
| It is very common when routing messages from one endpoint to another to |
| need to convert the body payloads from one type to another such as to |
| convert to and from the following common types: |
| |
| * File |
| * String |
| * byte[] and ByteBuffer |
| * InputStream and OutputStream |
| * Reader and Writer |
| * Document and Source |
| * ... |
| |
| The |
| http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Message.html[Message |
| interface] defines a helper method to allow conversions to be done via |
| the |
| http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Message.html#getBody(java.lang.Class)[getBody(Class)] |
| method. |
| |
| So in an endpoint you can convert a body to another type via: |
| |
| [source,java] |
| ---- |
| Message message = exchange.getIn(); |
| Document document = message.getBody(Document.class); |
| ---- |
| |
| [[TypeConverter-HowTypeConversionworks]] |
| ==== How Type Conversion works |
| |
| The type conversion strategy is defined by the |
| http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/TypeConverter.html[TypeConverter] |
| interface that can be customized on a |
| http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html[CamelContext]. |
| |
| Camel provides a default type converter registy. |
| From Camel 3 onwards there type converter registry implementation uses source code generation via the `camel-apt` compiler plugin to generate source code, that allows Camel at runtime to load and invoke these type converters via quick java method invocations. The older implementation in Camel 2 uses a annotation based discover to load and register type converters and invokes the type converters via Java method call reflections. |
| |
| |
| [[TypeConverter-TypeConverterRegistry]] |
| ==== TypeConverterRegistry |
| |
| Exposed the |
| http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/spi/TypeConverterRegistry.html[TypeConverterRegistry] |
| from xref:camelcontext.adoc[CamelContext] so end users more easily will |
| be able to add type converters at runtime. This is also usable in |
| situations where the default discovering of type converters fails on |
| platforms with classloading issues. |
| |
| To access the registry, you get it from the `CamelContext`: |
| |
| [source,java] |
| ---- |
| CamelContext context = ... |
| context.getTypeConverterRegistry() |
| ---- |
| |
| [[TypeConverter-TypeConverterRegistryutilizationstatistics]] |
| ===== TypeConverterRegistry utilization statistics |
| |
| Camel can gather utilization statistics of the runtime usage of type |
| converters. These stats are available in JMX, and as well as from the |
| `getStatistics()` method from `TypeConverterRegistry`. |
| |
| These statistics are turned off by |
| default as there is some performance overhead under very high concurrent |
| load. To enable the statistics in Java, do the following: |
| |
| [source,java] |
| ---- |
| CamelContext context = ... |
| context.setTypeConverterStatisticsEnabled(true); |
| ---- |
| |
| Or in the XML DSL with: |
| |
| [source,xml] |
| ---- |
| <camelContext xmlns="http://camel.apache.org/schema/spring" typeConverterStatisticsEnabled="true"> |
| ... |
| </camelContext> |
| ---- |
| |
| [[TypeConverter-Addtypeconverterclassesatruntime]] |
| ===== Add type converter classes at runtime |
| |
| You can add new type converters at runtime, by having your classes |
| implement `org.apache.camel.TypeConverters` which is an marker |
| interface. Then for each type converter you want use |
| the `@Converter` annotation. |
| |
| [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 you can add these converters to the registry using: |
| |
| [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 will automatic discover and load type converters from all the JARs in the classpath on startup. |
| |
| Camel will search the classpath for a file called |
| `META-INF/services/org/apache/camel/TypeConverterLoader`, which lists |
| all type converter loader classes (they are automatic source code generated by the `camel-apt` compilter plugin). |
| These _loader_ classes will load the type converters into the Camel type converter registry |
| and invoking these type converters is done in a _fast way_ using standard java method calls. |
| |
| The discover will fallback and discover Camel 2.x compatible type converters by |
| search the classpath for a file called `META-INF/services/org/apache/camel/TypeConverter`, |
| which lists all the type converter classes. These classes will then automatic |
| be registered in the type converter registry. However invoking these type converters |
| does **not** happen in a _fast way_ but uses Java method call reflection. It is therefore |
| recommended to migrate your type converters to use the faster way. See further below. |
| |
| TIP: You can turn off the fallback of discovering Camel 2.x compatible type converters by |
| setting the `loadTypeConverters` option to `false` on `CamelContext`. |
| |
| The converter classes must be annotated on the top class level with the `@Converter` annotation, |
| and each converter method as well. |
| |
| E.g. the following shows how to register a 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 `loader = true` |
| on the class level annotation as shown: |
| |
| [source,java] |
| ---- |
| @Converter(loader = 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-apt` JAR as dependency when compiling the project. |
| When using Maven you add: |
| |
| [source,xml] |
| ---- |
| <dependency> |
| <groupId>org.apache.camel</groupId> |
| <artifactId>apt</artifactId> |
| <scope>provided</scope> |
| </dependency> |
| ---- |
| |
| [[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; |
| } |
| } |
| ---- |
| |
| [[TypeConverter-DiscoveringFallbackTypeConverters]] |
| ==== Discovering Fallback Type Converters |
| |
| *Available in Camel 2.0* |
| |
| The |
| http://camel.apache.org/maven/current/camel-core/apidocs/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); |
| } |
| } |
| ---- |