Webserwis SOAP w aplikacji Android z wykorzystaniem generatora easyWSDL

Programowanie

W tym artykule pokażę, w jak prosty sposób wywoływać metody webserwisów opartych o SOAP w aplikacji mobilnej na system Android. W tym konkretnym przykładzie posłużymy się API systemu PayPal, jednak pokazane tutaj metody, sprawdzą się z dowolnym webserwisem SOAP.

Czym jest Webserwis?

Zanim przejdziemy do mięsa, warto wspomnieć czym jest webserwis. Określenie to (usługa internetowa) odnosi się do aplikacji udostępnionej w sieci (zwykle w Internecie), z którą mogą łączyć się inne aplikacje i korzystać z jej zasobów.

Usługi internetowe są obecnie bardzo popularne i praktycznie każdy większy dostawca oprogramowania udostępnia API w formie webserwisu. Dzięki temu w łatwy sposób możemy tworzyć aplikacje na telefon wyświetlające informacje z Facebooka, Twittera, pokazujące pogodę itp. Te wszystkie dane są pobierane przez aplikację mobilną z webserwisu.

Czym jest SOAP?

Skoro wiemy już czym jest usługa internetowa, to czas zapoznać się z rodzajami takich usług. Mamy dwa główne standardy tworzenia webserwisów:

  • REST (Representational State Transfer)
  • SOAP (Simple Object Access Protocol)

Nie wchodząc w szczegóły, SOAP był szeroko stosowany kilka-kilkanaście lat temu. Obecnie królują webserwisy REST i większość firm tworzy usługi internetowe właśnie w tym standardzie. Pomimo, iż obecnie REST jest w modzie, to przez ostatnie kilkanaście lat wiele firm stworzyło web serwisy SOAP, więc nadal, pisząc aplikację mobilną, możemy mieć konieczność korzystania właśnie z nich i o tym jest niniejszy post.

Jeśli interesuje Cię co spowodowało, że SOAP został zastąpiony RESTem, to już spieszę z odpowiedzią, gdyż jest ona mocno związana z treścią tego artykułu.

Problemy SOAP

Problem ze standardem SOAP jest taki, iż jest on w miarę skomplikowany i ciężki w użytkowniu. Oczywiście nie jest to rocket science, jednak budowanie zapytań SOAP (bazujące na XML) oraz parsowanie odpowiedzi wymaga bardziej złożonych narzędzi, takich jak:

  • Biblioteka do XML
  • Narzędzie generujące kod na podstawie WSDL

Na desktopach te problemy były jeszcze w miarę łatwe do rozwiązania. Większość języków i technologii posiada np biblioteki do parsowania XML, a także istnieją narzędzia do generowania kodu z WSDL.

W przypadku platform mobilnych sytuacja często jest o wiele gorsza. Niektóre języki nie posiadają bibliotek do XML, a te które takowe mają, to cierpią na brak narzędzi do generowania kodu.

Generatory kodu z WSDL

Już parę razy wspomniałem o narzędziach do generowania kodu, więc może czas wyjaśnić czym one są i dlaczego są takie istotne.

Zacznijmy jednak od tego, czym jest WSDL? Jest to plik opisujący strukturę zapytań i odpowiedzi danej usługi internetowej. Dzięki niemu wiemy jak stworzyć zapytanie, aby było poprawnie odebrane przez usługę oraz co dostaniemy w odpowiedzi. Jest to taki pradziadek Swaggera 😉

Teoretycznie mając plik WSDL programista mógłby zapytania tworzyć ręcznie, jednak jest to karkołomne zadanie, o wiele trudniejsze niż wysyłanie zapytań do API RESTowego. W związku z tym, powstały narzędzia, które analizują WSDLa i generują kod w różnych językach programowania, który może być użyty do komunikacji z usługą. W praktyce działa to bardzo dobrze. Np narzędzia takie jak Visual Studio mają taki generator wbudowany. W Javie też jest kilka profesjonalnych narzędzi (np. wsdl2Java), które sprawiają, że korzystanie z SOAP na desktopie jest proste.

W przypadku platform mobilnych, takich narzędzi jednak nie ma (lub są hobbistyczne narzędzia open source, które działają tylko w bardzo prostych przypadkach). Z tym problemem spotkałem się osobiście wiele lat temu, gdy tworzyłem aplikację BodyArchitect na system Android (więcej o niej możesz poczytać tutaj). Też miałem webserwis SOAP, z którym moja aplikacja mobilna miała „rozmawiać”. Okazało się wtedy, iż na platformach mobilnych nie jest to takie proste.

I tak pojawił się pomysł napisania własnego generatora kodu…

easyWSDL

easyWSDL jest aplikacją SaaS, która jest uniwersalnym generatorem kodu dla webserwisów SOAP. Jest rozwijana od roku 2014 i do tej pory skorzystało z niej kilkadziesiąt tysięcy użytkowników.

Obsługuje następujące języki programowania:

  • Java (Android)
  • Java (Pure)
  • Kotlin (Android)
  • Objective-C (iOS)
  • Swift (iOS)
  • Dart (Flutter)

Generator wspiera praktycznie wszystkie możliwe ficzery i konstrukcje, które mogą pojawić się w webserwisach SOAP (poza standardami WS-*, czyli WS-Security itp). Przez te wszystkie lata, za każdym razem gdy jakiś użytkownik znalazł błąd, to był on naprawiany i implementowany dla każdego powyższego języka. A generator jest sprawdzony w boju przez takie firmy jak: Dolby, Honeywell, Orange, Bosh i wiele innych.

Jak korzystać z easyWSDL?

Na początku warto wspomnieć, iż korzystanie z easyWSDL jest płatne. Sam generator posiada wersję darmową, ale ma ona następujące ograniczenia:

  • Wygenerowany kod nie implementuje zaawansowanych ficzerów
  • Wygenerowany kod implementuje tylko połowę dostępnych metod. Druga połowa jest niezaimplementowana
  • Kod wygenerowany wersją bezpłatną nie może być wykorzystany w celach komercyjnych

Podsumowując, wersja bezpłatna służy do tego, aby sprawdzić, czy generator będzie działał z konkretnym webserwisem.

Korzystać z easyWSDL można na dwa sposoby:

  • Przez stronę https://easywsdl.com – ten sposób działa dla wszystkich języków
  • Za pośrednictwem pluginu do IDE Android Studio lub IntelliJ – z tego sposobu mogą skorzystać tylko programiści używający jednego z tych środowisk

Na potrzeby tego artykułu pokaże jak wykorzystać easyWSDL w projekcie aplikacji mobilnej na system Android (Kotlin) przez stronę internetową. Jeśli piszesz aplikację w innym języku (np Swift na iOS) to nadal wiele z tego co przedstawię w tym artykule będzie aktualne.

Nasza przykładowa aplikacja będzie bardzo prosta. Głownym elementem jest przycisk, który po kliknięciu połączy się z API system PayPal, aby pobrać aktualny stan konta. PayPal posiada dwa różne API:

  • REST – jest to standardowe API, które umożliwia pobieranie danych związanych z płatnościami, fakturami, zamówieniami itp. Niestety nie obsługuje wielu funkcji, jak np. możliwość pobrania salda konta itp.
  • NVP/SOAP – jest to API, które nas interesuje, gdyż ono między innymi umożliwia pobranie stanu konta PayPal. Wykorzystuje standard SOAP, co sprawia, że na mobilkach ciężko użyć. I tutaj cały na biało wchodzi easyWSDL

Jak korzystać PayPal NVP/SOAP API?

Zanim będziemy mogli skorzystać z API systemu PayPal, należy założyć konto. Możesz to zrobić tutaj.

W celu ułatwienia developmentu, API PayPala udostępnia tryb piaskownicy, który pozwala nam na bezpieczne eksperymentowanie. Dostęp do trybu piaskownicy oraz wiele innych narzędzi pomocych podczas korzystania z API PayPal znajdziesz na stronie https://developer.paypal.com/home/.
Zanim będziemy mogli skorzystać z API, należy dostać dane autoryzujące. W odróżnieniu od REST API, (których dane do autoryzujące tworzymy na stronie https://developer.paypal.com/), to w przypadku API NVP/SOAP należy:

  1. Zalogować się na swoje konto PayPal i przejść do ustawień konta
Ustawienia konta PayPal
  1. Przechodzimy do sekcji Dostęp do API
Konfiguracja danych autoryzujących PayPal
  1. A następnie klikamy Zarządzaj danymi uwierzytelniającymi API  w Integracja interfejsu API NVP/SOAP (wersja klasyczna)
PayPal - dane autoryzujące API
  1. Tutaj znajdziesz trzy wartości, które sobie zapisz, bo będziesz potrzebował ich w kodzie:
    • Nazwa użytkownika API
    • Hasło API
    • Podpis
PayPal - dane autoryzujące API

W związku z tym, iż ten artykuł nie skupia się na korzystaniu z API PayPal, a na generatorze easyWSDL, to powyższe kroki są tylko skrótowym opisem. Jeśli chciałbyś dokładnie zapoznać się z API PayPala to odpowiednie informacje znajdziesz tutaj https://developer.paypal.com/home/.

Przykładowa aplikacja

Jak wspomniałem, w tym artykule skupimy się na generatorze easyWSDL oraz korzystaniu z webserwisów SOAP, a nie na tworzeniu aplikacji mobilnej na Androida. W związku z tym nasz projekt to głównie kod wygenerowany z szablonu Android Studio:

  1. Po uruchomieniu środowiska Android Studio wybieramy opcję File->New->New Project…
  2. Jako typ aplikacji wybieramy Phone and Tablet -> Basic Activity. Wygeneruje nam to aplikację z jedym przyciskiem, pod którym umieścimy kod do połączenia z webserwisem.
  3. Podajemy nazwę aplikacji oraz wybieramy Kotlin jako język programowania. Zwróć uwagę na pole Package name. Wartość z tego pola będziemy w dalszych krokach podawać w generatorze easyWSDL. Nasza testowa aplikacja ma nazwę pakietu com.example.paypaltester.
  4. Klikamy Finish.

W tym momencie mamy działającą aplikację. Teraz czas wygenerować kod, który umożliwi nam połączenie się z usługą internetową PayPala. Na początek musimy zdobyć plik WSDL do naszego API. Po małych poszukiwaniach, znalazłem właściwy URL na tej stronie: https://developer.paypal.com/api/nvp-soap/PayPalSOAPAPIArchitecture/.

Nasz plik WSDL jest tutaj: https://www.paypal.com/wsdl/PayPalSvc.wsdl.

Czas przejść do easyWSDL

Generowanie kodu w easyWSDL

Rozbijmy tą operację na poszczególne kroki:

  1. Zakładamy konto w serwisie https://easywsdl.com.
  2. Klikamy link Generate, co spowoduje przejście do strony generatora.
  3. W tym miejscu możemy skonfigurować nasz generator. Większość ustawień można zostawić bez zmian. Najważniejsze opcje, które możemy/musimy zmienić:
    • WSDL Location – adres pliku WSDL naszego webservisu. W naszym przykładzie wprowadzamy wartość https://www.paypal.com/wsdl/PayPalSvc.wsdl.
    • Platform – język programowania, w którym chcemy wygenerować kod. U nas będzie to Kotlin/Android.
    • Package name – klasy w języku Kotlin lub Java zwykle są umieszczane w pakiecie. W tym miejscu możemy podać nazwę pakietu dla wygenerowanych klas. W naszym przykładzie będzie to com.example.paypaltester.wsdl. Zwróć uwagę, że do nazwy pakietu naszego przykładowego projektu dodałem końcówkę wsdl, co wyjaśniej w dalszej części wpisu.

      Są to podstawowe opcje, które ustawiamy za każdym razem, gdy korzystamy z easyWSDL. Są też inne, które możemy zmienić:
    • Add MTOM transferMTOM transfer służy do wydajnego przesyłania dużych plików binarnych z/do webservisu. Jeśli masz taki przypadek to zaznacz tą opcję. Pamietaj jednak, że Twój webserwis musi obsługiwać transfer MTOM.
    • Detect dictionaries – zaznaczenie spowoduje, że generator wykryje w pliku WSDL klasy słowników (np Hashmap, Map etc) i zamiast standardowo wygenerowanych klas, użyje wbudowanych w Kotlin (Javę) klas słowników.
    • Implement Parcerable – zaznacz, jeśli chcesz aby wygenerowane klasy implementowały interface Parcerable
    • Implement Serializable – jak wyżej, z tym że chodzi o interface Serializable
    • Import documentation from wsdl – w plikach wsdl mogą znajdować się opisy (dokumentacja) typów używanych przez dany webserwis. Zaznacz tą opcję, aby zaimportować je do wygenerowanego kodu w postaci komentarzy.
easyWSDL Generator  - ustawienia
  1. Gdy ustawiliśmy wszystkie opcje, czas kliknąć przycisk Generate. Po dłuższej chwili powinniśmy zobaczyć informację o zakończeniu generowania, a sam plik zip z kodem zacznie się automatycznie ściągać.
easyWSDL zakończenie generowania

  1. Rozpakujmy plik zip i zobaczmy co jest w środku:
    • ReadMe.txt – zawiera najważniejsze informacje wraz z instrukcją użycia wygenerowanego kodu
    • docs – dokumentacja w formacie HTML wygenerowanego kodu
    • libs – zewnętrzne biblioteki, które należy dołączyć do projektu, aby kod się kompilował
    • src – najważniejsza część, czyli wygenerowany kod, służący do łączenia się z naszym API

Korzystanie z wygenerowanego kodu

Teraz nadszedł czas, aby wygenerowany kod wykorzystać w naszym testowym projekcie. Przejdzmy zatem do Android Studio. Dobrą praktyką jest wydzielenie wygenerowanego kodu od reszty projektu, w związku z czym stworzyłem folder wsdl, do którego skopiuję całą zawartość katalogu src (dlatego właśnie podczas generowania do nazwy pakietu dodałem wsdl).

Skopiowane pliki źródłowe z easyWSDL do naszego projektu

Następnie należy dołączyć do naszego projektu wszystkie pliki jar znajdujące się w katalogu libs (w zależności od wybranych opcji, może nie być żadnych plików jar). W związku z tym, iż w generatorze wybrałem opcję Add MTOM transfer, to mam dwa pliki:

  • apache-mime4j-core-0.8.9.jar
  • apache-mime4j-dom-0.8.9.jar

Kopiuję je zatem do katalogu libs w naszym projekcie wraz z odpowiednimi wpisami w pliku build.gradle

android {
    //added this section because sometimes there are conflicts during build phase
    packagingOptions {
        exclude 'META-INF/AL2.0'
        exclude 'META-INF/LGPL2.1'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
    implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

    implementation files('libs/apache-mime4j-core-0.8.9.jar', 'libs/apache-mime4j-dom-0.8.9.jar') //<-easyWSDL dependencies
}

Czasem podczas kompilacji gradle zgłasza następujący błąd:

2 files found with path 'META-INF/DEPENDENCIES'.

Mój sposób, aby rozwiązać ten problem jest dodanie sekcji packagingOptions. Jeśli znasz czytelniku lepszy sposób, to daj znać w komentarzach.

W tym momencie kod powinien się poprawnie skompilować.

Następnym krokiem jest dodanie uprawnienia do łączenia się z Internetem. W tym celu dodajemy linijkę:

<uses-permission android:name="android.permission.INTERNET" />

w pliku AndroidManifest.xml.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.PayPalTester"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.PayPalTester.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Pozostaje nam dodanie kodu, który korzystając z klas wygenerowanych przez easyWSDL, połączy się z PayPalem i pobierze stan naszego konta.

Struktura wygenerowanego kodu easyWSDL

W zależności od wielkości webserwisu, wygenerowanych klas może być nawet kilkaset, dlatego skąd mamy wiedzieć od czego zacząć?

Wygenerowane klasy możemy podzielić na:

  • Dane – są to wszystkie klasy, które służą do przesyłania danych z usługi internetowej. W tej kategorii mieszczą się wygenerowane enumy, kolekcje itp. Znajdziemy je jako parametry metod oraz to co one zwracają.
  • Klasy serwisów – jedna lub więcej klas reprezentujących webserwis. Te klasy nas najbardziej interesują, gdyż od nich zaczynamy programowanie
  • Klasy pomocnicze – wykorzystywane wewnętrznie przez easyWSDL. Zwykle nas nie interesują.

Z powyższego opisu wynika, że spośród tych wszystkich wygenerowanych klas musimy odnaleźć klasy serwisów. Możemy to zrobić na dwa sposoby:

  1. Na stronie easyWSDL po wygenerowaniu klas

Na poniższym obrazku pokazałem, gdzie znajduje się informacja o klasach serwisowych. Wystarczy skopiować nazwę takiej klasy i wyszukać ją wsród wygenerowanych plików

easyWSDL - nazwy klas serwisowych
  1. W pliku ReadMe.txt

W wygenerowanym pliku zip znajduje się plik ReadMe.txt, w którym znajduje się wiele praktycznych informacji, łącznie z instrukcją jak korzystać z kodu. Jeden z punktów zawiera pseudokod w stylu:

val service = PayPalAPISoapBinding()
service.MethodToInvoke(...)

W tym przypadku PayPalAPISoapBinding jest nazwą klasy serwisowej, która nas interesuje.

Wywołanie metody GetBalance za pośrednictwem easyWSDL

Pozostaje nam teraz po prostu użyć klasy serwisowej i wywołać wybraną metodę API. W naszym przypadku chcemy pobrać stan konta PayPal:

val service= PayPalAPISoapBinding("https://api-3t.paypal.com/2.0/")
val request= GetBalanceRequestType()
val credentials= CustomSecurityHeaderType()
credentials.Credentials= UserIdPasswordType()
credentials.Credentials!!.Username="podaj swojego użytkownika API"
credentials.Credentials!!.Password="podaj hasło "
credentials.Credentials!!.Signature="podaj podpis"
request.Version="200"
request.ReturnAllCurrencies="1"

val res=service.GetBalance(request,credentials)

var str=""
for (bal in res!!.GetBalanceResponse_1!!.BalanceHoldings)
{
      str=str+bal.currencyID+" = "+bal.value+"\r\n"
}

Po wygenerowaniu klas z PayPala, domyślnie łączą się one z kontami sandboxowymi. Aby nasz kod łączył się z produkcyjnym serwerem, należy ustawić odpowiedni URL (tak jak zrobiłem to powyżej). Listę dostępnych endpointów PayPala znajdziesz tutaj.

Zwróć uwagę, że w powyższym kodzie należy podać dane autoryzujące, które stworzyliśmy wcześniej. Dodatkowo należy podać wersję (nie ma ona dużego znaczenia, 200 działa dobrze 😉 ). Parametr ReturnAllCurrencies informuje PayPal, aby zwrócił saldo dla wszystkich walut w portfelu.

Powyższy kod wywoływany jest po kliknięciu w przycisk. W związku z tym, iż operacje sieciowe nie mogą być wykonywane w wątku głównym aplikacji, wykorzystałem lifecycleScope:

lifecycleScope.launch(Dispatchers.IO) {
    //kod pobierający saldo z PayPal
}

Tak wygląda nasza aplikacja z wyświetlonym stanem konta PayPal.

Testowa aplikacja wyświetlająca stan konta PayPal

Podsumowanie

Jak widać w naszym przykładzie, korzystanie z API SOAP na platformach mobilnych może być banalnym zadaniem, gdy skorzystamy z narzędzi do generowania, takich jak https://easywsdl.com. W przypadku chęci samodzielnego zaimplementowania całej komunikacji, byłoby to zadanie bardzo czasochłonne.

Mimo iż nasz przykład bazował na języku Kotlin i platformie Android, to w bardzo podobny (i prosty) sposób, korzystamy z easyWSDL dla pozostałych języków i platform (np. Swift, Flutter, itp).

Jeśli masz dodatkowe pytania lub pomysły na następne artykuły związane z generatorem easyWSDL, daj znać w komentarzach.

Share