Enterprise Java

Spring Integration Full Example

This article is part of our Academy Course titled Spring Integration for EAI.

In this course, you are introduced to Enterprise Application Integration patterns and how Spring Integration addresses them. Next, you delve into the fundamentals of Spring Integration, like channels, transformers and adapters. Check it out here!

1. Introduction

This tutorial will go through a complete example of an application that uses several of the components provided by Spring Integration in order to provide a service to its users. This service consists of a system prompting the user to choose among different theaters. After his selection, the system will make a request to the external system of the chosen theater and return the list of its available films. Each cinema offers its service through a different API; we will see this in the sections that explain each external system (sections three and four).

2. Overview of the system

The following activity diagram shows a high level view of the system.

Figure 1
Figure 1

  • User interface: On the left side of the diagram, we have the start of the flow; the request user entry. This entry request shown by the system along with the system responses to the user, are both examples of integration with streams.
  • Web service invocations: Depending on the selection made by the user, the system will retrieve the films list from a different external system. The Egyptian Theater exposes its service through HTTP, while the Pantages Theater exposes its service through SOAP.
  • Error handling: If there is an error during the flow, maybe because of an unexpected exception or because a web service is not available, the system will send information to another two external systems: a noSQL database (MongoDB) and an email address.

Next sections will enter more deeply in each of the parts of this system.

3. The Egyptian Theater service

The Egyptian Theater system exposes his service through HTTP. In this section we are going to take a quick look at the application. It is a Spring 4 MVC application that contains a RESTful web service.

The controller will serve requests to retrieve all available films that are being showed in the theater:

@RestController
@RequestMapping(value="/films")
public class FilmController {
    FilmService filmService;

    @Autowired
    public FilmController(FilmService service) {
        this.filmService = service;
    }

    @RequestMapping(method=RequestMethod.GET)
    public Film[] getFilms() {
        return filmService.getFilms();
    }
}

The API is simple, and for this example the service will return some dummy values, since the focus of this section is to just provide a little bit more of detail of what external system is being called:

@Service("filmService")
public class FilmServiceImpl implements FilmService {

    @Override
    public Film[] getFilms() {
        Film film1 = new Film(1, "Bladerunner", "10am");
        Film film2 = new Film(2, "Gran Torino", "12pm");

        return new Film[]{film1, film2};
    }
}

The Spring configuration is based on annotations:

<!-- Detects annotations like @Component, @Service, @Controller, @Repository, @Configuration -->
<context:component-scan base-package="xpadro.spring.mvc.films.controller,xpadro.spring.mvc.films.service"/>

<!-- Detects MVC annotations like @RequestMapping -->
<mvc:annotation-driven/>

The web.xml file configures the web application:

<!-- Root context configuration -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:xpadro/spring/mvc/config/root-context.xml</param-value>
</context-param>

<!-- Loads Spring root context, which will be the parent context -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Spring servlet -->
<servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:xpadro/spring/mvc/config/app-context.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/spring/*</url-pattern>
</servlet-mapping>

So, the system will serve requests sent to http://localhost:8080/rest-films/spring/films, with rest-films being the context path of the application.

4. The Pantages Theater service

The Pantages Theater service exposes its service through SOAP. Like the Egyptian Theater, it consists in a web application but in this case, it is implemented with Spring Web Services.

The endpoint will serve filmRequest requests with the namespace http://www.xpadro.spring.samples.com/films. The response is built from the result received from a film service:

@Endpoint
public class FilmEndpoint {
    @Autowired
    private FilmService filmService;
    
    @PayloadRoot(localPart="filmRequest", namespace="http://www.xpadro.spring.samples.com/films")
    public @ResponsePayload FilmResponse getFilms() {
        return buildResponse();
    }
    
    private FilmResponse buildResponse() {
        FilmResponse response = new FilmResponse();
        
        for (Film film : filmService.getFilms()) {
            response.getFilm().add(film);
        }
        
        return response;
    }
}

The film service is also a dummy service that will return some default values:

@Service
public class FilmServiceImpl implements FilmService {

    @Override
    public List<Film> getFilms() {
        List<Film> films = new ArrayList<>();
        
        Film film = new Film();
        film.setId(new BigInteger(("1")));
        film.setName("The Good, the Bad and the Uggly");
        film.setShowtime("6pm");
        films.add(film);
        
        film = new Film();
        film.setId(new BigInteger(("2")));
        film.setName("The Empire strikes back");
        film.setShowtime("8pm");
        films.add(film);
        
        return films;
    }
}

The Spring configuration is shown below:

<!-- Detects @Endpoint since it is a specialization of @Component -->
<context:component-scan base-package="xpadro.spring.ws"/>

<!-- detects @PayloadRoot -->
<ws:annotation-driven/>

<ws:dynamic-wsdl id="filmDefinition" portTypeName="Films" 
                 locationUri="http://localhost:8080/ws-films">
    <ws:xsd location="/WEB-INF/schemas/xsd/film-service.xsd"/>
</ws:dynamic-wsdl>

Finally, the web.xml file:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:xpadro/spring/ws/config/root-config.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>Films Servlet</servlet-name>
    <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:xpadro/spring/ws/config/servlet-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Films Servlet</servlet-name>
    <url-pattern>/films/*</url-pattern>
</servlet-mapping>

Based on this configuration, the Pantages Theater application will serve requests sent to http://localhost:8080/ws-films/films, where ws-films is the context path of the application.

5. The user interface

Once we have seen what the external systems are that will communicate with our Spring Integration application, let’s move on to see how this application is built.

The standalone application starts with a main method that bootstraps the Spring context, which contains all our integration components. Next, it prompts the user to enter his selection:

public class TheaterApp {
    private static Logger logger = LoggerFactory.getLogger("mainLogger");
    static AbstractApplicationContext context;

    public static void main(String[] args) {
        
        context = new ClassPathXmlApplicationContext("classpath:xpadro/spring/integration/config/int-config.xml");
        context.registerShutdownHook();
        
        logger.info("\\nSelect your option (1-Egyptian Theater / 2-Pantages Theater / 0-quit):\\n");
    }
    
    public static void shutdown() {
        logger.info("Shutting down...");
        context.close();
    }
}

The application also implements a shutdown method that will be invoked by the user. We will see this in more detail in a later section.

Ok, now how will the user selection get into the messaging system? This is where the integration with streams comes into play. The system entry to the messaging system is implemented with an inbound channel adapter that will read from stdin (System.in).

<!-- System entry -->
<int-stream:stdin-channel-adapter id="consoleIn" channel="systemEntry">
    <int:poller fixed-delay="1000" max-messages-per-poll="1" />
</int-stream:stdin-channel-adapter>

By using a poller, the inbound channel adapter will try to read from System.in each second and place the result into the systemEntry channel.

Now, we received a Spring Integration Message with the user entry as its payload. We can use our endpoints to transform the data and send it to the required system. Example: The console will prompt the user to choose among the different theaters:

2014-04-11 13:04:32,959|AbstractEndpoint|started org.springframework.integration.config.ConsumerEndpointFactoryBean#7

Select your option (1-Egyptian Theater / 2-Pantages Theater / 0-quit):

6. Logging each user request

The first thing the system does is logging the user selection. This is done by using a wire tap. This wire tap is an implementation of an interceptor that will intercept messages that travel across the channel, in our case, the systemEntry channel. It will not alter the flow; the message will continue to its destination, but the wire tap will also send it to another channel, usually for monitoring.

In our application, the message is also sent to a logging channel adapter.

<int:channel id="systemEntry">
    <int:interceptors>
        <int:wire-tap channel="requestLoggingChannel"/>
    </int:interceptors>
</int:channel>

<int:logging-channel-adapter id="requestLoggingChannel" 
    expression="'User selection: '.concat(payload)" level="INFO"/>

The logging channel adapter is implemented by the LoggingChannelAdapterParser. It basically creates a LoggingHandler that will log the payload of the message using the Apache Commons Logging library. If you want to log the full message instead of only its payload, you can add the log-full-message property to the logging channel adapter.

Example: The log shows the selection made by the user

Select your option (1-Egyptian Theater / 2-Pantages Theater / 0-quit):

1
2014-04-11 13:06:07,110|LoggingHandler|User selection: 1

7. Discarding invalid entries

Taking a glance at the user prompt, we see that the system accepts three valid entries:

logger.info("\\nSelect your option (1-Egyptian Theater / 2-Pantages Theater / 0-quit):\\n")

The treatment for invalid entries is very simple; the system will filter invalid entries, preventing them to move forward through the flow. These discarded messages will then be sent to a discards channel.

Subscribed to the invalidEntries discards channel, there is another stream channel adapter, in this case, an outbound adapter:

<int:filter input-channel="systemEntry" output-channel="validEntriesChannel" ref="entryFilter" 
    discard-channel="invalidEntries"/>

<!-- Invalid entries (show on console) -->
<int:chain input-channel="invalidEntries">
    <int:transformer ref="discardsTransformer"/>
    <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
</int:chain>

The function of this adapter is to write to stdout (System.out), so the user will receive that he entered an invalid request on the console.

One thing to keep in mind is that we didn’t create the invalidEntries channel. The adapter will be connected to the filter through an anonymous temporary direct channel.

Example: The console shows the user entered an invalid selection.

Select your option (1-Egyptian Theater / 2-Pantages Theater / 0-quit):

8
2014-04-11 13:07:41,808|LoggingHandler|User selection: 8
Invalid entry: 8

8. Choosing which theater to request

Valid entries that successfully pass the previous filter will be sent to a router:

<!-- Valid entries (continue processing) -->
<int:channel id="validEntriesChannel" />
<int:router input-channel="validEntriesChannel" ref="cinemaRedirector"/>

This router is responsible of deciding which external system is the one the user is requiring information from. It will also detect when the user wants to shut down the application:

@Component("cinemaRedirector")
public class CinemaRedirector {
    private static final String CINEMA_EGYPTIAN_CHANNEL = "egyptianRequestChannel";
    private static final String CINEMA_PANTAGES_CHANNEL = "pantagesRequestChannel";
    private static final String QUIT_REQUEST_CHANNEL = "quitRequestChannel";

    @Router
    public String redirectMessage(Message<String> msg) {
        String payload = msg.getPayload();
        
        if ("1".equals(payload)) {
            return CINEMA_EGYPTIAN_CHANNEL;
        }
        else if ("2".equals(payload)) {
            return CINEMA_PANTAGES_CHANNEL;
        }
        
        return QUIT_REQUEST_CHANNEL;
    }
}

So, here the flow splits in three different channels, a request to each theater and a request to finish the flow.

9. Requesting the Egyptian Theater

To communicate with the Egyptian Theater system, we need to send an HTTP request. We accomplish this by using an HTTP outbound gateway.

<int-http:outbound-gateway url="http://localhost:8080/rest-films/spring/films" 
        expected-response-type="java.lang.String" http-method="GET" charset="UTF-8"/>

This gateway is configured with several properties:

  • expected-response-type: The return type returned by the web service will be a String containing the JSON with the films listing.
  • http-method: We are making a GET request.
  • charset: the charset for converting the payload to bytes.

Once the response is received, we will use a transformer to convert the returned JSON to a Java Object. In our case, we will convert the response to a Film array:

<int:json-to-object-transformer type="xpadro.spring.integration.model.Film[]"/>

Next, a service activator will go through the array and build a String more appropriate to the user.

<int:service-activator ref="restResponseHandler"/>

The implementation is shown below:

@Component("restResponseHandler")
public class RestResponseHandler {
    private static final String NEW_LINE = "\\n";

    @ServiceActivator
    public String handle(Message<Film[]> msg) {
        Film[] films = msg.getPayload();
        
        StringBuilder response = new StringBuilder(NEW_LINE);
        if (films.length > 0) {
            response.append("Returned films:" + NEW_LINE);
        }
        else {
            response.append("No films returned" + NEW_LINE);
        }
        
        for (Film f:films) {
            response.append(f.getName()).append(NEW_LINE);
        }
        
        return response.toString();
    }
}

Finally, we will show the response to the user by printing it on the console. We are using the same channel adapter as the one used to show the user its invalid entries. The adapter will write to System.out:

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

The next code snippet shows the complete request. Since I did not want to create message channels for each interaction between these endpoints, I used a message handler chain. This type of endpoint simplifies the required XML configuration when we have several endpoints in a sequence. By using a message handler chain, all its endpoints are connected through anonymous direct channels.

<!-- Egyptian Theater request -->
<int:chain input-channel="egyptianRequestChannel">
    <int-http:outbound-gateway url="http://localhost:8080/rest-films/spring/films" 
        expected-response-type="java.lang.String" http-method="GET" charset="UTF-8"/>
    <int:json-to-object-transformer type="xpadro.spring.integration.model.Film[]"/>
    <int:service-activator ref="restResponseHandler"/>
    <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
</int:chain>

Example: The Egyptian Theater film listing is shown to the user.

1
2014-04-11 14:26:20,981|LoggingHandler|User selection: 1

Returned films:
Bladerunner
Gran Torino

10. Requesting the Pantages Theater

We are going to need a web service gateway to interact with the Pantages Theater system, but first, we have to build a request object of the type filmRequest in order to be served by the Egyptian web service endpoint.

We are using a transformer to change the message to a film web service request:

<int:transformer ref="soapRequestTransformer"/>

The implementation:

@Component("soapRequestTransformer")
public class SoapRequestTransformer {

    @Transformer
    public Message<?> createRequestMessage(Message<String> msg) {
        return MessageBuilder.withPayload(new FilmRequest()).copyHeadersIfAbsent(msg.getHeaders()).build();
    }
}

We got the request object so we can now invoke the web service using a web service gateway:

<int-ws:outbound-gateway uri="http://localhost:8080/ws-films/films" 
        marshaller="marshaller" unmarshaller="marshaller"/>

As seen in a previous tutorial, a marshaller will be required to convert the request and response. For this task we are going to use the oxm namespace:

<oxm:jaxb2-marshaller id="marshaller" contextPath="xpadro.spring.integration.ws.types" />

When we receive the web service response it will be in the form of a FilmResponse. The next endpoint in the sequence will adapt the response and return a String in order to be shown to the user in the next phase:

<int:service-activator ref="soapResponseHandler"/>

The implementation:

@Component("soapResponseHandler")
public class SoapResponseHandler {
    private static final String NEW_LINE = "\\n";

    @ServiceActivator
    public String handle(Message<FilmResponse> msg) {
        FilmResponse response = msg.getPayload();
        
        StringBuilder resp = new StringBuilder(NEW_LINE);
        if (response.getFilm().size() > 0) {
            resp.append("Returned films:" + NEW_LINE);
        }
        else {
            resp.append("No films returned" + NEW_LINE);
        }
        
        for (Film f : response.getFilm()) {
            resp.append(f.getName()).append(NEW_LINE);
        }
        
        return resp.toString();
    }
}

Like in the Egyptian request, this request also ends with another stream outbound channel adapter:

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

Since we have another sequence of endpoints, we use a message handler chain to minimize the quantity of configuration required:

<!-- Pantages Theater request -->
<int:chain input-channel="pantagesRequestChannel">
    <int:transformer ref="soapRequestTransformer"/>
    <int-ws:outbound-gateway uri="http://localhost:8080/ws-films/films" 
        marshaller="marshaller" unmarshaller="marshaller"/>
    <int:service-activator ref="soapResponseHandler"/>
    <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
</int:chain>

Example: The Pantages Theater film listing is shown to the user.

2
2014-04-11 14:27:54,796|LoggingHandler|User selection: 2

Returned films:
The Good, the Bad and the Uggly
The Empire strikes back


 

11. Handling errors

When anything goes wrong, we need to register it in some way. When this happens, the application will do two things:

  • Store the message to a database.
  • Send an email to a specified address.

The Spring Integration message channel called errorChannel will receive errors of type MessagingException thrown by message handlers. This special channel is a publish-subscribe channel, which means that it can have several subscribers to it. Our application subscribes two endpoints in order to execute the two previously specified actions.

11.1. Storing the message to the database

The following service activator is subscribed to the error channel, thus it will receive the MessagingException:

<int:service-activator ref="mongodbRequestHandler"/>

What this activator will do is to resolve what theater was the request sent to and then it will build a FailedMessage object, containing the information we want to log:

@Component("mongodbRequestHandler")
public class MongodbRequestHandler {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    private TheaterResolver theaterResolver;
    
    public FailedMessage handle(MessagingException exc) {
        logger.error("Request failed. Storing to the database");
        
        String theater = theaterResolver.resolve(exc);
        FailedMessage failedMsg = new FailedMessage(new Date(), exc.getMessage(), theater);
        
        return failedMsg;
    }
}

The failed message structure contains basic information:

public class FailedMessage implements Serializable {
    private static final long serialVersionUID = 4411815706008283621L;
    
    private final Date requestDate;
    private final String messsage;
    private final String theater;
    
    public FailedMessage(Date requestDate, String message, String theater) {
        this.requestDate = requestDate;
        this.messsage = message;
        this.theater = theater;
    }
    
    
    public Date getRequestDate() {
        return new Date(requestDate.getTime());
    }
    
    public String getMessage() {
        return this.messsage;
    }
    
    public String getTheater() {
        return this.theater;
    }
}

Once the message is built, we store it to the database by using a mongodb outbound channel adapter:

<int-mongodb:outbound-channel-adapter id="mongodbAdapter" collection-name="failedRequests" mongodb-factory="mongoDbFactory" />

The complete code for this part of the flow is shown below:

<import resource="mongodb-config.xml"/>
    
<int:chain input-channel="errorChannel">
    <int:service-activator ref="mongodbRequestHandler"/>
    <int-mongodb:outbound-channel-adapter id="mongodbAdapter" collection-name="failedRequests" mongodb-factory="mongoDbFactory" />
</int:chain>

The mongodb-config.xml file contains information specific to MongoDB configuration:

<bean id="mongoDbFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
    <constructor-arg>
        <bean class="com.mongodb.Mongo"/>
    </constructor-arg>
    <constructor-arg value="jcgdb"/>
</bean>
 
<bean id="mongoDbMessageStore" class="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageStore">
    <constructor-arg ref="mongoDbFactory"/>
</bean>

Example: The following screenshot shows the collection after two failed requests, one for each type of theater:

Figure 2
Figure 2

11.2. Sending an email to the person in charge

Another endpoint subscribed to the error channel is the mail request handler.

<int:service-activator ref="mailRequestHandler"/>

This handler is responsible of creating a MailMessage in order to send it to the mail gateway:

@Component("mailRequestHandler")
public class MailRequestHandler {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    private TheaterResolver theaterResolver;
    
    @ServiceActivator
    public MailMessage handle(MessagingException exc) {
        logger.error("Request failed. Sending mail");
        
        MailMessage mailMsg = new SimpleMailMessage();
        mailMsg.setFrom("Theater.System");
        mailMsg.setTo("my.mail@gmail.com");
        mailMsg.setSubject("theater request failed");
        
        String theater = theaterResolver.resolve(exc);
        StringBuilder textMessage = new StringBuilder("Invocation to ").append(theater).append(" failed\\n\\n")
                .append("Error message was: ").append(exc.getMessage());
        mailMsg.setText(textMessage.toString());
        
        return mailMsg;
    }
}

The theater resolver checks the user selection and returns the name of the requested theater.

The complete configuration is as follows:

<int:chain input-channel="errorChannel">
    <int:service-activator ref="mailRequestHandler"/>
    <int-mail:outbound-channel-adapter mail-sender="mailSender" />
</int:chain>

<import resource="mail-config.xml"/>

The mail-config.xml contains the configuration of the Spring mail sender:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
  <property name="host" value="smtp.gmail.com" />
  <property name="port" value="465" />
  <property name="username" value="my.mail@gmail.com" />
  <property name="password" value="myPassword" />
  <property name="javaMailProperties">
     <props>
        <prop key="mail.smtp.starttls.enable">true</prop>
        <prop key="mail.smtp.auth">true</prop>
        <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
     </props>
  </property>
</bean>

Example: The message sent to the gmail account:

Figure 3
Figure 3

12. Shutting down the application

When prompted, the user can decide to finish with the execution of the application by entering a zero. When he decides to do that, the router will redirect the message to the quitRequestChannel channel (see section 8). Subscribed to this channel, we have configured a message handler chain:

<!-- Quit message (shutdown application) -->
<int:chain input-channel="quitRequestChannel">
    <int:service-activator ref="shutdownActivator"/>
    <int-event:outbound-channel-adapter/>
</int:chain>

The service activator will create a ShutdownEvent and return it in order to be handled by the next endpoint of the message handler chain:

@Component("shutdownActivator")
public class ShutdownActivator {

    @ServiceActivator
    public ShutdownEvent createEvent(Message<String> msg) {
        return new ShutdownEvent(this);
    }
}

The ShutdownEvent is an instance of ApplicationEvent.

public class ShutdownEvent extends ApplicationEvent {
    private static final long serialVersionUID = -198696884593684436L;

    public ShutdownEvent(Object source) {
        super(source);
    }
    
    public ShutdownEvent(Object source, String message) {
        super(source);
    }

    public String toString() {
        return "Shutdown event";
    }
}

The event outbound channel adapter will publish as an ApplicationEvent, any message sent to the channel where it is subscribed. In this way, they will be handled by any ApplicationListener instance registered in the application context. However, if the payload of the message is an instance of ApplicationEvent, it will be passed as-is. As seen in the previous code, our ShutdownEvent is an instance of ApplicationEvent.

Listening to this kind of events, we have registered a listener:

@Component("shutdownListener")
public class ShutdownListener implements ApplicationListener<ShutdownEvent> {

    @Override
    public void onApplicationEvent(ShutdownEvent event) {
        TheaterApp.shutdown();
    }
}

The listener is used to shut down the application. If you remember the shutdown method in section five, the Theater application closes the application context.

13. Taking a glance at the complete flow

I have put all integration elements within the same file just for ease of clarity, but we could consider splitting it in smaller files:

<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:int="http://www.springframework.org/schema/integration"
    xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
    xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
    xmlns:int-event="http://www.springframework.org/schema/integration/event"
    xmlns:int-http="http://www.springframework.org/schema/integration/http"
    xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
    xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
    xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-3.0.xsd
        http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms-3.0.xsd
        http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-3.0.xsd
        http://www.springframework.org/schema/integration/event http://www.springframework.org/schema/integration/event/spring-integration-event-3.0.xsd
        http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-3.0.xsd
        http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws-3.0.xsd
        http://www.springframework.org/schema/integration/mongodb http://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb-3.0.xsd
        http://www.springframework.org/schema/integration/mail http://www.springframework.org/schema/integration/mail/spring-integration-mail-3.0.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">
    
    <context:component-scan base-package="xpadro.spring.integration"/>
    
    <!-- System entry -->
    <int-stream:stdin-channel-adapter id="consoleIn" channel="systemEntry">
        <int:poller fixed-delay="1000" max-messages-per-poll="1" />
    </int-stream:stdin-channel-adapter>

    <int:channel id="systemEntry">
        <int:interceptors>
            <int:wire-tap channel="requestLoggingChannel"/>
        </int:interceptors>
    </int:channel>
    
    <int:logging-channel-adapter id="requestLoggingChannel" 
        expression="'User selection: '.concat(payload)" level="INFO"/>
    
    <int:filter input-channel="systemEntry" output-channel="validEntriesChannel" ref="entryFilter" 
        discard-channel="invalidEntries"/>
    
    
    <!-- Invalid entries (show on console) -->
    <int:chain input-channel="invalidEntries">
        <int:transformer ref="discardsTransformer"/>
        <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
    </int:chain>

    
    <!-- Valid entries (continue processing) -->
    <int:channel id="validEntriesChannel" />
    <int:router input-channel="validEntriesChannel" ref="cinemaRedirector"/>
    
    
    <!-- Quit message (shutdown application) -->
    <int:chain input-channel="quitRequestChannel">
        <int:service-activator ref="shutdownActivator"/>
        <int-event:outbound-channel-adapter/>
    </int:chain>

    
    <!-- Continue processing (get data) -->

    <!-- Pantages Theater request -->
    <int:chain input-channel="pantagesRequestChannel">
        <int:transformer ref="soapRequestTransformer"/>
        <int-ws:outbound-gateway uri="http://localhost:8080/ws-films/films" 
            marshaller="marshaller" unmarshaller="marshaller"/>
        <int:service-activator ref="soapResponseHandler"/>
        <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
    </int:chain>
    
    <oxm:jaxb2-marshaller id="marshaller" contextPath="xpadro.spring.integration.ws.types" />

    
    <!-- Egyptian Theater request -->
    
    <int:chain input-channel="egyptianRequestChannel">
        <int-http:outbound-gateway url="http://localhost:8080/rest-films/spring/films" 
            expected-response-type="java.lang.String" http-method="GET" charset="UTF-8"/>
        <int:json-to-object-transformer type="xpadro.spring.integration.model.Film[]"/>
        <int:service-activator ref="restResponseHandler"/>
        <int-stream:stdout-channel-adapter id="consoleOut" append-newline="true" />
    </int:chain>
    
    
    <!-- Error handling -->
    <import resource="mongodb-config.xml"/>
    
    <int:chain input-channel="errorChannel">
        <int:service-activator ref="mongodbRequestHandler"/>
        <int-mongodb:outbound-channel-adapter id="mongodbAdapter" collection-name="failedRequests" mongodb-factory="mongoDbFactory" />
    </int:chain>
    
    <int:chain input-channel="errorChannel">
        <int:service-activator ref="mailRequestHandler"/>
        <int-mail:outbound-channel-adapter mail-sender="mailSender" />
    </int:chain>
    
    <import resource="mail-config.xml"/>
 
</beans>

14. Technology versions

For this application, I have used the 3.2.8 release version of the Spring framework and the latest 3.0.2 release version of Spring Integration.

The full dependency list is shown here:

<properties>
    <spring-version>3.2.8.RELEASE</spring-version>
    <spring-integration-version>3.0.2.RELEASE</spring-integration-version>
    <slf4j-version>1.7.5</slf4j-version>
    <jackson-version>2.3.0</jackson-version>
    <javax-mail-version>1.4.1</javax-mail-version>
</properties>

<dependencies>
    <!-- Spring Framework - Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring-version}</version>
    </dependency>

    <!-- Spring Framework - Integration -->
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-core</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-jms</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-stream</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-event</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-http</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-ws</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-mongodb</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-mail</artifactId>
        <version>${spring-integration-version}</version>
    </dependency>
    
    <!-- javax.mail -->
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>mail</artifactId>
        <version>${javax-mail-version}</version>
    </dependency>
    
    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${jackson-version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson-version}</version>
    </dependency>

    <!-- Logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j-version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j-version}</version>
    </dependency>
</dependencies>

Xavier Padro

Xavier is a software developer working in a consulting firm based in Barcelona. He is specialized in web application development with experience in both frontend and backend. He is interested in everything related to Java and the Spring framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
samir
samir
7 years ago

can i the src for this?

Tommy
Tommy
7 years ago

Can you provide the sources please?

Jaohn
Jaohn
6 years ago

Can you provide the sources please

Sanjeev Kumar
Sanjeev Kumar
5 years ago

Thanks a lot for writing this excellent spring integration tutorial. It was simply great. I read the complete description and understand it well. Now I am going to implement it what you have explained.

Thanks again for this tutorial.

Back to top button