PZR420 - ćw - temat D

WebServices, SOAP

Podstawowe informacje.

WebServices  (skrót WS) pełnią funkcję podobną do tej pełnionej przez Corbę ...

Definiuje się "usługę" czyli w zasadzie zbiór operacji w pewnym namespace XML,
klient może wywoływać te operacje,
parametry i wynik operacji mogą być złożonymi strukturami danych ...

Niezależność od platformy!!! (składniki WS mogą pochodzić z różnych światów)

Różnice WebServices vs Corba:
  1. klient WS może być BARDZO prosty (np może to być skrypt bash, gdyż komunikaty SOAP sa "tekstowe" ...)
  2. kliet/ serwer WS mogą mniej o sobie wiedzieć niż w przypadku Corby ("luźne powiązanie")
  3. WS nie są aż tak bardzo "zorientowane obiektowo" jak Corba
  4. brak w WS pojęcia referencji do zdalnego obiektu - czyli wszystko jest przekazywane przez wartość
  5. artykuły porównujące Corbe i WS/SOAP:
    * http://www.xs4all.nl/~irmen/comp/CORBA_vs_SOAP.html
    * http://www2002.org/CDROM/alternate/395/
  6. ???
Przykładowy dokument XML (na czerwono to komentarz!):
dokument XML składa sie z zagnieżdżonych elementów;
elementy mogą posiadać atrybuty; mogą także zawierać zwykły tekst ...
  Istnieją narzędzia programistyczne (API) do parsowania i budowania dokumentów XML;
  narzędzia te są "standardowe" czyli dostępne w różnych językach i wszędzie "wyglądające tak samo";
  przykłady takich narzędzi:
    DOM - przechowuje dokument XML w pamięci jako drzewo DOM, które można modyfikować
        i serializować spowrotem do pliku

    SAX - parser zdarzeniowy, który nie przechowuje całego dokumentu w pamięci a jedynie wykonuje
       zaprogramowane operacje po napotkaniu pewnej "cechy" dokumentu XML
       (np dla elementu o zadanej nazwie)
<?xml version="1.0" encoding="UTF-8"?>
<book>
<chapter to jest element o nazwie "chapter">
www eee rrr 123 321 111 222 element zawiera tekst
</chapter koniec elementu>
<chapter ID="ch01" ID2="ch01_2" qqq="tra la la" to są atrybuty>
qqqqqqqqqqqqqqqqqqqqqq
</chapter>
<qqq>
<www1>
qqq www eee
</www1>
<www2/> element ktory niczego nie zawiera może tak wygladać
<www3/>
</qqq>
</book>


Warstwy, z jakich składa sie WS:
  1. transport
    pozwala przesyłać komunikaty; używa się: http, ale także: smtp, ftp(!), ...
  2. SOAP (=Simple Object Access Protocol )
    określa zawartość komunikatów: komunikaty SOAP sa dokumentami XML !!!;
    w komunikatach SOAP można zapisać DOWOLNĄ strukturę danych (także z wsk/ref) !!!;
    SOAP może (ale nie musi) działać w trybie rpc;
    SOAP RPC: "wywołanie metody" oznacza wysłanie komunikatu SOAP (zawierajacego namespace, nazwę metody, jej parametry) do serwera oraz odebranie wyniku także w postaci komunikatu SOAP;
    istnieją różne style SOAP: rpc i document (chodzi o postać komunikatów soap)
    specyfikacja SOAP 1.1 http://www.w3.org/TR/soap11/
    specyfikacja SOAP 1.2 http://www.w3.org/TR/soap12-part1/
  3. WSDL (=WS Description Language)
    opis usługi (m.in. definiouje jakie są metody, jakie są ich parametry/ wynik, jakie są typy parametrów itp);
    może istnieć "wsparcie WSDL" po stronie klienta i/lub serwera ...
       po stronie klienta: automatyczne tworzenie pieńka na podstawie pliku wsdl
       po stronie serwera: automatyzne generowanie pliku wsdl (np na podstawie interfejsu Javy lub pliku .h) oraz tworzenie szkieletu
    specyfikacja WSDL http://www.w3.org/TR/wsdl
  4. UDDI (=Universal Description, Discovery, and Integration)
    spis usług; dostępny także programowo ...
Uwaga: używanie wyższych warstw (wsdl, uddi) nie jest obowiązkowe;
   w niektórych zastosowaniach buduje się serwery SOAP bez WSDL.

Uwaga 2: specyfikacje http://www.w3.org/TR/soap11 oraz http://www.w3.org/TR/wsdl
   należy czytać PO wykonaniu zadań (nie PRZED), gdyż wymagają one pewnego doświadczenia ...

Przykłady komunikatów SOAP:
	# to jest komunikat soap z "żądaniem" wywołania metody pomnozRazy2 z argumentem {1 2}
# widać jak jest zakodowana tablica (wg standardu soap1.1)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="qqq">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns:pomnozRazy2>
<par SOAP-ENC:arrayType="xsd:float[2]">
<item>1.0</item>
<item>2.0</item>
</par>
</ns:pomnozRazy2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

# komunikat soap z wynikiem działania operacji pomnozRazy2
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="qqq">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns:pomnozRazy2Response>
<return SOAP-ENC:arrayType="xsd:float[2]">
<item>2.0</item>
<item>4.0</item>
</return>
</ns:pomnozRazy2Response>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>



Implementacje WS.

gSOAP/ C i C++
Link do strony gSOAP: http://gsoap2.sourceforge.net/
Dokumentacja gSOAP: http://www.cs.fsu.edu/~engelen/soapdoc2.html
Artykuł wstępny o gSOAP:  "gSOAP & Web Services"

Instalacja oprogramowania:
1. rozpakuj gsoap.tar.gz
2. dodaj do PATH katalog z programami wsdl2h i soapcpp2
3. w katalogu, w ktorym programujesz pod gsoap utwórz dowiązania symboliczne do plikow: stdsoap2.c i stdsoap2.h
    (albo po prostu skopiuj tam te pliki!)

Użycie programów narzędziowych gsoap - klient:
tworzenie pienka klienta na podstawie pliku wsdl:
    wsdl2h -c -o q.h http://localhost:8015/soap/qqq/wsdl
      # -c dla jezyka C; tworzy tylko q.h
tworzenie pieńka (czyli serializatorów/deserializatorów) na podstawie pliku q.h:
    soapcpp2 -C -c q.h
      # -C tylko kod dla klienta!; oprócz pienka tworzy też przykładowe komunikaty SOAP!!!
      # -c język C a nie C++ (koniecznie podać tę opcję !!!)
kompilacja (alternatywa):
    gcc q.c soapC.c soapClient.c stdsoap2.c
    gcc q.c soapC.c soapClient.c stdsoap2.so
      # jeśli zrobiliśmy bibl dynamiczna z stdsoap2.c; pamiętać o LD_LIBRARY_PATH !
    gcc -DDEBUG q.c soapC.c soapClient.c stdsoap2.c
      # tworzy sie logi w biezacym katalogu (sa tam wszystkie wysyłane/odbierane komunikaty soap)

Użycie programów narzędziowych gsoap - serwer:
tworzenie szkieletu (czyli serializatorów/deserializatorów) na podstawie pliku q.h:
tworzy także plik wsdl oraz przykładowe komunikaty soap!!!
    soapcpp2 -S -c q.h
       # -S tylko kod dla serwera!
       # -c język C a nie C++ (koniecznie podać tę opcję !!!)
kompilacja (alternatywa):
    gcc q_serv.c soapC.c soapServer.c stdsoap2.c
    gcc q_serv.c soapC.c soapServer.c stdsoap2.so
    gcc -DDEBUG q_serv.c soapC.c soapServer.c stdsoap2.c


Programowanie klienta soap:
zakladam, ze nie mamy pliku wsdl i recznie tworzymy klienta soap ...
(jesli mamy plik wsdl to możemy poniższy plik nagłówkowy wygenerować automatycznie !!!)
// --- to jest plik q.h ---
// dyrektywy dla soapcpp2 zaczynaja się od //gsoap ...

//gsoap ns service name: qqq
//gsoap ns service namespace: qqq
// to jest nazwa namespace XML w ktorej serwer soap definiuje operacje!
// "ns" to prefix tego namespace-u
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded

struct t_tab { // tak sie impl tablice pod gsoap !!!
float *__ptr; int __size;
};
int ns__pomnozRazy2(struct t_tab par, struct t_tab *return_);
// "ns" w "ns__" to prefix namespace XML !!!
/* UWAGA 1:
NAZWY PARAMETROW maja znaczenie !!!
parametr wynikowy MUSI sie nazywac "return_" !!!
nazwa tego parametru ZALEZY od serwera soap !!!
UWAGA 2:
konwencja gsoap: wynik jest zwracany przez ostatni paramter,
który musi być przekazywany przez wsk (lub ref w C++)
NALEŻY definiować specjalną strukturę, której nazwa kończy się na "Response"
do zwracania wyniku ... (w tym przykładzie tego nie zrobiono)
struktura taka może wyglądać tak:
struct ns__pomnozRazy2Response {struct t_tab *return_};
*/


// --- to jest plik q.c ---
// ten plik przygotowujemy ręcznie !
// klient soap ...

#include "qqq.nsmap"
#include <stdio.h>

int main()
{
struct soap soap;
soap_init(&soap); // initialize runtime environment

struct t_tab ar;
struct t_tab ar2;

// przygotowanie danych wejsciowych ...
const int dlugosc=55;
ar.__ptr= soap_malloc(&soap, dlugosc*sizeof(float));
// dane tak alokowane beda automatycznie zwalniane przy soap_end()
ar.__size= dlugosc;
int i;
for(i=0; i<dlugosc; i++) ar.__ptr[i]= i+1;

// wywołanie operacji ...
// - adres serwera to "http://localhost:8015/soap/qqq/go"
if( soap_call_ns__pomnozRazy2(&soap,
"http://localhost:8015/soap/qqq/go", "", ar, &ar2) !=SOAP_OK ) {
soap_print_fault(&soap, stderr);
}

// pokazanie argumentu i wyniku:
int d;
printf("argument:\n"); d= ar.__size;
for(i=0; i<d; i++) printf("%f ", ar.__ptr[i]); printf("\n");
printf("wynik:\n"); d= ar2.__size;
for(i=0; i<d; i++) printf("%f ", ar2.__ptr[i]); printf("\n");

// zwalnianie pamiecie i inne ...
soap_end(&soap); // clean up and remove deserialized data
soap_done(&soap); // detach environment (last use and no longer in scope)

return 0;
}



Programowanie serwera soap:
// --- plik q.h ---
// ten plik przygotowujemy ręcznie !

//gsoap ns service name: qqq
//gsoap ns service namespace: qqq

//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
// gsoap ns service style: document
// gsoap ns service encoding: literal

struct t_tab { // bez tej struktury NIE MA tablicy!!
float *__ptr; int __size;
};
int ns__pomnozRazy2(struct t_tab par, struct t_tab *return_);


// --- plik q_serv.c ---
// ten plik przygotowujemy ręcznie !
// budujemy serwer 1-wątkowy ...

#include "qqq.nsmap"
// ten plik wlacza tez inne pliki nagłówkowe!

int main() {
struct soap soap;
soap_init(&soap);

int b=soap_bind(&soap, NULL, 10000, 100);
// 10000 -nr portu na ktorym serwer czeka na klientow
if(b<0) {soap_print_fault(&soap, stderr); soap_done(&soap); exit(1);}

while (1) {
soap_accept(&soap); // czeka na soap msg
soap_serve(&soap); // obsluga klienta
// UWAGA: można w tym miejscu tworzyc wątek POSIX i przekazywać mu obsługę klienta !!!
soap_end(&soap); // zwalniani pamieci
printf("."); fflush(stdout);
}

return 0;
}

// implementacja operacji "pomnozRazy2"
int ns__pomnozRazy2(struct soap* soap,
struct t_tab x, struct t_tab *return_) {
int i;

int d= x.__size; float *f1= x.__ptr;

printf("serwer/dbg: argument:\n");
for(i=0; i<d; i++) printf("%f ", x.__ptr[i]); printf("\n");

// zakladam ze return_ wskazuje na istniejaca struct t_tab !!!
return_->__size= d;
float *f2= soap_malloc(soap, d*sizeof(float));
return_->__ptr= f2;

for(i=0; i<d; i++) f2[i]= 2*f1[i];

return SOAP_OK;
}

// ---------------- koniec -------------------



Drugi przykład serwera soap w gsoap, wraz z klientem w tclsoap gsoapwin333.txt
w przykładzie tym występują procedury zwrocCos i zwrocCos2
zwracajace i przyjmujace przez parametr złożoną strukture danych
(tablice struktur z 3 polami)
Uwaga: w przypadku serwera gsoap i klienta tclsoap wymaga się aby
nazwy typów byly identyczne u klienta i na serwerze!!
dodatkowo powinny być umieszczone w odpowiednim namespace xml-a!

Kolejny, trochę bardziej rozbudowany przykład użycia gsoap: gsoap555.txt
tworzy sie serwer soap pod gsoap
podano przykłady klientów tclsoap i java/axis
do zwracania wyniku używa się struktury *Response (wcześniej brakowało takiego przykładu!)

Streszczenie zasad przekazywania parametrów/ zwracania wyniku w gsoap:

UWAGA (z 05.2011):
   1. jeśli plik nagłówkowy (np. q.h) jest generowany automatycznie przy pomocy wsdl2h,
       to zanim zaczniemy pisać kod klienta trzeba zajrzeć do tego pliku !!!
       zorientować się, jakie są prototypy funkcji,  jak się przekazuje parametry/ zwraca wynik itp...
   2. po następującym przekształceniu:
           q.h -(soapcpp2)-> q.wsdl -(wsdl2h)-> q2.h
       q2.h NIE jest dokładnie taki sam jak q.h !!!
       + (zapewne z powodu błędu) wsdl2h zmienia sposób reprezentacji struktur:
            zamiast pola "__ptr" jest pole "item", zamiast "__size" jest "__sizeitem" (trzeba to uwzględnić)
       + wartości są zwracane zawsze przy pomocy struktury *Response
       + prefiks namespace nazywa sie "ns1" a nie "ns" jak w powyższych przykładach




Axis/ Java
Dokumentacja Axis: http://ws.apache.org/axis/java/index.html
Instalacja Axis 1.4:
1. sciagnij i rozkompresuj axis.tar.gz
2. dodaj wszystkie pliki jar na początek zmiennej CLASSPATH
(Może być też przydatny tclBlend mini-blend.tar.gz  do interaktywnych  eksperymentów z konsoli ...)

W Javie istnieją oficjalne specyfikacje WebService: JAX_RPC i JAX_WS (następca JAX_RPC) ...
omawiany w tym rozdziale toolkit Axis1 jest implementacją specyfikacji JAX_RPC;
implementacją JAX_WS jest Axis2...
   cecha charakterystyczna Axis2: zakazano używania stylu rpc/encoded!


Programowanie klienta SOAP (bez wsdl):
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import javax.xml.namespace.QName;

public class TestClient2
{
public static void main(String [] args) {
try {
String endpoint = "http://localhost:8015/soap";
Service service = new Service();

Call call = (Call) service.createCall();

call.setTargetEndpointAddress( new java.net.URL(endpoint) );
call.setOperationName(new QName("urn:tclsoap:Test1", "square") );
// uri serwisu (=namespace xml serwisu) + nazwa metody

call.setOperationStyle("rpc");
call.setOperationUse("encoded");

// def nazwy i typy parametrów metody "square" oraz typ wyniku
// Uwaga: nie musimy tego robic; axis sam zgadnie typy przy pomocy "introspekcji" !!!
// Uwaga2: to jest proste tylko dla prostych typów !!!
 call.addParameter("num",
org.apache.axis.Constants.XSD_INT,
javax.xml.rpc.ParameterMode.IN);
call.setReturnType(org.apache.axis.Constants.XSD_INT);

// wywołanie operacji:
// - parametry to tablica Object! każdy parametr jest osobnym elementem tej tablicy
// w takiej kolejnosci w jakiej wywolywano addParameter()
Integer ret = (Integer)call.invoke( new Object[] { 10 } );
System.out.println(ret);

// 2 wywołanie operacji:
 ret = (Integer)call.invoke( new Object[] { 11 } );
System.out.println(ret);

} catch (Exception e) {
System.err.println(e.toString());
}
}
}

Programowanie klienta SOAP (bez wsdl) - przykład z konosoli Jacl/tclBlend:
package re java

set service [java::new org.apache.axis.client.Service]
set call [java::cast org.apache.axis.client.Call [$service createCall]]
$call setTargetEndpointAddress "http://localhost:8015/soap/qqq/go"
$call setOperationName [java::new javax.xml.namespace.QName \
"qqq" "pomnozRazy2"
]
$call setOperationStyle "rpc"
$call setOperationUse "encoded"

# przekazujemy tabl int... (parametry sa repr. przez tablice $q1)
set q1 [java::new {Object[]} 1]
$q1 set 0 [java::new {int[]} {} {1 2 3}]

set q2 [$call invoke $q1]

[java::cast {float[]} $q2] getrange
#% 1.0 2.0 3.0

# przekazujemy tabl double...
set q1 [java::new {Object[]} 1]
$q1 set 0 [java::new {double[]} {} {1 2 3 4 5 6.111}]

set q2 [$call invoke $q1]

[java::cast {float[]} $q2] getrange
#% 1.0 2.0 3.0 4.0 5.0 6.11100006104


Programowanie klienta WebServisu z użyciem pieńka wygenerowanego z wsdl-a:
# najpierw wygenerowac pieniek (z pliku wsdl) przy pomocy :
# java org.apache.axis.wsdl.WSDL2Java plik.wsdl
# pieniek to klasy Javy ... należy je skompilować!

# Uwaga: pieniek ukrywa wiele szczegółów, z którymi mamy do czynienia
# programoując "czystego" klienta soap !!!

package re java

set x [java::new www123.QqqLocator]
# nazwa serwisu to "www123"
# Uwaga 2: zdaje sie, ze axis wymaga aby "nazwa serwisu" = "namespace serwisu"
set y [$x getqqqSoap]

join [java::info methods $y] \n
# to nam wyswietli m.in. operacje jakie można wywoływać!

$y helloWorld
# wywołanie operacji helloWorld

set par [java::new {float[]} {} {1 2 3 4 5 6}]
set y2 [$y pomnozRazy2 $par]
$y2 getrange
# wywołanie operacji pomnozRazy2 i odczytanie wyniku




tclSOAP
Uwaga: pakiet tclSOAP obsługuje jedynie tzw. styl RPC soap,
ponadto tclSOAP NIE obsługuje wsdl...


Dokumentacja tclSOAP: http://tclsoap.sourceforge.net/

Instalacja oprogramowania:
rozpakować archiwum tclsoap.tar.gz; dodać ścieżkę do auto_path; katalog "pkg" zawiera:
    tclsoap_tdom - implementacja soap1.1 (używa tdom)
    tclhttpd3.5.1 - mały serwer http; serwer soap WYMAGA tclhttpd
    tdom - obsluga XML w j. Tcl
rozpakować tcllib: tcllib.tar.gz; dodać ścieżkę do auto_path

Uwaga:
  tworząc klienta definiujemy WYŁĄCZNIE typy parametrów operacji,
  tworzac serwer definiujemy WYŁĄCZNIE typ wyniku operacji
    (gdyż tylko takie informacje są potrzebne do utworzenia komunikatu soap ...)

Programowanie klienta:
lappend auto_path ./pkg ./tcllib
package re SOAP

# w razie potrzeby tez mozna definiowac typy złożone przy pomocy rpcvar::typedef !!!

SOAP::create pomnozRazy2 \
-uri qqq \
-proxy http://localhost:8015/soap \
-params {x float()}
# -uri to namespace xml, w którym są zdef operacje serwisu
# -proxy do url serwera (adres serwera)
# -params definiuje nazwy i typy parametrów procedury
# "typ()" oznacza sekwencje elementów typu "typ"
# aby przekazac sekwencję struktur trzeba zdef. typ strukturowy
# przy pomocy rpcvar::typedef

pomnozRazy2 {1 2 3}
# powinno zwrocic: 2 4 6

SOAP::dump -request pomnozRazy2
SOAP::dump -reply pomnozRazy2
# wyświetla komunikaty SOAP !!!

SOAP::create zwrocMojeDane \
-uri qqq \
-proxy http://localhost:8015/soap \
-params {}
# powinno zwrocic strukture, chociaż jej typ nie zostal zdef u klienta!!!
# xml jest samoopisujący, zatem tclsoap potrafi zinterpretować to co metoda zwróci...

Programowanie serwera:
## uruchamiamy serwer tclhttpd ---------------------------------------
source ./pkg/tclhttpd3.5.1/bin/httpd_app.tcl
# MUSI byc dostepna biblioteka tcllib!!!
# (jesli nie ma to rozpakuj tcllib.tar.gz + "lappend auto_path ./tcllib")

## serwer SOAP -------------------------------------------------------
lappend auto_path ./pkg
package re SOAP::Domain

SOAP::Domain::register -prefix /soap
# rejestrujemy prefix url przeznaczony dla serwera soap

# namespace tcl-a w którym definiujemy operacje to równocześnie
# "abstrakcyjny" namespace xml tych operacji ...
# (czyli klienci soap powinni podać "-uri qqq")
namespace eval qqq {
proc pomnozRazy2 x {
set y {}
foreach e $x {lappend y [expr {2*$e}]}
return [rpcvar::rpcvar float() $y]
# do zwracanej wartosci DODAJEMY typ tej wartości !!!
# robi sie to przy pomocy komendy "rpcvar::rpcvar": rpcvar typ wartosc
}
SOAP::export pomnozRazy2
# eksportujemy procedure czyli czynimy ją operacją naszego serwisu
# (robic to KONIECZNIE pod namespace eval)
}

# bardziej zlożony przykład ...

package re rpcvar
rpcvar::typedef -namespace ns {
imie string
wiek int
lista_f float()
lista_i int()
} typMojeDane
# definiujemy nowy typ (stukture) o nazwie "typMojeDane"
# typ ten wystąpi w namespace o prefiksie "ns" w komunikatach soap!

namespace eval qqq {
proc zwrocMojeDane {} {
puts [info level 0]; # informacje diagnostyczne o wywolaniu tej proc
return [rpcvar::rpcvar typMojeDane {
imie "Michal"
wiek 123
lista_f {111.111 222.222}
lista_i {1 2 3 4 5}
}]
}
SOAP::export zwrocMojeDane
}




SOAP niskopoziomowo ...
Pokażemy jak można używać soap bez narzędzi/ pakietów "wysokiego poziomu" ...
... jedynie korzystając z narzędzia do przetwarzania XML-a w Tcl: "tdom"
pakiet tdom: tdom.tar.gz
dokumentacja tdom: www.tdom.org


# niskopoziomowy klient SOAP

package re tdom; # pakiet typu DOM do przetwarzania XML

# tworzymy zadanie/ komunikat soap ...
# Uwaga: przykładowy komunikat soap można wyciągnąć z gsoap !!!
set xml_req {<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns2="qqq">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<ns2:pomnozRazy2>
<x SOAP-ENC:arrayType="xsd:float[5]">
<item>0.0</item>
<item>1.0</item>
<item>2.0</item>
<item>3.0</item>
<item>4.1111111111111</item>
</x>
</ns2:pomnozRazy2>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
}

# tworzymy komunikat http ...
# Uwaga na prawidłowa długość w Content-Length !!!
set len [expr {[string len $xml_req]+[regexp -all \n $xml_req]}]
set http_req "POST /soap HTTP/1.0
Accept: */*
Host: localhost:8015
User-Agent: TclSOAP/1.6.7 (Linux)
Content-Type: text/xml
Content-Length: $len

$xml_req"

# wysylamy zadanie soap i odbieramy wynik soap ...
set s1 [socket localhost 8015]
puts $s1 $http_req; flush $s1
set reply [read $s1]
close $s1

# interpretujemy wynik soap ...
set i [string first "\n\n" $reply]
set xml_reply [string range $reply [expr {$i+2}] end]
dom parse $xml_reply q2
set r2 [$q2 selectNodes //return/item/text()]
puts ""
foreach e $r2 {puts [$e nodeValue]}

# --- koniec ---

Drugi przykład niskopoziomowego soap, z poziomou tcl, z użyciem pakietu tdom
oraz http: lowlevelsoap.txt




Zadania.

Zadanie 40.
Zaprogramuj aplikacje SOAP z metodą "pomnozRazy2" monożącą i zwracającą ciąg liczb float dowolnej długości;
klienci SOAP: 2 klientów: gsoap i tclsoap
serwer SOAP: dowolny (najprościej tclsoap)
Wstaw do sprawozdania także komunikaty SOAP wysyłane/ odbierane przez klienta.

Zadanie 41.
Zaprogramuj WebSerwis z metodą "pomnozDwieMacierze" mnożącą dwie podane macierze (o dowolnych wymiarach)
i zwracajacą wynik w postaci macierzy.
serwer: gsoap; wygenerowany plik wsdl ma byc udostępniony klientom!
klient: java/axis, pieniek wygenerowany aoutomatycznie z pliku wsdl,
wyprobuj takze klienta tclsoap (bez wsparcia wsdl!)

Zadanie 42.
Zaprogamuj WebService z metoda "zwrocDrzewo", która zwraca dynamiczne drzewo reprezentujące strukturę katalogów serwera;
przez dynamiczne drzewo rozumiem strukturę danych "ze wskazaniami";
serwer: gsoap lub axis
klient: gsoap lub axis (pieniek zbudowany z wsdl);
serwer i klient powinni być zaprogramowani używając INNEJ impl. soap!!!
wypróbuj także klienta tclsoap (w jaki sposob zostanie zwrocona struktura danych skoro klient NIC nie wie o jej typie ???)

Zadanie 43.
Zaprogramuj niskopoziomowo (bez użycia żadnych pakietów soap) klienta SOAP do serwera z zadania 40 (pomnozRazy2);
użyj języka innego niż tcl (bo rozwiązanie tcl jest w przykładzie).


... zastosowania dostępnych, darmowych webserwisów ...

Zadanie 44.

Napisz program tłumaczący z j. angielskiego na chinski.
Program powinien posiadać interfejs graficzny (z uwagi na chinskie znaki).
Wskazówka: użyj webserwisu http://www.webservicex.net/TranslateService.asmx?WSDL
chyba najwygodniej będzie użyc j. java/ axis ...

Zadanie 45 (*).
Napisz program pokazujący "mapę temperaturową" wybranego kraju (wybór nie musi być duży np Polska lub Niemcy).
Program powinien pokazywać okienko z mapką konturową danego kraju, ważniejsze miasta powinny być zaznaczone,
oraz panująca w nich obecnie temperatura naniesiona ...
Wskazówka: użyj webserwisu http://www.webservicex.net/globalweather.asmx?WSDL; serwis ten ma 2 metody: zwracająca listę miast danego państwa, oraz opisującą pogodę w danym mieście/państwie; rezultaty są zwracane w postaci prostych dokumentów XML, dlatego dobrze będzie użyc pakietu typu DOM (tcl/ tdom.tar.gz) do wyciągania informacji; opis pogody w mieście zawiera dodatkowe info takie jak lat/lon ...
Wskazówka dla programujących w tcl: istnieje prosty pakiet geograficzny "tkgeomap", który pozwala pokazać mapę, zmieniać skalę, przemieszczać mapę, umieszczać miasta z opisem itp; geomap.tar.gz; należy ten plik rozpakować (nieszczególną procedurę instalacyjną uprościlem); w środku jest przykładowe zastosowanie pakietu "geo01a.tcl", które należy uruchomić; dokumentacja w podkatalogu geomap_bin; obecnie istnieje TYLKO wersja linuxowa tego pakietu (niestety!).
Wskazówka dla programujących w javie: mapy geograficzne można tworzyć przy pomocy bibl. javowej OpenMap
patrz http://openmap.bbn.com/;

Znaczenie styl/use ...

styl "rpc"
  - interpretujemy webserwis jako "zdalne procedury"...
styl "document"
  - przesyłamy dokumenty XML (zgodne ze schematem w WSDL)
use "encoded"
  - struktury danych są kodowane w XML wg specyfikacji SOAP1.1 rozdz. 5
  - info o typach są w komunikacie SOAP (?)
use "literal"
  - komunikat SOAP musi być zgodny ze schematem w WSDL
  - info o typach sa wyłącznie w WSDL (?)

sensowne kombinacje styl/use:
  - rpc/encoded (obecnie odradza się tego używać; popularne w przeszłości...)
  - document/literal

Zadanie 46 (styl: rpc czy document? use: encoded czy literal?).
Autor artykułu http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/
twierdzi, że jedynie styl rpc/encoded umożliwia przekazywanie cyklicznych struktur danych (np list cyklicznych)
Sprawdź eksperymentalnie czy to prawda!!!
Zaprogramuj serwer przy pomocy gsoap:
  // tak sie ustawia styl i encoding pod gsoap ...
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
// gsoap ns service style: document
// gsoap ns service encoding: literal

// przyklad operacji zwrocCos2, zawracającej liste struktur ...
struct t_mojaStruct2 {
int a, b, c; struct t_mojaStruct2 *nast;
};
struct ns__zwrocCos2Response {
struct t_mojaStruct2 *return_;
};
int ns__zwrocCos2(struct ns__zwrocCos2Response *r);
oraz klienta axisowego, z pieńkiem wygenerowanym z pliku wsdl (utworzonym przez gsoap).

.......................................................

Zadanie 46a (serwer soap "obliczeń arkuszowych" w Excel-u; SOAP+COM)
W pewnym katalogu znajdują się pliki xls excela ...
Zawierają one dane wejściowe (częściowe) oraz formuły.
Należy utworzyć serwer soap pozwalający wykonywać zdalnie obliczenia na tych arkuszach,
tj wstawiać dodatkowe dane wejściowe i odczytywać wartości formuł.
Oczywiście impl. serwera powinna wykonywać czynności na arkuszach
za pomocą obiektów COM Excela ....
Wskazówka dla programujących w Tcl: użyć pakietu "tcom" to wykonywania operacji na ob. COM;
dokumentacja ob. COM jest dostępna wraz z Excelem i edytorem VB... przykład użycia tcom com_excel.txt
serwer soap utworzyć przy pomocy tclSOAP;

Zadanie 46b (serwer soap sprawdzający ortografię tekstów; SOAP+COM)
Utworz serwer soap szukający błędów ortograficznych w przesyłanym tekście ...
Wykorzystaj w tym celu możliwości ortograficzne Worda (serwer ob. COM).
Tcl: patrz przykład użycia tcom com_word.txt




SOAP i SSL/TLS

Jak zapewnić bezpieczeństwo usług opartych na SOAP ???

..................................................

SSL = Secure Socket Layer
TLS = Transport Layer Security
OpenSSL = implementacja SSL/TLS

Strona główna OpenSSL: http://www.openssl.org/
Definicja TLS i SSL: http://pl.wikipedia.org/wiki/TLS
Certyfikaty SSL: http://tldp.org/HOWTO/SSL-Certificates-HOWTO/index.html
   !!! tu jest dobry opis działania ssl - patrz rozdz 1.2 !!!

Pakiet Tls języka Tcl oraz przykłady: tcl_openssl.tar.gz

..................................................

Skrócony opis pojęć SSL/TLS:
..................................................

Co zapewnia SSL/TLS?
..................................................

Mały CA: skrypt CA.pl
patrz uwagi w "security01b.tcl"
(dopuki nie pojawią się lepsze wskazówki można używać plików pem w tcl_openssl.tar.gz)

..................................................

Przykład użycia pakietu Tls spod w języku Tcl:

Uwaga: dokumentacja pakietu tls jest w katalogu tls1.5
   a także tutaj: http://aspn.activestate.com/ASPN/docs/ActiveTcl/8.4/tls/tls.html
## uwierzytelnianie serwera --------------------------------------------
# - klient upewnia się, że serwer jest tym za kogo się podaje ...

lappend auto_path tls1.5
package require tls
# uwaga: jesli pakiet się nie ładuje to naprawdopodobniej
# brakuje bibliotek libcrypto.so.0.9.7 i libssl.so.0.9.7
# kopie tych bibl. znajduja sie w katalogu tls1.5
# wystarczy ustawic zmienna LD_LIBRARY_PATH:
# export LD_LIBRARY_PATH=...../tls1.5

## serwer
tls::socket -server obsluga \
-keyfile nowy-private.pem -certfile nowy-public.pem \
-password haslo \
10000
# -keyfile: klucz pryw serwera (moze byc zawarty w cert!)
# -certfile: cert serwera (zawiera klucz pub)
proc haslo {} {return "qwerty"}; # haslo do klucza pryw serwera
proc obsluga {s args} {
_puts "server socket $s"; # zakladam ze uruchamiamy to z konsola2c.tcl
tls::handshake $s
}

gets sock???

## klient
set s [tls::socket -require 1 -cafile cacert.pem localhost 10000]
# -require 1: klient żąda sprawdzenia certyfikatu serwera
# -cafile: certyfikat CA (chodzi m.in. o klucz publiczny CA w tym pliku
# dzięki któremu klient sprawdza podpis na certyfikacie serwera)
tls::handshake $s

puts $s "A ku ku !!!"; flush $s


## uwierzytelnianie serwera ORAZ klientow ---------------------------
# - serwer sprawdza swoich klientów
# - jak widac, nic nie szkodzi ze ser i kli uzywaje tego samego cert!

lappend auto_path tls1.5
package require tls

## serwer
proc haslo {} {return "qwerty"}
tls::socket -server obsluga -require 1 -cafile cacert.pem \
-password haslo -keyfile nowy-private.pem -certfile nowy-public.pem \
10000
proc obsluga {s args} {
_puts "server socket $s"
tls::handshake $s
}

# wyciąganie informacji o kliencie (z jego certyfikatu)
tls::status sock???

gets sock???

## klient
proc haslo {} {return "qwerty"}
set s [tls::socket -require 1 -cafile cacert.pem \
-password haslo -keyfile nowy-private.pem -certfile nowy-public.pem \
localhost 10000
]
tls::handshake $s

puts $s "A ku ku !!!"; flush $s

...................................................................

Zadanie 48
Zaprogramuj klienta i serwer SOAP używające SSL/TLS ...
Klient: tclSOAP;  Serwer: gsoap
Wskazówka:
Klient tclSOAP + SSL:
  wystarczy wykonać
       package re SOAP::https
  i potem uzywać:
       -proxy https://....
Serwer gsoap + SSL:
  patrz dokumentacja gsoap: soapdoc2.html
Uwaga:
  trzeba wymienić certyfikaty używane przez tclhttpd na aktualne (utworzyć je !!!)




Axis c.d. -  serwer

Jak uruchomić serwer (WebService) przy pomocy Axis ???

Potrzebujemy kontenera servletów w wersji >= 2.2:
może to być Tomcat 5.5
   okrojona wersja tomcata - bez dokumentacji i przykładów - mini-tomcat5.5.tar.gz

Należy zainstalować Axisa pod Tomcatem:
   plik axis_web-inf.tar.gz należy rozpakować w katalogu webapps Tomcata


Ważne pliki i katalogi:
   webapps/axis/WEB-INF/server-config.wsdd
        w tym pliku umieszczamy definicje naszego serwisu w formacie WSDD
         (zdaje się że ten plik powstaje po pierwszym uruchomieniu AdminClient-a ...
             java org.apache.axis.client.AdminClient deploy.wsdd
         )
   webapps/axis/WEB-INF/classes
        w tym katalogu umieszczamy skompilowane klasy naszego serwisu


Jak zbudować WebService (serwer) pod Axisem:
    1. definiujemy klase publiczną z metodami publicznymi (bedą to operacje WS ...)
    2. jeśli metoda otrzymuje/zwraca "strukturę" (w znaczeniu jak w j. C) to struktura ta
        powinna być zdefiniowana jako JavaBean, tj pola powinny być dostępne przez set/get
    3. tworzymy opis serwisu w formacie WSDD (opis tego formatu: dokumentacja Axisa/ Ref Guide) 
        i umieszczamy go w pliku server-config.wsdd w odpowiednim miejsu
    4. wszystkie skompilowane klasy (głowna i JBeany) umieszczamy w katalogu classes

Uwaga: wyżej opisany sposób definiowana WS jest w dużym stopniu "ręczny",
    ale można go zautomatyzować ...


Obsługa Tomcata:
    spis wszystkich zainstalowanych WS  odczytujemy przez:
        http://localhost:8080/axis/services
    WSDL serwisu MojPrzyklad (generowany w locie!) można odczytać tak:
        http://localhost:8080/axis/services/MojPrzyklad?wsdl
    uruchamianie/ wyłączanie serwera Tomcat (linux):
        przejść do katalogu bin i uruchamiać skrypty: ./startup.sh i ./shutdown.sh


Konkretny przykład (WS o nazwie MojPrzyklad):
// ta klasa to imlp. naszego WS ...
public class MojPrzyklad {
// operacja WS o nazwie zwrocBeana zwracajac pojedynczą strukture MojBean
public MojBean zwrocBeana() {
MojBean mb= new MojBean();
mb.setA(123);
mb.setB(321);
return mb;
}
}
// definicja Beana ...
public class MojBean {
private int a;
private int b;
public int getA() {return a;}
public void setA(int pa) { a=pa; }
public int getB() {return b;}
public void setB(int pb) { b=pb; }
}

<!-- definicja serwisu w formacie WSDD:
parametr className zawiera główną klasę naszego WS
parametr allowedMethods zawiera listę dostępnych operacji (* oznacza wszystkie)
każdy JavaBean musi byc zadeklarowany przy pomocy elem. beanMapping
atrybut languageSpecificType zawiera "java:pakiet.klasa" (przedrostek java: jest konieczny!)
UWAGA: prosze zwrocic uwage ze namespace MojBean nie moze sie nazywac
tak samo jak serwis i glowna klasa!
-->
<service name="MojPrzyklad" provider="java:RPC">
<parameter name="className" value="MojPrzyklad"/>
<parameter name="allowedMethods" value="*"/>
<beanMapping languageSpecificType="java:MojBean"
qname="ns:MojBean" xmlns:ns="MojPrzyklad2"
/>
</service>

Przykład klienta tclSOAP używającego axisowego WS :
package re SOAP
package re rpcvar

SOAP::create zwrocBeana \
-uri MojPrzyklad \
-action MojPrzyklad \
-proxy http://localhost:8080/axis/services/MojPrzyklad \
-params {}
zwrocBeana
#% {a 123 b 321}

# gdyby pewna metoda także otrzymywała przez parametr MojBean
# to należy zdefiniować typ tMojBean i użyć go w -params !!!
rpcvar::typedef -namespace MojPrzyklad {
a int
b int
} tMojBean


..................................................

Zadanie 49.
Zdefiniuj WebService pod Axisem z metodą wykonajObliczenia, która
pobiera tablicę struktur z polami a,b typu double,
i zwraca tablicę struktur z polami a,b,c z wypełnionym polem c= a*b.
Klienta testującego ten WS napisz pod gsoap, axisem, oraz jeśli chcesz także pod tclSOAP.

Zadanie 50 (*).
Zdefiniuj WebService pod Axisem z operacja pomnozDwieMacierze,
pozwalający przekazywać efektywnie macierze, które mają dużo zer.
Obiekt reprezentujący macierz powinien zawierać listę prostokątów
z elementami niezerowymi.




Narzędzia XML

Narzędzia/ biblioteki XML są w dużym stopniu ustandaryzowane, czyli używa się ich identycznie w różnych językach programowania/ platformach ...
  1. dom - drzewo DOM reprezentujące (w pamięci) dokument xml;
    składa się z węzłów, które mogą być elementami, atrybutami, tekstem;
    można je modyfikować, a potem zserializować spowrotem do dokumentu xml w pliku;
    specyfikacja dom: DOM-Core
    przykład użycia dom w Javie JDK1.5 (pod tclBlend) xml_dom.txt,
       + dokument xml po.xml używany przez ten przykład,
       + jest tu też pokazane jak używać wyrażeń xpath, oraz inne rzeczy ...
    przykład użycia dom w Tcl (pakiet tdom) xml_dom_2.txt
       + pakiet tdom: tdom.tar.gz, dokumentacja www.tdom.org
  2. sax - parser zdarzeniowy;
    pozwala wyciągnąć informacje z dużego pliku xml, nie ładując go w całości do pamięci
  3. JAXP - warstwa abstrakcji w j. Java;
    pozwala przetwarzać XML niezależnie od konkretnej implementacji np parsera;
    składa się z pakietów javax.xml.*  w J2SE
  4. wyrażenia "xpath";
    pozwalają łatwo znależć elementy/atrybuty sełniające pewne warunki w drzewie dom
  5. "XML Schema" - definiuje klasę dokumentów xml, czyli jest to "typ";
    ???
  6. XSLT czyli style - przetwarzanie dokumentów xml do innych dokumentów xml lub np html;
    ???
  7. XQuery - język zapytań do dokumentów xml, podobny do SQL w relacyjnych bazach danych;
    tzw semi-strukturalny model danych ...
    ???
... manipulowanie komunikatami SOAP na poziomie XML ...

Zadanie 50a (zmiana stylu komunikatu SOAP)
Napisz procedurę, która modyfikuje komunikat soap w stylu "RPC/encoded",
tak że wygląda on jakby był w stylu "document/literal" ...
Czym się różnia komunikaty soap w obu stylach?
   + można to łatwo sprawdzić przy pomocy gsoap, zmieniając dyrektywy gsoap ns service style/encoding
      i generujac przykładowe komunikaty soap (wykonaj ten eksperyment)
   + jak widać zmiana dotyczy atrybutu encodingStyle oraz ns elementów zawierających parametry operacji...
Wypróbuj działanie procedury na przykładach komunikatów soap generowanych przez gSOAP!

Zadanie 50b (tclSOAP w stylu dokumentowym)
Mając procedurę z poprzedniego zadania spróbuj zmusić tclSOAP aby współpracował
z WS w stylu documentowym; wypróbuj działanie na webservisie "pogodowym":
package re SOAP
#% 1.6.7

proc rpc2doc {v args} {
soap_rpc_to_doc [eval SOAP::soap_request $v $args]
# zakładam, że proc soap_rpc_to_doc modyfikuje komunikat soap
# przekazany przez parametr, tak że ma styl document, a następnie go zwraca
# SOAP::soap_request to std. procedura produkujaca komunikat soap w stylu rpc...
}

SOAP::create GetWeather \
-wrapProc rpc2doc \
-proxy "http://www.webservicex.net/globalweather.asmx" \
-uri "http://www.webserviceX.NET" \
-action "http://www.webserviceX.NET/GetWeather" \
-params {CityName string CountryName string}
# opcja -wrapProc wymienia procedurę generującą komunikaty soap

GetWeather "Poznan" "Poland"
# powinno działać!!



SOAP w .NET/Mono

Remoting to obiekty rozproszone .NET ...
Pełnią podobną rolę jak ob. RMI w j. Java.

W ob. remotingowych można przełączać kanał/formater :
- tcp/binary 
- iiop/? (ob. remotingowy jest ob. Corby, patrz temat C)
- http/soap (wtedy ob. remotingowy jest serwerem SOAP!!!)

Patrz artykuły:
    "Remoting in .NET"
    "Remoting Architecture in .NET"

Przykład SampleObject.txt
    zawiera kod klienta, serwera, oraz interfejs ob. Remotingowego + impl.
    przykład ten należy rozbić na pliki i skompilować pod .NET lub Mono

Zadanie 50c (współpraca z Remotingowym serwerem SOAP)
Można się przekonać ze Remotingowy serwer SOAP nie wspołpracuje
z klientami innych implementacji SOAP (np z Axisem ...).
Należy zaprogramować niskopoziomowego klienta SOAP (w dowolnym języku),
który potrafi współpracować z Remotingowym serwerem soap (z powyższego przykładu).
Najpierw należy podejrzeć komunikaty soap jakie wysyłają sobie kli i ser .NET-owi
przy pomocy jakiegoś szpiega gniazdkowego (przykładowo można użyć programu sockspy.tcl,
   który wymaga pakietu "uri" z tcllib, czyli trzeba ustawic TCLLIBPATH odpowiednio...).
Klient powinien być wstanie wywołać metodę zwrocCos(int) i sekwencjaRazy2(int[])
z dowolnymi parametrami ...