Message Endpoints
4.1 What can you expect of an endpoint?
As noted in the introduction of this chapter, endpoint is a broad term. We need to be precise in our understanding of the properties of a certain type of endpoint before we can have a meaningful discussion about it.
One advantage of Spring Integration is the consistent naming in the namespace support. This section explores what makes endpoints different enough to give them different names. Several characteristics are important:
Polling or event-driven
Inbound or outbound
Unidirectional or bidirectional
Internal or external
Considering all the possible ways we can combine the switches, we end up with 16 can- didates for unique names. Multiply this by the number of supported protocols in case of an external endpoint, and the numbers become dizzying.
Luckily, only certain combinations make sense for a given protocol, which greatly reduces the number of options. Also, Spring Integration can implicitly take care of the polling concerns. You still have plenty of options to consider—polling or event- driven, inbound or outbound (from the perspective of the Spring Integration applica- tion), unidirectional or bidirectional (one-way or request-reply respectively), internal or external (with respect to the application context)—so let’s look at the most impor- tant examples in table 4.1.
As you might guess from the table, there are some naming conventions for end- points. Wherever possible, the names were chosen to closely mirror a corresponding pattern name. In addition, the properties are signified by consistently applied tokens. For example, a gateway is always capable of bidirectional communication. This doesn’t change between HTTP and JMS or between inbound and outbound. In con- trast, a channel adapter is always unidirectional: it’s either the beginning (inbound)
66 CHAPTER 4 Message Endpoints
or the end (outbound) of a message flow. Now that you know the choices you can make before selecting an endpoint, it’s time to look into the implications of those choices. First we ask what may be the most important question when designing a mes- saging system.
4.1.1 To poll or not to poll?
Those of you who’ve read Hamlet know that Shakespeare got some of it right: the important decision is whether you should be a slave to your invokers or take matters into your own hands. We shouldn’t stretch the analogy to suicide, so let’s get practical. When a user of your application is entering a lot of information, you’d like to save that input somewhere as soon as possible. Modern browsers support client-side stor- age facilities, but in many applications that need to be compatible with lesser brows- ers, this isn’t an option. Even if client-side storage is an option technically, in many cases it isn’t an option functionally. Imagine a web application that allows users to col- laborate, as they would with an online word processor. This would require changes to flow from one user to another in two directions with the server as a referee in the mid- dle. Although it’s important to get the changes from one user to another quickly, it’s much more important to get the changes from a single user to their client quickly. In other words, if a sentence reaches another user a second after one user typed it, it would be fine. If the cursor were trailing a sentence behind the current user’s typing in the client, nobody would use the application.
In most cases when you’re typing, an application should respond to a key press within 0.1 seconds; otherwise the application would be unusable. Changes from some- body else may take up to a second before you would notice that they’re delayed. You’ve probably seen a few applications that allow you to collaborate within these boundaries; the increased use of this type of collaborative editor came with the rise of Ajax. With the boundaries of network latency, the only option to make an application like this work is to asynchronously send one user’s changes to another user’s client without making the first user wait for confirmation. As discussed in chapter 3, we use messages as the central concept to transfer data from one place to another.
Table 4.1 Differentiating characteristics of Endpoints
Endpoint Polling/ Event-driven Inbound/ Outbound Unidirectional/ Bidirectional Internal/ External
<inbound-channel-adapter> Polling Inbound Unidirectional Internal <outbound-channel-adapter> Either Outbound Unidirectional Internal
<gateway> Event-driven Inbound Bidirectional Internal
<service-activator> Either Outbound Bidirectional Internal
<http:outbound-gateway> Either Outbound Bidirectional External
67
What can you expect of an endpoint?
If messages are handed off asynchronously, the assumption is that they should eventually be received by a polling consumer. Because the poller is an active process, it requires a separate worker thread. For this purpose, Spring Integration integrates with any implementation of the core Spring TaskExecutor abstraction. Thread man- agement is then a responsibility of that implementation and can even be delegated to a WorkManager when running in an application server environment that requires threads to be managed by the container. We discuss the details of scheduling and con- currency considerations in chapter 15. The problem at hand is to make sure the sepa- rate worker thread is available and used properly to continue processing messages that have been handed off asynchronously. Even more important, we need to under- stand how to switch between synchronous and asynchronous invocation.
POLLING ENDPOINTS
A polling endpoint will actively request new data to process. This data may be mes- sages in a QueueChannel, files in a directory, messages in a JMS destination, and so on. The endpoint needs at least a single thread to perform this polling. You could hand- code an endpoint like this by creating an infinite loop and invoking receive periodi- cally. Although it may be easier to understand the inner workings of an active end- point because the behavior is self-contained, coding components like this yourself has some serious downsides. First of all, it requires you to write threading code, which is notoriously hard to do right. Second, it makes the component much harder to test in a unit test. Third, it becomes troublesome to integrate the component where a passive component is needed. In Spring Integration, you don’t have to write different code depending on whether you want your component to be active. Instead, whether the component should be active or passive is inferred from the configuration and han- dled by the framework. Configuring the components that will take the responsibility for the polling concerns is still up to you.
EVENT-DRIVEN ENDPOINTS
Asynchronous handoff usually isn’t required, and in those cases, an event-driven model should be used. This can be as simple as wrapping a plain old Java object (POJO). Exposing a web service is also a good example. The essential thing about pas- sive, or event-driven, components is that they don’t take responsibility for thread man- agement. They’re still responsible for proper thread safety, but this could be as simple as not maintaining any mutable state.
4.1.2 Inbound endpoints
Just as important as getting information out of the messaging system is getting infor- mation into it. This is done through inbound endpoints, typically receiving informa- tion in a format that’s not native to Spring Integration. Examples are a web service invocation, an inbound email, or a file written to a directory. A Java method invoca- tion is also a plausible inbound integration point. The generic algorithm for an inbound endpoint is shown in figure 4.1.
68 CHAPTER 4 Message Endpoints
POLLING INBOUND ENDPOINTS
Whether an inbound endpoint needs to poll or can be event-driven depends on the architecture external to the message system. Some technical constraints, such as the lack of filesystem events in Java, affect this decision. But in most cases it depends on the system with which you’re integrating. If the external integration point is passive, the inbound endpoint becomes responsible for actively polling for updates.
In Spring Integration, several polling endpoints are provided: inbound channel adapters for files, JMS, email, and a generic method-invoking variant. The latter can be used to create custom channel adapters, such as for querying Twitter or a Really Simple Syndication (RSS) feed. Twitter and RSS adapters are both available as of Spring Integration 2.0, but before they were added, users could simply configure the generic method-invoking adapter to call their own implementations.
EVENT-DRIVEN INBOUND ENDPOINTS
Often, an external system actively invokes a service on the messaging system. When that happens, the responsibility of thread management can be left out of the messag- ing solution and left to the invoker (in the case of a local Java method call) or to the container (in the case of a web service invocation). But as soon as QueueChannels are to be used internally, thread management becomes a concern of the messaging system again.
Examples of event-driven endpoint types supported in Spring Integration include web services (through Spring WS), Remote Method Invocation (RMI), and JMS (where Spring’s listener container takes care of the polling). Again, you can generically create a custom event-driven endpoint using the @Gateway annotation.
4.1.3 Outbound endpoints
At some point in a message flow, you’ll likely need to invoke a service that’s external to the messaging system. This may be a local method call, a web service invocation, or a message sent on another messaging system such as JMS or Extensible Messaging and Presence Protocol (XMPP). An endpoint is responsible for making this call. The gen- eral responsibilities of an outbound endpoint are depicted in figure 4.2.
Channel Message
Inbound endpoint
Figure 4.1 Behavior of an inbound endpoint. Input is taken from an external source and then converted into a message, which is sent to a channel. This image doesn’t show the optional reply message.
69
What can you expect of an endpoint?
The algorithm shown in this figure usually must implement details such as transac- tions and security, which we cover later. The details of conversion and the invocation of external services are also assumed to be implementation details of the specific end- point. But the reception of the message and whether a result is generated are con- cepts that belong to the base API.
POLLING OUTBOUND ENDPOINTS
If an outbound endpoint is connected to a PollableChannel, it must invoke the receive method on its input channel to receive messages. That requires scheduling an active process to do the polling periodically. In other words, step 1 in the algo- rithm is triggered by the endpoint. Spring Integration automatically wraps any pas- sive components in a polling endpoint if they’re configured to be on the receiving end of a PollableChannel.
EVENT-DRIVEN OUTBOUND ENDPOINTS
When an outbound endpoint is connected to a SubscribableChannel, it can be pas- sive. The channel ensures that a thread exists to invoke the endpoint when a message arrives. In many cases, the thread that invoked the send method on the channel is used, but it could also be handled by a thread pool managed at the channel level. The advantage of using a thread pool is that the sender doesn’t have to wait, but the disad- vantage is that a transactional boundary would be broken because the transaction con- text is associated with the thread. This is discussed in detail in section 4.2.
4.1.4 Unidirectional and bidirectional endpoints
The previous examples all assume unidirectional communication. Endpoints using this style of communication are called channel adapters. Unidirectional communica- tion is often enough to establish a working solution, but bidirectional requirements are common. For these scenarios, you can use gateways. The EIP definition of gateway isn’t as clear as we would’ve liked, so we did a bit of interpretation. Mainly, the lack of clear distinction between synchronous invocation and asynchronous handoff makes the gateway concept too broad and widely overlapping with other concepts, such as channel adapter. In Spring Integration, a gateway is synonymous with synchronous two-way communication. If you need an asynchronous gateway, you should compose it
Channel
Message
Outbound endpoint
Figure 4.2 Behavior of an outbound endpoint. First a message is received from a channel; then the message is converted into something the external component understands. Finally the external API is invoked. The optional result is ignored in this image.
70 CHAPTER 4 Message Endpoints
from other base components (for example, an inbound and outbound channel adapter).
THE RETURN ADDRESS
When a message reaches an endpoint, through polling or otherwise, two things can happen:
1 The message is consumed and no response is generated. 2 The message is processed and a response is generated.
The first case is where you’d use a channel adapter, as shown in the previous sections. The second case becomes more complicated, but luckily Spring Integration has gate- ways to help you support it. The complexity lies in that you must do something with the response.
Usually you’ll want to either send it further along its way to the next endpoint or send a confirmation back to the original sender when all processing for this message is done. For the first option, you set an output channel, and for the second, you omit the output channel and Spring Integration uses the REPLY_CHANNEL header to find the channel on which the original sender wants the confirmation to go.
At this point you should have a good understanding of the different high-level cat- egories of endpoints. We explored the distinctions between polling and event-driven consumers, and we discussed both unidirectional and bidirectional behavior. In the next section, we add one of the most important pieces to the puzzle: transactions. Unfortunately, although their importance is undeniable, transactions can be confus- ing for many developers. We’ll do our best to remedy that situation, at least in the con- text of Spring Integration applications. If you’re an expert on transaction management, you might like to skip parts of the next section and just skim for Spring Integration particulars.