Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

15.5.2. Développer une application client JAX-WS

Cette section traite des clients de Services Web JAX-WS en général. Le client communique avec et réclame une tâche du point de terminaison de JAX-WS, qui est déployé dans le conteneur Java Enterprise Edition 6. Pour obtenir des informations détaillées sur les classes, méthodes et autres détails d'implémentation mentionnés ci-dessous, reportez-vous à Section 15.6.2, « Référence API Commun JAX-WS » et aux sections pertinentes du package Javadocs inclus dans JBoss EAP 6.

Service

Aperçu
Un Service est une abstraction qui représente un service WSDL. Un service WSDL est une collection de ports liés les uns aux autres, dont chacun comprend un type de port lié à un protocole particulier et une adresse de point de terminaison particulière.
Généralement, le service est créé quand le reste des stubs du composant sont générés à partir d'un contrat WSDL existant. Le contrat WSDL est disponible via l'URL WSDL du point de terminaison déployé, ou peut être créé à partir de la source de point de terminaison à l'aide de la commande wsprovide.sh dans le répertoire de /bin/ EAP_HOME.
On appelle cela une utilisation statique. Dans un tel cas, vous créez des instances de la classe Service , créée elle-même comme un des composants stubs.
Vous pouvez également créer le service manuellement, par la méthode Service.create. On appelle cela une utilisation dynamic.
Utilisation
Cas d'utilisation static
Le cas d'utilisation statique pour un client JAX-WS suppose que vous ayiez déjà un contrat WSDL. Ceci peut être généré par un outil externe ou généré à l'aide des annotations JAX-WS qui conviennent lorsque vous créez votre point de terminaison de JAX-WS.
Pour générer vos stubs de composants, vous pouvez utiliser le script wsconsume.sh ou wsconsume.bat inclus dans EAP_HOME/bin/. Ce script prend le WSDL URL ou fichier en tant que paramètre, et génère de nombreux fichiers structurés dans un arborescence de répertoires. La source et les fichiers de classe représentant votre Service s'appellent CLASSNAME_Service.java et CLASSNAME_Service.class, respectivement.
La classe d'implémentation générée possède deux constructeurs publics, un sans argument et un avec deux arguments. Les deux arguments représentent respectivement l'emplacement WSDL (un java.net.URL) et le nom du service (un javax.xml.namespace.QName).
Le constructeur sans argument est celui qui est le plus souvent utilisé. Dans ce cas, l'emplacement WSDL et le nom du service sont ceux que l'on trouve dans le fichier WSDL. Ceux-ci sont définis implicitement à partir de l'annotation @WebServiceClient qui décore la classe générée.

Exemple 15.19. Exemple de classe de service générée

@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl")
public class StockQuoteService extends javax.xml.ws.Service
{
   public StockQuoteService()
   {
      super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService"));
   }

   public StockQuoteService(String wsdlLocation, QName serviceName)
   {
      super(wsdlLocation, serviceName);
   }

   ...
}
Cas d'utilisation dynamic
Dans un cas d'utilisation dynamic, aucun stub n'est généré automatiquement. Au lieu de cela, le client de service web utilise la méthode Service.create pour créer des instances de Service. Le fragment de code suivant illustre ce processus.

Exemple 15.20. Création de service manuellement

URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "MyService");
Service service = Service.create(wsdlLocation, serviceName);

Handler Resolver
JAX-WS fournit un framework plug-in flexible pour des modules de traitement des messages, appelés gestionnaires. Ces gestionnaires étendent les fonctionnalités d'un système de runtime JAX-WS. Une instance de Service donnera accès à un HandlerResolver grâce à une paire de méthodes getHandlerResolver et setHandlerResolver qui peuvent configurer un ensemble de gestionnaires sur la base de service, de port ou de protocole.
Lorsqu'une instance de Service crée un proxy ou une instance Dispatch, le Handler Resolver enregistré dans le service crée la chaîne de gestionnaires requise. Les changements ultérieurs apportés au Handler Resolver qui sont configurés pour une instance de Service n'affectent pas les gestionnaires sur proxies préalablement créés ou les instances de Dispatch.
Exécuteur
Les instances de Service peuvent être configurées par un java.util.concurrent.Executor. Executor invoque les callbacks asynchrones demandés par l'application. Les méthodes setExecutor et getExecutor de Service peuvent modifier et extraire l' Executor configuré pour un service.
Proxy dynamique

Un dynamic proxy est une instance de proxy de client utilisant une des méthodes getPort fournie par le Service. Le portName indique le nom du port WSDL que le service utilise. La serviceEndpointInterface indique l'interface du point de terminaison du service prise en charge par l'instance du proxy dynamique créé.

Exemple 15.21. Méthodes getPort

public <T> T getPort(QName portName, Class<T> serviceEndpointInterface)
public <T> T getPort(Class<T> serviceEndpointInterface)
L'Interface du point de terminaison du service ou Service Endpoint Interface (SEI) est normalement créée par la commande wsconsume.sh qui interprète le WSDL et qui, à partir de cela, crée des classes Java.
Il y a aussi une méthode typée retournant un port qui est fournie. Ces méthodes renvoient également des proxys dynamiques qui implémentent la SEI. Voir les exemples suivants.

Exemple 15.22. Renvoie le Port d'un Service

@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref",
   wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl")

public class TestEndpointService extends Service
{
    ...

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

    @WebEndpoint(name = "TestEndpointPort")
    public TestEndpoint getTestEndpointPort()
    {
        return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class);
    }
}

@WebServiceRef

L'annotation @WebServiceRef déclare une référence à un Service Web. Elle suit un modèle de ressource indiqué par l'annotation javax.annotation.Resource définie dans http://www.jcp.org/en/jsr/summary?id=250.

Cas d'utilisateur @WebServiceRef

  • Vous pouvez l'utiliser pour définir une référence dont le type est une classe de Service générée. Dans ce cas, l'élément de valeur et le type font tous deux référence au type de classe de Service généré. En outre, si le type de référence peut être déduit grâce au champ ou à la déclaration de méthode à laquelle l'annotation est s'applique, les éléments de type et de valeur peuvent (mais n'y sont pas tenus) avoir la valeur par défaut de Object.class. Si le type ne peut pas être déduit, alors l'élément au moins doit être présent, avec une valeur par défaut.
  • Vous pouvez l'utiliser pour définir une référence de type SEI. Dans ce cas, l'élément de type peut (mais pas forcément) se présenter avec sa valeur par défaut si le type de référence peut être déduit de la déclaration de champ ou de méthode annotée. Toutefois, l'élément de valeur doit toujours être présent et faire référence à un type de classe de service généré, qui est un sous-type de javax.xml.ws.Service. L'élément wsdlLocation, s'il est présent, substitue les informations d'emplacement WSDL spécifiées dans l'annotation @WebService de la classe de service générée référencée.

    Exemple 15.23. Exemples de @WebServiceRef

    public class EJB3Client implements EJB3Remote
    {
       @WebServiceRef
       public TestEndpointService service4;
    
       @WebServiceRef
       public TestEndpoint port3;
    
Dispatch

Les Services Web XML utilisent des messages XML pour la communication entre le point de terminaison, qui est déployé dans le conteneur Java EE, et tous les clients. Les messages XML utilisent un langage XML appelé Simple Object Access Protocol (SOAP). L'API JAX-WS fournit les mécanismes pour que le point de terminaison et les clients puissent chacun être en mesure d'envoyer et de recevoir des messages SOAP. Le Marshalling, ou conversion de paramètres, est l'action de convertir le message XML SOAP en Objet Java.

Dans certains cas, vous devez accéder aux messages SOAP bruts eux-mêmes, plutôt qu'au résultat de la conversion. La classe de Dispatch fournit cette fonctionnalité. Dispatch opère dans un des deux modes d'utilisation, qui sont identifiés par l'une des constantes suivantes.
  • javax.xml.ws.Service.Mode.MESSAGE - ce mode ordonne aux applications clientes de travailler directement avec les structures de message qui sont spécifiques au protocole. Si utilisé avec une liaison de protocole SOAP, une application cliente fonctionne directement avec un message SOAP.
  • javax.xml.ws.Service.Mode.PAYLOAD - ce mode amène le client à travailler avec la charge elle-même. Par exemple, s'il est utilisé avec une liaison de protocole SOAP, une application cliente travaillera alors avec le contenu SOAP plutôt qu'avec l'intégralité du message SOAP.
Dispatch est une API de bas niveau qui exige des clients de structurer les messages ou les charges en XML, avec une adhérence stricte aux normes du protocole individuel et une connaissance approfondie de la structure de message ou de la charge. Dispatch est une classe générique qui prend en charge l'entrée et la sortie des messages ou des charges de message de n'importe quel type.

Exemple 15.24. Utilisation de Dispatch

Service service = Service.create(wsdlURL, serviceName);
Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD);

String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
dispatch.invokeOneWay(new StreamSource(new StringReader(payload)));

payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
Invocations asynchrones

L'interface BindingProvider représente un composant qui fournit une liaison de protocole que les clients peuvent utiliser. Elle est implémentée et étendue par l'interface Dispatch.

Les instances de BindingProvider peuvent fournir des possibilités d'opérations asynchrones. Comme les opérations asynchrones, les invocations sont découplées de l'instance BindingProvider au moment de l'invocation. Le contexte de réponse n'est pas mis à jour lorsque l'opération est terminée. Au lieu de cela, un contexte de réponse distinct est fourni à l'aide de l'interface de réponse.

Exemple 15.25. Invocation asynchrone

public void testInvokeAsync() throws Exception
{
   URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl");
   QName serviceName = new QName(targetNS, "TestEndpointService");
   Service service = Service.create(wsdlURL, serviceName);
   TestEndpoint port = service.getPort(TestEndpoint.class);
   Response response = port.echoAsync("Async");
   // access future
   String retStr = (String) response.get();
   assertEquals("Async", retStr);
}
Invocations @Oneway

L'annotation @Oneway indique que la méthode web donnée ne prend un message d'entrée mais ne renvoie aucun message de sortie. Habituellement, une méthode @Oneway renvoie le thread de contrôle à l'application appelante avant l'exécution de la méthode commerciale.

Exemple 15.26. Exemple d'invocation @Oneway

@WebService (name="PingEndpoint")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class PingEndpointImpl
{
   private static String feedback;
  
   @WebMethod
   @Oneway
   public void ping()
   {
      log.info("ping");
      feedback = "ok";
   }
  
   @WebMethod
   public String feedback()
   {
      log.info("feedback");
      return feedback;
   }
}
Configuration du timeout

Il y a deux propriétés qui contrôlent le comportement du délai d'expiration de la connexion HTTP et le délai d'attente d'un client qui attend de recevoir un message. Le premier est javax.xml.ws.client.connectionTimeout et le second est javax.xml.ws.client.receiveTimeout. Chacun est exprimé en millisecondes, et la syntaxe correcte est indiquée ci-dessous.

Exemple 15.27. Configuration de timeout JAX-WS

public void testConfigureTimeout() throws Exception
{
   //Set timeout until a connection is established
   ((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000");

   //Set timeout until the response is received
   ((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000");

   port.echo("testTimeout");
}