Połączenie Ksoap2 z Android

0

Witam, od kilku dni zmagam się z połączeniem ksoap2 z Androidem jako klient.
Sytuacja wygląda następująco. Mam napisany webservice najprostszy z możliwych klasyczny "hello".
Web Service:

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.ejb.Stateless;

/**
 *
 * @author bednar
 */
@WebService(serviceName = "Sendit", targetNamespace="http://bednar.org/")
@Stateless()
public class Sendit {

    /**
     * This is a sample web service operation
     */
    @WebMethod(operationName = "hello")
    public String hello(@WebParam(name = "name") String txt) {
        return "Hello " + txt + " !";
    }
} 

Oraz klienta na Androidzie:

 
package bednar.org.send;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.ksoap2.serialization.PropertyInfo;

public class MainActivity extends AppCompatActivity {

    private static final String NAMESPACE = "http://bednar.org/";
    private static String URL = "http://192.168.2.106:8080/Sendit/Sendit?WSDL";
    private static final String METHOD_NAME = "hello";
    private static final String SOAP_ACTION =  "http://bednar.org/hello";

    private TextView lblResult;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lblResult = (TextView) findViewById(R.id.result);

        SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

        PropertyInfo propInfo=new PropertyInfo();
        propInfo.name="txt";
        propInfo.type=PropertyInfo.STRING_CLASS;

        request.addProperty("hello", "John Smith");
        
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

        envelope.setOutputSoapObject(request);
        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

        try {
            androidHttpTransport.call(SOAP_ACTION, envelope);

            SoapPrimitive  resultsRequestSOAP = (SoapPrimitive) envelope.getResponse();

            lblResult.setText(resultsRequestSOAP.toString());


        } catch (Exception e) {

        }

    }
}

Wszystko się kompiluje jak należy. Prześledziłem kod niejednokrotnie lecz nie mogę znaleźć błędu.
Sprawdziłem działanie webservisu poprzez uruchomienie w NetBeans na serverze GlassFish. W przeglądarce po wpisaniu http://localhost:8080/Sendit/Sendit?WSDL wczytuje się składnia prawidłowo oraz przy testowaniu działa jak należy.

Więc błąd raczej znajduje się po stronie klienta Androida.
Za wszystkie sugestie oraz podpowiedzi będę wdzięczny.
Doda jeszcze, że próbowałem uruchomić przykład ze strony http://www.ibm.com/developerworks/webservices/library/ws-android/index.html

0
  1. Dlaczego korzystasz z SOAP pod Androida? To niezwykle słaby kontrowersyjny pomysł.
  2. Czy testowałeś usługę np, za pomocą SoapUI? Czy potrafisz ręcznie zbudować kopertę?
  3. Możliwe, że nie do końca rozumiesz co robisz:
private static String URL = "http://192.168.2.106:8080/Sendit/Sendit?WSDL";

Na pierwszy rzut oka wygląda, że chcesz wysłać do adresu WSDL, a nie endpointa! Sprawdź to za pomocą SoapUI. Adres endpointa powinien być w WSDL. SoapUI powoli to łatwo zweryfikować.
4. Biblioteka kSOAP2 to jest straszne badziewie. Nie używaj tego. Jak już musisz zbuduj sobie kopertę ręcznie i wyślij na chama XML za pomocą klienta Http. Mniej świadomość, że jak kontrakt (WSDL) się zmieni będziesz musiał zmodyfikować buildera. Najlepiej nie rób tego i migruj na REST.

0

Ale bzdury opowiadasz kolego wyżej. Sam jesteś badziewie i w ogóle idź stąd. A Rest to niewygodne g**no, omijać z daleka.

Co do ksoap2 to polecam: http://www.wsdl2code.com/pages/home.aspx

Podajesz mu wsdl-a a wypluwa ci kod klienta. Gotowy i działający. Szybko wygodnie i pięknie. Nie migruj kolego na resta i nie rób kroku wstecz.

0

Tak btw ten kod nigdy nie ma prawa zadziałać bo wykonujesz operacje sieciowe w głównym wątku, co jest zabronione. Mógłbyś także pochwalić się jaki wyjątek dostajesz.

0

Rest niekoniecznie musi być niewygodny: http://projects.spring.io/spring-android/.

Z kSOAP2 kiedyś poległem (może źle się za to zabrałem), ale się wycofuje z poprzedniego stwierdzenia. Na poparcie tezy dlaczego uważam, że dla klienta mobilnego SOAP jest słaby przesyłam raport:
http://www.ateam-oracle.com/performance-study-rest-vs-soap-for-mobile-applications/
Jak aplikacja nie musi się skalować to nie ma znaczenia.

0

Od zawsze wiadomo, że rest jest szybszy od soap i to nie jest żadna tajemnica. Wszystko jednak zależy od konkretnego przypadku, coś za coś. I to nie tylko mobilnego klienta dotyczy.

A generowanie/pisanie pojo dla springa jest znacznie mniej wygodne niż użycie np takiego generatora online dla soap. Już nie mówiąc o wygodzie uzycia - w soap wywołujesz normalnie metodę i dostajesz wynik, tak jakby to była normalna metoda klasy. W rest tak ładnie i spójnie to nie wygląda, a jak json się zmieni to też musisz zmieniać pojo u siebie.

0

No tak, generalnie SOAP ma np. wsparcie dla WS-Security. Używa się go w aplikacjach typu PITy. Podoba mi się idea zastąpienia aplikacji typu wsimport generatorem. Sam tak robię w aplikacjach JEE. Jednak po prostu mi ani kolegom kSOAP2 nie zadziałało i zawsze skończyło się na REST, w bardzo prostych przypadkach jak pisałem wyżej budowaniem ręcznym koperty (metoda siłowa, dopuszczalna dla małych aplikacji) i wywołaniem HttpClienta. To antypattern i nie lubię robić takich rzeczy. Bardzo możliwe, że błąd był po naszej stronie. Sam w przyszłości zamierzam spróbować kSOAP2 jeśli rzeczywiście rozwiązuje problem i nie będę wybierał rodzaju WS.

0

Nie wiem jak ksoap2 zadziała w przypadku bardziej zaawansowanych przypadków, jak np wssecurity. Ale z wywoływaniem w miarę prostych webserwisów, także ze złożonymi typami danych problemu nie miałem

0

Kolego Z dzięki za podpowiedz na pewno skorzystam z w/w generatora lecz był bym wdzięczy za pomoc przy rozwiązaniu problemu z kodem wklejonym przeze mnie. Ten technologie narzucił mi Profesor z uczelni wiec muszę się ich trzymać. Co do błędu przy wykonywaniu to własnie ani android studio nic nie zgłasza ani aplikacja na telefonie wiec jestem w ślepej uliczce. Wszystko się wykonuje lecz nie otrzymuje oczekiwanego efektu.

margot90 masz racje nie wszystko jest mi tu jasne stąd moje błędy. Lecz już trochę czasu spędziłem by uruchomić to i z własnej ciekawości chciał bym wiedzieć gdzie popełniam błędy.

0

A co ma ci zgłaszać jak wygasiłeś wyjątek? Przedubugguj to i zobacz co jest źle. Nikt tu szklanej kuli nie ma. Najprędzej pierwszy powód to wykonywanie operacji sieciowych w wątku gui

0

Oki poradziłem sobie. Użyłem trochę innego kodu. Lecz wciąż mam problem z wysłaniem zmiennej do Web service.
Web service ma prostą metodę która przyjmuje string a ma wypluć Hello "tu string", a wywala tym czasem Hello "null". Jeśli się znacie proszę o podpowiedz.
Tak wygląda mój web service.

<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://bednar.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://bednar.org/" name="Sendit">
<types>
<xsd:schema>
<xsd:import namespace="http://bednar.org/" schemaLocation="http://localhost:8080/Sendit/Sendit?xsd=1"/>
</xsd:schema>
</types>
<message name="hello">
<part name="parameters" element="tns:hello"/>
</message>
<message name="helloResponse">
<part name="parameters" element="tns:helloResponse"/>
</message>
<portType name="Sendit">
<operation name="hello">
<input wsam:Action="http://bednar.org/Sendit/helloRequest" message="tns:hello"/>
<output wsam:Action="http://bednar.org/Sendit/helloResponse" message="tns:helloResponse"/>
</operation>
</portType>
<binding name="SenditPortBinding" type="tns:Sendit">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="hello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="Sendit">
<port name="SenditPort" binding="tns:SenditPortBinding">
<soap:address location="http://localhost:8080/Sendit/Sendit"/>
</port>
</service>
</definitions> 

A tu bezpośrednio w java:

package bednar.org;

import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.ejb.Stateless;

/**
 *
 * @author bednar
 */
@WebService(serviceName = "Sendit", targetNamespace="http://bednar.org/")
@Stateless()
public class Sendit {

    /**
     * This is a sample web service operation
     */
    @WebMethod(operationName = "hello")
    public String hello(@WebParam(name = "name") String txt) {
        return "Hello " + txt + " !";
    }
}

Z kodu w/w wygląda na to że jeśli odniosę się do zmiennej name to powinno być oki.
Na na androidzie przekazuje przez :Request.addProperty("name", tekst);

Czy wszystko robię prawidłowo?

0

No to wygeneruj kod klienta tamtym generatorem i go użyj. Przecież kod z generatora też używa kSoap2, więc o co ci chodzi? Zobaczysz co miałeś źle w swoim jak sobie porównasz.

0

Na SoapUI działa.

Zapytanie:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:bed="http://bednar.org/">
   <soapenv:Header/>
   <soapenv:Body>
      <bed:hello>
         <!--Optional:-->
         <name>TEST</name>
      </bed:hello>
   </soapenv:Body>
</soapenv:Envelope>

I odpowiedz:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:helloResponse xmlns:ns2="http://bednar.org/">
         <return>Hello TEST !</return>
      </ns2:helloResponse>
   </S:Body>
</S:Envelope>

Wszystko działa prawidłowo lecz gdy wezmę podgląd zapytania RAW jest jedna niepokojąca sprawa. W miejscu gdzie jest SOAP_ACTION wartość jest pusta.

POST http://192.168.2.103:8080/Sendit/Sendit HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""      <-------------------- o tutaj
Content-Length: 275
Host: 192.168.2.103:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

I kolejna nie niepokojąca rzecz to, że na serwerze GlassFisz dostaje błąd przy wysłaniu zmiennej name Info: Received WS-I BP non-conformant Unquoted SoapAction HTTP header: http://bednar.org/hello

0

https://github.com/bednar1529/android/tree/master/Web_service Proszę to są źródła klienta.
Już brak mi pomysłów na uruchomienie tej apki.

0

W poprzednich postach dostałeś sugestię, aby wygenerować klienta online. Nie widzę kodu wygenerowanego klienta. Spróbuj tak jak było napisane wcześniej. Jeśli zfailujesz na wygenerowanym kliencie, zobacz co rzeczywiście odbierasz, tam gdzie masz serwer GlassFish (mam na myśli komunikat XML). Możesz użyć do tego sniffera np. WireShark: https://www.wireshark.org/. Tak samo jak masz VM możesz zobaczyć snifferkiem co wysyłasz. Bez tego jesteś ślepy.

0

Problem rozwiązany. Wystarczyło w kodzie klienta na androidzie wywalić linijkę soapEnvelope.dotNet = true; gdyż korzystałem z web serwisu java, a opis na którym się wzorowałem odwoływała się do web serwisu dotnetowego :).

1 użytkowników online, w tym zalogowanych: 0, gości: 1