Demo One from my UK Spring User Group talk: The simplicity of Spring Integration

February 22nd, 2009

I was asked to make the code available for the demos that were part of my talk for the UK Spring User Group at Skills Matter. The video of which is here.  Today I am posting the code for the first demo which demonstrates just how easy it is to get up and running with Spring Integration.  The demo shows a simple application which takes requests from JMS, routes according to business logic and calls through to a Java service to process the message. The projects used in this demo can be downloaded here febSugDemo1.zip.  The zip contains three Eclipse projects along with Maven poms so Eclipse is not a must.

  1. Application Generator: This project is there for testing and when run will create random LoanApplication instances and send them for processing over JMS. This can be started using LoanApplicationGenerator in src/main/java
  2. Demo 1 Quotes:  Contains the Spring Integration configuration and simple service implementations used to process applications.  Can be started from LoanQuoteStarter under src/test/java
  3. Loan Quote Domain: The LoanApplication and Quote types used by both previous proejcts

The example is that of a simple message flow which first receives a LoanApplication from a JMS queue.  The loan application is then routed based on simple business rules to either a quote generating service or to a rejection queue for processing later. I will start off with a detailed walk through of the example application so if you just want to get the code up and running you can skip through to the getting it running section towards the bottom.

Loan application message flow

loanquoteflow

Walk through

The configuration for the demo1 project can be found under src/main/resources.  The loanquote-context.xml first defines an inbound JMS channel adapter.  The fact that we are using a JMS channel adapter rather than a JMS gateway indicates that we are doing one way communication.  If we were participating in a request response scenario we would want to use one of the provided gateways instead.  The inbound adapter means we are receiving from JMS and placing an instance of  org.springframework.integration.core.Message on the configured channel.  Spring Integration also provides an outbound gateway to take a Spring Integration message from a Spring Integration channel and send that to a JMS destination.  JMS gateways also come in both inbound and outbound versions.

JMS Inbound channel adapter

    <jms:inbound-channel-adapter
        connection-factory="connectionFactory"
        destination-name="newApplications"
        channel="requests">
</jms:inbound-channel-adapter>

<channel id="requests" />

The JMS inbound adapter is configured very simply in this case with a reference to a JMS connection factory, the name of a JMS destination and the name of the Spring Integration channel on which received messages will be placed. Our connection factory is a Spring bean defined elsewhere.  In this case we are using the ActiveMQ configuration below.

ActiveMQ connection factory

<beans:bean id="connectionFactory"
    class="org.apache.activemq.spring.ActiveMQConnectionFactory">
    <beans:property name="brokerURL"
        value="tcp://localhost:61616" />
</beans:bean>

The next step in our flow is to route according to the credit rating which is already available in the LoanApplication. To do that we use a simple router implementation as below. Note that rather than accepting an instance of org.springframework.integration.core.Message the router accepts an instance of LoanApplication which is the payload of the message. This makes the router easier to unit test by decoupling it from messaging. The router in this case is simply returning a String channel name which will then be resolved to a channel instance by the framework. The default strategy, which will be used here, is to look for a Spring bean in the application context with an id matching the String returned by the router. This strategy is as with most things in Spring Integration pluggable if required.

Credit Checking Router

public class CreditCheckingRouter {
   
    public String route(LoanApplication loanApplication){  
        if(loanApplication.getApplicant()
                .creditBetterThan(CreditRating.AVERAGE)){
            return "quote";
        }
        return "reject";
    }
   
}

In order to connect the router into our integration application we simply declare the POJO router as a Spring bean and reference this from our router element. Our router element requires a reference to the channel from which messages will be received, a reference to the POJO router bean and the name of the method to invoke when a message is received. We also need to declare the two channels which our router will route to. Here the quote channel will be of the default DirectChannel type. This means that a call to send on this channel will dispatch the message to any consumer(s). In the case of rejected applications we are going to queue those up and process them later so we declare a queue element as a child of channel. This means that the reject channel will be an instance of QueueChannel and calls to send will not result in any message dispatch. The consumer responsible for processing rejections will need to come looking for our rejected loan applications by polling the queue.

Router configuration

<beans:bean id="creditCheckRouter"
    class="org.jpartner.CreditCheckingRouter" />

<router input-channel="requests" ref="creditCheckRouter"
    method="route" />

<channel id="quote" />
<channel id="reject">
    <queue />
</channel>

Our quote service is very simple in this case. If you have the good or great credit rating to get past our router then we are simply going to quote our standard fixed rate of 20% APR, sign of the times I’m afraid. Our service activator is again very testable and decoupled from the messaging framework, the LoanApplication is the payload of the message received from the input channel and the returned Quote becomes the payload of the message sent to our output channel.

Quote Service

public class QuoteService {
   
    public Quote createQuote(LoanApplication loanApplication){
        return new Quote(20,loanApplication);
    }

}

Hooking our quote service in is then just a case of declaring a service activator with a ref to the bean, the input and output channels. For the purposes of the demo we are then simply printing our quotes to standard out using a provided stream channel adapter.

Loan Quote Service Activator

<beans:bean id="quoteService" class="org.jpartner.QuoteService" />
<service-activator input-channel="quote"
    output-channel="createdQuotes" ref="quoteService"
    method="createQuote" />

<channel id="createdQuotes" />
<stream:stdout-channel-adapter id="stdout"
    channel="createdQuotes" append-newline="true"/>

IDE Setup

Since the projects are using Maven, if you don’t have a Maven plugin already installed in Eclipse I would recommend installing Q4E from here.
You can then import the projects from the zip using File-> Import -> General -> Import existing projects into workspace and navigate to the archive.

Running it

We sometimes get questions on the Spring Integration forum about starting up and deploying a Spring Integration application. The short answer is that all you need is a JRE, since Spring Integration is designed to be lightweight and it does not require an application server to run. In the case of the demo the application is started using a main method. In this case however since we are using JMS you will need a broker. I used ActiveMQ 5.2 which you can download here. Simply unzip it and run activemq-admin start from the bin directory. You should then be able to browse to http://localhost:8161/admin/queueGraph.jsp and see a number of example destinations. Start the LoanApplicationGenerator by running the main method. So Run As-> Java Application if you are using Eclipse. Refreshing the ActiveMQ console should now show you that we have a build up of loan applications on our newApplication JMS queue. To start our loan quote application simply run the LoanQuoteStarter class in the demo project, the code of which is shown below. This creates our Spring application context and since our beans are lifecycle aware the application will immediately start processing loan applications.

Startup class for loan processing application

public class LoanQuoteStarter {
   
    public static void main(String[] args){
        new ClassPathXmlApplicationContext("loanquote-context.xml",
            LoanQuoteStarter.class);
    }

}

You should now see console output as below.

Applicant name:Sue rating: GOOD amount:79053.0 years:8 quoted rate : 20.0
Applicant name:Greg rating: GOOD amount:47561.0 years:1 quoted rate : 20.0
Applicant name:Simon rating: GREAT amount:41472.0 years:7 quoted rate : 20.0
Applicant name:John rating: GOOD amount:19438.0 years:1 quoted rate : 20.0
Applicant name:Emma rating: GREAT amount:66612.0 years:3 quoted rate : 20.0
Applicant name:Bob rating: GOOD amount:80851.0 years:0 quoted rate : 20.0
Applicant name:Greg rating: GOOD amount:19175.0 years:9 quoted rate : 20.0
Applicant name:John rating: GOOD amount:18578.0 years:2 quoted rate : 20.0
Applicant name:Simon rating: GOOD amount:64692.0 years:4 quoted rate : 20.0

The next post will be based on demo number two from my talk which will show how we can break up an application such as the example loan quote application into OSGi bundles, and in doing so we will create a more modular, testable and flexible application.

Filed under: Code Sample, Spring Integration

8 Responses to “Demo One from my UK Spring User Group talk: The simplicity of Spring Integration”

  1. Ronald Says:

    Where is the destination name “newApplications” defined?
    Sorry, kinda new with spring integration and thinking it’s the best way for our project.

  2. Ronald Says:

    And one more thing, we just need to poll a queue and wanted to process it. The queue is on an oracle AQ and as far as I know, MDB’s cannot be used so we just need some sort of listener or poller. And I’ve been interested on your post because that’s exactly what we need. If you could shed some more light about the process, that would be of great help.

  3. Jonas Says:

    The newApplications destination name is created by ActiveMQ on the fly in this case so other than the string name it is not defined. A Spring JMS template is used to interact with JMS. This supports different destination management strategies docs for that here http://static.springframework.org/spring/docs/2.5.x/reference/jms.html#jms-destinations. Where more control is needed you would probably want to inject a JMSTemplate instance into the gateway using the jms-template attribute on the gateway.

  4. Ronald Says:

    so that means i could consume/poll message from the queue using just a main() method from a normal application and won’t be needing to deploy into an appserver?

  5. Jonas Says:

    Yes there is no requirement to run Spring Integration within a server.

  6. Ronald Says:

    thank you very much for your prompt reply. I would definitely take a deeper look into it later tonight. thanks

  7. Keith Says:

    Nice article.
    Do you happen to have a more advanced example that gets into transactions and error handling?

    It seems that in my version of this type of use case, messages will get ripped off the queue even if an exception is thrown.

  8. Jonas Says:

    Hi Keith
    No unfortunately I don’t have examples covering that to hand and not sure when/if I will get time to put something together at the moment. The reference docs here http://static.springframework.org/spring-integration/reference/htmlsingle/spring-integration-reference.html provide some coverage.

Leave a Reply