3.4. Message Senders

A 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 their contract.

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.

The init() and 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.

The preSend() and postSend() methods are still not part of the performance measurement and can prepare the message for actual sending or handle any cleanup.

The 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 be thrown.

The messages must always be sent somewhere. This is specified through the target property.

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 AbstractSender.setTarget() 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 MessageSender.

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 returnthis.

It might be more convenient to extend AbstractSender. This class does some work for you already. First, it can cache a connection to the target 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 keepConnection is false because in the case of the cached connection, we do not reuse the target property.

When building your own MessageSender on top of the AbstractSender you just take care of the following methods.

In the 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 created connection.

You can still use preSend() and 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 (super.preSend() and super.postSend()).

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 JmsSender and JdbcSender for instance, however these do not allow any other properties to carry templates.

The 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 internally).

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.