A Domain Specific Language (DSL) is a mini-language designed for a special purpose. A DSL does not have to be logically complete but needs enough expressive power to describe problems adequately in the chosen domain. Typically, a DSL does not require a dedicated parser, interpreter, or compiler. A DSL can piggyback on top of an existing object-oriented host language, provided DSL constructs map cleanly to constructs in the host language API.
Consider the following sequence of commands in a hypothetical DSL:
command01; command02; command03;
You can map these commands to Java method invocations, as follows:
command01().command02().command03()
You can even map blocks to Java method invocations. For example:
command01().startBlock().command02().command03().endBlock()
The DSL syntax is implicitly defined by the data types of the host language API. For example, the return type of a Java method determines which methods you can legally invoke next (equivalent to the next command in the DSL).
FUSE Mediation Router defines a router DSL for defining routing rules. You can
use this DSL to define rules in the body of a RouteBuilder.configure()
implementation. Figure 2.1 shows an overview of the basic syntax for
defining local routing rules.
A local rule always starts with a
from("
method, which specifies
the source of messages for the routing rule. You can then add an arbitrarily long chain of
processors to the rule (for example, EndpointURL
")filter()
). You typically finish off
the rule with a to("
method,
which specifies the target for the messages that pass through the rule. However, it is not
always necessary to end a rule with EndpointURL
")to()
. There are alternative ways of
specifying the message target in a rule.
![]() | Note |
---|---|
You can also define a global routing rule, by starting the rule with a special
processor type (such as |
A local rule always starts by defining a source endpoint, using
from("
, and typically (but
not always) ends by defining a target endpoint, using
EndpointURL
")to("
. The endpoint URLs,
EndpointURL
")EndpointURL
, can use any of the components configured at deploy
time. For example, you can use a file endpoint, file:MyMessageDirectory
,
a Apache CXF endpoint, cxf:MyServiceName
, or an Apache ActiveMQ endpoint,
activemq:queue:MyQName
. For a complete list of component types, see
http://camel.apache.org/components.html.
A processor is a method that can access and modify the stream of
messages passing through a rule. If a message is part of a remote procedure call
(InOut call), the processor can potentially act on the messages
flowing in both directions. It can act on request
messages, flowing from source to target; and it can act on reply messages, flowing from
target back to source (see Message exchanges). Processors can take
expression or predicate arguments, that
modify their behavior. For example, the rule shown in Figure 2.1 includes
a filter()
processor that takes an xpath()
predicate
as its argument.
Expressions (evaluating to strings or other data types) and predicates (evaluating to
true or false) occur frequently as arguments to the built-in processor types. You do not
have to be concerned about which type to pass to an expression argument, because arguments
are usually automatically converted to the required type. For example, you can usually just
pass a string into an expression argument. Predicate expressions are useful for defining
conditional behavior in a route. For example, the following filter rule propagates In messages, only if the foo
header is equal
to the value bar
:
from("seda:a").filter(header("foo").isEqualTo("bar")).to("seda:b");
Where the filter is qualified by the predicate,
header("foo").isEqualTo("bar")
. To construct more sophisticated
predicates and expressions, based on the message content, you can use one of the expression
and predicate languages (see Languages for Expressions and Predicates).
When a router rule is activated, it can process messages passing in either direction:
from source to target or from target back to source. For example, if a router rule is
mediating a remote procedure call (RPC), the rule processes requests, replies, and faults.
How do you manage message correlation in this case? One of the most effective and
straightforward ways is to use a message exchange object as the basis
for processing messages. FUSE Mediation Router uses message exchange objects (of
org.apache.camel.Exchange
type) in its API for processing router
rules.
The basic idea of the message exchange is that, instead of accessing requests, replies,
and faults separately, you encapsulate the correlated messages inside a single object (an
Exchange
object). Message correlation now becomes trivial from the
perspective of a processor, because correlated messages are encapsulated in a single
Exchange
object and processors gain access to messages through the
Exchange
object.
Using an Exchange
object makes it easy to generalize message
processing to different message exchange patterns. For example, an
asynchronous protocol might define a message exchange pattern that consists of a single
message that flows from the source to the target (an In message). An
RPC protocol, on the other hand, might define a message exchange pattern that consists of a
request message correlated with either a reply or a fault message. Currently, FUSE Mediation Router
supports the following message exchange patterns:
InOnly
RobustInOnly
InOut
InOptionalOut
OutOnly
RobustOutOnly
OutIn
OutOptionalIn
Where these message exchange patterns are represented by constants in the enumeration
type, org.apache.camel.ExchangePattern
.