Klient JAX-WS: jaka jest poprawna ścieżka dostępu do lokalnego WSDL?


86

Problem polega na tym, że muszę zbudować klienta usługi sieciowej na podstawie otrzymanego pliku. Zapisałem ten plik w lokalnym systemie plików i chociaż trzymam plik WSDL w odpowiednim folderze systemu plików, wszystko jest w porządku. Kiedy wdrażam go na serwerze lub usuwam WSDL z folderu systemu plików, proxy nie może znaleźć pliku WSDL i generuje błąd. Przeszukałem sieć i znalazłem następujące posty, ale nie udało mi się to zrobić:
JAX-WS Ładowanie WSDL z jar
http://www.java.net/forum/topic/glassfish/metro -and-jaxb / client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

Używam NetBeans 6.1 (jest to starsza aplikacja, którą muszę zaktualizować za pomocą tego nowego klienta usługi sieciowej). Poniżej znajduje się klasa proxy JAX-WS:

    @WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl")
public class SOAService
    extends Service
{

    private final static URL SOASERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
            url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        SOASERVICE_WSDL_LOCATION = url;
    }

    public SOAService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public SOAService() {
        super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService"));
    }

    /**
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP() {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class);
    }

    /**
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features);
    }

}


Oto mój kod do korzystania z serwera proxy:

   WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class);
   // trying to replicate proxy settings
   URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "."
   URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl");
   //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); 
   SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name()));
   proxy = serviceObj.getSOAServiceSOAP();
   /* baseUrl;

   //classes\com\ibm\eci\soaservice
   //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl");

   proxy = new SOAService().getSOAServiceSOAP();*/
   //updating service endpoint 
   Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
   ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
   ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl);

NetBeans umieścił kopię WSDL w web-inf / wsdl / client / SOAService , więc nie chcę też dodawać jej do META-INF . Klasy usług znajdują się w WEB-INF / classes / com / ibm / eci / soaservice /, a zmienna baseurl zawiera pełną ścieżkę do systemu plików (c: \ path \ to \ the \ project ... \ soaservice). Powyższy kod wywołuje błąd:

javax.xml.ws.WebServiceException: nie można uzyskać dostępu do WSDL pod adresem: plik: /WEB-INF/wsdl/client/SOAService.wsdl. Błąd: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (nie można znaleźć ścieżki)

Więc przede wszystkim, czy powinienem zaktualizować wsdllocation klasy proxy? Jak więc powiedzieć klasie SOAService w WEB-INF / classes / com / ibm / eci / soaservice, aby wyszukała WSDL w \ WEB-INF \ wsdl \ client \ SOAService.wsdl?

EDITED : Znalazłem ten inny link - http://jianmingli.com/wp/?cat=41 , który mówi, że należy umieścić WSDL w ścieżce klas. Wstydzę się zapytać: jak umieścić to w ścieżce klas aplikacji internetowej?


Odpowiedzi:


117

Najlepszą opcją jest użycie jax-ws-catalog.xml

Kiedy kompilujesz lokalny plik WSDL, zastąp lokalizację WSDL i ustaw ją na coś podobnego

http: //localhost/wsdl/SOAService.wsdl

Nie martw się, to jest tylko identyfikator URI, a nie adres URL, co oznacza, że ​​nie musisz mieć dostępnego WSDL pod tym adresem.
Możesz to zrobić, przekazując opcję wsdllocation do wsdl do kompilatora java.

Spowoduje to zmianę kodu proxy z

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

do

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

Plik z powiadomieniami: // zmieniono na http: // w konstruktorze adresu URL.

Teraz jest w jax-ws-catalog.xml. Bez jax-ws-catalog.xml jax-ws rzeczywiście spróbuje załadować WSDL z tej lokalizacji

http: //localhost/wsdl/SOAService.wsdl
i zakończy się niepowodzeniem, ponieważ żaden taki WSDL nie będzie dostępny.

Ale dzięki jax-ws-catalog.xml możesz przekierować jax-ws do lokalnie spakowanego WSDL, gdy próbuje uzyskać dostęp do WSDL @

http: //localhost/wsdl/SOAService.wsdl
.

Oto jax-ws-catalog.xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
        <system systemId="http://localhost/wsdl/SOAService.wsdl"
                uri="wsdl/SOAService.wsdl"/>
    </catalog>

To, co robisz, to mówienie jax-ws, że kiedykolwiek będzie musiał załadować WSDL z

http: //localhost/wsdl/SOAService.wsdl
, powinien załadować go ze ścieżki lokalnej wsdl / SOAService.wsdl.

Teraz gdzie należy umieścić wsdl / SOAService.wsdl i jax-ws-catalog.xml? To jest pytanie za milion dolarów, prawda?
Powinien znajdować się w katalogu META-INF pliku jar aplikacji.

więc coś takiego

ABCD.jar  
| __ META-INF    
    | __ jax-ws-catalog.xml  
    | __ wsdl  
        | __ SOAService.wsdl  

W ten sposób nie musisz nawet zastępować adresu URL w swoim kliencie, który uzyskuje dostęp do serwera proxy. WSDL jest pobierany z twojego JAR, a Ty unikasz konieczności umieszczania w kodzie na stałe zakodowanych ścieżek systemu plików.

Więcej informacji na temat jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

Mam nadzieję, że to pomoże


ok, nie jestem w stanie rozwiązać problemu w ten sposób w aplikacji internetowej: próbowałem umieścić wsdl wewnątrz web-inf bez powodzenia, prawdopodobnie z powodu mojej niewiedzy. W każdym razie działa z słoikiem, więc zrobię bibliotekę opakowującą, tak jak powinno być to zrobione od początku. Dziękuję za wsparcie

Udało mi się z powodzeniem zastosować tę odpowiedź i uważam, że jest to lepsze rozwiązanie niż wszystkie inne alternatywy, które dokumentują inne artykuły i samouczki. Więc dla mnie to najlepsza praktyka. Zastanawiam się tylko, dlaczego to rozwiązanie nie jest udokumentowane w innych oficjalnych artykułach i samouczkach, które obejmują temat JAX-WS.
Rahul Khimasia

19

Innym podejściem, które z powodzeniem przyjęliśmy, jest wygenerowanie kodu proxy klienta WS przy użyciu narzędzia wsimport (z Ant, jako zadanie Ant) i określenie atrybutu wsdlLocation.

<wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}">
</wsimport>

Ponieważ uruchamiamy to dla projektu z wieloma plikami WSDL, skrypt rozwiązuje wartość $ (wsdl.file} dynamicznie, która jest ustawiona jako /META-INF/wsdl/YourWebServiceName.wsdl względem lokalizacji JavaSource (lub / src, w zależności od konfiguracji projektu) .Podczas procesu kompilacji pliki WSDL i XSD są kopiowane do tej lokalizacji i pakowane w plik JAR. (podobnie do rozwiązania opisanego powyżej przez Bhasakara)

MyApp.jar
|__META-INF
   |__wsdl
      |__YourWebServiceName.wsdl
      |__YourWebServiceName_schema1.xsd
      |__YourWebServiceName_schmea2.xsd

Uwaga: upewnij się, że pliki WSDL używają względnych odniesień do wszelkich zaimportowanych plików XSD, a nie adresów URL http:

  <types>
    <xsd:schema>
      <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/>
    </xsd:schema>
    <xsd:schema>
      <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/>
    </xsd:schema>
  </types>

W wygenerowanym kodzie znajdujemy to:

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2-b05-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl")
public class YourService_Service
    extends Service
{

    private final static URL YOURWEBSERVICE_WSDL_LOCATION;
    private final static WebServiceException YOURWEBSERVICE_EXCEPTION;
    private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService");

    static {
        YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl");
        WebServiceException e = null;
        if (YOURWEBSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        YOURWEBSERVICE_EXCEPTION = e;
    }

    public YourService_Service() {
        super(__getWsdlLocation(), YOURWEBSERVICE_QNAME);
    }

    public YourService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    /**
     * 
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort() {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort(WebServiceFeature... features) {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (YOURWEBSERVICE_EXCEPTION!= null) {
            throw YOURWEBSERVICE_EXCEPTION;
        }
        return YOURWEBSERVICE_WSDL_LOCATION;
    }

}

Być może to też pomoże. Jest to po prostu inne podejście, które nie wykorzystuje podejścia „katalogowego”.


Podoba mi się to podejście ... ale dlaczego katalog META-INF?
IcedDante

1
Należy pamiętać, że wymaga to użycia JAX-WS RI 2.2, a nie 2.1, który jest domyślnie dostarczany z JDK 6
ᄂ ᄀ

4

Wielkie dzięki za odpowiedź Bhaskara Karambelkara, która szczegółowo wyjaśnia i rozwiązuje mój problem. Ale chciałbym również sformułować odpowiedź w trzech prostych krokach dla kogoś, kto spieszy się z naprawą

  1. Utwórz odniesienie do lokalnej lokalizacji wsdl jako wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Utwórz folder META-INF bezpośrednio pod src. Umieść plik / pliki wsdl w folderze pod META-INF, powiedzmy META-INF / wsdl
  3. Utwórz plik xml jax-ws-catalog.xml w META-INF, jak poniżej

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

Teraz zapakuj słoik. Nie ma już odniesienia do katalogu lokalnego, wszystko jest spakowane i zawiera odwołania


4

Dla tych, którzy wciąż szukają tutaj rozwiązania, najłatwiejszym rozwiązaniem byłoby użycie <wsdlLocation>bez zmiany kodu. Kroki robocze są podane poniżej:

  1. Umieść swój wsdl w katalogu zasobów, takim jak: src/main/resource
  2. W pliku pom dodaj zarówno wsdlDirectory, jak i wsdlLocation (nie przegap / na początku wsdlLocation), jak poniżej. Podczas gdy wsdlDirectory jest używany do generowania kodu, a wsdlLocation jest używany w czasie wykonywania do tworzenia dynamicznego serwera proxy.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. Następnie w swoim kodzie java (z konstruktorem no-arg):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. Dla kompletności podaję tutaj pełną część generującą kod, z płynnym api w generowanym kodzie.

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



0

Miał dokładnie ten sam problem, który jest tutaj opisany. Niezależnie od tego, co zrobiłem, postępując zgodnie z powyższymi przykładami, aby zmienić lokalizację mojego pliku WSDL (w naszym przypadku z serwera WWW), nadal odwoływałem się do oryginalnej lokalizacji osadzonej w drzewie źródłowym procesu serwera.

Po WIELU godzinach prób debugowania tego zauważyłem, że wyjątek był zawsze wyrzucany z dokładnie tej samej linii (w moim przypadku 41). W końcu dziś rano zdecydowałem się po prostu wysłać mój kod źródłowy klienta do naszego partnera handlowego, aby mogli przynajmniej zrozumieć, jak wygląda kod, ale być może zbudują własny. Ku mojemu zaskoczeniu i przerażeniu znalazłem kilka plików klas zmieszanych z moimi plikami .java w drzewie źródłowym mojego klienta. Jakie dziwne !! Podejrzewam, że były to produkty uboczne narzędzia do tworzenia klientów JAX-WS.

Po usunięciu tych głupich plików .class i całkowitym wyczyszczeniu i odbudowaniu kodu klienta wszystko działa idealnie !! Redonculous !!

YMMV, Andrew

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.