Sunday, January 5, 2014

For the following scenario I am looking for your advices and tips on best practices:
In a distributed (mainly Java-based) system with:
  • many (different) client applications (web-app, command-line tools, REST API)
  • a central JMS message broker (currently in favor of using ActiveMQ)
  • multiple stand-alone processing nodes (running on multiple remote machines, computing expensive operations of different types as specified by the JMS message payload)
How would one best apply the JMS support provided by the Spring Integration framework to decouple the clients from the worker nodes? When reading through the reference documentation and some very first experiments it looks like the configuration of an JMS inbound adapter inherently require to use a subscriber, which in a decoupled scenario does not exist.
Small side note: communication should happen via JMS text messages (using a JSON data structure for future extensibility).
share|improve this question
add comment

3 Answers

This doesn't really answer your question, but make sure you look into Apache Camel for connecting your different components. I found it extremely useful for connecting a JMS queue up to an existing web service and plan to use it for other components also.
An example that monitors an ActiveMQ queue for messages, transforms them, and posts them to a web service:
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:camel="http://camel.apache.org/schema/spring"
   xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.3.0.xsd">

<bean id="callbackProcessor" class="com.package.CallbackProcessor"/>

<bean id="activemq" class="org.apache.camel.component.jms.JmsComponent">
    <property name="connectionFactory" ref="jmsFactory" />
</bean>

<camel:camelContext id="camel">
    
    <camel:endpoint id="callbackQueue" uri="activemq:queue:${jms.callback-queue-name}"/>
    <camel:route>
        <camel:from ref="callbackQueue"/>
        <camel:process ref="callbackProcessor"/>
        <camel:to uri="http://dummy"/>
    </camel:route>
</camel:camelContext>
</beans>
That's all that's necessary in our Spring application to fire up Camel and start processing messages.
share|improve this answer
 
Thanks, Camel is of course also an option, but wouldn't that require more configuration overhead or is this feasible? –  ngeek Jun 10 '10 at 12:28
 
I don't think it's too much configuration overhead. I've updated my answer with an example. –  scompt.com Jun 10 '10 at 13:54
 
True, this looks indeed very concise. Thanks for posting more details. –  ngeek Jun 10 '10 at 21:51
add comment
Here is the Spring Integration I was coming up with today, if you find things which could be improved please follow up.
On the client side the messages can be send out and received through a SimpleMessagingGateway:
xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"   
    xmlns:integration="http://www.springframework.org/schema/integration"
    xmlns:jms="http://www.springframework.org/schema/integration/jms"   
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/integration/jms
            http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
            http://www.springframework.org/schema/integration
            http://www.springframework.org/schema/integration/spring-integration.xsd">

    <import resource="integration-common.xml"/>

    
    <bean id="gateway" class="org.springframework.integration.gateway.SimpleMessagingGateway">
        <property name="requestChannel" ref="SenderChannel"/>
        <property name="replyChannel" ref="InboundChannel"/>
        <property name="replyTimeout" value="1000"/>
    </bean>

    
    <integration:channel id="SenderChannel"/>
    <jms:outbound-channel-adapter
                        channel="SenderChannel" 
                        destination="requestQueue" />

    
    <integration:channel id="InboundChannel">
        <integration:queue/>
    </integration:channel>
    <jms:message-driven-channel-adapter
            destination="replyQueue"
            channel="InboundChannel" />

</beans>
And the configuration on the processing node side looks like (please see the comments inline for more explanation of the Spring Integration elements):
xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"   
    xmlns:integration="http://www.springframework.org/schema/integration"
    xmlns:jms="http://www.springframework.org/schema/integration/jms"   
    xmlns:stream="http://www.springframework.org/schema/integration/stream" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/integration/jms
            http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
            http://www.springframework.org/schema/integration
            http://www.springframework.org/schema/integration/spring-integration.xsd">

    <import resource="integration-common.xml"/>

     
    <context:component-scan base-package="sample.integration.jmsbasic"/>

    
    <integration:channel id="jmsinToProcChannel"/>
    <jms:message-driven-channel-adapter
            destination="requestQueue"
            channel="jmsinToProcChannel"/>

    
    <integration:service-activator 
            input-channel="jmsinToProcChannel" 
            ref="procService"
            output-channel="jmsBackChannel" />

    
    <integration:channel id="jmsBackChannel"/>
    <jms:outbound-channel-adapter
                        channel="jmsBackChannel" 
                        destination="replyQueue" />

</beans>
share|improve this answer
add comment
Are you asking if Spring Integration can be used to implement a protocol bridge? Then the answer is yes, and does so quite simply.
share|improve this answer
 
I am not sure this would solve my scenario: on the one hand side a client app would be configured to use an inbound channel adapter to hand in the messages into the JMS queue, and on a processing node would be configured to use an outbound adapter to pick it up and process it. –  ngeek Jun 10 '10 at 12:32
 
I assumed that you wanted to decouple the protocols. The client apps would be largely unchanged, except to communicate to a bridging application with their protocol of choice WS, REST etc). The bridging app would turn all of these requests into JMS requests which your worker nodes would consume on a competing-consumer pattern. –  Paul McKenzie Jun 10 '10 at 15:19
 
That's right in the optimal case the client should not have to know anything about the protocol. Could you please elaborate how to best make use of the bridge pattern in the described scenario? Thanks. –  ngeek Jun 10 '10 at 22:01
add comment

No comments: