Apache Camel Series: This post forms part of a series of blog posts about writing a “Real World Application” with Apache Camel. Covering topics like Persistence, Testing, IoC, API Integration and “Big Data”. Using the example of processing the NYC Trip Data, you can follow my progress here
DI / IoC is good! It’s great… blah, blah, blah. You should really use it makes you code super flexible. Which makes it super easy to test, which makes it super easy to refactor. DI / IoC is great! It’s good… blah, blah, blah. Dependency Injection and Inversion of Control are well understood software engineering pattern. According to Martin Fowler:
In the Java community there’s been a rush of lightweight containers that help to assemble components from different projects into a cohesive application. Underlying these containers is a common pattern to how they perform the wiring, a concept they refer under the very generic name of “Inversion of Control”… under the more specific name of “Dependency Injection”, and contrast it with the Service Locator alternative. The choice between them is less important than the principle of separating configuration from use.
Great! I need some of that Guice for my app.
Glad you asked.
Guice (pronounced ‘juice’) is a lightweight dependency injection framework for Java 6 and above, brought to you by Google.
Obviously DI is such a great idea that Google created there own super fast implementation of the pattern to help solve “Google Scale” problems. And obviously my problems are also “Google Scale”. It also happens to be the simplest, which is why I like it (honestly I just can’t deal with all the xml in Spring).
I’m going to assume a basic understanding of Camel and it’s purpose. For a more detailed explanation of What? Why? Where? Checkout my previous post on it.
Give me code.
If code is not your thing (what are you doing here?) skip to the next section.
package me.martinrichards.apache.camel.address;
import...
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
private final CamelContext camel;
private final IAddressService addressService;
@Inject
public Application(CamelContext camelContext, IAddressService addressService) throws Exception {
camel = camelContext;
this.addressService = addressService;
}
public static void main(String... args) throws Exception {
Injector injector = Guice.createInjector(new CamelModuleWithRouteTypes(AddressRoute.class),
new Module());
final Application app = injector.getInstance(Application.class);
app.start();
}
}
package me.martinrichards.apache.camel.address.routes;
import...
public class AddressRoute extends org.apache.camel.builder.RouteBuilder {
public static final String ROUTE_NAME = "Default";
private final IAddressService addressService;
@Inject
public AddressRoute(IAddressService addressService) {
this.addressService = addressService;
}
@Override
public void configure() throws Exception {
from("direct:input").routeId(ROUTE_NAME).unmarshal(new CsvDataFormat())
.split(body()).streaming().parallelProcessing()
.bean(addressService, "filterZone")
.filter(header(addressService.getIsValidHeader()).isEqualTo(true))
.throttle(addressService.getTPS()).bean(addressService, "getAddress")
.aggregate(constant(true)).aggregationStrategy(new BundlingStrategy())
.completionPredicate(new MethodCallExpression(addressService, "isComplete"))
.marshal().csvLazyLoad()
.to("direct:out").log("done.").end();
}
}
package me.martinrichards.apache.camel.address;
import...
public class Module extends AbstractModule {
@Override
protected void configure() {
bind(IAddressService.class).to(AddressService.class);
}
}
package me.martinrichards.apache.camel.address.services;
import...
public interface IAddressService extends Service {
int getTPS();
String getIsValidHeader();
Map<String, String> filterZone(List<String> row, @Headers Map<String, Object> headers);
Map<String, String> getAddress(List<String> row, @Headers Map<String, Object> headers);
boolean isComplete(List<String> row, @Headers Map<String, Object> headers);
}
package me.martinrichards.apache.camel.address.services;
import...
public class AddressService implements IAddressService {
public static final String ISVALID = "1";
@Override
.
.
.
}
Wait Waht?!
Too much code, too much code, too much code…
Okay so what is going on? Let’s start from the top. The first thing we do in the Application#main
method is get a hold of the Guice Injector
class:
Injector injector = Guice.createInjector(new CamelModuleWithRouteTypes(AddressRoute.class),
new Module());
That creates my Injector
with the Modules
I want to use.
Hold up! What are Modules
?
In Guice we define our binding between interface (type) and implemetation in Modules
. More specifically according to the Getting Started Guide:
Guice uses bindings to map types to their implementations. A module is a collection of bindings specified using fluent, English-like method calls. The modules are the building blocks of an injector, which is Guice’s object-graph builder.
Oh okay so this how Guice knows what objects to create when I ask the Injector
for an object.
We’ve told our injector about two Modules
, the first is a generic module for creating camel routes using Guice. Camel is so smart and so nice, it comes with built in support for Guice. The second is my own simple Module
using those “English-like method calls” to define some bindings, well one to be specific.
final Application app = injector.getInstance(Application.class);
app.start();
Finally, I grab an instance of my Application
using my injector. Since I’ve defined how all my classes are made via those nifty Modules
. It gives me back a fully instantiated object I can use.
Next
It’s a new year and well looking back I feel like I was quite successful with my blogging last year. In terms of acctually posting farely often. I mean I got six posts in a year. Which is a respectable 1 post every 2 months. But let’s be honest I can do better.
After my previous post about EIP and Apache Camel, I wanted to follow it up with a little demo app. And by little I meant a nice juicy example using DI and tests and fancy databases and large datasets.
Obviously that has taken longer to do but I don’t want to make it simpler because there are lots of dumb simple examples of how to use camel. I want to create a proper example that shows how to use these technologies to solve real problems. Which they are doing all the time, in enterprises all over the world. Just no one is writing about it cause they can’t.
This is hopefully the first in a series of posts about how I implemeted this nice juicy example. Nice quick, conscience posts about a single aspect of the example. Ship early, ship often.