Integration

Spring Integration Http Gateway Example

1. Introduction

In this post, we feature a comprehensive Example on Spring Integration Http Gateway. Messaging Gateway is an Enterprise Integration pattern that encapsulates the messaging code enabling the application to have only business logic code. In Spring Integration, the Messaging Gateway pattern is implemented in the gateway component, which is used to provide an entry / exit system between the application and an external system.

If you use just message channels and adapters, your application will need to have plumbing code of the messaging system, which results in tight coupling. Gateways eliminate tight coupling. Essentially, gateways are facades that abstract away the functionality of the messaging system into a discrete interface, thus your interface to the messaging system is just a method or a service call.

The message flow can happen in a bi-directional fashion. Output gateways send messages out of the application and take in the response where as inbound gateways receive messages into the application from an external system, process it and emit a response.

2. Application

In this article, we will discuss a Spring Integration application which sends a GET request to a REST web service and processes the response received, both operations being done through an output gateway that has a send channel and a receive channel. The REST service and HTTP application are built and packaged separately.

3. Environment

  • Java 1.8
  • Spring Boot 2.0.1
  • Maven 3.5.4
  • Windows 10

4. Source Code

Let’s look at the files and code. First we start with the accounts application which is a REST web service. It is a Maven based project, so all the required libraries are specified in the pom file.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.javacodegeeks.webservices</groupId>
	<artifactId>accounts</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>accounts</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

The base domain class of the application is AccountsTransaction.

AccountsTransaction.java

package org.javacodegeeks.webservices.accounts.domain;

public class AccountsTransaction {
	String description = "";
	Double amount = 0.0;
	
	public String getDescription() {
		return description;
	}
	
	public Double getAmount() {
		return amount;
	}
}

This is a simple class with two properties, a description of type String and an amount of type Double, which are initialized to a null string and 0.0 respectively. Each property has its own getter defined.

In the real world, there are many types of accounting transactions. In our application, we use only one, Income.

Income.java

package org.javacodegeeks.webservices.accounts.domain;

public class Income extends AccountsTransaction {

	public Income(String description, Double amount) {
		this.description = description;
		this.amount = amount;
	}
}

This class is a sub-class of AccountsTransaction and has a constructor using the two base class properties.

The endpoint class handling the GET requests is TransactionsEndpoint.

TransactionsEndpoint.java

package org.javacodegeeks.webservices.accounts.controllers;

import java.util.ArrayList;
import java.util.List;

import org.javacodegeeks.webservices.accounts.domain.Income;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TransactionsEndpoint {

	static List incomeList = new ArrayList();

	static {
		incomeList.add(new Income("First Income", 1000.0));
	}

	@GetMapping("/incomes")
	public List getAllIncomes() {
		return incomeList;
	}
}

The @RestController annotation itself has two annotations, @Controller and @ResponseBody. These make the class to be identified as a web controller and auto-detected through scanning and make the method return values bound to the response body.

The class declares incomeList to be a List of Income objects and refers it to an ArrayList object. Next, inside a static block, it constructs an Income object with “First Income” as the description and an amount of value 1000.0. This new object is added to the ArrayList. As a quick refresher, static blocks in Java are executed only once, when the JVM loads the class into memory; they are also called initialization blocks and are executed before the constructors.

The annotation @GetMapping is used to map incoming GET requests with “/incomes” in the URL to the ListAllIncomes method. As per the Spring framework documentation, “@GetMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.GET).” The method getAllIncomes just returns incomeList.

The main class of the accounts service is AccountsApplication.

AccountsApplication.java

package org.javacodegeeks.webservices.accounts;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AccountsApplication {

	public static void main(String[] args) {
		SpringApplication.run(AccountsApplication.class, args);
	}
}

The @SpringBootApplication annotation is a convenience annotation that combines three other annotations, @EnableConfiguration, @ComponentScan, and @Configuration. In other words, the class is marked for auto-configuration, component scan and having the ability to register additional beans and import extra configuration classes. The main method invokes SpringApplication.run to start the application which waits for incoming web service requests.

Now, we will go through the second application which uses a Spring Integration gateway to interoperate with the accounts application. This is also a Maven based project and hence all the required libraries are specified in pom.xml.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.javacodegeeks.springintegration.gateway</groupId>
	<artifactId>http-get</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>http-get</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.1.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-http</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

The gateway along with its request and reply channels are configured in the file http-outbound-gateway.xml.

http-outbound-gateway.xml

<?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:int="http://www.springframework.org/schema/integration"
	xmlns:int-http="http://www.springframework.org/schema/integration/http"
	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
    http://www.springframework.org/schema/integration/http
    http://www.springframework.org/schema/integration/http/spring-integration-http.xsd">

	<int:channel id="get_send_channel" />

	<int:channel id="get_receive_channel">
		<int:queue capacity='10' />
	</int:channel>

	<int-http:outbound-gateway id="get.outbound.gateway"
		request-channel="get_send_channel" url="http://localhost:8080/incomes"
		http-method="GET" reply-channel="get_receive_channel"
		expected-response-type="java.lang.String">
	</int-http:outbound-gateway>
</beans>

In this file, two DirectChannels are defined, with ids get_send_channel and get_receive_channel. You would recall that a DirectChannel has point-to-point semantics with its unique feature being that one message is to only one subscriber in a round-robin fashion.

Next, we define a HTTP outbound gateway with the id get.outbound.gateway and configure the get_send_channel to be its request channel and get_receive_channel to be its reply channel. We also specify the URL to communicate to as http://localhost:8080/incomes and specify that a String is expected as the response type.

The main class of this application is HttpApplication.

HttpApplication.java

package org.javacodegeeks.springintegration.gateway.http;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;

@SpringBootApplication
@ImportResource("http-outbound-gateway.xml")
public class HttpApplication {

	@Autowired
	@Qualifier("get_send_channel")
	MessageChannel getSendChannel;

	@Autowired
	@Qualifier("get_receive_channel")
	PollableChannel getReceiveChannel;

	public static void main(String[] args) {
		SpringApplication.run(HttpApplication.class, args);
	}

	@Bean
	public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
		return args -> {
			Message message = MessageBuilder.withPayload("").build();
			getSendChannel.send(message);
			System.out.println(getReceiveChannel.receive().getPayload());
		};
	}
}

As explained above, the @SpringBootApplication annotation gives a class the combined features of @EnableAutoConfiguration, @ComponentScan, and @Configuration.

With @ImportResource we tell the class to import beans from the file “http-outbound-gateway.xml“. The annotation @Autowired is used on a variable for Spring bean autowiring, thus allowing it to be used for dependency injection by the Spring container. The annotation @Qualifier is used to clearly identify which exact bean has to be auto-wired. In our case, we use it to specify that the getSendChannel bean configured with the configuration identified by “get_send_channel” and the getReceiveChannel bean configured with the identifier “get_receive_channel.”

The commandLineRunner method is annotated with @Bean indicating that it returns a bean to the spring container, which, in this case is a CommandLineRunner object. Within the method, a message is built with an empty payload by invoking the build method of MessageBuilder class. The message is sent to getSendChannel after which the payload of the message received on the getReceiveChannel is printed to the console.

5. How To Run

Starts the accounts application first. In a terminal window, go to the accounts directory and enter

mvn spring-boot:run

In another terminal window, go to the http-get folder and enter

mvn spring-boot:run

You will see that the application has called the accounts service and printed the following response.
[{"description":"First Income","amount":1000.0}] See the screen shot given below.

Spring Integration Http Gateway - Terminal output showing response from the REST Service
Terminal output showing response from the REST Service

6. Summary

In this article, we have seen a REST web service invoked from a Spring Boot application using a Spring Integration gateway.

7. Download the Source Code

That was an example of the Spring Integration Http Gateway.

Download
You can download the full source code of this example here: http-outbound-gateway.zip

Mahboob Hussain

Mahboob Hussain graduated in Engineering from NIT Nagpur, India and has an MBA from Webster University, USA. He has executed roles in various aspects of software development and technical governance. He started with FORTRAN and has programmed in a variety of languages in his career, the mainstay of which has been Java. He is an associate editor in our team and has his personal homepage at http://bit.ly/mahboob
Subscribe
Notify of
guest

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

0 Comments
Inline Feedbacks
View all comments
Back to top button