1.1.34.11. fejezet, SOAP szolgáltatás

Kapcsolódó hivatokzások

Alkalmazás konfiguráció

Ha a componensek (pl.: @Configuration) különböző könyvtárakban vannak, használjuk a @ComponentScan annotációt (dokumentáció).

package hu.infokristaly.forrasuploadsoapserver;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
 
@SpringBootApplication
@ComponentScan(basePackages = {"hu.infokristaly"})
public class ForrasUploadSoapServerApplication {
    public static void main(String[] args) {
       SpringApplication.run(ForrasUploadSoapServerApplication.class, args);
    }
}

WSDL generálása Schema XSD-ből

A következő formában szerkesszük meg a séma definíciót, és helyezzük a resources könyvtárba (src/main/resources/fileuplaod.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://infokristaly.hu/fileupload"
           targetNamespace="http://infokristaly.hu/fileupload" elementFormDefault="qualified">
 
      <xs:element name="uploadRequest">
          <xs:complexType>
              <xs:sequence>
                  <xs:element name="fileName" type="xs:string"/>
                  <xs:element name="content" type="xs:base64Binary" xmime:expectedContentTypes="application/octet-stream"/>
              </xs:sequence>
          </xs:complexType>
      </xs:element>
 
      <xs:element name="uploadResponse">
          <xs:complexType>
              <xs:sequence>
                  <xs:element name="return" type="xs:int"/>
              </xs:sequence>
          </xs:complexType>
      </xs:element>
</xs:schema>

Az operátorok a Request / Response előtagjai lesznek (upload).

Alkalmazzuk a jaxb2-maven-plugin maven bővítményt a pom.xml-ben a következőként:

<plugins>
...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <sources>
                        <source>src/main/resources/fileupload.xsd</source> <!-- Saját xsd állományunk helye -->
                    </sources>
                    <outputDirectory>src/main/java</outputDirectory> <!-- generált kód helye -->
                    <clearOutputDir>false</clearOutputDir>
                </configuration>
            </plugin>
...
</plugins>

Majd a compile parancsal generáljuk le a Java osztályokat:

mvn compile

Újra generálás előtt töröljük a projekt target könyvtárát!

WebService konfiguráció

package hu.infokristaly.soap;
 
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
 
@Configuration
@EnableWs
public class SOAPWebServiceConfig extends WsConfigurerAdapter {
    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");  // a servlet a http://localhost:8080/ws/* alatti kéréseket szolgálja ki
    }
 
    @Bean(name = "FileUploadServiceWsdl")
    public DefaultWsdl11Definition calculatorServiceDefinition(XsdSchema calculatorServiceSchema) {
        DefaultWsdl11Definition wsdlDefinition = new DefaultWsdl11Definition();
        wsdlDefinition.setPortTypeName("FileUploadServicePort");
        wsdlDefinition.setLocationUri("/ws/fileuplaod-service");  // a kérések a http://localhost:8080/ws/fileuplaod-service soap:address-re érkeznek majd
        wsdlDefinition.setTargetNamespace("http://infokristaly.hu/fileuplaod");
        wsdlDefinition.setSchema(fileUplaodServiceSchema());
        return wsdlDefinition;
    }
 
    @Bean
    public XsdSchema fileUplaodServiceSchema() {
        return new SimpleXsdSchema(new ClassPathResource("fileupload.xsd"));
    }
}

Ügyeljünk arra, hogy a wsdlDefinition.setTargetNamespace() metódusnak a xs:schema targetNamespace értékével megegyező paraméterű legyen. Ez a wsdl generálásánál játszik szerepet.

WebService végpont

package hu.infokristaly.soap;
 
import hu.infokristaly.fileservice.FileUploadService;
import hu.infokristaly.fileupload.UploadRequest;
import hu.infokristaly.fileupload.UploadResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.server.endpoint.annotation.SoapHeader;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
 
@Endpoint
public class FileUploadEndpoint {
 
    private FileUploadService fileUploadService;
 
    @Autowired
    public FileUploadEndpoint(FileUploadService fileUploadService) {
        this.fileUploadService = fileUploadService;
    }
 
    @PayloadRoot(namespace = "http://infokristaly.hu/fileupload", localPart = "uploadRequest")
    @ResponsePayload
    public UploadResponse fileUpload(@RequestPayload UploadRequest request, @SoapHeader("{" + Authentication.AUTH_NS + "}authentication") SoapHeaderElement auth) {
        Authentication authentication = getAuthentication(auth);
        if (authentication == null || !authentication.getUsername().equals("username") || !authentication.getPassword().equals("password")) {
            System.out.println("Invalid authentication");
        }
        UploadResponse response = new UploadResponse();
        response.setReturn(fileUploadService.fileUpload(request));
        return response;
    }
 
    private Authentication getAuthentication(SoapHeaderElement header){
        Authentication authentication = null;
        try {
            JAXBContext context = JAXBContext.newInstance(Authentication.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            authentication = (Authentication) unmarshaller.unmarshal(header.getSource());
 
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return authentication;
    }
 
}

Ügyeljünk arra, hogy az xsd fájlban a namespace és a targetNamespace / xmlns:tns megegyező legyen a @PayloadRoot namespace értékével, és a localPart értéke kis betűvel kezdődjön, mint ahogy az xsd-ben van definiálva.

Végül a szolgáltatás leíróját ellenőrizzük a http://localhost:8080/ws/FileUploadServiceWsdl.wsdl címen. A szolgáltatás leíró címét a SOAPWebServiceConfig-ban definiáltuk (FileUploadServiceWsdl).

Authenticatoin.java

A felhasználó azonosításra használt osztály a következő:

package hu.infokristaly.soap;
 
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
 
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = Authentication.AUTH_NS)
public class Authentication {
 
    public static final String AUTH_NS = "https://infokristaly.hu/security";
 
    @XmlElement(namespace = AUTH_NS)
    private String username;
    @XmlElement(namespace = AUTH_NS)
    private String password;
 
    public Authentication() {
    }
 
    public Authentication(String username, String password) {
        this.username = username;
        this.password = password;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
}

FileUploadServiceImpl.java

A szolgáltatás implementációja pedig a következő:

package hu.infokristaly.fileservice;
 
import hu.infokristaly.fileupload.UploadRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
 
@Service
public class FileUploadServiceImpl implements FileUploadService {
    @Value("${upload.dir}")
    private String uploadDir;
 
    public int fileUpload(UploadRequest uploadRequest) {
        try {
            uploadRequest.getContent().writeTo(new FileOutputStream(Paths.get(uploadDir, uploadRequest.getFileName()).toFile()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return uploadRequest.getFileName().length();
    }
}

Tesztelés SoapUI-al

WSDL-ből generálhatunk SOAP XML-t a SoapUI-ba. Az xml tartalomra minta az alábbi:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fil="http://infokristaly.hu/fileupload">
   <soapenv:Header>
     <auth:authentication xmlns:auth="https://infokristaly.hu/security">
         <auth:username>username</auth:username>
         <auth:password>password</auth:password>
      </auth:authentication>
   </soapenv:Header>
   <soapenv:Body>
      <fil:uploadRequest>
         <fil:fileName>README.md</fil:fileName>
         <fil:content>dGVzdA==</fil:content>
      </fil:uploadRequest>
   </soapenv:Body>
</soapenv:Envelope>

Tesztelés Postman-al

Készítsünk egy POST hívást, másoljuk be a WebService URL-jét (pl.: http://localhost:8080/ws/fileuplaod-service). Állítsuk be a Headers-be a "Content-Type"-ot "text/xml"-re. A Body-ba másoljuk be "raw" tartalomba a SOAP xml-t és állítsuk be a formátumot "XML"-re.




Lásd még a Make HTTP calls using the SOAP protocol dokumentációban.