본문 바로가기

Spring/Spring

Spring+Apache cxf를 이용한 SOAP XML 통신 튜토리얼 (RESTful api? Web Services, SOAP UI)

반응형

Spring + Apache cxf로 SOAP 통신하기 

apache cxf 프레임워크를 이용하면 SOAP 통신을 할 수 있다.

apache cxf의 장점은 spring과의 연동이 가능하다는 점이다.

각설하고 다소 유행(RESTful API - JSON)에 뒤떨어지지만 산업에서 표준 문제로 사용하는 SOAP를 이용해서 XML을 만들어본다. (웹에서 OPEN API, oauth2.0 등에 사용할꺼면 json을 이용하는 RESTful API를 쓰는게 맞다)


아무것도 모르는데 일단 따라하는 tutorial

* 개발 환경

JDK1.8 / Spring 4.1.7 / apache cxf 2.6.2

 

1. spring legacy project 생성

2. apache cxf 2.6.2 버전 다운로드 (https://archive.apache.org/dist/cxf/2.6.2/)

    - 이상한거 다운로드하면 안되고 제일 밑에 apache-cxf.zip 다운로드하면 됨.

3. web services에 apache cxf 등록

이클립스 상단 메뉴에서 window > Perferences에 Web Services에 Server and Runtime 탭에서 Web service runtime을 Apache CXF 2.x로 변경

그 다음에 똑같이 Web Services에 CXF 2.x Perferences 탭에서 Add... 버튼을 누른 후 Browse를 눌러서 아까 2번에서 다운받은 apache-cxf-2.6.2.zip을 압축을 풀은 디렉토리를 잡아줌

그러면 알아서 Version이랑 Type이 생성됨. finish하고 Apply and Close

4. pom.xml에 dependency 추가

apache cxf 사용을 위한 메이븐 디펜던시 추가

5. web.xml 수정

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        /WEB-INF/spring/appServlet/cxf-context.xml
        </param-value>
    </context-param>
    
    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!-- cxf servlet -->
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    
    <!-- encoding filter -->
     <filter>
      <filter-name>encodingFilter</filter-name>
      <filter-class>
       org.springframework.web.filter.CharacterEncodingFilter
      </filter-class>
      <init-param>
       <param-name>encoding</param-name>
       <param-value>UTF-8</param-value>
      </init-param>
      <init-param>
       <param-name>forceEncoding</param-name>
       <param-value>true</param-value>
      </init-param>
     </filter>
     
     <filter-mapping>
      <filter-name>encodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
     </filter-mapping>

</web-app>

기존에 있던 servlet 지워버리고 cxf servlet 만듦

+ cxf 설정이 있는 cxf-context.xml을 연결시킴 (있다가 cxf-context.xml 만들꺼임, 이름 마음대로 해도 됨)

+ encoding 설정 (해도 되고 안 해도 됨)

6. cxf-context.xml 생성

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 <import resource="classpath:META-INF/cxf/cxf.xml" />
 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> 

 <jaxws:endpoint 
  id="deptProcess" 
  implementor="net.jeong.pro.DeptProcessImpl" 
  address="/DeptProcess" />
   
</beans>
 

import로 cxf 자원들 불러오고 endpoint만들어서 어떤 address로 왔을 때 어떤 인터페이스에서 제공할 것인지 설정.

7. 본인 패키지에 POJO로 DI받을 것(DeptVo) 준비 (여기서는 부서정보)

package net.jeong.pro;

import java.io.Serializable;

public class DeptVo implements Serializable {
    private int deptNo;
    private String deptName;
    private String location;
    public int getDeptNo() {
        return deptNo;
    }
    public void setDeptNo(int deptNo) {
        this.deptNo = deptNo;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public String getLocation() {
        return location;
    }
    public void setLocation(String location) {
        this.location = location;
    }
}
 

딱, net.jeong.pro 패키지에 뭐 DAO니 DTO니 안만들고 바로 class만들면 됨. 제일 밑에 디렉토리 구조 있음.

(부서번호, 부서명, 위치) getter/setter

8. 인터페이스 만들기 (DeptProcess)

package net.jeong.pro;

import java.util.List;

import javax.jws.WebService;
@WebService
public interface DeptProcess {
    List<DeptVo> processDept();
}
 

,9. 인터페이스 구현하기 (DeptProcessImpl)

package net.jeong.pro;

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

import javax.jws.WebService;
@WebService(endpointInterface = "net.jeong.pro.DeptProcess")
public class DeptProcessImpl implements DeptProcess {
    //20개 부서 데이터 생성
    @Override
    public List<DeptVo> processDept() {
        System.out.println("웹서비스호출");
        List<DeptVo> deptVos = new ArrayList<DeptVo>();
        for(int i=0;i<20;i++) {
            DeptVo deptVo = new DeptVo();
            deptVo.setDeptName("부서명"+i);
            deptVo.setDeptNo(i);
            deptVo.setLocation("지역"+i);
            deptVos.add(deptVo);
        }
        return deptVos;
    }
}
 

* 프로젝트 구조

별로 건드린 것 없이 그대로 만들고 적으면 됨.


결과화면

 

localhost:8080으로 갔을 때와 localhost:8080/DeptProcess?wsdl로 갔을 때

apache cxf는 wsdl을 저절로 만들어줘서 편리

실제로 내가 작성한 내용인 부서 20개를 보려면 SOAP UI라는 TEST 프로그램을 설치해야함.

(https://www.soapui.org/downloads/latest-release.html)


SOAP UI를 통해 데이터 확인해보기

SOAP UI 사용법은 간단하게 정리함.

1. file > new SOAP Project 클릭

2. 2화면이 뜸

3. initial WSDL 란에다가 아까 우리가 확인하고 싶었던 WSDL, "localhost:8080/DeptProcess?wsdl" 이것을 복사 붙여넣기 한뒤 OK버튼

4. 왼쪽에 request 1 이런식으로 나온 것을 더블 클릭

5. 왼쪽 상단에 실행 화살표 누르면 옆에 XML이 쭉 나옴(부서명1,부서번호,지역1)

 

반응형
  • 익명 2018.07.16 17:21

    비밀댓글입니다

  • 익명 2018.11.14 16:15

    비밀댓글입니다

  • 1q2w3e4r 2018.12.23 15:06

    잘 봤습니다

  • 재능인 2019.08.10 11:13

    잘봤습니다.
    질문하나드려도될까요?
    SOAP/WSDL/UDDI를 사용해서 웹서비스를 만들려 하는중에 본 게시글을 찾아 보게되었습니다

    SOAP/WSDL을 사용해서 웹서비스는 구현하였지만 UDDI라는 것에대해 개념만 잡고있을뿐 실체를 만들어보진 못했어요.
    WSDL에 기술된 서비스의 내용을 UDDI 저장소에 등록하고 소비자는 등록된 서비스를 검색하고 사용한다.
    라는것을 가능하게 해주는 것이 UDDI라고 저는 생각하고있습니다.

    UDDI를 개발하기위해선 어떻게해야하나요..?
    따로 서버를 구축해야하는건가요?

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2019.08.10 21:58 신고

      UDDI는 저도 잘 모르겠습니다...

      github나 maven repository 처럼 대형IT기업이나 대형 비영리단체들이 관리하는게 일반적이지 않았을까 합니다.
      (SOAP은 사양길로...)

      굳이 만드시려면 다른 서버가 필요할 것으로 보입니다.
      WSDL을 제공하는게 목적이면 해당 본문에도 있듯 URL을 제공하는것도 나쁘지 않아보입니다...

  • 꽃보다정 2019.12.12 09:04

    아주 잘 됩니다 깔끔한 정리 감사합니다.

    이게 잘되면 내가 원하는 data를 송신할수 있는 송신 서버가 완성된거고

    수신을 하려면 어떻게 해야되는지도 설명해주시면 안될까요?

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2019.12.12 09:48 신고

      클라이언트를 만들고 싶다는 말씀 같습니다.
      클라이언트를 만드려면 code-generator가 필요합니다.
      위에서 서버가 제공하는 wsdl을 보고 그에 맞는 클라이언트 코드는 자동으로 만들어주는 것입니다.
      저는 메이븐에서 cxf-codegen-plugin 이라는 플러그인을 이용했습니다.
      서버가 제공하는 wsdl파일과 해당 플러그인의 적절한 옵션(어떤형태로 클래스를 만들것인지 등)을 적어서 코드로 떨구면 됩니다.
      상세코드까지 다 적긴 애매하고 wsdl2java를 검색해보시길 바랍니다.
      마찬가지로 다른 클라이언트를 만들고 싶으시면 wsdl2js, wsdl2go 등을 검색하시면 됩니다.

  • 아마추어 2021.06.08 00:17

    http://localhost/DeptProcess?wsdl
    No service was found. 전 이렇게 나오네요... ㅜㅜ
    web.xml에는
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/appServlet/cxf-context.xml</param-value>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!-- cxf servlet -->
    <servlet>
    <servlet-name>cxf</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>cxf</servlet-name>
    <url-pattern>/*</url-pattern>
    </servlet-mapping>
    위와 같이 설정했는데 설정이 잘못된걸까요?
    다른 부분은 위와 같이 그대로 설정한거 같아서요

    • Favicon of https://jeong-pro.tistory.com BlogIcon JEONG_AMATEUR 2021.06.08 08:26 신고

      Dispatcher Servlet이 "/" 패턴이고,
      CXFServlet이 "/*" 패턴이어서 문제가 되지 않았을까 싶네요
      서로 mapping 패턴이 겹치지 않게 바꿔보시면 좋을 것 같습니다.

      아래 링크 참조
      https://stackoverflow.com/questions/18876395/cxf-and-spring-mvc-no-service-was-found

태그