Limitations of Persistent Channel Implementation
While the persistent channel implementation combined with Spring Integration goes a long way toward a complete messaging solution, it has a few limitations. First, it does not support multiple consumers per queue because messages are selected from the database and not deleted until the transaction commits. This means that a single message would be processed by all the consumers at the same time, which you probably don't want. The next section discusses a solution for this.
The implementation's shutdown process presents another limitation. Spring Integration supports cleanly shutting down when the Spring Framework application context is shut down (normally by a servlet shutdown). However, the implementation's shutdown process doesn't properly flush the channels or allow active tasks to complete, which can result in a ThreadDeath exception from the JVM. Discussions in the SpringSource forums have documented some workarounds, and the issue is being tracked in the bug tracker.
Another limitation is related to transaction management and message redelivery. One of the original application requirements was to support message redelivery, which is achieved with a transaction that the inbound channel adapter poller starts. Without careful error handling, an unexpected runtime exception causes the transaction to roll back and the message will be redelivered indefinitely. This behavior is undesirable in most cases because the runtime exception may continually be raised, consuming valuable resources. You can prevent indefinite redelivery by using the error handler previously discussed or by implementing an extension to perform retry counting (more about this shortly).
The final limitation is related to round-trip messages. SI supports round-trip messages that can simulate a method call over the message bus with parameters and a return value. This feature is contrary to the initial requirement linking the business transaction with the delivery of the outbound message. Receiving a response message would be impossible. If the business service is blocked waiting for a response while the transaction is still active, the request message would never be sent. It may be possible to implement this feature if the transaction requirements are relaxed.
One extension you may want to implement is the ability to have multiple consumers on a single persistent channel adapter. One way to accomplish this is by modifying the message DAO to use Oracle row locking. Each request for a new message performs a "select for update" condition, which locks the row returned. At the same time, a "skip locked rows" condition is used to skip any rows already locked. This allows each consumer to locate, lock, and process the first row in the message table that is not currently locked by another consumer. While this relies on an Oracle-specific feature, a similar approach could be implemented using a shared lock table or in-memory list of locked message identifiers.
Using the multiple-consumer locking approach, you can also implement fault tolerance by having parallel deployments of the application share a locking mechanism. This allows each deployment to process messages from a single message table while not processing the same message at the same time in each instance. The Oracle row-locking approach makes this simple. If a deployment fails, the messages will continue to be processed in the other instance immediately.
One final extension that would be useful is to implement message redelivery counting. In the current implementation, SI supports a per-message error channel that you can use to your advantage. While logging the error and deleting the message (as the current error handler does) works fine, in some cases you may want to re-queue the message for a future retry attempt. You could do this by adding a counter to the persisted message and updating the retry count in the error handler. Once the count exceeds a configured maximum, simply drop the message. You can use the same approach for sending an email to an administrator, sending an IM, posting a Twitter message, or simply leaving the message in the queue for redelivery.
Make SI What Your Application Needs It to Be
While SI is young and has a few shortcomings, it does have a bright future. The extensibility of the framework opens it up for a number of interesting integration scenarios, including communications with proprietary systems. As demonstrated in this article, with a persistent message adapter implementation in place, SI can provide a reliable alternative to JMS for an application that needs simple, in-process message passing and asynchronous processing. By loosely coupling your application's components in a message-passing architecture, you create a more flexible and future-proof system.
SI also offers the benefit of simple POJO bean configuration, testability, and a simple XML configuration format, just like the core Spring Framework.