Documentation

Architecture

The following links are to the individual parts of the Architecture.

 

1.Bean component

http://camel.apache.org/bean.html

camelContext xmlns="http://camel.apache.org/schema/spring"><route><from uri="direct:start"/><to uri="myBean"/><to uri="mock:results"/></route></camelContext><bean id="myBean" class="org.apache.camel.spring.bind.ExampleBean"/>

What happens is that when the exchange is routed to the myBean Camel will use theBean Binding to invoke the bean.
The source for the bean is just a plain POJO:


public class ExampleBean {

    public String sayHello(String name) {
        return "Hello " + name + "!";
    }
}


 

Camel will use Bean Binding to invoke the sayHello method, by converting the Exchange's In body to theString

 

http://camel.apache.org/bean-binding.html

Bean Binding in Camel defines both which methods are invoked and also how the Message is converted into the parameters of the method when it is invoked.

Choosing the method to invoke

The binding of a Camel Message to a bean method call can occur in different ways, in the following order of importance:

  • if the message contains the header CamelBeanMethodName then that method is invoked, converting the body to the type of the method's argument.
  • From Camel 2.8 onwards you can qualify parameter types to select exactly which method to use among overloads with the same name (see below for more details).
  • From Camel 2.9 onwards you can specify parameter values directly in the method option (see below for more details).
  • you can explicitly specify the method name in the DSL or when using POJO Consuming or POJO Producing
  • if the bean has a method marked with the @Handler
  • if the bean can be converted to a Processor using the Type Converter mechanism, then this is used to process the message. The ActiveMQ component uses this mechanism to allow any JMS MessageListener to be invoked directly by Camel without having to write any integration glue code. You can use the same mechanism to integrate Camel into any other messaging/remoting frameworks.
  • if the body of the message can be converted to a BeanInvocation (the default payload used by the ProxyHelper) component - then that is used to invoke the method and pass its arguments
  • otherwise the type of the body is used to find a matching method; an error is thrown if a single method cannot be chosen unambiguously.
  • you can also use Exchange as the parameter itself, but then the return type must be void.
  • if the bean class is private (or package-private), interface methods will be preferred (fromCamel 2.9 onwards) since Camel can't invoke class methods on such beans

In cases where Camel cannot choose a method to invoke, an AmbiguousMethodCallException

By default the return value is set on the outbound message body.

 

The BeanProcessor  uses the input message    to bind its body to the first parameter
of the method   , which happens to be the  String  name parameter. Camel does this
by  creating  an  expression  that  type-converts  the  input  message  body  to  the String
type.  This  ensures  that  when  Camel  invokes  the  echo  method   ,  the  parameter
matches the expected type.

Working with multiple parameters
Using multiple parameters is more complex than using single parameters. It’s gener-ally a good idea to follow these rules of thumb:

Use the first parameter as the message body, which may or may not use the
@Body  annotation.

Use either a built-in type or add Camel annotations for subsequent parameters.
In  our  experience,  it  becomes  complicated  when  multiple  parameters  don’t  follow
these guidelines, but Camel will make its best attempt to adapt the parameters to
the method signature

 

Binding using built-in types

Let’s look at a couple of examples using the types from table 4.2. First, suppose you
add a second parameter that’s one of the built-in types to the  echo method:
public string echo(String echo, CamelContext context)
In this example, you bind the CamelContext, which gives you access to all the moving
parts of Camel.
 Or you could bind the registry, in case you need to look up some beans:
public string echo(String echo, Registry registry) {
   OtherBean other = registry.lookup("other", OtherBean.class);
   ...
}
You  aren’t  restricted  to  having  only  one  additional  parameter;  you  can  have  as  many
as you like. For example, you could bind both the  CamelContext and the registry:
public string echo(String echo, CamelContext context, Registry registry)

 

Binding using Camel annotations

ith the help of Camel annotations, you can bind the Exchange  to the method signa-ture as follows:
public String orderStatus(@Header("customerId") Integer customerId,
                          @Body Integer orderId)
Notice  how  you  can  use  the @Header  annotation  to  bind  the  message  header  to  the
first parameter and  @Body  to bind the message body to the second parameter.

 

Binding using Camel language annotations

For example, suppose the message contains the fol-lowing XML document:
<order customerId="123">
  <status>in progress</status>
</order>
By using  XPath expressions, you can extract parts of the document and bind them to
parameters, like this:
public void updateStatus(@XPath("/order/@customerId") Integer customerId,
                         @XPath("/order/status/text()") String status)

2.

Type Converter

http://camel.apache.org/type-converter.html

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

  • File
  • String
  • byte[] and ByteBuffer
  • InputStream and OutputStream
  • Reader and Writer
  • Document and Source
  • ...

The Message interface defines a helper method to allow conversions to be done via thegetBody(Class) method.

So in an endpoint you can convert a body to another type via


Message message = exchange.getIn();
Document document = message.getBody(Document.class);


How Type Conversion works

The type conversion strategy is defined by the TypeConverter interface which can be customized on a CamelContext.

The default implementation, DefaultTypeConverter uses pluggable strategies to load type converters via TypeConverterLoader. The default strategy, AnnotationTypeConverterLoader uses a discovery mechanism to find converters.

TypeConverterRegistry

New in Camel 2.0

Exposed the TypeConverterRegistry from 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


CamelContext context = ...
   context.getTypeConverterRegistry()


Add type converter at runtime

The following sample demonstrates how to add a type converter at runtime:


// add our own type converter manually that converts from String -> MyOrder using MyOrderTypeConverter
context.getTypeConverterRegistry().addTypeConverter(MyOrder.class, String.class, new MyOrderTypeConverter());


And our type converter is implemented as:


private static class MyOrderTypeConverter extends TypeConverterSupport {

    @SuppressWarnings("unchecked")
    public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
        // converter from value to the MyOrder bean
        MyOrder order = new MyOrder();
        order.setId(Integer.parseInt(value.toString()));
        return (T) order;
    }
}


And then we can convert from String to MyOrder as we are used to with the type converter:


MyOrder order = context.getTypeConverter().convertTo(MyOrder.class,"123");

Discovering Type Converters

The AnnotationTypeConverterLoader will search the classpath for a file called META-INF/services/org/apache/camel/TypeConverter. The contents are expected to be comma separated package names. These packages are then recursively searched for any objects with the@Converter 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.

e.g. the following shows how to register a converter from File -> InputStream



@Converter
public class IOConverter {

    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }
}



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.

We have most of the common converters for common Java types in the org.apache.camel.converter package and its children.

Writing your own Type Converters



Use FQN

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



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 calledMETA-INF/services/org/apache/camel/TypeConverter in your jar. Remember to make sure that :-

  • 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

Examples of TypeConverter file

The file in the JAR: META-INF/services/org/apache/camel/TypeConverter



com.foo
com.bar



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.

Improved TypeConverter by using FQN class names

Available as of Camel 2.8
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.

Examples of TypeConverter file

The file in the JAR: META-INF/services/org/apache/camel/TypeConverter



com.foo.MyConverter
com.bar.MyOtherConverter
com.bar.YetOtherConverter



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

Encoding support for byte[] and String Conversion

Available in Camel 1.5

Since Java provides converting the byte[] to String and String to byte[] with thecharset name parameter. You can define the charset name by setting the exchange property nameExchange.CHARSET_NAME with the charset name, such as"UTF-8" or"iso-8859-1".

Exchange parameter

Available in Camel 1.5

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
    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);
    }



 


 

3.JMS message converter

Message Mapping between JMS and Camel

Camel automatically maps messages between javax.jms.Message and org.apache.camel.Message.

When sending a JMS message, Camel converts the message body to the following JMS message types:


Body Type

JMS Message

Comment

String

javax.jms.TextMessage

 

org.w3c.dom.Node

javax.jms.TextMessage

The DOM will be converted to String.

Map

javax.jms.MapMessage

 

java.io.Serializable

javax.jms.ObjectMessage

 

byte[]

javax.jms.BytesMessage

 

java.io.File

javax.jms.BytesMessage

 

java.io.Reader

javax.jms.BytesMessage

 

java.io.InputStream

javax.jms.BytesMessage

 

java.nio.ByteBuffer

javax.jms.BytesMessage

 


When receiving a JMS message, Camel converts the JMS message to the following body type:


JMS Message

Body Type

javax.jms.TextMessage

String

javax.jms.BytesMessage

byte[]

javax.jms.MapMessage

Map<String, Object>

javax.jms.ObjectMessage

Object


Disabling auto-mapping of JMS messages

You can use the mapJmsMessage option to disable the auto-mapping above. If disabled, Camel will not try to map the received JMS message, but instead uses it directly as the payload. This allows you to avoid the overhead of mapping and let Camel just pass through the JMS message. For instance, it even allows you to route javax.jms.ObjectMessage JMS messages with classes you do not have on the classpath.

Using a custom MessageConverter

You can use the messageConverter option to do the mapping yourself in a Springorg.springframework.jms.support.converter.MessageConverter

For example, in the route below we use a custom message converter when sending a message to the JMS order queue:


from("file://inbox/order").to("jms:queue:order?messageConverter=#myMessageConverter");


You can also use a custom message converter when consuming from a JMS destination.

Controlling the mapping strategy selected

You can use the jmsMessageType option on the endpoint URL to force a specific message type for all messages.
In the route below, we poll files from a folder and send them as javax.jms.TextMessage


from("file://inbox/order").to("jms:queue:order?jmsMessageType=Text");


You can also specify the message type to use for each messabe by setting the header with the keyCamelJmsMessageType. For example:


from("file://inbox/order").setHeader("CamelJmsMessageType", JmsMessageType.Text).to("jms:queue:order");


The possible values are defined in the enum class, org.apache.camel.jms.JmsMessageType.

 

 

4.Active MQ component

http://camel.apache.org/activemq.html

setup the activemq Camel component as follows:


<bean id="jmsConnectionFactory" 
   class="org.apache.activemq.ActiveMQConnectionFactory">
   <property name="brokerURL" value="tcp://localhost:61616" />
</bean>

<bean id="pooledConnectionFactory" 
   class="org.apache.activemq.pool.PooledConnectionFactory" init-method="start" destroy-method="stop">
   <property name="maxConnections" value="8" />
   <property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>

<bean id="jmsConfig" 
   class="org.apache.camel.component.jms.JmsConfiguration">
   <property name="connectionFactory" ref="pooledConnectionFactory"/>
   <property name="concurrentConsumers" value="10"/>
</bean>

<bean id="activemq" 
    class="org.apache.activemq.camel.component.ActiveMQComponent">
    <property name="configuration" ref="jmsConfig"/>
</bean>



Invoking MessageListener POJOs in a Camel route

The ActiveMQ component also provides a helper Type Converter from a JMS MessageListener to a Processor. This means that the Bean component is capable of invoking any JMS MessageListener bean directly inside any route.

So for example you can create a MessageListener in JMS like this:



public class MyListener implements MessageListener {
   public void onMessage(Message jmsMessage) {
       // ...
   }
}



Then use it in your Camel route as follows



from("file://foo/bar").
  bean(MyListener.class);



That is, you can reuse any of the Camel Components and easily integrate them into your JMS MessageListener

Using ActiveMQ Destination Options

Available as of ActiveMQ 5.6

You can configure the Destination Options in the endpoint uri, using the "destination." prefix. For example to mark a consumer as exclusive, and set its prefetch size to 50, you can do as follows:



<camelContext xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="file://src/test/data?noop=true"/>
    <to uri="activemq:queue:foo"/>
  </route>
  <route>
    <!-- use consumer.exclusive ActiveMQ destination option, notice we have to prefix with destination. -->
    <from uri="activemq:foo?destination.consumer.exclusive=true&destination.consumer.prefetchSize=50"/>
    <to uri="mock:results"/>
  </route>
</camelContext>