Gert Vanthienen's blog

Tuesday, January 13, 2009

A better CBR

Implementing a content based router in Camel is very simple. However, this also means that the routing rules are hard-coded in the Camel RouteBuilder.

Building a more dynamic solution is equally simple with the new @RecipientList annotation. You just create bean with a method that returns a (collection of) endpoints. You can off course use other annotations (like the @XPath annotation) to make your work easier.

public class RecipientListService {
@RecipientList
public List getTargets(@XPath("/po/supplier/@id") String id) {
// lookup endpoint(s) in db or config file
}
}

When using Camel inside ServiceMix, you then register this bean in your camel-context.xml file.

<beans>
<camelContext>...

<bean id="RecipientListService" class="be.anova.camel.RecipientListService">
<!-- this is plain Spring so you can inject any db, webservice, ... you need -->
</bean>
</beans>

By using plain Java and a Spring configuration file, we can write any kind of Java code or access any resource (e.g. a database or external config file) to determine the next routing step. This way, we can build a SA that can be reconfigured without redeployment.

An example use case? You can now build a generic PO handling SA. Every time a new supplier wants to receive their PO electronically, you just create a SA with the supplier-specific endpoints and add some config and you're done! You can even keep the config and the endpoints together in a single bundle if you're using ServiceMix 4 and leverage the OSGi registry to hold your configuration data.

And if you wonder what the Camel route itself looks like, it is a simple as it gets:

from("jbi:service:urn:my:service").beanRef("RecipientListService", "getTargets");

Labels: ,

Sunday, November 2, 2008

... and another release

Just a brief note to help spread the word on the Camel 1.5.0 release. For more details, just check out the release notes or find out what Bruce, Claus or Jon are saying about it.

Labels:

Tuesday, September 23, 2008

ServiceMix Kernel and Camel

Guillaume already announced the release of ServiceMix Kernel 1.0.0 on his blog. We use it as the base for building SMX 4, but you can add any behavior to it by just installing a few bundles.

An example: to convert ServiceMix Kernel into an Apache Camel route container, we just type these three commands in the kernel's console:

osgi install -s mvn:org.springframework/spring-tx/2.5.5
osgi install -s mvn:org.apache.camel/camel-core/1.4.0
osgi install -s mvn:org.apache.camel/camel-spring/1.4.0

This will get the bundles from a local or remote Maven repository, install and start them. Once this is done, you can just create a simple XML file containing the route definitions in the deploy and kernel's file monitor will install and deploy them. You can check the log file with the log d command to see the route in action.

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://activemq.apache.org/camel/schema/spring
http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">

<camelContext xmlns="http://activemq.apache.org/camel/schema/spring">
<route>
<from uri="timer://myTimer?fixedRate=true&period=2000"/>
<to uri="log:demo?showBodyType=false"/>
</route>
</camelContext>

</beans>

Labels: , ,

Thursday, September 11, 2008

What does *DTAQ mean to a Camel?

If you ever worked with an AS/400 system (aka iSeries, System i, IBM i on IBM Power Systems, ...), you probably know what a *DTAQ is all about. For those that don't: a data queue is a native object on the AS/400 that allow you to set up fast and asynchronous communication between jobs and machines.

Apache Camel now has a component to support AS/400 data queues. An example: to read a message of a data queue named REQUESTS in library ERP and send it to RESPONSES queue, all you have to code is

<route>
  <from uri="jt400://myuser:secretpwd@as400_host/qsys.lib/erp.lib/requests.dtaq"/>
  <to uri="jt400://myuser:secretpwd@as400_host/qsys.lib/erp.lib/responses.dtaq"/>
</route>


Moving messages from one queue to the other isn't that spectacular, but you can just as easily replace the to uri to send the message using FTP, HTTP, mail or use any of the other components available in Camel.

It's time to start leveraging all the good code that is available on AS/400 systems all over the world and make that ride the Camel too.

Labels:

Thursday, July 24, 2008

Apache Camel 1.4 released


Apache Camel 1.4 was officially released. Only 3.5 months after the previous release, this one includes another 261 new features, bugfixes, ... (including two new components and a first preview of new Scala DSL). Check out the release notes!

Claus Ibsen also added a second tutorial to ensure you get started with Camel in no time!

Labels:

Thursday, June 5, 2008

Unit testing Camel routes for ServiceMix

ServiceMix allows you to write integration tests using the Spring framework. In addition to this, there is a little technique that allows you to write unit tests for the Camel routes you'll use inside ServiceMix as well. Write the core routing logic (in this example: a content based router) in an abstract RouteBuilder class ...

public abstract class AbstractRouteBuilder extends RouteBuilder {

protected static final String DIRECT_ROUTER = "direct:router";
protected static final String DIRECT_SUPPLIER1 = "direct:supplier1";
protected static final String DIRECT_SUPPLIER2 = "direct:supplier2";

public void configure() {
from(DIRECT_ROUTER)
.choice()
.when(xpath("/po/supplier/@id = '3125'"))
.to(DIRECT_SUPPLIER1)
.otherwise()
.to(DIRECT_SUPPLIER2);
}
}

... and add the JBI endpoint bindings in a separate class, extending the abstract base routing rules.

public class JbiRouteBuilder extends AbstractRouteBuilder {

@Override
public void configure() {
super.configure();

//adding the JBI bindings
from(DIRECT_SUPPLIER1).to("jbi:service:urn:be:anova:po:supplier1");
from(DIRECT_SUPPLIER2).to("jbi:service:urn:be:anova:po:supplier2");
from("jbi:service:urn:be:anova:po:router).to(DIRECT_ROUTER);
}

}

This will allow you to write your unit tests using Camel mock endpoints by extending the same abstract base class. In the example below, we put one sample XML document for every supplier in a test resources folder and we have a file: endpoint to send both of them through our RouteBuilder for testing.

public class RouteBuilderTest extends ContextTestSupport {

public void testContentBasedRouter() throws Exception {
getMockEndpoint("mock:supplier1").expectedMessageCount(1);
getMockEndpoint("mock:supplier2").expectedMessageCount(1);
assertMockEndpointsSatisifed();
}

@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new AbstractRouteBuilder() {
@Override
public void configure() {
super.configure();
from(AbstractRouteBuilder.DIRECT_SUPPLIER1).to("mock:supplier1");
from(AbstractRouteBuilder.DIRECT_SUPPLIER2).to("mock:supplier2");
from("file:src/test/resources/be/anova/smx/purchase?noop=true")
.to(DIRECT_ROUTER);
}
};
}
}

Labels: ,

Friday, April 25, 2008

Unwrapping the wrapper

When we started development on the Scala DSL for Camel, we were using implicit conversion to convert a Java DSL type into a Scala DSL type like this:

implicit def enrichChoice(choice: ChoiceType) = new RichChoiceType(choice)

However, when we try to create a nice-looking DSL this way, we run into problems with the explicits-first rule for implicit conversion in Scala sometimes: methods on the original Java DSL type would take precedence over the 'new' Scala DSL methods.

We actually wanted the behavior to be the other way around: prefer the Scala DSL methods and only fail back to the Java DSL when necessary. Just to whet your appetite for Scala, all it takes to implement this is a simple trait...

trait Wrapper[T] {
val unwrap : T
}
... implemented on our Scala DSL types ...

class RichChoiceType(val unwrap: ChoiceType) with Wrapper[ChoiceType]

... and one equally simple implicit conversion method.

implicit def unwrap[T](wrapper: Wrapper[T]) = wrapper.unwrap

Labels: , ,

Saturday, April 12, 2008

Scala DSL for Camel

I attended ApacheCon Europe this week in Amsterdam. Finally got a chance to meet Guillaume Nodet after chatting with him for hours on IRC and Bruce Snyder was there as well, so I guess the the ServiceMix team was duly represented.

Personally, I was particularly interested in the talks on JCR, Jackrabbit and Sling. I was planning to build a message archiving and monitoring solution for ServiceMix and the talks have convinced me to go for it.

Also took this opportunity to give a 15 minute Fast Feather talk on the new Scala DSL in Camel. The slides are available here.

Labels: , , ,