Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreIn my recent post, I had mentioned that the Subversion repository for Spring Integration would be publicly accessible soon, and I'm pleased to provide that link now. You can checkout the project with the following command:
svn co https://anonsvn.springframework.org/svn/spring-integration/base/trunk spring-integration
If the checkout is successful, you should see the following directory structure:
spring-integration/
+--build-spring-integration/
+--spring-build/
+--spring-integration-core/
+--spring-integration-samples/
I would like to take this opportunity to walk through a couple of the samples that are in 'spring-integration-samples'. Keep in mind this project is definitely a work-in-progress (currently a 0.5 SNAPSHOT), but the samples should give you an idea of how the programming model is taking shape, and I'm very much looking forward to getting some feedback.
public class HelloService {
public String sayHello(String name) {
return "Hello " + name;
}
}
This example uses an XML-based configuration for the Message Endpoint (we'll see the annotation approach next):
<endpoint input-channel="inputChannel"
default-output-channel="outputChannel"
handler-ref="helloService"
handler-method="sayHello"/>
There you see that 'handler-ref' simply points to a Spring-managed bean. If you have used Spring's MessageListenerAdapter for asynchronous JMS reception, then this should look familiar - especially if you are using Spring 2.5's new jms namespace and the "jms:listener" element. Finally, the HelloWorldDemo starts the application context and then interacts with the channels:
ChannelRegistry channelRegistry = (ChannelRegistry) context.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME);
MessageChannel inputChannel = channelRegistry.lookupChannel("inputChannel");
MessageChannel outputChannel = channelRegistry.lookupChannel("outputChannel");
inputChannel.send(new StringMessage(1, "World"));
System.out.println(outputChannel.receive().getPayload());
That example involves lookup of the MessageBus bean - which implements the ChannelRegistry interface. However, in a non-demo "real world" scenario, any component that would access channels can have the registry provided via dependency injection. All it needs to do is implement ChannelRegistryAware (or use @Autowired). This is the same approach used elsewhere in Spring - such as ApplicationEventPublisherAware.
@MessageEndpoint(defaultOutput="quotes")
public class QuotePublisher {
@Polled(period=300)
public Quote getQuote() {
BigDecimal price = new BigDecimal(new Random().nextDouble() * 100);
return new Quote(generateTicker(), price.setScale(2, RoundingMode.HALF_EVEN));
}
private String generateTicker() {
// randomly generates 3-letter tickers
}
}
On the receiving side, there is a @Subscriber annotation:
public class QuoteSubscriber {
@Subscriber(channel="quotes")
public void log(Object o) {
System.out.println(o);
}
}
Here is the XML which registers the annotation post-processor and the 2 Spring-managed beans (notice that this example is using the 'spring-integration' schema as the primary namespace.
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">
<message-bus/>
<annotation-driven/>
<channel id="quotes"/>
<beans:bean id="publisher" class="org.springframework.integration.samples.quote.QuotePublisher"/>
<beans:bean id="subscriber" class="org.springframework.integration.samples.quote.QuoteSubscriber"/>
</beans:beans>
By the way, the 'annotation-driven' element also enables the @Publisher annotation which triggers the creation of AOP advice for asynchronously sending the return value of any annotated method to a channel.
@MessageEndpoint
public class Counter {
private AtomicInteger count = new AtomicInteger();
@Polled(period=3000)
public int getNumber() {
return count.incrementAndGet();
}
@Router
public String resolveChannel(int i) {
if (i % 2 == 0) {
return "even";
}
return "odd";
}
}
On the receiving end of these channels, we have 2 different methods that simply log the message payload:
@Component
public class NumberLogger {
@Subscriber(channel="even")
public void even(int i) {
System.out.println("even: " + i);
}
@Subscriber(channel="odd")
public void odd(int i) {
System.out.println("odd: " + i);
}
}
By the way, notice that the NumberLogger is annotated with Spring's @Component. The @MessageEndpoint annotation also contains @Component as a meta-annotation. Both are therefore "stereotypes" and eligible for autodetection with Spring 2.5's classpath-scanning. The XML for this example is extremely simple:
<context:component-scan base-package="org.springframework.integration.samples.oddeven"/>
<message-bus auto-create-channels="true"/>
<annotation-driven/>