CXF 또는 JAX-WS 생성 웹 서비스 클라이언트에서 WSDL 위치를 지정하지 않아도되는 방법은 무엇입니까?


165

maven을 통해 CXF에서 wsdl2java를 사용하여 웹 서비스 클라이언트를 생성하면 (wsimport와 비슷한 것을 생성) maven을 통해 서비스는 다음과 같은 코드로 시작됩니다.

@WebServiceClient(name = "StatusManagement", 
                  wsdlLocation = "c:/some_absolute_path_to_a_wsdl_file.wsdl",
                  targetNamespace = "http://tempuri.org/") 
public class StatusManagement extends Service {

    public final static URL WSDL_LOCATION;
    public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
    public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
    static {
        URL url = null;
        try {
            url = new URL("c:/some_absolute_path_to_a_wsdl_file.wsdl");
        } catch (MalformedURLException e) {
            System.err.println("Can not initialize the default wsdl from c:/some_absolute_path_to_a_wsdl_file.wsdl");
            // e.printStackTrace();
        }
        WSDL_LOCATION = url;
    }

하드 코딩 된 절대 경로는 정말 짜증납니다. 생성 된 클래스는 내 컴퓨터 이외의 다른 컴퓨터에서는 작동하지 않습니다.

첫 번째 아이디어는 WSDL 파일 (가져 오는 모든 것, 다른 WSDL 및 XSD)을 jar 파일의 어딘가에 놓고 클래스 경로를 지정하는 것입니다. 그러나 우리는 이것을 피하고 싶습니다. 이 모든 것이 WSDL과 XSD에 기반한 CXF와 JAXB에 의해 생성되었으므로 런타임에 WSDL을 알 필요가 없습니다.

wsdlLocation 속성은 WSDL 위치를 재정의하기위한 것이며 (적어도이 위치에서 읽은 것임) 기본값은 ""입니다. maven을 사용 <wsdlLocation></wsdlLocation>하고 있으므로 소스 생성기가 wsdlLocation을 비워 두도록 강제로 CXF 구성에 포함 하려고 시도했습니다. 그러나 이는 XML 태그가 비어 있기 때문에 단순히 무시합니다. 우리는를 사용하여 정말 못생긴 부끄러운 해킹을했습니다 <wsdlLocation>" + "</wsdlLocation>.

다른 장소도 변경됩니다.

@WebServiceClient(name = "StatusManagement", 
                  wsdlLocation = "" + "",
                  targetNamespace = "http://tempuri.org/") 
public class StatusManagement extends Service {

    public final static URL WSDL_LOCATION;
    public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
    public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
    static {
        URL url = null;
        try {
            url = new URL("" + "");
        } catch (MalformedURLException e) {
            System.err.println("Can not initialize the default wsdl from " + "");
            // e.printStackTrace();
        }
        WSDL_LOCATION = url;
    }

그래서 내 질문은 :

  1. 모든 클래스가 CXF 및 JAXB에 의해 생성 된 경우에도 실제로 WSDL 위치가 필요합니까? 그렇다면 왜 그렇습니까?

  2. 실제로 WSDL 위치가 필요하지 않은 경우 CXF가이를 생성하지 않고 완전히 피하지 않도록하는 적절하고 깨끗한 방법은 무엇입니까?

  3. 그 해킹으로 어떤 부작용이 생길 수 있습니까? 우리는 여전히 어떤 일이 일어나는지 확인하기 위해 테스트 할 수 없으므로 누군가 미리 말할 수 있다면 좋을 것입니다.

답변:


206

나는 오늘이 질문에 대한 정답을 찾아 냈다.

<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>${project.basedir}/src/main/resources/wsdl/FooService.wsdl</wsdl>
                        <wsdlLocation>classpath:wsdl/FooService.wsdl</wsdlLocation>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
</plugin>

나는의 값 접두어 것을주의 wsdlLocation와를 classpath:. 이는 플러그인이 wsdl이 절대 경로 대신 클래스 경로에 있음을 알려줍니다. 그런 다음 다음과 유사한 코드를 생성합니다.

@WebServiceClient(name = "FooService", 
                  wsdlLocation = "classpath:wsdl/FooService.wsdl",
                  targetNamespace = "http://org/example/foo") 
public class Foo_Service extends Service {

    public final static URL WSDL_LOCATION;

    public final static QName SERVICE = new QName("http://org/example/foo", "Foo");
    public final static QName FooSOAPOverHTTP = new QName("http://org/example/foo", "Foo_SOAPOverHTTP");
    static {
        URL url = Foo_Service.class.getClassLoader().getResource("wsdl/FooService.wsdl");
        if (url == null) {
            java.util.logging.Logger.getLogger(Foo_Service.class.getName())
                .log(java.util.logging.Level.INFO, 
                     "Can not initialize the default wsdl from {0}", "classpath:wsdl/FooService.wsdl");
        }       
        WSDL_LOCATION = url;
    }

이것은 버전 2.4.1 이상 cxf-codegen-plugin에서만 작동합니다.


8
CXF 대신 JAX Maven Plugin을 사용하는 classpath:경우 <wsdlLocation...줄 에서 생략 하십시오 .
Twilite

위의 방법으로 생성 된 코드에서 네임 스페이스 문제에 직면 한 사람이 있습니까?
Narendra Jaggi 님이

여러 wsdl이있는 경우 각 wsdl을 개별적으로 나열해야합니까? 그것을 피할 수 있습니까?
pitseeker

21

우리는 사용

wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"

즉, 클래스 경로에 상대적인 경로를 사용하십시오.

marshal / unmarshal 중 메시지 유효성 검사를 위해 런타임에 WSDL이 필요할 수 있다고 생각합니다.


17

사용하는 사람들을 위해 org.jvnet.jax-ws-commons:jaxws-maven-plugin빌드 타임에 WSDL에서 클라이언트를 생성합니다 :

  • WSDL을 어딘가에 배치하십시오 src/main/resources
  • 하지 접두사wsdlLocationclasspath:
  • 접두사 wsdlLocation/

예:

  • WSDL은 /src/main/resources/foo/bar.wsdl
  • 구성 jaxws-maven-plugin<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory><wsdlLocation>/foo/bar.wsdl</wsdlLocation>

왜 "클래스 경로와 그러나 wsdlLocation을"접두어를 사용, 나는 그것을 사용하고 그것은 작동하지 않습니다
모하마드 사데 Rafiei

9

1) 경우에 따라 가능합니다. WSDL에 정책과 같은 것들이 포함되어 런타임 동작을 지시하는 경우 런타임에 WSDL이 필요할 수 있습니다. 정책 관련 사물 등에는 아티팩트가 생성되지 않습니다. 또한 일부 모호한 RPC / Literal 사례에서는 필요한 모든 네임 스페이스가 생성 된 코드 (사양에 따라)로 출력되는 것은 아닙니다. 따라서 wsdl이 필요합니다. 그러나 모호한 경우.

2) 나는 무언가가 효과가 있다고 생각했다. CXF의 버전은 무엇입니까? 그것은 버그처럼 들립니다. 빈 문자열을 공백으로 시도해 볼 수 있습니다. 그것이 작동하는지 확실하지 않습니다. 즉, 코드에서 WSDL URL을 사용하고 null을 전달하는 생성자를 사용할 수 있습니다. wsdl은 사용되지 않습니다.

3) 위의 제한 사항.


최신 CXF 2.3.1입니다. 8 일 전에 출시되었습니다. null을 전달하는 것이 좋습니다. 이전에 분명한 대답을 보았습니다. 나는 여전히 공간을 시험해 볼 것입니다.
Victor Stafusa

아니요, 빈 공간은 아무것도 아닌 것과 같습니다. 즉, XML 태그는 완전히 무시됩니다.
Victor Stafusa

5

나는 생성 할 수 있었다

static {
    WSDL_LOCATION = null;
}

wsdlurl에 대해 null을 갖도록 pom 파일을 구성하여 :

    <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                    <sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
                    <wsdlOptions>
                        <wsdlOption>
                            <wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
                            <extraargs>
                                <extraarg>-client</extraarg>
                                <extraarg>-wsdlLocation</extraarg>
                                <wsdlurl />
                            </extraargs>
                        </wsdlOption>
                    </wsdlOptions>
                </configuration>
                <goals>
                    <goal>wsdl2java</goal>
                </goals>
            </execution>
        </executions>
    </plugin>

2
이 솔루션은 CXF 3.1.0에서 작동하지 않았습니다. 오류 org.apache.cxf.tools.common.toolspec.parser.BadUsageException를 가지고 : 예기치 않은 옵션 : -wsdlLocation
샨 드루에게

4

wsdl2java 사용을 피할 수 있습니까? CXF FrontEnd API를 즉시 사용하여 SOAP 웹 서비스를 호출 할 수 있습니다. 클라이언트 쪽에서 SEI와 VO를 만들어야한다는 것이 유일한 문제입니다. 다음은 샘플 코드입니다.

package com.aranin.weblog4j.client;

import com.aranin.weblog4j.services.BookShelfService;
import com.aranin.weblog4j.vo.BookVO;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class DemoClient {
    public static void main(String[] args){
        String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice";
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setServiceClass(BookShelfService.class);
        factory.setAddress(serviceUrl);
        BookShelfService bookService = (BookShelfService) factory.create();

        //insert book
        BookVO bookVO = new BookVO();
        bookVO.setAuthor("Issac Asimov");
        bookVO.setBookName("Foundation and Earth");

        String result = bookService.insertBook(bookVO);

        System.out.println("result : " + result);

        bookVO = new BookVO();
        bookVO.setAuthor("Issac Asimov");
        bookVO.setBookName("Foundation and Empire");

        result = bookService.insertBook(bookVO);

        System.out.println("result : " + result);

        bookVO = new BookVO();
        bookVO.setAuthor("Arthur C Clarke");
        bookVO.setBookName("Rama Revealed");

        result = bookService.insertBook(bookVO);

        System.out.println("result : " + result);

        //retrieve book

        bookVO = bookService.getBook("Foundation and Earth");

        System.out.println("book name : " + bookVO.getBookName());
        System.out.println("book author : " + bookVO.getAuthor());

    }
}

전체 자습서는 여기 http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/에서 볼 수 있습니다 .


2
WSDL 파일은 매우 복잡하여 호환성을 보장하는 방법으로 자동 생성을 사용했습니다. 자동 생성은 상당히 복잡한 VO 및 SEI를 만들었습니다. 자동 생성 된 도메인 개체와 완전히 분리 된 별도의 도메인 개체 집합을 사용하기로 결정 했으므로 자동 생성을 방해하지 않았거나 제한을 받지도 않았습니다. 자동 생성 된 VO는 서비스 통신 환경에서만 사용되었으며 가능한 한 수명이 짧았습니다. 다시 말해, 우리의 관심사 중 하나는 모든 VO를 수동으로 코딩하고 관리 할 필요가 없다는 것입니다.
Victor Stafusa

2
나는 수동으로 VO를 유지하는 것은 시간 낭비이며, 차이의 위험이 다소 눈에 잘 띄고 자격이 있기 때문에 Victor에 동의합니다. 그것이 wsdl2java의 목적과 정확히 일치합니다. 그것이 유용하고 안전한 이유입니다!
Donatello

4

CXF 3.1.7 업데이트

내 경우에는 WSDL 파일을 src/main/resources Eclipse의 Srouces 에이 경로를 추가했습니다 (프로젝트-> 빌드 경로-> 빌드 경로 구성 ...-> 소스 [탭]-> 폴더 추가를 마우스 오른쪽 버튼으로 클릭하십시오).

다음은 내 pom파일 모양과 wsdlLocation 필요한 옵션 이 없음을 보여줍니다 .

       <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/cxf</sourceRoot>
                        <wsdlOptions>
                            <wsdlOption>
                                <wsdl>classpath:wsdl/FOO_SERVICE.wsdl</wsdl>
                            </wsdlOption>
                        </wsdlOptions>
                    </configuration>
                    <goals>
                        <goal>wsdl2java</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

그리고 여기 생성 된 서비스가 있습니다. 알 수 있듯이 URL은 절대 파일 경로가 아닌 ClassLoader에서 가져옵니다.

@WebServiceClient(name = "EventService", 
              wsdlLocation = "classpath:wsdl/FOO_SERVICE.wsdl",
              targetNamespace = "http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/") 
public class EventService extends Service {

public final static URL WSDL_LOCATION;

public final static QName SERVICE = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventService");
public final static QName EventPort = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventPort");
static {
    URL url = EventService.class.getClassLoader().getResource("wsdl/FOO_SERVICE.wsdl");
    if (url == null) {
        java.util.logging.Logger.getLogger(EventService.class.getName())
            .log(java.util.logging.Level.INFO, 
                 "Can not initialize the default wsdl from {0}", "classpath:wsdl/FOO_SERVICE.wsdl");
    }       
    WSDL_LOCATION = url;   
}

<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> 클래스 경로에 모든 .wsdl 파일을 포함시킨 다음 어떻게 wsdl 위치를 지정하여 생성 된 모든 .java 파일을 각각의 .wsdl 경로에 포함시킬 수 있습니까? 미리 감사드립니다. @Mazy
Khalid Shah

2

진심으로, 최고의 대답은 나를 위해 작동하지 않습니다. cxf.version 2.4.1 및 3.0.10을 시도했습니다. 매번 wsdlLocation으로 절대 경로를 생성하십시오.

내 해결책은 with 의 wsdl2java명령 을 사용하는 것 apache-cxf-3.0.10\bin\입니다 -wsdlLocation classpath:wsdl/QueryService.wsdl.

세부 묘사:

    wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl

0

@Martin Devillers 솔루션은 정상적으로 작동합니다. 완전성을 위해 아래 단계를 제공하십시오.

  1. wsdl을 다음과 같은 자원 디렉토리에 넣으십시오. src/main/resource
  2. pom 파일에서 아래와 같이 wsdlDirectory와 wsdlLocation (wsdlLocation의 시작 부분을 놓치지 마십시오)을 추가하십시오. wsdlDirectory가 코드를 생성하는 데 사용되고 wsdlLocation은 런타임에 동적 프록시를 작성하는 데 사용됩니다.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. 그런 다음 Java 코드 (비 인수 생성자)에서 :

    MyPort myPort = new MyPortService().getMyPort();
  4. 다음은 생성 된 코드에 유창한 API가있는 pom 파일의 전체 코드 생성 부분입니다.

    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-fluent-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-tools</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    
    <executions>
        <execution>
            <id>wsdl-to-java-generator</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <xjcArgs>
                    <xjcArg>-Xfluent-api</xjcArg>
                </xjcArgs>
                <keep>true</keep>
                <wsdlDirectory>src/main/resources/package</wsdlDirectory>
                <wsdlLocation>/package/my.wsdl</wsdlLocation>
                <sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
                <packageName>full.package.here</packageName>
            </configuration>
        </execution>
    </executions>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.