camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Camel > Type Converter
Date Sat, 16 Jul 2011 09:39:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2042/9/1/_/styles/combined.css?spaceKey=CAMEL&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/CAMEL/Type+Converter">Type
Converter</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~davsclaus">Claus
Ibsen</a>
    </h4>
        <br/>
                         <h4>Changes (2)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >h3. Writing your own Type Converters
<br> <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">{tip:title=Use
FQN} <br>In *Camel 2.8* the TypeConverter file now supports specifying the FQN class
name. This is recommended to be used. See below for more details <br>{tip} <br>
<br></td></tr>
            <tr><td class="diff-unchanged" >You are welcome to write your own
converters. Remember to use the @Converter annotations on the classes and methods you wish
to use. Then add the packages to a file called _META-INF/services/org/apache/camel/TypeConverter_
in your jar. Remember to make sure that :- <br> <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >* converter methods should be thread
safe and reentrant <br> <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h4.
Examples of TypeConverter file <br>The file in the JAR: {{META-INF/services/org/apache/camel/TypeConverter}}
contains the following line(s)  <br>{code} <br>com.foo <br>com.bar <br>{code}
<br> <br>Each line in the file is package name. This tells Camel to go scan those
packages for any classes which has been annotated with the @Converter. <br> <br>h3.
Improved TypeConverter by using FQN class names <br>*Available as of Camel 2.8* <br>In
Camel 2.8 we improved the type converter loader to support specifying the FQN class name of
the converter classes. This has a much better advantage as it avoids having to scan packages
for @Converter classes. Instead it loads the @Converter class directly. This is *highly* recommend
approach to use going forward. <br> <br>h4. Examples of TypeConverter file <br>The
file in the JAR: {{META-INF/services/org/apache/camel/TypeConverter}} contains the following
line(s) for FQN class names  <br>{code} <br>com.foo.MyConverter <br>com.bar.MyOtherConverter
<br>com.bar.YetOtherConverter <br>{code} <br> <br>As you can see each
line in the file now contains a FQN class name. This is the recommended approach. <br>
<br></td></tr>
            <tr><td class="diff-unchanged" >h3. Encoding support for byte[] and
String Conversion <br> <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h2><a name="TypeConverter-TypeConverter"></a>Type Converter</h2>

<p>Its 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</p>

<ul>
	<li>File</li>
	<li>String</li>
	<li>byte[] and ByteBuffer</li>
	<li>InputStream and OutputStream</li>
	<li>Reader and Writer</li>
	<li>Document and Source</li>
	<li>...</li>
</ul>


<p>The <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Message.html"
class="external-link" rel="nofollow">Message interface</a> defines a helper method
to allow conversions to be done via the <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Message.html#getBody(java.lang.Class)"
class="external-link" rel="nofollow">getBody(Class) method</a>.</p>

<p>So in an endpoint you can convert a body to another type via</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
Message message = exchange.getIn();
Document document = message.getBody(Document.class);
</pre>
</div></div>

<h3><a name="TypeConverter-HowTypeConversionworks"></a>How Type Conversion
works</h3>

<p>The type conversion strategy is defined by the <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/TypeConverter.html"
class="external-link" rel="nofollow">TypeConverter</a> interface which can be customized
on a <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/CamelContext.html"
class="external-link" rel="nofollow">CamelContext</a>. </p>

<p>The default implementation, <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/DefaultTypeConverter.html"
class="external-link" rel="nofollow">DefaultTypeConverter</a> uses pluggable strategies
to load type converters via <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/TypeConverterLoader.html"
class="external-link" rel="nofollow">TypeConverterLoader</a>. The default strategy,
<a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/AnnotationTypeConverterLoader.html"
class="external-link" rel="nofollow">AnnotationTypeConverterLoader</a> uses a discovery
mechanism to find converters.</p>

<p><b>New in Camel 1.5</b></p>

<p>The default implementation, <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/DefaultTypeConverter.html"
class="external-link" rel="nofollow">DefaultTypeConverter</a> now throws a <a
href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/NoTypeConversionAvailableException.html"
class="external-link" rel="nofollow">NoTypeConversionAvailableException</a> if a
suitable conversion cannot be found (CAMEL-84).  The semantical ambiguity of null (both valid
result and indication of no conversion found) is now resolved, but this may impact existing
code in that it should now catch the exception instead of checking for null.</p>

<h3><a name="TypeConverter-TypeConverterRegistry"></a>TypeConverterRegistry</h3>

<p><b>New in Camel 2.0</b></p>

<p>Exposed the <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/spi/TypeConverterRegistry.html"
class="external-link" rel="nofollow">TypeConverterRegistry</a> from <a href="/confluence/display/CAMEL/CamelContext"
title="CamelContext">CamelContext</a> 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. </p>

<p>To access the registry you get it from the CamelContext</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
   CamelContext context = ...
   context.getTypeConverterRegistry()
</pre>
</div></div>

<h4><a name="TypeConverter-Addtypeconverteratruntime"></a>Add type converter
at runtime</h4>
<p>The following sample demonstrates how to add a type converter at runtime:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-comment">// add our own type converter
manually that converts from <span class="code-object">String</span> -&gt;
MyOrder using MyOrderTypeConverter
</span>context.getTypeConverterRegistry().addTypeConverter(MyOrder.class, <span class="code-object">String</span>.class,
<span class="code-keyword">new</span> MyOrderTypeConverter());
</pre>
</div></div>

<p>And our type converter is implemented as:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">private</span> class MyOrderTypeConverter
<span class="code-keyword">implements</span> TypeConverter {

    @SuppressWarnings(<span class="code-quote">"unchecked"</span>)
    <span class="code-keyword">public</span> &lt;T&gt; T convertTo(<span
class="code-object">Class</span>&lt;T&gt; type, <span class="code-object">Object</span>
value) {
        <span class="code-comment">// converter from value to the MyOrder bean
</span>        MyOrder order = <span class="code-keyword">new</span> MyOrder();
        order.setId(<span class="code-object">Integer</span>.parseInt(value.toString()));
        <span class="code-keyword">return</span> (T) order;
    }

    <span class="code-keyword">public</span> &lt;T&gt; T convertTo(<span
class="code-object">Class</span>&lt;T&gt; type, Exchange exchange, <span
class="code-object">Object</span> value) {
        <span class="code-comment">// <span class="code-keyword">this</span>
method with the Exchange parameter will be preferred by Camel to invoke
</span>        <span class="code-comment">// <span class="code-keyword">this</span>
allows you to fetch information from the exchange during conversions
</span>        <span class="code-comment">// such as an encoding parameter or
the likes
</span>        <span class="code-keyword">return</span> convertTo(type,
value);
    }

    <span class="code-keyword">public</span> &lt;T&gt; T mandatoryConvertTo(<span
class="code-object">Class</span>&lt;T&gt; type, <span class="code-object">Object</span>
value) {
        <span class="code-keyword">return</span> convertTo(type, value);
    }

    <span class="code-keyword">public</span> &lt;T&gt; T mandatoryConvertTo(<span
class="code-object">Class</span>&lt;T&gt; type, Exchange exchange, <span
class="code-object">Object</span> value) {
        <span class="code-keyword">return</span> convertTo(type, value);
    }
}
</pre>
</div></div>

<p>And then we can convert from String to MyOrder as we are used to with the type converter:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">MyOrder order = context.getTypeConverter().convertTo(MyOrder.class,
<span class="code-quote">"123"</span>);
</pre>
</div></div>

<h3><a name="TypeConverter-DiscoveringTypeConverters"></a>Discovering Type
Converters</h3>

<p>The <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/AnnotationTypeConverterLoader.html"
class="external-link" rel="nofollow">AnnotationTypeConverterLoader</a> will search
the classpath for a file called <em>META-INF/services/org/apache/camel/TypeConverter</em>.
The contents are expected to be comma separated package names. These packages are then recursively
searched for any objects with the <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Converter"
class="external-link" rel="nofollow">@Converter</a> annotation. Then any method marked
with @Converter is assumed to be a conversion method; where the parameter is the from value
and the return is the to value.</p>

<p>e.g. the following shows how to register a converter from File -&gt; InputStream</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Converter
<span class="code-keyword">public</span> class IOConverter {

    @Converter
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
InputStream toInputStream(File file) <span class="code-keyword">throws</span>
FileNotFoundException {
        <span class="code-keyword">return</span> <span class="code-keyword">new</span>
BufferedInputStream(<span class="code-keyword">new</span> FileInputStream(file));
    }
}
</pre>
</div></div>

<p>Static methods are invoked; non-static methods require an instance of the converter
object to be created (which is then cached). If a converter requires configuration you can
plug in an Injector interface to the DefaultTypeConverter which can construct and inject converter
objects via Spring or Guice. </p>

<p>We have most of the common converters for common Java types in the <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/converter/package-summary.html"
class="external-link" rel="nofollow">org.apache.camel.converter</a> package and its
children.</p>

<h3><a name="TypeConverter-DiscoveringFallbackTypeConverters"></a>Discovering
Fallback Type Converters</h3>
<p><b>Available in Camel 2.0</b><br/>
The <a href="http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/impl/converter/AnnotationTypeConverterLoader.html"
class="external-link" rel="nofollow">AnnotationTypeConverterLoader</a> has been enhanced
to also look for methods defined with a @FallbackConverter annotation, and register it as
a fallback type converter.</p>

<p>Fallback type converters is used as a last resort of hopes for converting a given
value to another type. Its used when the regular type converters give up.<br/>
The fallback converters is also meant for a broader scope so its method signature is a bit
different:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @FallbackConverter
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
&lt;T&gt; T convertTo(<span class="code-object">Class</span>&lt;T&gt;
type, Exchange exchange, <span class="code-object">Object</span> value, TypeConverterRegistry
registry)
</pre>
</div></div>

<p>Or you can use the non generic signature.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @FallbackConverter
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
<span class="code-object">Object</span> convertTo(<span class="code-object">Class</span>
type, Exchange exchange, <span class="code-object">Object</span> value, TypeConverterRegistry
registry)
</pre>
</div></div>

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

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

<p>For instance in the method below we will handle all type conversions that is based
on the wrapper class <tt>GenericFile</tt> and we let Camel do the type conversions
on its body instead.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @FallbackConverter
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
&lt;T&gt; T convertTo(<span class="code-object">Class</span>&lt;T&gt;
type, Exchange exchange, <span class="code-object">Object</span> value, TypeConverterRegistry
registry) {
        <span class="code-comment">// use a fallback type converter so we can convert
the embedded body <span class="code-keyword">if</span> the value is GenericFile
</span>        <span class="code-keyword">if</span> (GenericFile.class.isAssignableFrom(value.getClass()))
{
            GenericFile file = (GenericFile) value;
            <span class="code-object">Class</span> from = file.getBody().getClass();
            TypeConverter tc = registry.lookup(type, from);
            <span class="code-keyword">if</span> (tc != <span class="code-keyword">null</span>)
{
                <span class="code-object">Object</span> body = file.getBody();
                <span class="code-keyword">return</span> tc.convertTo(type, exchange,
body);
            }
        }
        
        <span class="code-keyword">return</span> <span class="code-keyword">null</span>;
    }
</pre>
</div></div>

<h3><a name="TypeConverter-WritingyourownTypeConverters"></a>Writing your
own Type Converters</h3>

<div class='panelMacro'><table class='tipMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/check.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td><b>Use FQN</b><br
/>In <b>Camel 2.8</b> the TypeConverter file now supports specifying the FQN
class name. This is recommended to be used. See below for more details</td></tr></table></div>

<p>You are welcome to write your own converters. Remember to use the @Converter annotations
on the classes and methods you wish to use. Then add the packages to a file called <em>META-INF/services/org/apache/camel/TypeConverter</em>
in your jar. Remember to make sure that :-</p>

<ul>
	<li>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</li>
	<li>converter methods should be thread safe and reentrant</li>
</ul>


<h4><a name="TypeConverter-ExamplesofTypeConverterfile"></a>Examples of
TypeConverter file</h4>
<p>The file in the JAR: <tt>META-INF/services/org/apache/camel/TypeConverter</tt>
contains the following line(s) </p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
com.foo
com.bar
</pre>
</div></div>

<p>Each line in the file is package name. This tells Camel to go scan those packages
for any classes which has been annotated with the @Converter.</p>

<h3><a name="TypeConverter-ImprovedTypeConverterbyusingFQNclassnames"></a>Improved
TypeConverter by using FQN class names</h3>
<p><b>Available as of Camel 2.8</b><br/>
In Camel 2.8 we improved the type converter loader to support specifying the FQN class name
of the converter classes. This has a much better advantage as it avoids having to scan packages
for @Converter classes. Instead it loads the @Converter class directly. This is <b>highly</b>
recommend approach to use going forward.</p>

<h4><a name="TypeConverter-ExamplesofTypeConverterfile"></a>Examples of
TypeConverter file</h4>
<p>The file in the JAR: <tt>META-INF/services/org/apache/camel/TypeConverter</tt>
contains the following line(s) for FQN class names </p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
com.foo.MyConverter
com.bar.MyOtherConverter
com.bar.YetOtherConverter
</pre>
</div></div>

<p>As you can see each line in the file now contains a FQN class name. This is the recommended
approach.</p>

<h3><a name="TypeConverter-EncodingsupportforbyteandStringConversion"></a>Encoding
support for byte[] and String Conversion</h3>

<p><b>Available in Camel 1.5</b></p>

<p>Since Java provides converting the byte[] to String and String to byte[] with the
<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/nio/charset/Charset.html" class="external-link"
rel="nofollow">charset name</a> parameter. You can define the charset name by setting
the exchange property name <tt>Exchange.CHARSET_NAME</tt> with the charset name,
such as <tt>"UTF-8"</tt> or <tt>"iso-8859-1"</tt>.</p>

<h3><a name="TypeConverter-Exchangeparameter"></a>Exchange parameter</h3>

<p><b>Available in Camel 1.5</b></p>

<p>The type converter accepts the <tt>Exchange</tt> 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 <tt>byte[] -&gt;
String</tt> converter:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
    @Converter
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
<span class="code-object">String</span> toString(<span class="code-object">byte</span>[]
data, Exchange exchange) {
        <span class="code-keyword">if</span> (exchange != <span class="code-keyword">null</span>)
{
            <span class="code-object">String</span> charsetName = exchange.getProperty(Exchange.CHARSET_NAME,
<span class="code-object">String</span>.class);
            <span class="code-keyword">if</span> (charsetName != <span class="code-keyword">null</span>)
{
                <span class="code-keyword">try</span> {
                    <span class="code-keyword">return</span> <span class="code-keyword">new</span>
<span class="code-object">String</span>(data, charsetName);
                } <span class="code-keyword">catch</span> (UnsupportedEncodingException
e) {
                    LOG.warn(<span class="code-quote">"Can't convert the <span class="code-object">byte</span>
to <span class="code-object">String</span> with the charset "</span> + charsetName,
e);
                }
            }
        }
        <span class="code-keyword">return</span> <span class="code-keyword">new</span>
<span class="code-object">String</span>(data);
    }
</pre>
</div></div>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/CAMEL/Type+Converter">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=50335&revisedVersion=22&originalVersion=21">View
Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/CAMEL/Type+Converter?showComments=true&amp;showCommentArea=true#addcomment">Add
Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message