On Github bsaunder / camel_from_the_field
Integration is Critical for Business
Framework does the Heavy Lifting
Lets you Focus on the Problem
Prevents Re-inventing the Wheel
Started in March 2007
Founded by
First Release in June 2007
Spawned from Apache ServiceMix and Apache ActiveMQ
ServiceMix and ActiveMQ both had a need to implement EIP's. A decision was made to put that functionality into a seperate Framework that could be used anywhere. Thus Camel was born.Very Leightweight , Consists of only a handful of JARs
Easy Configuration
No Heavy Specification
No Container Dependencies
Payload Agnostic
Minimal Configuration
DSL Based Programming
6 DSL's and Counting
from("file:src/data?noop=true") .choice() .when(xpath("/person/city = 'London'")) .to("file:target/messages/uk") .otherwise() .to("file:target/messages/others");
<route> <from uri="direct:a"/> <choice> <when> <xpath>$foo = 'bar'</xpath> <to uri="direct:b"/> </when> <when> <xpath>$foo = 'cheese'</xpath> <to uri="direct:c"/> </when> <otherwise> <to uri="direct:d"/> </otherwise> </choice> </route>
rest("/say") .get("/hello").to("direct:hello") .get("/bye").consumes("application/json").to("direct:bye")
<rest path="/say"> <get uri="/hello"> <to uri="direct:hello"/> </get> <get uri="/bye" consumes="application/json"> <to uri="direct:bye"/> </get> </rest>Not Available until Camel 2.14
191 Components and counting
Component for pretty much anything
Most commonley used components
Try...Catch...Finally
Exception Clause in Java DSL
Error Handlers
Support for Exponential Backoff of Retries
DefaultErrorHandler does not retry, DeadLetterChannel has DLQ and retries.from("direct:start") .doTry() .process(new ProcessorFail()) .to("mock:result") .doCatch(IOException.class, IllegalStateException.class) .to("mock:catch") .doFinally() .to("mock:finally") .end();
onException(ValidationException.class) .to("activemq:validationFailed"); onException(ShipOrderException.class) .to("activemq:shipFailed"); from("seda:order").to("bean:processOrder");
errorHandler(deadLetterChannel("jms:queue:dead") .useOriginalMessage() .mamimumRedeliveries(5) .redeliverDelay(5000)); errorHandler(defaultErrorHandler() .allowRedeliveryWhileStopping(false) .maximumRedeliveries(20) .redeliveryDelay(1000) .retryAttemptedLogLevel(LoggingLevel.INFO));
Allows for custom backoff of retries.
Syntax: limit:delay;limit2:delay2;limit3:delay3;...;limitN:delayN
If delayPattern=5:1000;10:5000;20:20000 then we get
Four Broad Categories of Security Offered
Policy driven security for routes or route segments
Encryption and decryption services for secure paylods
Some components can be secured, but not all
Externalize configuration properties
Load Balancing Policies
Thread and Service Pools
Asynchronous API
Clustering
Not natively supported
Provided by the following components
Native test frameworks
Supports unit and integration Testing
Built in mock & stub support
Advanced testing with NotifyBuilder and AdviceWith
Works with 3rd party test frameworks
public class FilterTest extends CamelTestSupport { @EndpointInject(uri = "mock:result") protected MockEndpoint resultEndpoint; @Produce(uri = "direct:start") protected ProducerTemplate template; @Test public void testSendMatchingMessage() throws Exception { String expectedBody = "matched"; resultEndpoint.expectedBodiesReceived(expectedBody); template.sendBodyAndHeader(expectedBody, "foo", "bar"); resultEndpoint.assertIsSatisfied(); } @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result"); } }; } }
Several tools exist for working with and monitoring Camel
Similar to Design Patterns
Focused specifically on Integration
Written by Gregor Hohpe & Bobby Wolf
65 Documented Patterns
Camel supports 53 of them
Endpoint newOrder = endpoint("activemq:queue:newOrder"); Predicate isWidget = xpath("/order/product ='widget'"); Endpoint widget = endpoint("activemq:queue:widget"); Endpoint gadget = endpoint("activemq:queue:gadget"); from(newOrder) .choice() .when(isWidget) .to(widget) .otherwise() .to(gadget) .end();
Used for defining routes
Multiple DSL's exist
Set of rules that define message flow
Consists of:
Implementation of the Message Endpoint pattern
Created by components
Referred to by uniue URI's in the DSL
Consumers receive messages
Producers send messages
Essentially a factory for Endpoint instances
Adds functionality to Camel
Custom components extend DefaultComponent
Consumes message exchanges
Processor interface for building custom Processors
Used to encapsulate custom business logic
Can be turned into a full Component
Supports multiple Exchange Patterns
Implements Message EIP
Anywhere that supports Java 1.6
Included in JBoss Fuse
Included in JBoss Fuse Service Works
Supported on EAP 6.1.1+
Use Fuse if...
Use Fuse Service Works if...
For Fuse...
For Fuse Service Works w/ SwitchYard...
For Camel on EAP...
Use Camel without SwitchYard or Karaf
Supported on EAP 6.1.1 and up with Camel 2.12 Red Hat Libraries
Two ways to use Libraries
Camel Started with 3 methods
Requires Spring
Best if already using Spring
Camel uses Spring's Bean Registry
Started using Spring ContextLoaderListener
Configuration very similar to Fuse with Spring or Blueprint
Example web.xml
<web-app ...> <display-name>soap-contract-first</display-name> <!-- Config Location --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:META-INF/spring/*.xml</param-value> </context-param> <!-- Spring Listener Servlet --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
Example camel-route.xml
<beans ...> <import resource="classpath:META-INF/spring/camel-cxf.xml" /> <camelContext xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="cxf:bean:orderEndpoint" /> <log message=">> Received SOAP Endpoint: ${body}"></log> <setBody> <simple>DONE</simple> </setBody> </route> </camelContext> </beans>
https://github.com/bsaunder/camel/tree/master/eap_6/soap-contract-first
Does not Require Spring
Best if using JEE, but do not need to access Camel with JEE Stack
Camel uses a Simple Map based Bean Registry (Can also use JNDI)
Started using CamelServletContextListener
Configuration XML Based
Example web.xml
<web-app ...> <display-name>servlet_listener</display-name> <!-- Set CamelContext name --> <context-param> <param-name>name</param-name> <param-value>myCamelContext</param-value> </context-param> <context-param> <param-name>routeBuilder-JavaRoute</param-name> <param-value>net.bryansaunders.camel.servlet_listener.JavaRoute</param-value> </context-param> <context-param> <param-name>routeBuilder-XmlRoute</param-name> <param-value>classpath:camel-routes.xml</param-value> </context-param> <listener> <listener-class>org.apache.camel.component.servletlistener.SimpleCamelServletContextListener</listener-class> </listener> </web-app>
https://github.com/bsaunder/camel/tree/master/eap_6/servlet_listener
Does not Require Spring or use Servlets
Best if using JEE, Especially CDI
Camel uses CDI Bean Manager
Started using an @Startup @Singleton Annotated Bean
All Configuration done in Java, No XML
Must be a Singleton, @Singleton
Must run at Startup. @Startup
Inject an instance of CdiCamelContext
@PostConstruct method should Configure/Start Context and Add Routes
@PreDestroy should Stop Camel Context
@Singleton @Startup public class Bootstrap { @Inject private CdiCamelContext camelCtx; @Inject private SomeRouteBuilder someRoute; @PostConstruct public void init() throws Exception { this.camelCtx.addRoutes(someRoute); this.camelCtx.setName("my-camel-context"); this.camelCtx.start(); } @PreDestroy public void stop() throws Exception { this.camelCtx.stop(); } }
Spring and Blueprint XML routes can also be used
Routes loaded from XML file in Bootstrap Class
InputStream is = this.getClass().getClassLoader().getResourceAsStream("camel-routes.xml"); if (is != null) { RoutesDefinition routes = this.camelCtx.loadRoutesDefinition(is); this.camelCtx.addRouteDefinitions(routes.getRoutes()); }
https://github.com/bsaunder/camel/tree/master/eap_6/cdi-soap-consume
No Spring Config or Context
With Camel CDI Components are Configured in Java
Configured on the Camel Context
All Components can be Configured
Configred in your Bootstrap Class
Must be Configured Prior to Start of Camel Context
Lookup Existing Components with getComponent()
Add New Components with addComponent()
Most Components Configuration Not Discussed in Docs, Lookup in JavaDocs.@Resource(mappedName = "java:/ConnectionFactory") private ConnectionFactory jmsConnFactory; ... JmsConfiguration jmsConfig = new JmsConfiguration(jmsConnFactory); jmsConfig.setConcurrentConsumers(10); jmsConfig.setMaxConcurrentConsumers(10); Component component = this.camelCtx.getComponent("jms"); if (component != null) { JmsComponent jmsComponent = (JmsComponent) component; jmsComponent.setConfiguration(jmsConfig); } else { this.camelCtx.addComponent("jms", new JmsComponent(jmsConfig)); }
Camel has no JBoss A-MQ Component
Several ways to Connect to A-MQ from Camel
JMS component
ActiveMQ Component
Using an MDB Generally offers Higher Performance
Use a Producer Template to send to a Direct Camel Route
MDB Uses Containers Pooled Resource Adapter
Camel JMS Component Built on Spring JMS
Not Supported by Camel or any of its JMS based Components
Two Solutions
Replaces Message Data with a Unique ID for Future Retrieval
Data stored into Persistent Data Store
Data Retrieved later using Unique ID
from("direct:start").to("bean:checkLuggage").to("jms:queue:SomeQueue"); from("jms:queue:SomeQueue").to("bean:dataEnricher").log("Do Stuff");
public static final class CheckLuggageBean { public void checkLuggage(Exchange exchange, @Body String body, @XPath("/order/@custId") String custId) { dataStore.put(custId, body); exchange.getIn().setHeader("claimCheck", custId); exchange.getIn().setBody(null); } } public static final class DataEnricherBean { public void addDataBackIn(Exchange exchange, @Header("claimCheck") String claimCheck) { exchange.getIn().setBody(dataStore.get(claimCheck)); dataStore.remove(claimCheck); exchange.getIn().removeHeader("claimCheck"); } }
Web Services are common place in Integration
Camel has Several Web Service Related Components
Spring Web Services require Spring
Rest and Spark-Rest Components not Available
Only Leaves CXF and CXFRS
Configured via Camel Context
Endpoints created Programmatically
Requires Http Jetty Transport to Publish Service
CxfEndpoint orderEndpoint = new CxfEndpoint(); orderEndpoint.setAddress("http://localhost:9595/order"); orderEndpoint.setServiceClass("net.bryansaunders.camel.OrderEndpoint"); orderEndpoint.setWsdlURL("wsdl/order.wsdl"); orderEndpoint.setCamelContext(this.camelCtx); camelCtx.addEndpoint("cxf:bean:orderEndpoint", orderEndpoint);Stands up Jetty on a seperate port for each Endpoint, Not ideal for most Production Environments. Does not have access to the Containers Transport, Can not use CDI with Servlet because of the Bean Manager.
Use the Web Services Subsystem
Deploy Web Services using JAX-WS
Use Producer Template to Call Camel from Implementation
Use a Direct Route
Route is more Flexible, Can be Called without SOAP
Easier to Configure more Advanced Web Services
Same Problems exist with REST and CXF-RS Component. Use Rest DSL when using 2.14, In the Meantime take the same approach with JAX-RS.https://github.com/bsaunder/camel/tree/master/eap_6/cdi-jaxws-publish
Camel and SwitchYard both include Test Frameworks
Mocks created with Camel Test cannot replace SwitchYard Endpoints
SwitchYard expects Route to Start with SwitchYard Endpoint
public class SomeTestClass extends CamelTestSupport { private static final String MESSAGE = "..."; @Test public void someTest() throws Exception { MockEndpoint endpoint = getMockEndpoint("mock:switchyard:WebOutService"); endpoint.expectedBodiesReceived(MESSAGE); template.sendBody("direct:WebInService", MESSAGE); endpoint.assertIsSatisfied(); } @Override protected RouteBuilder createRouteBuilder() throws Exception { return new WebInServiceRoute() { @Override public void configure() throws Exception { interceptSendToEndpoint("switchyard://WebOutService") .skipSendToOriginalEndpoint().to( "mock:switchyard:WebOutService"); super.configure(); } }; } }
from("switchyard://WebInService").to("direct:WebInService"); from("direct:WebInService").log("Doing something very clever").to("switchyard://WebInCanonicalSystem");
In-Container Testing Framework for OSGi, Java EE, and CDI
Similar to Arquillian
Uses a Test Driver and a Test Container
JUnit and TestNG Drivers
Drivers are Annotation Based
Supports All Major OSGi Frameworks
Multiple Strategies for Restarting and Reusing the Running OSGi Framework for each Test
Two Types of Containers
Eases Integration Testing with Pax Exam and Karaf
Provides an Actual Karaf Container for Testing
Supports any Karaf Based Distribution (Fuse, Service Mix, Geronimo)
Maintained as Official Pax Exam modules
Adds Karaf Specific Support
Controls the Host Container
Determines the Set of Bundles and Features provisioned to the Container
Builds and Configures the Container Environment
Multiple Methods of Specifying Configuration Options
Artifact added to the Container for Testing
Created on the Fly by Pax TinyBundles
Contains the Current Test Classes and All Classes/Resources under the Same Root
Can be Configured inside the Test Class if needed
Anything that needs to Change during a Test should be Externalized
Integrations with External Resources should be Mock able
Generate using the Maven Plugin
<plugin> <groupId>org.apache.servicemix.tooling</groupId> <artifactId>depends-maven-plugin</artifactId> <version>1.2</version> <executions> <execution> <id>generate-depends-file</id> <goals> <goal>generate-depends-file</goal> </goals> </execution> </executions> </plugin>
Should use the versionAsInProject() method in Container Configuration
Container Configuration is Done with the @Configuration Annotation
Method should Return Option[]
Used for
@Configuration public static Option[] configure() throws Exception { return new Option[] { karafDistributionConfiguration() .frameworkUrl( maven().groupId("org.apache.karaf").artifactId("apache-karaf").type("zip") .versionAsInProject()).useDeployFolder(false).karafVersion("3.0.0") .unpackDirectory(new File("target/paxexam/unpack/")), logLevel(LogLevel.WARN), features( maven().groupId("org.apache.camel.karaf").artifactId("apache-camel").type("xml") .classifier("features").versionAsInProject(), "camel-blueprint", "camel-jms", "camel-jpa", "camel-mvel", "camel-jdbc", "camel-cxf", "camel-test"), KarafDistributionOption.editConfigurationFilePut("etc/org.ops4j.pax.url.mvn.cfg", "org.ops4j.pax.url.mvn.proxySupport", "true"), keepRuntimeFolder(), KarafDistributionOption.replaceConfigurationFile("etc/com.walmart.mqm.gateway.routes.cfg", new File( "src/test/resources/com.walmart.mqm.gateway.routes.cfg")), mavenBundle().groupId("net.bryansaunders").artifactId("routeBundle").versionAsInProject() }; }
Modified Using the @ProbeBuilder Annotation
Uses the Following Method Signature
@ProbeBuilder public TestProbeBuilder probeConfiguration(TestProbeBuilder probe) { // Do Things }
Method should return the probe Parameter after Modification
Sets Probe Specific Configurations via Headers
Dynamically Imports all Packages by Default
Provisional Packages are by Default NOT Imported
Probe must be Modified to Import Them
Add the Following Line to your ProbeBuilder Method
probe.setHeader(Constants.DYNAMICIMPORT_PACKAGE, "*;status=provisional");Only imports required packages by default. Packages exported as Provisional are not imported.
There will potentially be Multiple Camel Contexts running in the Container
Need to get the Correct Context for Our Tests
Best Done in doPreSetup() Method that can be Overridden from CamelTestSupport
Camel Context should be Looked Up by it’s name in the BundleContext
@Override protected void doPreSetup() throws Exception { camelContext = PaxExamTestUtil.getOsgiService(CamelContext.class, "(camel.context.name=" + CAMEL_CONTEXT_NAME + ")", 10000, bundleContext); assertNotNull(camelContext); }
2 Steps
// Set Headers To Be Sent final Map<String, Object> headerMap = new HashMap<String, Object>(); headerMap.put("WM_MSG_ID", null); headerMap.put("WM_HO_WMQ_QUEUE", null); // Send the Message Body ProducerTemplate template = camelContext.createProducerTemplate(); template.start(); template.send("direct:gateway_in", new Processor() { public void process(Exchange exchange) { Message in = exchange.getIn(); in.setBody("Hello from Camel"); in.setHeaders(headerMap); } });
Get the Endpoint from the Camel Context and Cast to a MockEndpoint
MockEndpoint mockNoHomeOfficeDlq = (MockEndpoint) CamelContext.getEndpoint("mock:gateway_noHomeOfficeDlq");
Set Mock Endpoint’s Expectations
mockNoHomeOfficeDlq.expectedMessageCount(1); mockNoHomeOfficeDlq.expectedBodiesReceived("Hello from Camel");
Send Messages to Mock Endpoint
Assert Mock Endpoint is Satisfied
mockNoHomeOfficeDlq.assertIsSatisfied(2500);
Arquillian has OSGi support
Works like standard Arquillian
Supports Multiple Containers
Not as Feature Rich as Pax Exam
Improved Test Support
Persistent Message Store
Java 8 DSL
Split/Optimize Camel-CXF
Full EAP Subsystem
Improved Integration with JEE Standards
Updated JEE Related Camel Components
Full Arquillian Support
Available now in Wildfly 8.1
Official Support in future Fuse Release
Presentation is Available At
https://github.com/bsaunder/camel_from_the_field
All Demo Code is Available At