Say Hello to Spring Integration

By Mark Fisher, Jonas Partner, Marius Bogoevici, and Iwein Fuld, authors of Spring Integration in Action

Spring Integration aims to increase productivity, simplify development, and provide a solid platform from which you can tackle the complexities. It offers a lightweight, noninvasive, and declarative model for constructing message-driven applications. On top of this, it includes a toolbox of commonly required integration components and adapters. With these tools in hand, developers can build the types of applications that literally change the way their companies do business. In this article, based on chapter 1 of Spring Integration in Action, we say hello to Spring Integration.

Spring Integration aims to provide a clear line between code and configuration. The components provided by the framework, which often represent the enterprise integration patterns, are typically configured in a declarative way using either XML or Java annotations as metadata. But, many of those components act as stereotypes or templates. They play a role that's understood by the framework, but they require a reference to some user-defined, domain-specific behavior in order to fulfill that role.

For our Hello World example, the domain-specific behavior is the following:

package siia.helloworld.channel;

public class MyHelloService implements HelloService {

  @Override
  public void sayHello(String name) {
    System.out.println("Hello " + name);
  }
}

The interface that MyHelloService implements is HelloService, defined as follows:

package siia.helloworld.channel;

public interface HelloService {
    void sayHello(String name);
}

There you have it: a classic Hello World example. This one is flexible enough to say hello to anyone. Because this book is about Spring Integration, and we've already established that it's a framework for building messaging applications based on the fundamental enterprise integration patterns, you may be asking, where are the Message, Channel, and Endpoint? The answer is that you typically don't have to think about those components when defining the behavior. What we implemented here is a straightforward POJO with no awareness whatsoever of the Spring Integration API. This is consistent with the general Spring emphasis on noninvasiveness and separation of concerns. That said, let's now tackle those other concerns, but separately, in the configuration:

<beans:beans
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
   xmlns="http://www.springframework.org/schema/integration"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/integration
   http://www.springframework.org/schema/integration/
     ➥ spring-integration.xsd">

  <channel id="names"/>
  <service-activator input-channel="names" ref="helloService"
                     method="sayHello"/>

  <beans:bean id="helloService"
              class="siia.helloworld.channel.MyHelloService"/>

</beans:beans>

If you look in chapter 1 of Spring Integration in Action, you would see an example of the Spring Framework's support for message-driven POJOs and that the configuration for that example includes an element from the jms namespace that similarly includes the ref and method attributes for delegating to a POJO via an internally created Message-ListenerAdapter instance. Spring Integration's service activator plays the same role, except that this time it's more generic. Rather than being tied to the JMS transport, the service activator is connected to a Spring Integration MessageChannel within the ApplicationContext. Any component could be sending messages to this service activator's input-channel. The key point is that the service activator doesn't require any awareness or make any assumptions about that sending component.

All of the configured elements contribute components to a Spring Application-Context. In this simple case, you can bootstrap that context programmatically by instantiating the Spring context directly. Then, you can retrieve the MessageChannel from that context and send it a message. We use Spring Integration's MessageBuilder to construct the actual message, shown in the following listing.

Listing 1 Hello World with Spring Integration

package siia.helloworld.channel;

import org.springframework.context.ApplicationContext;
import
   org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.support.MessageBuilder;

public class HelloWorldExample {

  public static void main(String args[]) {
    String cfg = "siia/helloworld/channel/context.xml";
    ApplicationContext context = new ClassPathXmlApplicationContext(cfg);
    MessageChannel channel =
      context.getBean("names", MessageChannel.class);
      Message message =
                 MessageBuilder.withPayload("World").build();
      channel.send(message);
   }
}

Running that code produces "Hello World" in the standard output console. That's pretty simple, but it would be even nicer if there were no direct dependencies on Spring Integration components even on the caller's side. Let's make a few minor changes to eradicate those dependencies.

First, to provide a more realistic example, let's modify the HelloService interface so that it returns a value rather than simply printing out the result itself:

package siia.helloworld.gateway;

public class MyHelloService implements HelloService {

  @Override
  public String sayHello(String name) {
    return "Hello " + name;
  }
}

Spring Integration handles the return value in a way that's similar to the Spring JMS support described earlier. You add one other component to the configuration, a gateway proxy, to simplify the caller's interaction. Here's the revised configuration:

<beans:beans
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://www.springframework.org/schema/integration"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.springframework.org/schema/integration
   http://www.springframework.org/schema/integration/
     [CA] spring-integration.xsd">

 <gateway id="helloGateway"
          service-interface="siia.helloworld.gateway.HelloService"
          default-request-channel="names"/>

 <channel id="names"/>

 <service-activator input-channel="names" ref="helloService"
                    method="sayHello"/>

 <beans:bean id="helloService"
             class="siia.helloworld.gateway.MyHelloService"/>
</beans:beans>

Note that the gateway element refers to a service interface. This is similar to the way the Spring Framework handles remoting. The caller should only need to be aware of an interface, while the framework creates a proxy that implements that interface. The proxy is responsible for handling the underlying concerns such as serialization and remote invocation, or in this case, message construction and delivery. You may have noticed that the MyHelloService class does implement an interface. Here's what the HelloService interface looks like:

package siia.helloworld.gateway;

public interface HelloService {

  String sayHello(String name);
}

Now the caller only needs to know about the interface. It can do whatever it wants with the return value. In the following listing, you just move the console printing to the caller's side. The service instance would be reusable in a number of situations. The key point is that this revised main method now has no direct dependencies on the Spring Integration API.

Listing 2 Hello World revised to use a Gateway proxy

package siia.helloworld.gateway;

import org.springframework.context.ApplicationContext;
import
   org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloWorldExample {

  public static void main(String args[]) {
    String cfg = "siia/helloworld/gateway/context.xml";
    ApplicationContext context = new ClassPathXmlApplicationContext(cfg);
    HelloService helloService =
      context.getBean("helloGateway", HelloService.class);
    System.out.println(helloService.sayHello("World"));
  }
}

Summary

Spring Integration brings the enterprise integration patterns and the Spring programming model together. Even in this simple example, you can see some of the characteristics of that programming model, such as IoC, separation of concerns, and an emphasis on noninvasiveness of the API.


Spring Batch in Action

Spring Batch in Action
Arnaud Cogoluegnes, Thierry Templier, Gary Gregory, Olivier Bazoud

Spring in Practice

Spring in Practice
Willie Wheeler, John Wheeler, and Joshua White

Spring in Action, Fourth Edition

Spring in Action, Fourth Edition
Craig Wells