Spring Boot이 포함된 SOAP 마이크로서비스, 1부에서는 Apache CXF 사용

이것은 일련의 문장의 첫 번째 부분이다. 이 문장에서 우리는 몇 개의 라이브러리를 사용하여 SOAP를 위해Spring Boot가 있는 SOAP 마이크로서비스를 구축했다.이 문서에서는 Apache CXF 라이브러리의 구현을 설명합니다.가져온 XSD 파일로 WSDL을 만들었습니다. 이 파일은 마이크로 서비스 구조의 유류 시스템에 업데이트하고 싶은 서비스를 표시합니다.코드를 생성하고 서비스를 실현하는 절차를 보여 드리겠습니다.

If you want to skip the introduction and go directly to the code, then you can find it in my GitHub repository ws-employee-soapcxf


회사 명 / 직원 soapcxf


Spring Boot 2.3 및 Apache CXF 3.4가 포함된 SOAP 마이크로서비스


Spring Boot 및 Apache CXF가 포함된 SOAP 마이크로서비스


Spring Boot에서 만든 docker 용기를 사용하여 남겨진 클라이언트에 SOAP 노드를 공개합니다
POC의 기술 스택은 다음과 같습니다.
  • 스프링 먼지 방지 커버 2.3.4
  • 자바 15
  • Apache CXF 3.4
  • 안심 4.3
  • Docker
  • 소프트웨어 요구 사항


    워크스테이션은 다음 도구를 올바르게 구성해야 합니다.
  • Java 15 JDK-https://jdk.java.net/15/
  • Maven-https://maven.apache.org/download.cgi
  • 옵션 도구
  • Docker-https://www.docker.com/products/docker-desktop
  • 토큰에 액세스할 수 있는 Docker hub 계정
  • WSDL 및 도메인 모델


    예제에서는 허구의 직원 SOAP 서비스에서 다음 두 가지 작업을 사용합니다.
  • GetEmployeeById
  • GetEmployeeByName
  • 프레젠테이션에서 XSD와 WSDL을 분리합니다.실제 장면에서는 이 모드가 가장 많이 사용되지만 다른 폴더에는 여러 개의 XSD가 있을 수 있습니다.employee.xsd 서비스의 완전한 도메인 모델을 가지고 다음 그림은 클라이언트에게 보내는 주요 응답을 나타낸다

    WSDL…
    View on GitHub

    소개


    잠깐만...SOAP 마이크로서비스?현재 우리는 REST 웹 서비스나 RPC 프로토콜(예를 들어 gRPC) 등 기술을 바탕으로 하는 마이크로서비스 구조가 다르다.그렇다면 우리는 왜 SOAP와 같은 낡은 협의를 이용하여 마이크로서비스를 실현하는 것에 흥미를 가져야 합니까?좋습니다. 오래된 유류된 단일 프로그램에서 이동하고 있을 때 특히 유용할 수 있습니다. 이 프로그램에서 SOAP 서비스를 통해 다른 시스템이 연결되어 있습니다.이러한 시스템 (클라이언트) 은 업데이트할 수 없으므로 다음과 같은 새로운 인프라에 연결할 수 없는 경우가 많습니다.
  • 이 회사는 이런 고객의 원본 코드가 없습니다. 저를 믿습니다. 이런 상황이 자주 발생하거나 생산에 배치된 원본 버전을 모릅니다.
  • 변경 및 테스트를 위한 인적 자원 또는 기술 지원이 없습니다.
  • 앞서 언급한 이유 또는 라이센스 비용 지불로 인해 업데이트 비용이 매우 높음
  • 이러한 클라이언트를 업데이트하는 것은 조직에 어떠한 업무 가치도 가져다 주지 않기 때문에 새로운 체계 구조를 도입했다고 해서 정상적인 업무를 바꿀 필요가 없다.
  • 이러한 이유와 다른 이유 중, 우리는 이 오래된 클라이언트들에게 같은 인터페이스를 제공해야 한다.

    서비스의 WSDL 가져오기

    Getting the WSDL from an existing SOAP web service is very simple; the standardized way to do it (although it may depend on the framework used to create the webservice) is by postfixing the URL with ?WSDL or .WSDL example:

    http://mywebservice.domain:8080/endpoint?WSDL
    

    This approach is commonly known as contract-first; we start with the WSDL contract, and then we use Java to implement it. Many times the WSDL comes with the domain model in separate XSD's files.

    In our example, we are going to work in a fictitious Employee SOAP service with 2 operations:

    • GetEmployeeById
    • GetEmployeeByName
    For the demo, I separated the XSD from the WSDL. This will be the most followed pattern but expect to have more than one XSD in different folders. The employee.xsd은 서비스의 전역 모델을 가지고 있다.다음 그림은 클라이언트에게 보내는 주요 응답을 보여 줍니다.

    WSDL은 SOAP 기반 웹 서비스의 기능을 설명합니다.본고의 가장 중요한 부분은wsdl:portType.이 절은 우리가 실현하고자 하는 서비스의 인터페이스를 정의합니다: 조작, 입력과 출력 매개 변수:

    Next is the WSDL section for wsdl:portType :
    <!--This element defines the service operations and the combination of input and output elements to clients-->
    <wsdl:portType name="EmployeeServicePortType">
        <wsdl:operation name="GetEmployeeById">
            <wsdl:input message="tns:EmployeeByIdRequest"/>
            <wsdl:output message="tns:EmployeeResponse"/>
        </wsdl:operation>
        <wsdl:operation name="GetEmployeesByName">
            <wsdl:input message="tns:EmployeeByNameRequest"/>
            <wsdl:output message="tns:EmployeesResponse"/>
        </wsdl:operation>
    </wsdl:portType>
    
    The complete WSDL file is here: EmployeeServices.wsdl

    항목 설정


    이 항목은 maven을 사용합니다. pom.xml의 속성과 의존성은 다음과 같습니다.
    <properties>
        <!-- Override BOM property in Spring Boot for Rest Assured and Groovy-->
        <!-- With rest-assured 4.3.X upgrade the Groovy from 2.5.7 to 3.0.2 -->
        <rest-assured.version>4.3.1</rest-assured.version>
        <groovy.version>3.0.2</groovy.version>
    
        <!-- Other properties-->
        <java.version>15</java.version>
        <cxf.version>3.4.0</cxf.version>
        <springboot.version>2.3.3.RELEASE</springboot.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    
        <!-- CXF Framework -->
        <dependency>    
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>${cxf.version}</version>
        </dependency>
        <!-- Required if you need to log the request/response -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-features-logging</artifactId>
            <version>${cxf.version}</version>    
        </dependency>
    
        <!-- Testing -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    메모:
  • 안심하세요.본문을 작성할 때, 우리는 최신 판본을 사용하고 있다.버전 4.3.1은groovy 3.0.2가 필요합니다.우리는pom 속성
  • 을 사용하여 부모pom 라이브러리를 덮어쓰고 정확한 설정을 압축합니다
  • Apache CXF.요청/응답을 기록해야 할 때만 의존성 cxfrt 특성 로그 기록이 필요합니다.이 프레젠테이션은 애플리케이션 콘솔에서 요청/응답 로드를 인쇄합니다.
  • 코드 생성


    우리는 영역 모델과 WSDL에 기술된 조작에 비추는 자바 클래스를 생성할 것입니다.Apache CXF 프로젝트의 maven 플러그인을 사용하여 코드를 생성합니다.

    XSD 및 wsdl 파일은 다음 구조에 있습니다.
    <source-code>
    ├───src
        └───main
            └───resources
                └───wsdl
    
    다음은 pom.xml 플러그인 구성
    <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>${cxf.version}</version>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                    <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
                    <wsdlOptions>
                        <wsdlOption>                        <wsdl>${basedir}/src/main/resources/wsdl/EmployeeServices.wsdl</wsdl>  
                        </wsdlOption>
                    </wsdlOptions>
                </configuration>
                <goals>
                    <goal>wsdl2java</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    
    이 플러그인은 다음 디렉터리에 자바 클래스를 생성합니다.
    <source-code>/target/generated-sources/cxf
    
    프로젝트 루트 디렉터리의 명령 창에서 실행하여 자바 클래스를 생성합니다.
    mvn compile
    
    다음 그림은 생성된 클래스를 표시하고 웹 서비스를 실현하는 단점으로 사용할 클래스를 강조합니다. EmployeeServicePortType.
  • 도메인 클래스입니다.이것들은 웹 서비스의 입력과 출력 파라미터를 나타내는 XSD 파일 요소입니다.이러한 클래스는 Address, Employee, Employee Response, Employee ByIdRequest, Employee ByNameRequest입니다.
  • WSDL 클래스.이러한 클래스는 서비스 작업과 입력 및 출력 요소를 설명합니다: Employee Service, Employee ServicePortType.
  • Apache CXF 실행 구성 시


    구성이 간단합니다.application.yml 에서는 Apache CXF의 기본 경로를 덮어쓸 수 있습니다. 기본적으로 이 경로는 /services 입니다. 예제에서는 /soap. 를 사용합니다.
    cxf:
      path: /soap
    
    우리는 공개된 서비스에 대한 경로를 제공해야 한다: /service/employee.@Bean 노드를 생성하여 다음과 같은 구성을 제공합니다.
    import org.springframework.context.annotation.Configuration;
    
    import javax.xml.ws.Endpoint;
    
    @Configuration
    public class ApplicationConfiguration {
    
        @Autowired
        private Bus bus;
    
        @Bean
        public Endpoint endpoint(EmployeeEndpoint employeeEndpoint) {
            EndpointImpl endpoint = new EndpointImpl(bus, employeeEndpoint);
            endpoint.publish("/service/employee");
            return endpoint;
        }
    }
    

    구현 서비스


    생성된 클래스 중 하나는 EmployeeServicePortType입니다.이것은 2개의 조작과 입력/출력 파라미터가 있는 인터페이스입니다.다음 예에서 우리는 실현 wsdl:portType section 의 클래스 EmployeeEndpoint 를 만들었다.프레젠테이션 목적으로 우리는 가짜 백엔드를 사용했는데, 이 백엔드는 서비스에 데이터를 제공할 것이다.예를 들어, 백엔드는 데이터 저장소 또는 기타 웹 서비스일 수 있습니다.
    import lombok.extern.slf4j.Slf4j;
    import org.apache.cxf.feature.Features;
    import org.springframework.stereotype.Service;
    import com.jpworks.employee.*;
    
    @Service
    @Slf4j
    @Features(features = "org.apache.cxf.ext.logging.LoggingFeature")
    public class EmployeeEndpoint implements EmployeeServicePortType{
    
        BackendService backendService;
    
        public EmployeeEndpoint (BackendService backendService){
            this.backendService = backendService;
        }
    
        @Override
        public EmployeesResponse getEmployeesByName(EmployeeByNameRequest parameters) {
            EmployeesResponse employeesResponse = new EmployeesResponse();
            try{
                employeesResponse.getEmployee().addAll(backendService.getEmployeesByName(parameters.getFirstname(), parameters.getLastname()));
            }
            catch (Exception e){
                log.error("Error while setting values for employee object", e);
            }
            return employeesResponse;
        }
    
        @Override
        public EmployeeResponse getEmployeeById(EmployeeByIdRequest parameters) {
            EmployeeResponse employeeResponse = new EmployeeResponse();
            try{
                employeeResponse.setEmployee(backendService.getEmployeeById(parameters.getId()));
            }
            catch (Exception e){
                log.error("Error while setting values for employee object", e);
            }
            return employeeResponse;
        }
    }
    
    위의 예시에서 우리는 EmployeeServicePortType 을 사용하여 요청과 응답을 응용 프로그램 로그에 기록합니다.

    @ 기능 응용 프로그램 실행


    루트 항목이 실행되는 명령 창에서:
    mvn spring-boot:run
    
    콘솔의 로그:
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.3.4.RELEASE)
    
    2020-10-02 22:23:49.563  INFO 1 --- [           main] com.jpworks.datajdbc.MainApplication     : Starting MainApplication v1.0.1-SNAPSHOT on b6e50b2f461b with PID 1 (/app.jar started by root in /)
    2020-10-02 22:23:49.572 DEBUG 1 --- [           main] com.jpworks.datajdbc.MainApplication     : Running with Spring Boot v2.3.4.RELEASE, Spring v5.2.9.RELEASE
    2020-10-02 22:23:49.573  INFO 1 --- [           main] com.jpworks.datajdbc.MainApplication     : The following profiles are active: local
    2020-10-02 22:23:51.163  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8081 (http)
    2020-10-02 22:23:51.179  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-10-02 22:23:51.179  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.38]
    2020-10-02 22:23:51.254  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-10-02 22:23:51.255  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1625 ms
    2020-10-02 22:23:51.541  INFO 1 --- [           main] o.s.boot.web.servlet.RegistrationBean    : Servlet CXFServlet was not registered (possibly already registered?)
    2020-10-02 22:23:51.885  INFO 1 --- [           main] o.a.c.w.s.f.ReflectionServiceFactoryBean : Creating Service {http://service.datajdbc.jpworks.com/}EmployeeEndpointService from class com.jpworks.employee.EmployeeService
    2020-10-02 22:23:52.455  INFO 1 --- [           main] org.apache.cxf.endpoint.ServerImpl       : Setting the server's publish address to be /service/employee
    2020-10-02 22:23:52.646  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-10-02 22:23:52.870  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8081 (http) with context path ''
    2020-10-02 22:23:52.889  INFO 1 --- [           main] com.jpworks.datajdbc.MainApplication     : Started MainApplication in 4.018 seconds (JVM running for 4.625)
    
    사용 가능한 끝점을 얻으려면 브라우저에 다음과 같이 쓰십시오.
    http://localhost:8081/soap
    

    서비스의 wsdl을 가져오려면 브라우저에 다음과 같이 쓰십시오.
    http://localhost:8081/soap/service/employee?wsdl
    

    우리는 POST 호출을 사용하여 Http 클라이언트(예를 들어 PostMan이나 Jmeter)를 사용하여 응용 프로그램을 테스트할 수 있다. (테스트 용례에 대해 응용 프로그램은 다시 발표된 라이브러리를 사용한다.)그러나 저는 전통적인 SoapUI를 사용할 것입니다. 서비스의 단점은 다음과 같습니다. http://localhost:8081/soap/service/employee.
    애플리케이션 콘솔에서 기록된 요청/응답을 볼 수 있습니다.
    2020-10-06 15:36:01.396  INFO 102696 --- [           main] com.jpworks.datajdbc.MainApplication     : Started MainApplication in 3.104 seconds (JVM running for 3.988)
    2020-10-06 15:36:23.958  INFO 102696 --- [nio-8081-exec-1] o.a.c.s.EmployeeServicePortType.REQ_IN   : REQ_IN
        Address: http://localhost:8081/soap/service/employee
        HttpMethod: POST
        Content-Type: text/xml;charset=UTF-8
        ExchangeId: 872e1281-4545-45ad-9871-331d96c450cf
        ServiceName: EmployeeEndpointService
        PortName: EmployeeEndpointPort
        PortTypeName: EmployeeServicePortType
        Headers: {SOAPAction="http://www.jpworks.com/employee/GetEmployeesByName", host=localhost:8081, connection=Keep-Alive, content-type=text/xml;charset=UTF-8, Content-Length=273, accept-encoding=gzip,deflate, user-agent=Apache-HttpClient/4.5.5 (Java/12.0.1)}
        Payload: <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:emp="http://www.jpworks.com/employee">
       <soapenv:Header/>
       <soapenv:Body>
          <emp:EmployeeByNameRequest firstname="John" lastname="Miller"/>
       </soapenv:Body>
    </soapenv:Envelope>
    
    2020-10-06 15:36:23.995  INFO 102696 --- [nio-8081-exec-1] o.a.c.s.E.RESP_OUT                       : RESP_OUT
        Address: http://localhost:8081/soap/service/employee
        Content-Type: text/xml
        ResponseCode: 200
        ExchangeId: 872e1281-4545-45ad-9871-331d96c450cf
        ServiceName: EmployeeEndpointService
        PortName: EmployeeEndpointPort
        PortTypeName: EmployeeServicePortType
        Headers: {}
        Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><EmployeesResponse xmlns="http://www.jpworks.com/employee"><employee id="1" firstname="Jeffery" lastname="Lewis" birthdate="2000-01-01" gender="M"/><employee id="2" firstname="Francis" lastname="Stevens" birthdate="1999-01-01" gender="M"/></EmployeesResponse></soap:Body></soap:Envelope>
    

    결론


    본고는 모든 남겨진 응용 프로그램의 이동 경로에서 SOAP를 도구로 간주하는 것이 왜 여전히 중요한지 설명한다.우리는 데모 WSDL을 사용하고 관련 부분을 되돌아보았다.우리는 Apache CXF를 사용하여 SOAP를 구현하고 구성하는 방법을 배웠습니다.
    질문이 있으시면 언제든지 댓글로 질문해 주십시오.읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기