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!
Table Of Contents
- 1. Introduction
- 2. Overview of the system
- 3. The Egyptian Theater service
- 4. The Pantages Theater service
- 5. The user interface
- 6. Logging each user request
- 7. Discarding invalid entries
- 8. Choosing which theater to request
- 9. Requesting the Egyptian Theater
- 10. Requesting the Pantages Theater
- 11. Handling errors
- 12. Shutting down the application
- 13. Taking a glance at the complete flow
- 14. Technology versions
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.
- 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:
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:
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>
can i the src for this?
Can you provide the sources please?
Can you provide the sources please
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.