Spring Boot – SOAP Web Service

Spring Boot – SOAP Web Service

Artykuł ten ma na celu wystawienie usługi sieciowej z użyciem Spring Boota i technologii SOAP. Są dwa podejścia do tworzenia usług sieciowych. Pierwsze polega na wygenerowaniu kontraktu – WSDL na podstawie istniejących w projekcie klas. Drugie natomiast polega na zaprojektowaniu kontraktu a następnie na wygenerowaniu odpowiednich powiązanych z kontraktem klas. Zaczynamy od nowego projektu Spring Boota i dodania odpowiednich zależności do pliku pom.xml:

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

Jako, że spring-boot-starter-web-services to biblioteka wspierająca jedynie podejście contract-first należy zaprojektować schemat XSD (standard służący do definiowania struktury dokumentu XML) który zostanie użyty do wygenerowania kontraktu WSDL. Przykładowy schemat:

Plik ./resources/xsd-schema.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="https://www.javaleader.pl"
           targetNamespace="https://www.javaleader.pl" elementFormDefault="qualified">
 
    <xs:element name="getCountryRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
    <xs:element name="getCountryResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="country" type="tns:country"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
    <xs:complexType name="country">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="population" type="xs:int"/>
            <xs:element name="capital" type="xs:string"/>
            <xs:element name="currency" type="tns:currency"/>
        </xs:sequence>
    </xs:complexType>
 
    <xs:simpleType name="currency">
        <xs:restriction base="xs:string">
            <xs:enumeration value="GBP"/>
            <xs:enumeration value="EUR"/>
            <xs:enumeration value="PLN"/>
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

Po zdefiniowaniu schematu należy wygenerować powiązane z nim klasy, w tym celu należy dodać do pliku pom.xml plugin: – jaxb2-maven-plugin:

<!-- Generate the Domain Java Classes -->
<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>jaxb2-maven-plugin</artifactId>
	<version>1.6</version>
	<executions>
		<execution>
			<id>xjc</id>
			<goals>
				<goal>xjc</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
		<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
		<clearOutputDir>false</clearOutputDir>
	</configuration>
</plugin>

W fazie kompilacji projektu zostaną wygenerowane odpowiednie klasy:

mvn compile

Tworzymy repozytorium – namiastkę bazy danych:

@Component
public class CountryRepository {
 
    private static final Map<String, Country> countries = new HashMap<>();
 
    @PostConstruct
    public void initData() {
        Country poland = new Country();
        poland.setCapital("Warszawa");
        poland.setCurrency(Currency.PLN);
        poland.setPopulation(37866107);
        countries.put("Poland", poland);
    }
 
    public Country findCountry(String name) {
        return countries.get(name);
    }
}

definiujemy usługę:

@Endpoint
public class CountryEndpoint {
 
    private static final String NAMESPACE_URI = "https://www.javaleader.pl";
 
    private CountryRepository countryRepository;
 
    @Autowired
    public CountryEndpoint(CountryRepository countryRepository) {
        this.countryRepository = countryRepository;
    }
 
    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
    @ResponsePayload
    public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) {
        GetCountryResponse response = new GetCountryResponse();
        response.setCountry(countryRepository.findCountry(request.getName()));
 
        return response;
    }
}
  • @Endpoint – rejestruje klasę jako endpoint (punkt końcowy usługi) ,
  • @PayloadRoot – odwzorowanie żądania na odpowiednią metodę,
  • @ResponsePayload, @ResponseRequest – mapowanie na wygenerowaną klasę żądania, odpowiedzi,

Klasa konfiguracyjna – WebServiceConfig:

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
 
    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }
 
    @Bean(name = "countries")
    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("CountriesPort");
        wsdl11Definition.setLocationUri("/ws");
        wsdl11Definition.setTargetNamespace("https://www.javaleader.pl");
        wsdl11Definition.setSchema(countriesSchema);
        return wsdl11Definition;
    }
 
    @Bean
    public XsdSchema countriesSchema() {
        return new SimpleXsdSchema(new ClassPathResource("xsd-schema.xsd"));
    }
}

Klasa konfiguracyjna rejestruje następujące ziarna:

  • ServletRegistrationBean – przechwytywanie żądań dostępnych pod adresem ./ws/*, jako parametr wstrzykiwany jest parametr kontekstu aplikacji dzięki czemu inne beany są widoczne przez Spring-WS,
  • DefaultWsdl11Definition – standard WSDL 1.1,
  • XsdSchema – lokalizacja schematu XSD, w tym przypadku katalog ./resources.

Klasa startowa projektu:

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

Po uruchomieniu aplikacji zostanie wygenerowany plik kontraktu WSDL dostępny pod adresem:

http://localhost:8080/ws/countries.wsdl

Do przetestowania aplikacji użyjemy narzędzia SoapUI:

Wysyłamy żądanie (request):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:jav="https://www.javaleader.pl">
   <soapenv:Header/>
   <soapenv:Body>
      <jav:getCountryRequest>
         <jav:name>Poland</jav:name>
      </jav:getCountryRequest>
   </soapenv:Body>
</soapenv:Envelope>

w wyniku otrzymujemy (response):

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <ns2:getCountryResponse xmlns:ns2="https://www.javaleader.pl">
         <ns2:country>
            <ns2:population>37866107</ns2:population>
            <ns2:capital>Warszawa</ns2:capital>
            <ns2:currency>PLN</ns2:currency>
         </ns2:country>
      </ns2:getCountryResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Zobacz kod na GitHubie i zapisz się na bezpłatny newsletter!

.

Leave a comment

Your email address will not be published.


*