Come eseguire una chiamata al servizio Web SOAP dalla classe Java?


116

Sono relativamente nuovo nel mondo dei webservices e la mia ricerca sembra avermi confuso più che illuminarmi, il mio problema è che mi è stata data una libreria (jar) che devo estendere con alcune funzionalità webservice.

Questa libreria sarà condivisa con altri sviluppatori e tra le classi nel jar ci saranno classi che hanno un metodo che chiama un webservice (che essenzialmente imposta un attributo della classe, fa un po 'di logica aziendale, come memorizzare l'oggetto in un db, ecc e invia indietro l'oggetto con quelle modifiche). Voglio rendere la chiamata a questo servizio il più semplice possibile, si spera semplice in modo che lo sviluppatore che utilizza la classe debba solo farlo.

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

Ho studiato JAX-WS da utilizzare sul server ma mi sembra di non aver bisogno di creare un wsimportnel server né wsimportsul client, poiché so che entrambi hanno le classi, ho solo bisogno di qualche interazione tra le classi condiviso sia nel server che nel client. Come pensi abbia senso fare il webservice e la chiamata in classe?


La tua domanda è un po 'poco chiara. Il metodo che si desidera creare (1) otterrà l'oggetto dal servizio web; (2) lavorare un po 'con l'oggetto; e (3) ripubblicalo al servizio web. È così?
acdcjunior

No, l'oggetto verrà creato nel client, verrà inviato al ws nella chiamata, il ws imposterà una variabile, ad esempio currentTime, farà una logica di business come memorizzarlo in un db, quindi invierà l'oggetto torna al client con l'ora corrente ora impostata. Spero di essermi spiegato un po 'meglio. Grazie.
jpz

Risposte:


273

Capisco che il tuo problema si riduce a come chiamare un servizio web SOAP (JAX-WS) da Java e ottenere il suo oggetto restituito . In tal caso, hai due possibili approcci:

  1. Genera le classi Java tramite wsimporte usale; o
  2. Crea un client SOAP che:
    1. Serializza i parametri del servizio in XML;
    2. Chiama il metodo web tramite la manipolazione HTTP; e
    3. Analizza la risposta XML restituita in un oggetto.


Informazioni sul primo approccio (utilizzando wsimport):

Vedo che hai già le classi di business dei servizi (entità o altro), ed è un dato di fatto che wsimportgenera un intero nuovo insieme di classi (che sono in qualche modo duplicati delle classi che già hai).

Temo, tuttavia, in questo scenario, puoi solo:

  • Adatta (modifica) il wsimportcodice generato per far sì che utilizzi le tue classi di business (questo è difficile e in qualche modo non ne vale la pena - tieni presente che ogni volta che il WSDL cambia, dovrai rigenerare e riadattare il codice); o
  • Abbandona e usa le wsimportclassi generate. (In questa soluzione, il codice aziendale potrebbe "utilizzare" le classi generate come un servizio da un altro livello di architettura.)

Informazioni sul secondo approccio (crea il tuo client SOAP personalizzato):

Per implementare il secondo approccio, dovrai:

  1. Effettua la chiamata:
    • Utilizza il framework SAAJ (SOAP with Attachments API for Java) (vedi sotto, viene fornito con Java SE 1.6 o successivo) per effettuare le chiamate; o
    • Puoi anche farlo attraverso java.net.HttpUrlconnection(e un po 'di java.iomanipolazione).
  2. Trasforma gli oggetti in e viceversa da XML:
    • Utilizza un framework OXM (Object to XML Mapping) come JAXB per serializzare / deserializzare l'XML da / in oggetti
    • Oppure, se necessario, creare / analizzare manualmente l'XML (questa può essere la soluzione migliore se l'oggetto ricevuto è solo leggermente diverso da quello inviato).

Creare un client SOAP usando il classico java.net.HttpUrlConnectionnon è così difficile (ma nemmeno così semplice), e puoi trovare in questo link un ottimo codice di partenza.

Ti consiglio di utilizzare il framework SAAJ:

SOAP con allegati API per Java (SAAJ) viene utilizzato principalmente per gestire direttamente i messaggi di richiesta / risposta SOAP che avvengono dietro le quinte in qualsiasi API del servizio Web. Consente agli sviluppatori di inviare e ricevere direttamente messaggi soap invece di utilizzare JAX-WS.

Vedi sotto un esempio funzionante (eseguilo!) Di una chiamata al servizio web SOAP utilizzando SAAJ. Chiama questo servizio 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;
    }

}

Informazioni sull'utilizzo di JAXB per la serializzazione / deserializzazione, è molto facile trovare informazioni su di esso. Puoi iniziare qui: http://www.mkyong.com/java/jaxb-hello-world-example/ .


Come imposto la versione del sapone usando il metodo sopra menzionato?
Rifatto il

Sono stato in grado di utilizzare il tuo metodo e ha funzionato quando ho usato il tuo URI, ma per la mia richiesta SOAP ottengo una risposta in base alla quale nessuno dei valori viene mostrato come previsto, ad es <xsd:element name="Incident_Number" type="xsd:string"/>. Come puoi vedere, l'elemento è chiuso e nessuna informazione viene generata dal WS.
Martin Erlic

Lo GetInfoByCityè 503Service Unavailable, sembra. :(
Brad Turek

@BradTurek D * mn! L'ho appena sostituito. Grazie per avermi fatto sapere! Ne troverò un altro e lo cambierò tra un po '.
acdcjunior

1
Per il passante: se il codice sopra (l'esempio endpoint del servizio Web SOAP) smette di funzionare o inizia a fornire errori (come 500, 503, ecc.), Fammelo sapere in modo che io possa risolverlo.
acdcjunior

3

Oppure usa semplicemente wsdl2java di Apache CXF per generare oggetti che puoi usare.

È incluso nel pacchetto binario che puoi scaricare dal loro sito web. Puoi semplicemente eseguire un comando come questo:

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

Usa il wsdl per generare oggetti, che puoi usare in questo modo (i nomi degli oggetti vengono anche presi dal wsdl, quindi il tuo sarà leggermente diverso):

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

C'è anche un plug-in Maven che genera i sorgenti: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

Nota: se generi sorgenti utilizzando CXF e IDEA, potresti dare un'occhiata a questo: https://stackoverflow.com/a/46812593/840315


1
Ho più di 30 wsdl nella mia applicazione. Durante la preparazione delle risorse per solo 1 wsdl (che ha 5 soapActions), il mio IDE Eclipse si è bloccato e ha generato circa 100+ MB di classi / oggetti.
Manmohan_singh

-1

Ho trovato un modo alternativo molto più semplice per generare messaggi soap. Dato un oggetto persona:

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
}

Di seguito è riportato un semplice generatore di messaggi di sapone:

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

Puoi usare da:

 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);
      }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.