Jak wywołać usługę sieciową SOAP z klasy Java?

116

Jestem stosunkowo nowy w świecie usług internetowych i wydaje mi się, że moje badania bardziej mnie zdezorientowały niż oświeciły, moim problemem jest to, że dostałem bibliotekę (słoik), którą muszę rozszerzyć o niektóre funkcje usług internetowych.

Ta biblioteka zostanie udostępniona innym programistom, a wśród klas w jar będą klasy, które mają metodę wywołującą usługę sieciową (która zasadniczo ustawia atrybut klasy, wykonuje jakąś logikę biznesową, taką jak przechowywanie obiektu w bazie danych, etc i odsyła obiekt z tymi modyfikacjami). Chcę, aby wywołanie tej usługi było tak proste, jak to tylko możliwe, miejmy nadzieję, że tak proste, aby programista korzystający z klasy musiał to zrobić.

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

Uczyłem się JAX-WS do wykorzystania na serwerze, ale wydaje mi się, że nie muszę tworzyć wsimportani wsimportna serwerze, ani na kliencie, ponieważ wiem, że oba mają zajęcia, potrzebuję tylko interakcji między zajęciami udostępniane na serwerze i kliencie. Jak myślisz, jaki sens ma wykonywanie usługi sieciowej i połączenia w klasie?

jpz
źródło
Twoje pytanie jest trochę niejasne. Metoda, którą chcesz utworzyć, (1) pobierze obiekt z usługi sieciowej; (2) trochę popracuj z obiektem; i (3) prześlij go z powrotem do serwisu internetowego. Czy to to?
acdcjunior
Nie, obiekt zostanie utworzony w kliencie, zostanie wysłany do ws w wywołaniu, ws ustawi zmienną, na przykład currentTime, zrobi jakąś logikę biznesową, taką jak przechowywanie go w bazie danych, a następnie wyśle ​​obiekt z powrotem do klienta z ustawioną wartością currentTime. Mam nadzieję, że wyjaśniłem siebie trochę lepiej. Dziękuję Ci.
jpz

Odpowiedzi:

273

Rozumiem, że problem sprowadza się do tego, jak wywołać usługę sieciową SOAP (JAX-WS) z języka Java i uzyskać zwracający ją obiekt . W takim przypadku masz dwa możliwe podejścia:

  1. Wygeneruj klasy Java wsimporti używaj ich; lub
  2. Utwórz klienta SOAP, który:
    1. Serializuje parametry usługi do XML;
    2. Wywołuje metodę sieciową poprzez manipulację HTTP; i
    3. Przeanalizuj zwracającą odpowiedź XML z powrotem do obiektu.


O pierwszym podejściu (używając wsimport):

Widzę, że masz już klasy biznesowe usług (podmiotów lub innych) i faktem jest, że wsimportgenerują one zupełnie nowy zestaw klas (które są w pewnym sensie duplikatami klas, które już masz).

Obawiam się jednak, że w tym scenariuszu możesz tylko:

  • Dostosuj (edytuj) wsimportwygenerowany kod, aby używał Twoich klas biznesowych (jest to trudne i jakoś nie warte - pamiętaj, że za każdym razem, gdy WSDL się zmieni, będziesz musiał ponownie wygenerować i ponownie dostosować kod); lub
  • Zrezygnuj i korzystaj z wsimportwygenerowanych klas. (W tym rozwiązaniu kod biznesowy mógłby „używać” wygenerowanych klas jako usługi z innej warstwy architektonicznej).

O drugim podejściu (stwórz własnego klienta SOAP):

Aby wdrożyć drugie podejście, musisz:

  1. Zatelefonować:
    • Użyj struktury SAAJ (SOAP with Attachments API for Java) (patrz poniżej, jest dostarczana z Java SE 1.6 lub nowszą) do wykonywania wywołań; lub
    • Możesz to również zrobić przez java.net.HttpUrlconnection(i trochę java.ioobsługi).
  2. Zamień obiekty do i z powrotem z XML:
    • Użyj struktury OXM (Object to XML Mapping), takiej jak JAXB, aby serializować / deserializować XML z / do obiektów
    • Lub, jeśli musisz, ręcznie utwórz / przeanalizuj XML (może to być najlepsze rozwiązanie, jeśli otrzymany obiekt różni się tylko trochę od wysłanego).

Tworzenie klienta SOAP przy użyciu klasycznego java.net.HttpUrlConnectionnie jest takie trudne (ale też nie takie proste), a w tym linku można znaleźć bardzo dobry kod początkowy.

Polecam korzystanie z frameworka SAAJ:

SOAP with Attachments API for Java (SAAJ) jest używany głównie do bezpośredniego radzenia sobie z komunikatami SOAP Request / Response, które mają miejsce za kulisami w dowolnym interfejsie API usługi sieci Web. Umożliwia programistom bezpośrednie wysyłanie i odbieranie wiadomości mydlanych zamiast korzystania z JAX-WS.

Zobacz poniżej działający przykład (uruchom go!) Wywołania usługi WWW SOAP przy użyciu SAAJ. Nazywa tę usługę internetową .

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

}

Informacje o używaniu JAXB do serializacji / deserializacji są bardzo łatwe do znalezienia. Możesz zacząć tutaj: http://www.mkyong.com/java/jaxb-hello-world-example/ .

acdcjunior
źródło
Jak ustawić wersję mydła powyższą metodą?
Redone
Udało mi się użyć twojej metody i zadziałało, gdy użyłem twojego URI, ale dla mojego własnego żądania SOAP otrzymuję odpowiedź, w której żadna z wartości nie jest wyświetlana zgodnie z oczekiwaniami, tj <xsd:element name="Incident_Number" type="xsd:string"/>. Jak widać, element jest zamknięty i żadne informacje nie są generowane z WS.
Martin Erlic
To GetInfoByCityjest 503Service Unavailable, to widzi. :(
Brad Turek
@BradTurek D * mn! Ja po prostu zastąpić go. Dzięki, że dałeś mi znać! Znajdę inny i za chwilę się na niego przestawię.
acdcjunior
1
Do przechodnia: Jeśli powyższy kod (przykładowy punkt końcowy usługi sieci Web SOAP) przestaje działać lub zaczyna generować błędy (np. 500, 503 itd.), Daj mi znać, abym mógł to naprawić.
acdcjunior,
3

Lub po prostu użyj narzędzia wsdl2java Apache CXF do wygenerowania obiektów, których możesz użyć.

Jest zawarty w pakiecie binarnym, który możesz pobrać z ich strony internetowej. Możesz po prostu uruchomić takie polecenie:

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

Używa wsdl do generowania obiektów, których możesz użyć w ten sposób (nazwy obiektów są również pobierane z wsdl, więc twoje będą trochę inne):

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

Istnieje nawet wtyczka Maven, która generuje źródła: https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html

Uwaga: jeśli generujesz źródła za pomocą CXF i IDEA, możesz spojrzeć na to: https://stackoverflow.com/a/46812593/840315

szab.kel
źródło
1
W mojej aplikacji mam ponad 30 wsdl. Przygotowując zasoby dla zaledwie 1 wsdl (który ma 5 soapActions), moje Eclipse IDE zawiesiło się i wygenerowało około 100+ MB klas / obiektów.
Manmohan_singh
-1

Znalazłem znacznie prostszy alternatywny sposób generowania wiadomości mydlanej. Biorąc pod uwagę obiekt osoby:

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
}

Poniżej znajduje się prosty generator wiadomości mydlanych:

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

Możesz użyć:

 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
źródło