Skip to content

Spring Cloud OpenFeign With Web Services#

Using OpenFeign For Web Services#

  • In some projects, sometime we will meet some user stories that need us to make a connection to the SOAP service from our spring boot service by giving a file .wsdl. So This post will guild you step by step to archive it easily with the support of Spring Cloud Feign.

Prepare The Environment#

  • To make an example we need an online WebService, so we can go to free SOAP service urls and choose one to make an example.
  • In this example I will choose country list SOAP service because it is the one that is working well.
  • So you need go to country list SOAP service by your browser. Then Right Click and choose Save As. After that you save the file with type .wsdl into any package in src/main/resources of your spring boot project as the images below:

 #zoom

 #zoom

Dependencies#

  • We need to add some dependencies for Spring cloud fiegn and SOAP in pom.xml file. In which, we need to note about the declaration of generatePackage which is the package that contains all generated java classes when we run command mvn compile and the schemaDirectory which is the path to our countries.wsdl file of us.
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- spring cloud feign dependencies -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.6.3</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-soap</artifactId>
        <version>12.0</version>
    </dependency>


    <!-- dependencies for WSDL -->
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-ws-core</artifactId>
        <version>3.1.4</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>
<!-- pom.xml plugin for WSDL -->
<build>
    <plugins>
        <plugin>
            <groupId>org.jvnet.jaxb2.maven2</groupId>
            <artifactId>maven-jaxb2-plugin</artifactId>
            <version>0.14.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <schemaLanguage>WSDL</schemaLanguage>
                <generateDirectory>${project.basedir}/src/main/java</generateDirectory>
                <!-- the package that will contain our generated java classes -->
                    <generatePackage>com.springboot.cloud.openfeign.web.services.models.gen</generatePackage>
                <!-- the path where we put the wsdl file -->
                <schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory>
                <schemaIncludes>
                        <include>countries.wsdl</include>
                </schemaIncludes>
            </configuration>
        </plugin>
    </plugins>
</build>

Generate Java Classes From WSDL File#

  • So let's open the terminal/command line then run mvn compile, then Maven will read the file countries.wsdl to generate java classes and put them into the package that we have already defined in the pom.xml of Step 2.
  • If you note that, java classes have been configured with many annotation of XML, So these class will be formatted as xml type when we transfer them to SOAP service.

 #zoom

Configuration#

  • In this step we need to create a configuration class to configure the Encoder and Decoder of Feign Client which we will use for the feign client soap adapter later.
  • I will name this configuration class as FeignSOAPConfiguration
FeignSOAPConfiguration
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.springboot.cloud.openfeign.web.services.configuration;

import feign.jaxb.JAXBContextFactory;
import feign.soap.SOAPDecoder;
import feign.soap.SOAPEncoder;
import org.springframework.context.annotation.Bean;

/**
 We should not put the annotation @Configuration here
 because Feign will override all other Feign clients with
 this configuration class
 */
public class FeignSOAPConfiguration {

    @Bean
    public JAXBContextFactory configJAXBContextFactory() {
        return new JAXBContextFactory.Builder()
                .withMarshallerJAXBEncoding("UTF-8")
                .withMarshallerSchemaLocation("http://api rest http://api host/schema.xsd")
                .build();
    }

    @Bean
    public SOAPDecoder feignDecoder() {
        return new SOAPDecoder(configJAXBContextFactory());
    }

    @Bean
    public SOAPEncoder feignEncoder() {
        return new SOAPEncoder(configJAXBContextFactory());
    }

}
  • Then in the Main Class, we need to enable OpenFeign Client by adding @EnableFeignClients annotation as below.
OpenFeignWebServicesApplication.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.springboot.cloud.openfeign.web.services;  

import org.springframework.boot.SpringApplication;  
import org.springframework.boot.autoconfigure.SpringBootApplication;  
import org.springframework.cloud.openfeign.EnableFeignClients;  

@EnableFeignClients  
@SpringBootApplication  
public class OpenFeignWebServicesApplication {  
    public static void main(String[] args) {  
        SpringApplication.run(OpenFeignWebServicesApplication.class, args);  
    }  
}

Creating Adapter#

  • So in this step we will crate an interface feign client adapter to call to our SOAP service.

  • Remember that we have to put the class FeignSOAPConfiguration into the attribute configuration of annotation @FeignClient. Don't for get give it a name with attribute name and put the url of SOAP service (the url that we used to save countries.wsdl from browser).

  • Because generated java classes (CountryName.java and CountryNameResponse.java) are formatted with xml so we have to define the consumes (Input) and produces (Output) as text/xml type.

```java linenums="1" title="CountryClientAdapter.java"

package com.springboot.cloud.openfeign.web.services.api;

import com.springboot.cloud.openfeign.web.services.configuration.FeignSOAPConfiguration; import com.springboot.cloud.openfeign.web.services.models.gen.CountryName; import com.springboot.cloud.openfeign.web.services.models.gen.CountryNameResponse; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody;

/* this @FeignClient includes 3 attributes: name of @FeignClient, url of SOAP service, configuration class for this FeignClient / @FeignClient(name = "soap.client.example", url = "${country.client.soap.url}", configuration = FeignSOAPConfiguration.class) public interface CountryClientAdapter {

/**
 @RequestBody (CountryName) and CountryNameResponse  are the Object classes
 with format xml that was generated from the wsdl file.
 So the attribute consumes and produces must be "text/xml"
 */
@PostMapping(value = "", consumes = MediaType.TEXT_XML_VALUE, produces = MediaType.TEXT_XML_VALUE)
CountryNameResponse get(@RequestBody CountryName countryName);

}

```

```yaml linenums="1" title="application.yml" country: client: soap: package: com.example.workflow.models.gen url: http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL

### Create Service
- Let's create a simple Service which use `CountryClientAdapter.java` as below.

```java linenums="1" title="CountryCodeHandler.java"

package com.springboot.cloud.openfeign.web.services.service;

import com.springboot.cloud.openfeign.web.services.api.CountryClientAdapter;
import com.springboot.cloud.openfeign.web.services.models.gen.CountryName;
import com.springboot.cloud.openfeign.web.services.models.gen.CountryNameResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CountryCodeHandler {

    @Autowired
    private CountryClientAdapter countryClientAdapter;

    public CountryNameResponse getCountryName(String countryISO) {
        CountryName countryName = new CountryName();
        countryName.setSCountryISOCode(countryISO);
        return countryClientAdapter.get(countryName);
    }

}

Create Controller#

  • Let's create a simple Service which use CountryCodeHandler.java as below.
WebServicesController.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.springboot.cloud.openfeign.web.services.controller;

import com.springboot.cloud.openfeign.web.services.models.gen.CountryNameResponse;
import com.springboot.cloud.openfeign.web.services.service.CountryCodeHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebServicesController {

    @Autowired
    private CountryCodeHandler countryCodeHandler;

    @GetMapping(path = "/v1/soap/country/{countryISO}", produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<CountryNameResponse> getCountryNameByISO(@PathVariable("countryISO") String countryISO) {
        return ResponseEntity.ok(this.countryCodeHandler.getCountryName(countryISO));
    }

}

Testing#

  • Finally, let's start our spring boot application project and call the exported api by postman to test.
  • The result we be showed as below, please not that the we use 3 ISO country code for test Ex: VNM, IDN

 #zoom

See Also#

References#