Cliente JAX-WS: qual é o caminho correto para acessar o WSDL local?

86

O problema é que preciso construir um cliente de serviço da web a partir de um arquivo que me foi fornecido. Eu armazenei este arquivo no sistema de arquivos local e, enquanto mantenho o arquivo WSDL na pasta correta do sistema de arquivos, está tudo bem. Quando eu implanto em um servidor ou removo o WSDL da pasta do sistema de arquivos, o proxy não consegue encontrar o WSDL e surge um erro. Eu pesquisei na web e encontrei os seguintes posts ainda não consegui fazer funcionar:
JAX-WS Carregando WSDL do 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

Estou usando o NetBeans 6.1 (este é um aplicativo legado que devo atualizar com este novo cliente de serviço da web). Abaixo está a classe de 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);
    }

}


Este é o meu código para usar o 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);

O NetBeans colocou uma cópia do WSDL em web-inf / wsdl / client / SOAService , então não quero adicioná-lo ao META-INF também. As classes de serviço estão em WEB-INF / classes / com / ibm / eci / soaservice / e a variável baseurl contém o caminho completo do sistema de arquivos para ele (c: \ path \ to \ the \ project ... \ soaservice). O código acima gera o erro:

javax.xml.ws.WebServiceException: Falha ao acessar o WSDL em: arquivo: /WEB-INF/wsdl/client/SOAService.wsdl. Ele falhou com: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (não é possível encontrar o caminho)

Portanto, em primeiro lugar, devo atualizar o wsdllocation da classe proxy? Então, como faço para informar a classe SOAService em WEB-INF / classes / com / ibm / eci / soaservice para pesquisar o WSDL em \ WEB-INF \ wsdl \ client \ SOAService.wsdl?

EDITADO : Encontrei este outro link - http://jianmingli.com/wp/?cat=41 , que diz para colocar o WSDL no classpath. Tenho vergonha de perguntar: como faço para colocá-lo no classpath do aplicativo da web?

ROMANIA_engineer
fonte
Pergunta semelhante: JAX-WS carregando WSDL de jar
sleske,

Respostas:

117

A melhor opção é usar jax-ws-catalog.xml

Ao compilar o arquivo WSDL local, substitua o local WSDL e defina-o para algo como

http: //localhost/wsdl/SOAService.wsdl

Não se preocupe, isso é apenas um URI e não um URL, o que significa que você não precisa ter o WSDL disponível nesse endereço.
Você pode fazer isso passando a opção wsdllocation para o compilador wsdl para java.

Isso mudará seu código de proxy de

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

para

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

Observe o arquivo: // alterado para http: // no construtor de URL.

Agora vem em jax-ws-catalog.xml. Sem jax-ws-catalog.xml, o jax-ws realmente tentará carregar o WSDL do local

http: //localhost/wsdl/SOAService.wsdl
e falhar, pois nenhum WSDL estará disponível.

Mas com jax-ws-catalog.xml, você pode redirecionar jax-ws para um WSDL empacotado localmente sempre que tentar acessar o WSDL @

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

Aqui está 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>

O que você está fazendo é dizer a jax-ws que sempre que ele precisar carregar WSDL

http: //localhost/wsdl/SOAService.wsdl
, ele deve carregá-lo do caminho local wsdl / SOAService.wsdl.

Agora, onde você deve colocar wsdl / SOAService.wsdl e jax-ws-catalog.xml? Essa é a pergunta de um milhão de dólares, não é?
Ele deve estar no diretório META-INF do jar do seu aplicativo.

então algo assim

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

Dessa forma, você não precisa nem substituir a URL em seu cliente que acessa o proxy. O WSDL é obtido de seu JAR e você evita ter que ter caminhos de sistema de arquivos embutidos em seu código.

Mais informações sobre jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

espero que ajude


fonte
ok, não consegui resolver o problema desta forma dentro da aplicação web: tentei colocar wsdl dentro do web-inf sem sucesso, provavelmente por falta de conhecimento. De qualquer forma, ele funciona com um jar, então farei uma biblioteca wrapper, como deveria ter sido feito desde o início. Obrigado pelo seu apoio
Consegui usar essa resposta com sucesso e acredito que essa seja uma solução melhor do que todas as outras alternativas que outros artigos e tutoriais documentam. Portanto, para mim, esta é a melhor prática. Eu só me pergunto por que essa solução não está documentada em outros artigos e tutoriais oficiais que cobrem o tópico JAX-WS.
Rahul Khimasia
19

Uma outra abordagem que adotamos com sucesso é gerar o código de proxy do cliente WS usando wsimport (do Ant, como uma tarefa Ant) e especificar o atributo wsdlLocation.

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

Como executamos isso para um projeto com vários WSDLs, o script resolve o valor $ (wsdl.file} dinamicamente, que é configurado para ser /META-INF/wsdl/YourWebServiceName.wsdl em relação ao local do JavaSource (ou / src, dependendo de como você configurou seu projeto). Durante o processo de construção, os arquivos WSDL e XSDs são copiados para este local e empacotados no arquivo JAR. (semelhante à solução descrita por Bhasakar acima)

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

Observação: certifique-se de que os arquivos WSDL estejam usando referências relativas a quaisquer XSDs importados e não URLs 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>

No código gerado , encontramos o seguinte:

/**
 * 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;
    }

}

Talvez isso também possa ajudar. É apenas uma abordagem diferente que não usa a abordagem de "catálogo".

marca
fonte
Eu gosto dessa abordagem ... mas por que o diretório META-INF?
IcedDante de
1
Observe que isso requer o uso de JAX-WS RI 2.2, não 2.1 que vem com JDK 6 por padrão
ᄂ ᄀ
4

Muito obrigado pela resposta de Bhaskar Karambelkar, que explica em detalhes e corrige meu problema. Mas também gostaria de reformular a resposta em três etapas simples para alguém que está com pressa de corrigir

  1. Faça sua referência de local wsdl local como wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Crie uma pasta META-INF sob o src. Coloque seu (s) arquivo (s) wsdl em uma pasta sob META-INF, digamos META-INF / wsdl
  3. Crie um arquivo xml jax-ws-catalog.xml em META-INF como abaixo

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

Agora empacote seu frasco. Não há mais referência ao diretório local, está tudo empacotado e referenciado dentro

Hemus7
fonte
4

Para quem ainda está vindo em busca de solução por aqui, a solução mais fácil seria usar <wsdlLocation>, sem alterar nenhum código. As etapas de trabalho são fornecidas abaixo:

  1. Coloque seu wsdl no diretório de recursos como: src/main/resource
  2. No arquivo pom, adicione wsdlDirectory e wsdlLocation (não perca / no início de wsdlLocation), como abaixo. Enquanto wsdlDirectory é usado para gerar código e wsdlLocation é usado no tempo de execução para criar proxy dinâmico.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. Em seguida, em seu código Java (com construtor sem arg):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. Para completar, estou fornecendo aqui a parte completa da geração do código, com API fluente no código gerado.

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

Shafiul
fonte
0

Teve exatamente o mesmo problema que é descrito aqui. Não importa o que eu fiz, seguindo os exemplos acima, para alterar a localização do meu arquivo WSDL (em nosso caso, de um servidor da web), ele ainda estava se referindo ao local original embutido na árvore de origem do processo do servidor.

Depois de MUITAS horas tentando depurar isso, percebi que a Exceção sempre estava sendo lançada exatamente da mesma linha (no meu caso 41). Finalmente, esta manhã, decidi apenas enviar meu código-fonte do cliente para nosso parceiro comercial para que eles possam pelo menos entender como o código se parece, mas talvez construir o seu próprio. Para meu choque e horror , encontrei um monte de arquivos de classe misturados com meus arquivos .java dentro da árvore de código-fonte do meu cliente. Que bizarro !! Suspeito que isso tenha sido um subproduto da ferramenta de construtor de cliente JAX-WS.

Depois de zapear aqueles arquivos .class idiotas e realizar uma limpeza e reconstrução completa do código do cliente, tudo funciona perfeitamente !! Redonculous !!

YMMV, Andrew

Piko
fonte