MessageSender is the typical interface that most of the developers are
likely to implement. It encapsulates the specific protocol communication. It does just one
thing and it should do it properly. Implementing a simple sender can be really fast but there
are some low hanging fruits that can make their development and usage easier. Let's start with
The ultimate goal of the MessageSender is to send a message (or any other unit of communication work), and possibly receive a response. Any implementation should not do anything but the communication. It should be a pure wrapper of the message exchange layer.
close() methods should be used to
establish and close a permanent connection. It is a design consideration of any implementation
whether to handle the connection establishment separately (and not measure it), or to open and
close a connection with every single request (and make it part of the performance
measurement). Most provided implementations (if not all) handle the connection separately as
we are really interested only in measuring the message exchange.
postSend() methods are still not
part of the performance measurement and can prepare the message for actual sending or handle
send() methods must handle just the message exchange. No logging or
complex error handling code should be placed here. Therefore we allow any generic exception to
The messages must always be sent somewhere. This is specified through the
In general, any of the configuration properties (including target) of the
MessageSender can contain templates that get replaced. If you want your
sender to support templates in any of the properties, always store them as a
StringTemplate as in the
method. Do not forget to render the resulting string each time it is used (if there are no
other intentions) typically by using message attributes that are passed to many methods in the
TODO: How to develop a new sender, how to use inheritance from existing senders, how are senders used in a thread pool, are they thread safe?
To allow fluent API usage with senders, we strongly encourage you to make sure all your
senders' setters return
It might be more convenient to extend
AbstractSender. This class does
some work for you already. First, it can cache a connection to the
so that it is not established separately for each message (see configurtion property
keepConnection). Second, it stores
target as a
StringTemplate. It also combines these two features together so that the
target property can contain templates. However, replacing templates with
message attributes only works when
false because in the case of the cached connection, we do not reuse the
When building your own
MessageSender on top of the
AbstractSender you just take care of the following methods.
doInit() method, you should establish the connection to the
target system. It is advised to use
AbstractSender.safeGetTarget(messageAttributes) call to obtain the
target address with properly replaced templates.
The counterpart method is
doClose(). You should close the previously
You can still use
postSend() methods to
do any preparations and cleanup like creating the protocol specific version of the message. In
this case, do not forget to call the methods in the ancestor as well
Finally, the core part is the
doSend() method where you just send the
message, optionally returning a response if there was any expected.
Good examples of the described features are
JdbcSender for instance, however these do not allow any other properties
to carry templates.
HttpSender on the other hand can use templates in the HTTP method
name. But the connection is handled in a different way here as
HttpUrlConnection is not reusable (Java handles its own cache
If in doubt, you can use the
DummySender in the debug mode and see what
happens and what methods are being called depending on the configuration.