JBossESB comes with many routing and mediation features out-of-the-box. However, there is always a thirst for more. Given the beauty of open source, we have the privilege to quickly expand its capabilities by leveraging other open source technologies. One which has been growing in popularity is Apache Camel. Camel does not claim to be a full-featured ESB, but rather focuses on routing and mediation concerns. It has minimal dependencies and has already been integrated into other projects, including ServiceMix. It only makes sense that we explore the possibilities of embedding it within JBossESB, and quickly gain more functionality.
From now on, it is assumed that the reader is familiar with the base concepts of Apache Camel. If not, please refer to the online manual.
A key concept within Camel is the CamelContext, which - among other things, provides a lifecycle container around endpoints and routes that have been defined and added via various mechanisms (Java DSL, Scala DSL and Spring XML). A similar concept also exists within JBossESB, where within the scope of an ESB deployment, one can define various Listeners - themselves being created, initialized, started, stopped and destroyed according to their lifecycle. The base class which provides this functionality is AbstractManagedLifecycle. There are two types of ESB Listeners: one type is aware of the internal ESB Message construct, and the other is not. The second, called a “Gateway”, has the job of encapsulating external events into ESB Messages so other code can interact with the event via a normalized API. This document describes a new kind of gateway: the CamelGateway.
The most apparent difference between the CamelGateway and the other Gateways provided within JBossESB is that the CamelGateway is not specific to any one type of transport. This is because we want to leverage all the different transports that Camel supports, so the role of the CamelGateway is really all about integration. To see all the different transports Camel can handle, please visit the Camel Component list here:
http://camel.apache.org/components.html
Note: Different Camel Components have different library dependencies. JBossESB only contains the camel-core jar. You will have to add any other dependencies (including other camel-* jars or third-party jars) you require. Instructions for doing that can be found CamelGateway: Using non-core Components.
One important goal with the integration is that the configuration should feel natural to two different audiences: people with a background knowledge of JBossESB, and people with a background knowledge of Camel. Let’s look at an example jboss-esb.xml configuration file, and describe the sections:
Note: The "..." in the examples below would be your filesystem path.
<jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd" parameterReloadSecs="5"> <providers> <camel-provider name="provider1"> <camel-bus busid="bus1"> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true"/> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input2?delete=true"/> </camel-bus> </camel-provider> </providers> <services> <service category="camel_helloworld" name="service1" description="Hello World" invmScope="GLOBAL"> <listeners> <camel-gateway name="gateway1" busidref="bus1"/> </listeners> <actions> <action name="action1" class="org.jboss.esb.actions.SystemPrintln"/> </actions> </service> </services> </jbossesb>
- The first thing to note is the jbossesb-1.3.0.xsd version of the schema. This allows us to introduce the new camel configuration elements.
- Next note the <camel-provider> and <camel-bus> elements. Those familiar with JBossESB configuration should be very comfortable with this, as it maps well to other existing elements, like <jms-provider> and <jms-bus>.
- What is contained within the <camel-bus> element is the most interesting part, where you can have an unbounded number of <from uri=""/> elements. Those familiar with Camel XML configuration should be very comfortable with that element, as it does exactly what it does in native Camel XML configuration.
- Finally, the <camel-gateway> element in the <listeners> section of the <service> references the bus via the busidref attribute. For every <from> element defined, a new Camel <route> is dynamically created, with an implicit <to> element added at the end of each route, which invokes that Service using the ServiceInvoker. How this is done will be described later...
When you deploy the above example, this is the output you see in your ESB server console (if INFO and DEBUG log4j levels are enabled):
09:36:52,827 INFO [JBoss4ESBDeployer] create esb service, Quickstart_camel_helloworld.esb 09:36:53,017 DEBUG [CamelGateway] adding routes [ <routes xmlns="http://camel.apache.org/schema/spring"> <route> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true"/> <to uri="jbossesb://service?category=camel_helloworld&name=service1&async=false&timeout=30000"/> </route> <route> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input2?delete=true"/> <to uri="jbossesb://service?category=camel_helloworld&name=service1&async=false&timeout=30000"/> </route> </routes> ] 09:36:53,347 INFO [DefaultCamelContext] Apache Camel 2.4.0 (CamelContext: camel-1) is starting 09:36:53,347 INFO [DefaultCamelContext] JMX is disabled. Using DefaultManagementStrategy. 09:36:53,999 INFO [AnnotationTypeConverterLoader] Found 3 packages with 13 @Converter classes to load 09:36:54,021 INFO [DefaultTypeConverter] Loaded 143 type converters in 0.635 seconds 09:36:54,122 INFO [DefaultCamelContext] Route: route1 started and consuming from: Endpoint[file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true] 09:36:54,122 INFO [DefaultCamelContext] Route: route2 started and consuming from: Endpoint[file://.../samples/quickstarts/camel_helloworld/build/input2?delete=true] 09:36:54,122 INFO [DefaultCamelContext] Started 2 routes 09:36:54,122 INFO [DefaultCamelContext] Apache Camel 2.4.0 (CamelContext: camel-1) started in 0.775 seconds
Under the hood, we use the camel-core to dynamically create the routes with the definition you see above.
Note: Even though you see "spring" in the namespace above, the CamelGateway has NO dependency on Spring. The namespace is necessary since under-the-hood, JAXB is used to parse the above routes and map them to camel-core's base model definitions.
If you drop a text file in each of those directories, this is the output you see in your ESB server console:
09:37:35,194 INFO [STDOUT] Message structure: 09:37:35,194 INFO [STDOUT] [Hello user! (2)]. 09:37:35,196 INFO [STDOUT] Message structure: 09:37:35,196 INFO [STDOUT] [Hello user! (1)].
When you undeploy the above example, this is the output you see in your ESB server console:
09:38:09,249 INFO [DefaultCamelContext] Apache Camel 2.4.0 (CamelContext:camel-1) is shutting down 09:38:09,249 INFO [DefaultShutdownStrategy] Starting to graceful shutdown 2 routes (timeout 300 seconds) 09:38:09,261 INFO [DefaultShutdownStrategy] Route: route2 suspended and shutdown deferred, was consuming from: Endpoint[file://.../samples/quickstarts/camel_helloworld/build/input2?delete=true] 09:38:09,261 INFO [DefaultShutdownStrategy] Route: route1 suspended and shutdown deferred, was consuming from: Endpoint[file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true] 09:38:09,262 INFO [DefaultShutdownStrategy] Route: route2 shutdown complete. 09:38:09,262 INFO [DefaultShutdownStrategy] Route: route1 shutdown complete. 09:38:09,262 INFO [DefaultShutdownStrategy] Graceful shutdown of 2 routes completed in 0 seconds 09:38:09,280 INFO [DefaultInflightRepository] Shutting down with no inflight exchanges. 09:38:09,281 INFO [DefaultCamelContext] Uptime: 1 minute 09:38:09,281 INFO [DefaultCamelContext] Apache Camel 2.4.0 (CamelContext: camel-1) is shutdown in 0.032 seconds 09:38:09,321 WARN [ServiceMessageCounterLifecycleResource] Calling cleanup on existing service message counters for identity ID-7
You can also configure <from>s at the <camel-gateway> level without needing a <camel-bus> section, like so:
<jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd" parameterReloadSecs="5"> <services> <service category="camel_helloworld" name="service1" description="Hello World" invmScope="GLOBAL"> <listeners> <camel-gateway name="gateway1"> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true"/> <from uri="file://.../samples/quickstarts/camel_helloworld/build/input2?delete=true"/> </camel-gateway> </listeners> <actions> <action name="action1" class="org.jboss.esb.actions.SystemPrintln"/> </actions> </service> </services> </jbossesb>
If you configure <from>s at both the <camel-bus> and <camel-gateway> levels, then the <from>s defined at the <camel-gateway> level are added to the <from>s defined at the <camel-bus> level.
Note: A separate instance of CamelContext is created for, and lifecycle-managed by, each instance of the <camel-gateway>, including all routes defined in both sections. When a new ESB deployment is deployed, the CamelGateway creates and starts the CamelContext right in line with when it is initialized and started, and similarly so for stopping and destroying.
Finally, there is a short-hand mechanism, like this:
<jbossesb xmlns="http://anonsvn.labs.jboss.com/labs/jbossesb/trunk/product/etc/schemas/xml/jbossesb-1.3.0.xsd" parameterReloadSecs="5"> <services> <service category="camel_helloworld" name="service1" description="Hello World" invmScope="GLOBAL"> <listeners> <camel-gateway name="gateway1" from-uri="file://.../samples/quickstarts/camel_helloworld/build/input1?delete=true"/> </listeners> <actions> <action name="action1" class="org.jboss.esb.actions.SystemPrintln"/> </actions> </service> </services> </jbossesb>
In this example, the from-uri attribute is the same thing as having one nested <from uri=””/> element. This short-hand can also exist at the <camel-bus> level. If you configure from-uri's at both the <camel-bus> and <camel-gateway> levels, then the from-uri defined at the <camel-gateway> level is added to the from-uri defined at the <camel-bus> level. But remember, every <from uri=""> or from-uri defined will always
have an implicit <to uri=””/> added after it in a route, which executes the <service> being configured. We will explain how this works now.
Camel endpoints are URI-based, and Camel provides a mechanism for creating your own endpoints. This is done by implementing custom Camel Components (really, Components should be called EndpointFactory-s, but I digress...). So, a JBossESBComponent has been developed. In fact, the CamelGateway creates a JBossESBComponent and adds it to the CamelContext it manages, under the jbossesb:// URI prefix. This is the “hook” that allows us to interact with JBossESB from Camel. In the examples we’ve been looking at above, the implicit <to> URI looks like this:
<to uri=”jbossesb://service?category=camel_helloworld&name=service1”/>
What does the jbossesb:// endpoint do? Here is a breakdown of the syntax:
jbossesb://<JBossESB command>?<param name>=<param value>&<param name>=<param value>
Note: When using ampersands (&) in your URIs, you will have to use the XML entity version: & - otherwise parsing of the jboss-esb.xml will fail.
Currently the only JBossESB command implemented is “service”. Under the hood, this command is associated with a custom Camel Processor implementation, specifically the ServiceProcessor. The ServiceProcessor, as one would assume, uses the JBossESB ServiceInvoker to natively invoke the JBossESB Service defined by the category and name URI parameters. The optional async parameter (default is "false") specifies whether the deliverSync or deliverAsync method of the ServiceInvoker should be used, and the optional timeout parameter (default is "30000") specifies how many milliseconds the ServiceInvoker should wait before failing when invoking deliverySync. To set the async or timeout parameters, include them as attributes at the <camel-gateway> or <camel-bus> levels. The <camel-gateway> values for those attributes will always take precedence.
Note: The default value for async is false. This requires you to either specify a MessageExchangePattern (mep) of RequestResponse (<actions mep="RequestResponse">), or just let it default to that (<actions>). If you specify a mep of OneWay (<actions mep="OneWay">), then async cannot be false, otherwise the consumer thread will hang and timeout.
To run the camel_helloworld quickstart, please refer to the .../samples/quickstarts/camel_helloworld/readme.txt file, although if you’re already acquainted with the JBossESB quickstarts, you will be familiar with the “deploy” and “runtest” ant targets. The “runtest” target simply drops test files into a couple temp directories watched by Camel. Camel then picks them up, and executes the associated JBossESB Service at the end of each route, which in the example will simply print out the file contents to the console.
Well, that’s about it for now. Please check back again for further developments, or better yet, provide feedback in the JBossESB Developer Forum (for architectural suggestions) or User Forum (for assistance)!
Comments