Tuesday, November 8, 2011

Creating A Custom Camel Component

While Camel supports an ever growing number of components, you might have a need to create a custom component. This could be to either promote reuse across projects, customize an existing component or provide a simplified interface to an existing system. Whatever the reason, here is an overview of the options that are available within the Camel framework...

first, consider just creating a Bean or Processor

Before you jump in and create a component, consider just creating a simple class to handle your custom logic. Behind the scenes, all components are just Processors with a bunch of lifecycle support around them.  Beans and Processors are simple, streamlined and easy to manage.

using a Bean...

from(uri).bean(MyBean.class);
... 
public class MyBean {
    public void doSomething(Exchange exchange) {
      //do something...
   }
}

using a Processor...

from(uri).process(new MyProcessor());
... 
public class MyProcessor implements Processor {
    public void process(Exchange exchange) throws Exception {
        //do something... 
    }
}

create a custom component

If you decide to go down this route, you should start by start by using a Maven archetype to stub out a new component project for you.

mvn archetype:generate
    -DarchetypeGroupId=org.apache.camel.archetypes
    -DarchetypeArtifactId=camel-archetype-component
    -DarchetypeVersion=2.9.2
    -DarchetypeRepository=https://repository.apache.org/content/groups/snapshots-group
    -DgroupId=org.apache.camel.component
    -DartifactId=camel-ben
 
This will create a new Maven component project that contains an example HelloWorld component as seen here...
 
 
 





The following core classes are created and have the following responsibilities:

  • HelloWorldComponent
    • endpoint factory which implements createEndpoint()
  • HelloWorldEndpoint
    • producer/consumer factory which implements createConsumer(), createProducer(), createExchange()
  • HelloWorldConsumer
    • acts as a service to consumes request at the start of a route
  • HelloWorldProducer
    • acts as a service consumer to dispatch outgoing requests and receive incoming replies
  • Exchange
    • encapsulate the in/out message payloads and meta data about the data flowing between endpoints
  • Message
    • represent the message payload
    • their is an IN and OUT message for each exchange 
So, how do all these classes/method actually work?  The best way to get your head around this is to load the project into Eclipse (or IntelliJ) and debug the unit test.  This will allow you to step into the route initialization and message processing to trace the flow.

Consumer Lifecycle

When you define a route that uses your new component as a consumer, like this
from("helloworld:foo").to("log:result");

It does the following:
  • creates a HelloWorldComponent instance (one per CamelContext)
  • calls HelloWorldComponent createEndpoint() with the given URI
  • creates a HelloWorldEndpoint instance (one per route reference)
  • creates a HelloWorldConsumer instance (one per route reference)
  • register the route with the CamelContext and call doStart() on the Consumer
  • consumers will then start in one of the following modes:
    • event driven - wait for message to trigger route
    • polling consumer - manually polls a resource for events
    • scheduled polling consumer - events automatically generated by timer
    • custom threading - custom management of the event lifecyle

Producer Lifecycle

When you define a route that uses your new component as a producer, like this
from("direct:start").to("helloworld:foo");
It does the following:
  • creates a HelloWorldComponent instance (one per CamelContext)
  • calls HelloWorldComponent createEndpoint() with the given URI
  • creates a HelloWorldEndpoint instance (one per route reference)
  • creates a HelloWorldProducer instance (one per route reference)
  • register the route with the CamelContext and start the route consumer
  • the Producer's process(Exchange) method is then executed
    • generally, this will decorate the Exchange by interfacing with some external resource (file, jms, database, etc)
Other Resources 
http://camel.apache.org/writing-components.html
http://fusesource.com/docs/mirrors/camel/developers/writing-components.html
http://fusesource.com/docs/mirrors/camel/documentation/user-guide/creating-a-new-camel-component.html
 

11 comments:

  1. I created a new component which should connect Activiti with Servicemix 4.3.0 using the mvn archetype as your example shows, I would like to know I can I deploy it in Servicemix, I mean create a SU and pack inside a SA, could you help me

    ReplyDelete
    Replies
    1. you should just be able to install your new component as an OSGi bundle using 'osgi:install -s mvn://'

      Delete
    2. Thank you I was able to install the bundle.
      My purpose is to call the created component from Servicemix-camel route component as described below, but after I install the bundle when I try to deploy the project where I have the route it through a error:

      No component found with scheme: activiti

      What could be the error?

      Delete
    3. because your component isn't packaged with Camel, you also need to explicitly register your component with the CamelContext:

      CamelContext context = new DefaultCamelContext();
      context.addComponent("foo", new FooComponent(context));

      Delete
    4. I did not get when you say package with Camel. You mean package both project in the in the bundle and instal it?
      I used the auto discover technique on the installed component, as you example shows.
      Sorry I am new in Apache Camel so could you give me more tips on how to do it.
      Thank you for your attention!!!

      Delete
  2. Antonio, I see your point, autodiscovery should work in OSGi as well. I'll poke around a bit, but I imagine its because your new component's bundle manifest isn't setup properly...

    In the meantime, just add this to your application to make sure the new component is registered/available to your route...

    in Java - context.addComponent("foo", new FooComponent(context));
    in Spring - <bean id="helloworld" class="com.test.helloworldComponent"/>

    ReplyDelete
    Replies
    1. I have a doubt I should build the component and deploy it as an OSGi bundle, then refer to it as a camel route on another application? This is the way I have been doing.
      There is any known limitation on integrating Activiti 5.9 with Servicemix 4.3.0? ...because I start to doubt if Apache Camel has the same functionality as Servicemix-Camel or vice versa, which should be the same.
      What do you mean with pack the component with Camel?

      I created the component and them I edited with codes of a integration example between Apache Camel and Activiti,

      auto discover located in: resources/META-INF/services/org/apache/camel/component class=org.apache.camel.component.ActivitiComponent

      the camel-context file:

      Delete
  3. Antonio, I'm not sure what you are trying to do exactly...but you can deploy a custom camel component into OSGI and reference it from another bundle...I just tested this with karaf 2.2.8. That said, I had to explicitly register it with the camel context (as mentioned in my previous comment). what I meant by "packaged with camel" is components that are part of the distribution already (see http://camel.apache.org/components.html)...they don't need to be explicitly registered with the context.

    Otherwise, you might want to take a look at this camel-activiti component: https://github.com/mproch/camel-activiti for examples or to use instead of your custom component...

    good luck...

    ReplyDelete
  4. (hoping you're still listening to this 5 years later...)
    I'm developing a custom component with just a producer. My custom component configures some additional routes that it uses. I can see in Camel debugging output that the routes are being configured but are apparently not started - an Exchange sent to one of them just hangs. This is how I add the routes from my component:

    @Override
    public void onCamelContextStarted(CamelContext context, boolean alreadyStarted) throws Exception {
    if (!alreadyStarted) {
    context.setUseMDCLogging(true);
    context.addRoutes(context.getInjector().newInstance(MyOtherRoutes.class));
    }
    }

    ReplyDelete
    Replies
    1. yep, I'm still around...that said, I'd need more details to help...I recommend posting this question (with more details) on Stackoverflow and you should get a quick response from someone in the Camel community...thanks

      Delete
    2. Done - thank you for your reply, Ben.

      Delete