CXF veya JAX-WS tarafından oluşturulan bir web hizmeti istemcisinde WSDL konumunu belirtme gereksinimi nasıl önlenir?


165

Maven üzerinden (wsimport benzeri bir şey üretir) wsdl2java kullanarak bir webservice istemci oluşturduğumda, hizmetlerim böyle kodlarla başlar:

@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;
    }

Sabit kodlu mutlak yol gerçekten berbat. Oluşturulan sınıf benimkinden başka hiçbir bilgisayarda çalışmaz.

İlk fikir, WSDL dosyasını (artı aldığı her şeyi, diğer WSDL'leri ve XSD'leri) bir jar dosyasına bir yere koymak ve sınıflamaktır. Ama bundan kaçınmak istiyoruz. Bütün bunlar WSDL ve XSD'lere dayanan CXF ve JAXB tarafından üretildiğinden, çalışma zamanında WSDL'yi bilmenin bir anlamı yok.

WsdlLocation özniteliği WSDL konumunu geçersiz kılmak için tasarlanmıştır (en azından bir yerde okudum budur) ve varsayılan değer "". Maven kullandığımız <wsdlLocation></wsdlLocation>için, kaynak oluşturucuyu wsdlLocation boş bırakmaya zorlamak için CXF yapılandırmasının içine dahil etmeye çalıştık . Ancak, bu boş olduğu için XML etiketini yoksaymasını sağlar. Kullanarak gerçekten çirkin utanç verici bir kesmek yaptık <wsdlLocation>" + "</wsdlLocation>.

Bu, diğer yerleri de değiştirir:

@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;
    }

Yani, sorularım:

  1. Tüm sınıflar CXF ve JAXB tarafından oluşturulmuş olsa bile gerçekten bir WSDL konumuna ihtiyacımız var mı? Evet ise, neden?

  2. WSDL konumuna gerçekten ihtiyacımız yoksa, CXF'nin onu üretmemesini ve tamamen kaçınmasını sağlamanın doğru ve temiz yolu nedir?

  3. Bu hack ile ne gibi kötü yan etkiler elde edebiliriz? Ne olduğunu görmek için bunu hala test edemiyoruz, bu yüzden birisi önceden söyleyebilirse, iyi olurdu.

Yanıtlar:


206

Sonunda bugün bu sorunun doğru cevabını buldum.

<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>

Ben değeri öneki olduğunu Bildirimi wsdlLocationile classpath:. Bu, eklentiye wsdl'nin mutlak bir yol yerine sınıfyolunda olacağını söyler. Sonra buna benzer kod üretecektir:

@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;
    }

Bunun yalnızca 2.4.1 veya daha yeni bir sürüm olan cxf-codegen-plugin ile çalıştığını unutmayın.


8
CXF yerine JAX Maven Eklentisini kullanırken classpath:, <wsdlLocation...satırı atlayın .
Twilite

Yukarıdaki yöntemle oluşturulan kodda ad alanı sorunu olan var mı?
Narendra Jaggi

Birkaç tane varsa, her wsdl ayrı ayrı listelemek zorunda mı? Bundan kaçınmak mümkün mü?
pitseeker

21

Kullanırız

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

Başka bir deyişle, sınıfyoluna göre bir yol kullanın.

Mareşal / mareşal sırasında mesajların doğrulanması için WSDL'nin çalışma zamanında gerekli olabileceğine inanıyorum.


17

org.jvnet.jax-ws-commons:jaxws-maven-pluginOluşturma zamanında WSDL'den bir istemci oluşturmak isteyenler için:

  • WSDL'yi, src/main/resources
  • Do not önünewsdlLocation ileclasspath:
  • İle ön ek wsdlLocationyap/

Misal:

  • WSDL, /src/main/resources/foo/bar.wsdl
  • Yapılandırma jaxws-maven-pluginile <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>ve<wsdlLocation>/foo/bar.wsdl</wsdlLocation>

neden "wsdlLocation with classpath" önekini kullanmıyorum, bunu kullanıyorum ve çalışıyor
Mohammad Sadegh Rafiei

9

1) Bazı durumlarda, evet. WSDL, İlkeler gibi ve çalışma zamanı davranışını yönlendiren şeyler içeriyorsa, çalışma zamanında WSDL gerekebilir. Politikaya ilişkin şeyler ve benzerleri için yapay ürünler oluşturulmaz. Ayrıca, bazı belirsiz RPC / Değişmez durumlarda, gereken tüm ad alanları üretilen kodda (spesifikasyon başına) çıkarılmaz. Böylece, wsdl onlar için gerekli olacaktı. Olsa da karanlık vakaları.

2) Böyle bir şeyin işe yarayacağını düşündüm. Hangi CXF sürümü? Kulağa bir böcek gibi geliyor. Orada boş bir dize deneyebilirsiniz (sadece boşluklar). Bunun işe yarayıp yaramadığından emin değilim. Bununla birlikte, kodunuzda WSDL URL'sini alan ve yalnızca null ileten yapıcıyı kullanabilirsiniz. Wsdl kullanılmaz.

3) Sadece yukarıdaki sınırlamalar.


En yeni CXF 2.3.1'dir. Sadece 8 gün önce yayınlandı. Null değerinin geçmesi iyi bir fikirdir, daha önce bu açık cevabı görmeliydim. Hala boşlukları deneyeceğim.
Victor Stafusa

Hayır, boş alanlar hiçbir şeyle aynı şeyi yapmaz. yani: XML etiketi tamamen yok sayılır.
Victor Stafusa

5

Üretebildim

static {
    WSDL_LOCATION = null;
}

pom dosyasını wsdlurl için null değerine sahip olacak şekilde yapılandırarak:

    <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
Bu çözüm benim için CXF 3.1.0 ile işe yaramadı. hata aldım org.apache.cxf.tools.common.toolspec.parser.BadUsageException: Beklenmedik seçenek: -wsdlLocation
Chandru

4

Wsdl2java kullanmaktan kaçınmanız mümkün mü? SOAP Web Hizmetinizi çağırmak için CXF FrontEnd API'lerini hemen kullanabilirsiniz. Tek yakalama, SEI ve VO'larınızı müşteri ucunuzda oluşturmanız gerektiğidir. İşte bir örnek kod.

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());

    }
}

Eğiticinin tamamını burada görebilirsiniz http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/


2
WSDL dosyaları büyük ölçüde karmaşıktı, bu yüzden uyumluluk sağlamak için otojenerasyon kullandık. Otojenerasyon, eşit derecede büyük ölçüde karmaşık VO'lar ve SEI'ler yarattı. Otomatik olarak oluşturulmuş olanlara tamamen ayrıştırılmış ayrı bir etki alanı nesneleri kümesi seçtik, bu nedenle otomatik üretime müdahale etmedik ya da bununla kısıtlanmadık ya da onun tarafından kısıtlanmadık. Otomatik üretilen VO'lar sadece hizmet iletişimi bağlamında kullanıldı ve mümkün olduğunca kısa ömürlü olduk. Başka bir deyişle, endişemizden biri, tüm VO'ları manuel olarak kodlama ve yönetme ihtiyacından kaçınmaktır.
Victor Stafusa

2
Victor'a katılıyorum, çünkü VO'yu manuel olarak korumak zaman kaybı olabilir ve daha fazla veya daha az görünür ve nitelikli farklılıklar riski olabilir .. tam olarak wsdl2java'nın amacı, bu yüzden yararlı ve güvenli!
Donatello

4

CXF 3.1.7 Güncellemesi

Benim durumumda WSDL dosyalarını src/main/resources ve Eclipse'deki Srouces'ime bu yolu ekledim (Project-> Build Path -> Build Path'ı Yapılandır ...-> Source [Tab] -> Klasör Ekle).

İşte benim pomdosya gibi görünüyor ve görülebileceği gibi hiçbir wsdlLocation seçenek gerekli:

       <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>

Ve burada üretilen Hizmet. Görüldüğü gibi URL, Mutlak Dosya Yolundan değil ClassLoader'dan alınır

@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> Sınıf yolundaki tüm .wsdl dosyalarını dahil sonra nasıl wsdl konumu belirtebilirsiniz böylece her oluşturulan .java dosyası ilgili .wsdl yolu contaon? Şimdiden teşekkürler. @Mazy
Khalid Shah

2

Cidden, en iyi cevap benim için çalışmıyor. cxf.version 2.4.1 ve 3.0.10'u denedi. ve her seferinde wsdlLocation ile mutlak yol üretir.

Benim çözüm kullanmaktır wsdl2javakomutunu apache-cxf-3.0.10\bin\ ile -wsdlLocation classpath:wsdl/QueryService.wsdl.

Detay:

    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 çözümü iyi çalışıyor. Tamlık için aşağıdaki adımları uygulayın:

  1. Wsdl'nizi aşağıdaki gibi kaynak dizinine koyun: src/main/resource
  2. Pom dosyasında, aşağıdaki gibi hem wsdlDirectory hem de wsdlLocation öğelerini ekleyin (wsdlLocation öğesinin başında / kaçırmayın). WsdlDirectory kodu oluşturmak için kullanılırken, wsdlLocation çalışma zamanında dinamik proxy oluşturmak için kullanılır.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Sonra java kodunuzda (no-arg yapıcısı ile):

    MyPort myPort = new MyPortService().getMyPort();
  4. İşte pom kodundaki tam kod üretme kısmı, üretilen kodda akıcı api.

    <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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.