Comment faire un appel de service Web SOAP à partir d'une classe Java?

116

Je suis relativement nouveau dans le monde des services Web et mes recherches semblent m'avoir plus dérouté que m'éclairer, mon problème est que j'ai reçu une bibliothèque (jar) que je dois étendre avec des fonctionnalités de services Web.

Cette bibliothèque sera partagée avec d'autres développeurs, et parmi les classes du jar, il y aura des classes qui ont une méthode qui appelle un webservice (qui définit essentiellement un attribut de la classe, fait une logique métier, comme le stockage de l'objet dans une base de données, etc et renvoie l'objet avec ces modifications). Je veux rendre l'appel à ce service aussi simple que possible, espérons-le aussi simple afin que le développeur utilisant la classe n'ait qu'à le faire.

Car c = new Car("Blue");
c.webmethod();

J'ai étudié JAX-WS à utiliser sur le serveur mais il me semble que je n'ai pas besoin de créer un wsimportdans le serveur ni le wsimportsur le client, puisque je sais que les deux ont les classes, j'ai juste besoin d'une interaction entre les classes partagé à la fois sur le serveur et le client. Comment pensez-vous qu'il est logique de faire le service Web et l'appel en classe?

jpz
la source
Votre question n'est pas claire. La méthode que vous souhaitez créer (1) obtiendra l'objet du service Web; (2) travailler un peu avec l'objet; et (3) le renvoyer sur le service Web. Est-ce que c'est ça?
acdcjunior
Non, l'objet sera créé dans le client, il sera envoyé au ws lors de l'appel, le ws définira une variable, par exemple currentTime, fera une logique métier comme le stocker dans une base de données, puis enverra l'objet retour au client avec le currentTime maintenant défini. J'espère que je me suis expliqué un peu mieux. Je vous remercie.
jpz

Réponses:

273

Je comprends que votre problème se résume à la façon d'appeler un service Web SOAP (JAX-WS) à partir de Java et d'obtenir son objet de retour . Dans ce cas, vous avez deux approches possibles:

  1. Générez les classes Java via wsimport et utilisez-les; ou
  2. Créez un client SOAP qui:
    1. Sérialise les paramètres du service en XML;
    2. Appelle la méthode Web via la manipulation HTTP; et
    3. Analyser la réponse XML renvoyée dans un objet.


À propos de la première approche (en utilisant wsimport ):

Je vois que vous avez déjà les services (entités ou autres) classes d'affaires, et c'est un fait que le wsimport génère un tout nouvel ensemble de classes (qui sont en quelque sorte des doublons des classes que vous avez déjà).

J'ai peur, cependant, dans ce scénario, vous ne pouvez que:

  • Adaptez (éditez) le wsimportcode généré pour qu'il utilise votre classes métier (c'est difficile et ça ne vaut pas la peine - gardez à l'esprit que chaque fois que le WSDL change, vous devrez régénérer et réadapter le code); ou
  • Abandonnez et utilisez les wsimportclasses générées. (Dans cette solution, votre code métier pourrait "utiliser" les classes générées en tant que service d'une autre couche architecturale.)

À propos de la deuxième approche (créez votre client SOAP personnalisé):

Afin de mettre en œuvre la deuxième approche, vous devrez:

  1. Faire l'appel:
    • Utilisez le framework SAAJ (SOAP with Attachments API for Java) (voir ci-dessous, il est livré avec Java SE 1.6 ou supérieur) pour effectuer les appels; ou
    • Vous pouvez également le faire via java.net.HttpUrlconnection(et une certaine java.iomanipulation).
  2. Transformez les objets en et en retour depuis XML:
    • Utilisez un framework OXM (Object to XML Mapping) tel que JAXB pour sérialiser / désérialiser le XML depuis / vers des objets
    • Ou, si vous devez, créer / analyser manuellement le XML (cela peut être la meilleure solution si l'objet reçu n'est que légèrement différent de celui envoyé).

Créer un client SOAP avec classic java.net.HttpUrlConnectionn'est pas si difficile (mais pas si simple non plus), et vous pouvez trouver dans ce lien un très bon code de démarrage.

Je vous recommande d'utiliser le framework SAAJ:

L'API SOAP avec pièces jointes pour Java (SAAJ) est principalement utilisée pour traiter directement les messages de demande / réponse SOAP qui se produisent en arrière-plan dans n'importe quelle API de service Web. Il permet aux développeurs d'envoyer et de recevoir directement des messages soap au lieu d'utiliser JAX-WS.

Voir ci-dessous un exemple fonctionnel (exécutez-le!) D'un appel de service Web SOAP utilisant SAAJ. Il appelle ce service Web .

import javax.xml.soap.*;

public class SOAPClientSAAJ {

    // SAAJ - SOAP Client Testing
    public static void main(String args[]) {
        /*
            The example below requests from the Web Service at:
             https://www.w3schools.com/xml/tempconvert.asmx?op=CelsiusToFahrenheit


            To call other WS, change the parameters below, which are:
             - the SOAP Endpoint URL (that is, where the service is responding from)
             - the SOAP Action

            Also change the contents of the method createSoapEnvelope() in this class. It constructs
             the inner part of the SOAP envelope that is actually sent.
         */
        String soapEndpointUrl = "https://www.w3schools.com/xml/tempconvert.asmx";
        String soapAction = "https://www.w3schools.com/xml/CelsiusToFahrenheit";

        callSoapWebService(soapEndpointUrl, soapAction);
    }

    private static void createSoapEnvelope(SOAPMessage soapMessage) throws SOAPException {
        SOAPPart soapPart = soapMessage.getSOAPPart();

        String myNamespace = "myNamespace";
        String myNamespaceURI = "https://www.w3schools.com/xml/";

        // SOAP Envelope
        SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration(myNamespace, myNamespaceURI);

            /*
            Constructed SOAP Request Message:
            <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:myNamespace="https://www.w3schools.com/xml/">
                <SOAP-ENV:Header/>
                <SOAP-ENV:Body>
                    <myNamespace:CelsiusToFahrenheit>
                        <myNamespace:Celsius>100</myNamespace:Celsius>
                    </myNamespace:CelsiusToFahrenheit>
                </SOAP-ENV:Body>
            </SOAP-ENV:Envelope>
            */

        // SOAP Body
        SOAPBody soapBody = envelope.getBody();
        SOAPElement soapBodyElem = soapBody.addChildElement("CelsiusToFahrenheit", myNamespace);
        SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("Celsius", myNamespace);
        soapBodyElem1.addTextNode("100");
    }

    private static void callSoapWebService(String soapEndpointUrl, String soapAction) {
        try {
            // Create SOAP Connection
            SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
            SOAPConnection soapConnection = soapConnectionFactory.createConnection();

            // Send SOAP Message to SOAP Server
            SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(soapAction), soapEndpointUrl);

            // Print the SOAP Response
            System.out.println("Response SOAP Message:");
            soapResponse.writeTo(System.out);
            System.out.println();

            soapConnection.close();
        } catch (Exception e) {
            System.err.println("\nError occurred while sending SOAP Request to Server!\nMake sure you have the correct endpoint URL and SOAPAction!\n");
            e.printStackTrace();
        }
    }

    private static SOAPMessage createSOAPRequest(String soapAction) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();

        createSoapEnvelope(soapMessage);

        MimeHeaders headers = soapMessage.getMimeHeaders();
        headers.addHeader("SOAPAction", soapAction);

        soapMessage.saveChanges();

        /* Print the request message, just for debugging purposes */
        System.out.println("Request SOAP Message:");
        soapMessage.writeTo(System.out);
        System.out.println("\n");

        return soapMessage;
    }

}

À propos de l'utilisation de JAXB pour la sérialisation / désérialisation, il est très facile de trouver des informations à ce sujet. Vous pouvez commencer ici: http://www.mkyong.com/java/jaxb-hello-world-example/ .

acdcjunior
la source
Comment définir la version de savon en utilisant la méthode mentionnée ci-dessus?
Refait
J'ai pu utiliser votre méthode et cela a fonctionné lorsque j'ai utilisé votre URI, mais pour ma propre requête SOAP, j'obtiens une réponse dans laquelle aucune des valeurs ne s'affiche comme prévu, c'est-à-dire <xsd:element name="Incident_Number" type="xsd:string"/>. Comme vous pouvez le voir, l'élément est fermé et aucune information n'est générée à partir du WS.
Martin Erlic
Le GetInfoByCityest 503Service Unavailable, il voit. :(
Brad Turek
@BradTurek D * mn! Je viens de le remplacer. Merci de me le faire savoir! Je vais en trouver un autre et y changer un peu.
acdcjunior
1
Au passant: Si le code ci-dessus (l'exemple de point de terminaison du service Web SOAP) cesse de fonctionner ou commence à donner des erreurs (comme 500, 503, etc.), merci de me le faire savoir pour que je puisse le corriger.
acdcjunior
3

Ou utilisez simplement wsdl2java d'Apache CXF pour générer des objets que vous pouvez utiliser.

Il est inclus dans le package binaire que vous pouvez télécharger depuis leur site Web. Vous pouvez simplement exécuter une commande comme celle-ci:

$ ./wsdl2java -p com.mynamespace.for.the.api.objects -autoNameResolution http://www.someurl.com/DefaultWebService?wsdl

Il utilise le wsdl pour générer des objets, que vous pouvez utiliser comme ceci (les noms d'objet sont également extraits du wsdl, donc le vôtre sera un peu différent):

DefaultWebService defaultWebService = new DefaultWebService();
String res = defaultWebService.getDefaultWebServiceHttpSoap11Endpoint().login("webservice","dadsadasdasd");
System.out.println(res);

Il existe même un plug-in Maven qui génère les sources: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

Remarque: Si vous générez des sources à l'aide de CXF et IDEA, vous pouvez consulter ceci: https://stackoverflow.com/a/46812593/840315

szab.kel
la source
1
J'ai plus de 30 wsdl dans mon application. Lors de la préparation des ressources pour seulement 1 wsdl (qui a 5 soapActions), mon Eclipse IDE a pendu et généré plus de 100 Mo de classes / objets.
Manmohan_singh
-1

J'ai trouvé une manière alternative beaucoup plus simple de générer un message savon. Étant donné un objet Personne:

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Person {
  private String name;
  private int age;
  private String address; //setter and getters below
}

Vous trouverez ci-dessous un simple générateur de messages Soap:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

@Slf4j
public class SoapGenerator {

  protected static final ObjectMapper XML_MAPPER = new XmlMapper()
      .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
      .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
      .registerModule(new JavaTimeModule());

  private static final String SOAP_BODY_OPEN = "<soap:Body>";
  private static final String SOAP_BODY_CLOSE = "</soap:Body>";
  private static final String SOAP_ENVELOPE_OPEN = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">";
  private static final String SOAP_ENVELOPE_CLOSE = "</soap:Envelope>";

  public static String soapWrap(String xml) {
    return SOAP_ENVELOPE_OPEN + SOAP_BODY_OPEN + xml + SOAP_BODY_CLOSE + SOAP_ENVELOPE_CLOSE;
  }

  public static String soapUnwrap(String xml) {
    return StringUtils.substringBetween(xml, SOAP_BODY_OPEN, SOAP_BODY_CLOSE);
  }
}

Vous pouvez utiliser par:

 public static void main(String[] args) throws Exception{
        Person p = new Person();
        p.setName("Test");
        p.setAge(12);

        String xml = SoapGenerator.soapWrap(XML_MAPPER.writeValueAsString(p));
        log.info("Generated String");
        log.info(xml);
      }
mel3kings
la source