18 stycznia 2010

Przygotowywanie (się do) projektu z JAX-WS i Apache Maven - modularyzacja wychodzi sama

Po tych moich certyfikacyjnych wojażach i bojach z recenzjami (jeszcze jedna czeka w kolejce do napisania) wracam (na blogu) do tematu mojego obecnego projektu, którego pierwsze rezultaty można było "podziwiać" w Każdemu będzie w końcu dane - wreszcie Java EE 5 w projektach!.

Komentarzy posypało się co nie miara, za co wszystkim dziękuję, bo chciałbym móc powiedzieć, że to nie moje, albo że miałem jedynie 5-10 minut na poprawki przed publikacją, aby wybronić się z kilku, ale nic z tego. Wszystko, co opublikowałem było moje na bazie tego, co nie było moje :] Migracja miała na celu wypuszczenie działającego rozwiązania możliwie niewielkim nakładem pracy. Udało się. Jakkolwiek nie oznacza to zakończenia prac nad tym "dziwolągiem", ale przyświecał mi jeden cel - dzisiaj testuję prototyp, aby zweryfikować mój punkt widzenia, aby jutro i w kolejnych dniach wyciosać coś rzeczywiście przyzwoitego. Od niewielkiego tworu, ale działającego, do większego, bardziej naładowanego wzorcami i dobrymi praktykami, i wciąż działającego. Takie iteracyjne programowanie. Prace trwają.

Dzisiaj postanowiłem wprowadzić zmiany w obszarze zarządzania projektem, w którym można wyróżnić część serwerową (uruchomioną w centrali) oraz część kliencką. Ich zależność polega na interfejsie, z którego korzystają. W tym przypadku nie tylko interfejs programistyczny (w sensie programowania w Javie), ale również technologiczny (w sensie wykorzystania technologii/szkieletu aplikacyjnego w Javie) łączy oba moduły. Skorzystałem z technologii Web Services (JAX-WS), więc moduł centralny wystawia usługę sieciową, którą konsumuje moduł kliencki. Logiczne, co?

I tutaj natrafiłem na problem zdefiniowania zależności między modułami. Gdyby założyć, że część serwerowa jest aplikacją webową, to w jaki sposób określić zależność w module klienckim? Nie ma jak, bo war to podział technologiczny, a nie programistyczny i nie tędy droga. Potrzebuję zdefiniować SEI (ang. Service Endpoint Interfejs), abym na nim oparł technologiczny moduł serwerowy i programistyczny, kliencki. Moduł serwerowy to war, który zanurzy moduł SEI, z którego będzie mógł skorzystać moduł kliencki.

Teraz jest dla mnie jasny komentarz Artura Karazniewicza (aczkolwiek jego pierwszy "wyjazd" był do kosza, co się ostatecznie stało :)):

"Po drugie uniezależnienie samego WebService od implementacji endpointu w java (zastosowanie SEI oraz nazwanie poszczególnych elementów WSDL w adnotacjach WebService, WebMethod itd.)."

Początkowo pomyślałem "Kto by się tym przejmował?!", ale teraz samo rozczłonkowanie projektu na moduły wymusiło na mnie poćwiartowanie modułu serwerowego z wydzieleniem modułu z SEI. Inaczej po prostu nie dałoby rady. Do mnie przemawiają przykłady, co daje mi dane podejście i w tym przypadku samo stwierdzenie "Bo tak się robi. Bo to dobra praktyka." to zdecydowanie za mało. Potrzebuję konkretów, a nie kiwania głowami, że tak się robi, a kiedy zapytać "Dlaczego?", nikt nie jest skłonny wytłumaczyć.

Bardzo ucieszyłem się, kiedy pojawiły się komentarze wskazujące konkretne błędy z chociażby skromną, ale zawsze, próbą wytłumaczenia "Dlaczego nie". Niestety pojawiły się też takie ogólnikowe, zrób to, zrób tamto, bez jakiegokolwiek wyjaśnienia "Dlaczego". A szkoda, bo jestem w stanie wyobrazić sobie niedoświadczonych programistów, którzy postawieni w tej roli, w jakiej ja byłem przed chwilą - wystawienia ich kodu na publiczną krytykę - dostają multum dobrych rad, które są dobre jedynie z ich nazwy. Co mi po dobrej radzie, jeśli nie dostaję racjonalnego ich wytłumaczenia? Są dla mnie równie wartościowe, jak wskazanie palcem, co mam zrobić...milcząco.

Zestawiam projekt z pomocą Apache Maven, bo nie tylko wymusiło to na mnie poćwiartowanie (aka zmodularyzowanie) aplikacji, ale również uniezależni mnie od zależności środowiska programistycznego (IDE). Do tej pory w zespole królował Eclipse/RAD. Teraz, z wprowadzeniem mnie do zespołu, wszedł NetBeans IDE (bo jakoś łatwiej mi było stworzyć i przetestować Web Service), a ostateczny cel to uruchomienie narzędzia budowania aplikacji bez udziału ludków z zespołu. Bez wykorzystania Apache Maven, czy jemu podobnego rozwiązania, nie byłoby to możliwe.

Zastanawiam się, jak testować działanie usługi sieciowej? Czy w ogóle warto, skoro mogę przetestować implementację bez narzutu infrastruktury JAX-WS? To w końcu "oznakowane" POJO? A co z klientem? On jest zależny od wygenerowanego przez wsgen. Jak sobie z tym tematem poradzić? Może po prostu zaniechać testowania komunikacji usługa-klienta, bo to ma działać (jest poza moją kontrolą i jestem tego klientem), a przetestować metody, które otrzymają wynik wywołania przesłoniętego zaślepką (ang. mock object)? Rady mile widziane (zlitujcie się jednak nade mną i dodajcie słów kilka dlaczego, albo chociaż odnośniki do dokumentacji).

21 komentarzy:

  1. Hej Jacek,
    Może (p)odpowiedź będzie trochę z "bani", ale w takim wypadku może lepiej było by skorzystać z kontraktu - czyli podejścia WSDL-first. Od dłuższego czasu korzystam z takiego podejścia i nie miałem problemów z tym. Stworzenie klienta jest równie proste co stworzenie klienta.

    Procedura wygląda następująco:
    1. Piszemy, bądź klikamy, WSDL oraz XSD.
    2. Uruchamiamy cxf-codegen. W wyniku pracy codegena mamy kod z adnotacjami JAX-WS oraz JAXB.
    3. Implementujemy wygenerowany interfejs po stronie serwera.
    4. Po stronie klienta korzystamy ze Spring + JaxWsProxyFactoryBean.

    W takim wypadku zachowujesz separację klienta i serwera. Dość dawno temu opisywałem cały proces na blogu:
    Kontrakt oraz serwer:
    http://blog.dywicki.pl/2008/07/23/budowanie-uslugi-sieciowej-w-oparciu-o-apache-cxf/

    Klient:
    http://blog.dywicki.pl/2008/09/03/budowanie-klienta-uslugi-sieciowej-w-oparciu-o-apache-cxf/

    Niestety kod źródłowy był się zapodział .. :-(

    OdpowiedzUsuń
  2. Jeszcze raz przepraszam za moją wcześniejszą reakcję :>

    Ale wracając do przykładu. Jak powiedział splatch podstawową, dobrą praktyką przy tworzeniu serwisów, które nie są wrażliwe oraz nie wiążą komsumenta z dostawcą usługi jest podejście 'contract first'. Jak powiedział kiedyś Dan Diephouse (jeden z liderów projektu CXF) to jeśli tworzymy serwisy 'java first' to tak naprawdę tworzymy rodzaj zdalnego wywołania procedury - ze wszystkimi tego skutkami. Zauważ, że każda praktycznie refaktoryzacja spowoduje zmianę interfejsu (WSDL) serwisu i tym samym problemy dla klienta. Załóżmy, że jest ich kilku (tworzymy usługę uniwersalną, prawda?) to problem robi się jeszcze większy. Skutki? Raczej opłakane - albo przypadkowo łamiemy kontrakt, albo sami siebie odcinamy się od jakichkolwiek zmian w naszym kodzie. Czyli wszystko to co najgorsze. Pomijam już problemy przy wersjonowaniu kontraktu - bo to temat na inną dyskusję. Zauważ, że przy takim podejściu jakie zaprezentowałeś:

    targetNamespace w wygenerowanym WSDL (i XSD) będzie odpowiadał nazwie pakietu java (!!)

    portType - będzie równy nazwie metody

    itd. zasady mapowania są dokładnie opisane w JSR-181

    Czemu warto wydzielić SEI od implementacji WebService? Ponieważ tak jak WSDL, opis serwisu dzieli sie na dwie części abstrakcyjną (SEI w Java) oraz konkretną (Implementacja serwisu w Java). Niektóre adnotacje stosuje się jedynie do SEI a niektóre jedynie do implementacji. Każdy przypadek, poza najbardziej trywialnymi przykładami wymaga takiego podziału.

    Wiem, że Twoim celem było pokazanie jak łatwo można zrealizować usługę WebService w javie i rzeczywiście wystarczy jedna adnotacja. Niemniej warto przy tej okazji wspomnieć o tym co napisałem powyżej. Każdy nietrywialny przypadek powinien być realizowany w koncepcji contract (czyli w tym przypadku WSDL i XSD) first - to pozwoli zaoszczędzić naprawdę całej masy kłopotów. Wiem, że nie wygląda to już tak spektakularnie (bo samo stworzenie WSDL nie jest znowu takie łatwe i wymaga sporej znajomości rzeczy) ale tak to wygląda w prawdziwym życiu. Mało kiedy stać nas na ten komfort, żeby ustalać kontrakty *po* implementacji serwisu. Zwykle w prawdziwym świecie jest tak, że kontrakt ustalany jest na etapie analizy lub wczesnego projektu - o implementacji trudno wówczas mówić.

    A teraz ostatnia rzecz - czy warto testować serwisy? Oczywiście, że warto! Serwis to nie POJO. Testując POJO nie przetestujesz prawdziwego zachowania serwisu. Nie przetestujesz kontraktu. Nie testując serwisu pomijasz *bardzo wiele* aspektów:

    * Walidacja - należy pamiętać, że XSD ma *dużo bardziej* złożoną semantykę niż java (dobrym przykładem było zwrócenie w poprzednim wpisie 'xmla' w postaci literału. To zapewne nie było intencją.

    * Aspekty "prostopadłe", które nie są do wychwycenia w przypadku POJO (cały stos WS-* będący w pewnym sensie prostopadłym do samej implementacji logiki serwisu: np. jak przetestować bezpieczeństwo (WS-Security), WS-Addresing itp.)

    A teraz jak testować? Tutaj może warto (ekhem, ekhem - shameless plug) zaglądnąć do mojej prezentacji z Javarsovii (do we need ESB any more?). Generalnie mamy dwa podejścia:

    Każdy endpoint możemy bardzo prosto wystawić bez żadnego kontenera za pomocą Endpoint.publish(...). To podejście jest przenośne - ma niestety jedną wadę - binduje usługę do konkretnego adresu i portu. Pozwala to co prawda na łatwe 'wystawienie' takiego interfejsu w unit teście - niemniej nie jest to dobry pomysł np. w przypadku serwerów CI (co, kiedy dwóch developerów uruchomi te same testy w tym samym czasie? Oczywiście testy zakończą się błędem mówiącym o tym, że usługi nie mogą zbindować się na dany port ponieważ jest on już zajęty).

    Dużo lepszym sposobem jest użycie tzw. transportu in-vm (pominięcie warstwy sieciowej). Nie jest to rozwiązanie przenośne pomiędzy implementacjami JAX-WS - jednak każda ze znanych mi implementacji (CXF, Metro Axis2) posiada wsparcie dla transportu in-vm. Mam nadzieję, że teraz jest znacznie jaśniej i z przykładami :)

    OdpowiedzUsuń
  3. Celem doprecyzowania - masz oczywiście rację, że nie warto testować WebService za pomocą wygenerowanego klienta (on zwykle, z definicji powinien działać :>). Przy podejściu TDD czekanie na wygenerowanie klienta to też nie jest najlepszy pomysł. Rozwiązaniem tego problemu jest coś co się nazywa Dynamic Dispatch API. API to pozwala na 'dynamiczne' wywołanie webservice. Nie jest ono tak wygodne jak gotowy, wygenerowany klient - ale jest bardzo wygodne - pozwala napisać wywołania zarówno prawidłowe oraz takie, które są nieprawidłowe (to też warto przetestować - czy nasz serwis jest odporny na błędy). Całkiem sensowny opis dynamic dispatch jest tutaj:

    http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.wsfep.multiplatform.doc/info/ae/ae/twbs_jaxwsdynclient.html

    Artur

    OdpowiedzUsuń
  4. Jacek,

    JAX-WS + Maven 2 w podejściu WSDL First (jedynym słusznym) masz opisany tutaj:

    http://www.mariuszlipinski.pl/2008/03/jax-ws-i-maven-2-w-podejciu-contract.html

    OdpowiedzUsuń
  5. Ja jednak nie wyobrażam sobie w prawdziwym projekcie użycia podejścia 'contract first'. W demonstracyjnych przykładach to zawsze fajnie wygląda, ale w praktyce nie wszyscy członkowie zespołu wiedzą jak pisać WSDL'a, a do tego uważają za zbyt czasochłonne (mimo wszelkich fajnych edytorków).

    W podejściu 'java first' wprost rezygnujemy z pisania WSDL'a, natomiast dzięki adnotacjom uniezależniamy go od implementacji. Co prawda wymusza to stosowanie większej liczby adnotacji, ale wygenerowany WSDL zmienia się wtedy równie często jak podczas korzystania z podejścia 'contract first', zwalniając nas z odpowiedzialności za jego zarządzanie.

    Nadal jednak nie wiem co (a przede wszystkim dlaczego!) powinno się znaleść w SEI.

    OdpowiedzUsuń
  6. @JEE Blog, nie pozostaje nic innego jak zabrać się za specyfikację :) Ja mam te same pytania i mam nieodparte wrażenie, że tylko ona da mi pełny obraz. Zabieram się za lekturę i relację na blogu. Powinno zaspokoić obie potrzeby - Twoją i moją (może jeszcze innych?).

    p.s. Miałem wątpliwości, czy to jest ta pora na lekturę JAX-WS 2.2, ale teraz już nie mam. Dzięki!

    OdpowiedzUsuń
  7. @JEE Blog

    Ja, pracując jako architekt głównie w obszarze SOA nie wyobrażam sobie prawdziwego projektu z podejściem code first. Usprawiedliwieniem nie może tu być w żadnym razie to, że 'developerzy nie znają WSDLa' ;). Przy podejściu takim jak opisałeś, chcąc zachować podstawową higienę (separację kontraktu od implementacji) w sposób nawet podstawowy, wymagana jest *doskonała* znajomość WSDLa, jak i *konkretnej* implementacji JAX-WS oraz JAXB. Czemu w takim razie nie zastosujecie czegoś dużo prostszego, i moim zdaniem lepszego, jak: REST?

    Poniżej kilka oczywistych argumentów przeciw podejściu 'code first':

    1. Kontrakt zwykle potrzebny jest przed napisaniem nawet linijki kodu. Naprawdę możecie powiedzieć dowolnemu konsumentowi waszych usług, na pytanie o kontrakt, 'poczekaj aż napiszemy serwis w javie, potem wygenerujemy kontrakt'?

    2. W jaki sposób reużywacie istniejących komponentów? Podstowym podejściemw SOA jest reużycie komponentów i serwisów. Czy za każdym razem, w nowym kontekście tworzycie podstawowe komponenty jak 'adres', 'osoba' i tego typu uniwersalne byty biznesowe?

    3. Czy jesteście w stanie zapewnić niezmienność kontraktu np. przy migracjach? Przecież kolejne wersje implementacji JAX-WS generują różne kontrakty, dla tego samego środowiska. Nie wspominam tutaj nawet o różnych implementacjach (METRO, CXF, Axis etc.).

    4. Jak radzicie sobie z JAXB, integralną częścią stosu WS. Kontract to nie tylko WSDL ale też, z właściwie przede wszystkim XSD jest kontraktem?

    5. Czy komfortowo czujecie się, defacto zamykając sobie drogę do refaktoryzacji? Przeniesienie klasy między pakietami nie wchodzi w grę. Namespace na poziomie JAXB deklaruje się na poziomie pakietu. Przeniesienie klasy, bez jednoczesnej zmiany napespace jest niemożliwe (pakiet może mieć co najwyżej jeden namespace)?

    6. Jak podchodzicie do wersjonowania? Czy zachowujecie wsteczną kompatybilność kontraktu? Jeśli tak to jak?

    Z ciekawości, o jakim typie serwisów mówisz? Czy to są serwisy ogólnego użytku? Czy coś co służy do integracji ad-hoc systemów, nad którymi panujecie w 100%?

    ps. O podejściu contract first, jako jedynym sensownym mówi większość producentów stosów WS:

    http://static.springsource.org/spring-ws/sites/1.5/reference/html/why-contract-first.html

    OdpowiedzUsuń
  8. @JEE Blog, Jacek

    Sprawa zrozumienia SEI i implementacji jest dość prosta... o ile rozumie się konstrukcję WSDL. Generalnie WSDL skłąda się z dwóch części:

    1. Abstrakcyjnej, definiującej interfejs. Będący defacto kontraktem. Ta część jest wszystkim czego potrzebuje klient aby znać kontrakt (ta część zawiera elementy: types, message i the portType)

    2. Konkretnej (nie lubię tego tłumaczenia. Czyli wiążącej powyższy interfejs z konkretnym 'transportem' (ta część zawiera elementy: binding, service). Ta część jest potrzebna do implementacji serwisu.

    (więcej, np. http://fusesource.com/docs/framework/2.1/wsdl/WSDLStructure.html)

    SEI (tym samym adnotacje na nim) odpowiadają abstrakcyjnej części WSDL. Implementacja serwisu odpowiada konkretnej części WSDL. To jest dość proste, ale faktycznie warunkiem jest znajomość WSDL.

    SEI w pewnym sensie jest kontraktem (w javie). Jest jedynym czego potrzebujemy po stronie klienta JAX-WS aby wywołać serwis. Jak widać podział jest dość jasny, klarowny i sensowny.

    Tak jak pisałem. Pewne elementy czy wzroce w WSDL stosuje się jedynie w części abstrakcyjnej czy konkretnej. Dokładnie tak samo ma się rzecz z Annotacjami. Np. annotacje ReqestWrapper, RequestResponse czy Address są stosowane i mają sens TYLKO na SEI. Czyli jeśli chcemy stosować najbardziej popularny schemat document/literal wrapped *musimy* użyć SEI.

    Jak już JEE Blog zauważył, WSDL nie jest najprostszym mechanizmym na tej planecie. A to dopiero początek. Nie wspomnieliśmy o żadnym standardzie WS-* ;). Jeśli ktoś czuje się przygnieciony tym krótkim wstępem, powinien poznać REST i JAX-RS :). Jako technologia mająca prawie wszystkie zalety SOAP i prawie żadnej z jego wad :).

    Artur

    OdpowiedzUsuń
  9. @Artur, mimo wszystko "code first" jest tym, w czym czujemy się najlepiej - najpierw programowanie. Nie dziwię się @JEE Blog, że wspomina o takim podejściu, bo dla mnie też jest przyjemniejsze. Mimo, że tracę wiele z tego, co piszesz, na chwilę obecną *mogę* sobie na to pozwolić (np. bo kontroluję całe środowisko - serwer i klienta). Można to porównać do baz relacyjnych vs obiektowe. Mimo całego dobrodziejstwa tych drugich (obiektowych), to te pierwsze (relacyjne) zagościły się na dobre w naszych rozwiązaniach. Nie wszystko, co dobre, jest dobre dla wszystkich. Gusta i dobre praktyki ewoluują i to jest dlaczego w tej chwili możemy w ogóle rozmawiać o JAX-WS. Czyż nie?

    OdpowiedzUsuń
  10. Oczywiście, że dla developera jest przyjemniejsze szczególnie w porównaniu do WSDLa. Szczególnie, że specyfikacja WSDL i SOAP to dobre kilkaset stron. No i powiedzmy sobie szczerze - ani najprostszej, ani najbardziej ekscytującej na ziemi specyfikacji ;). Może w tym przypadku przeskoczyć ten ciężki etap i od razu zacząć od czegoś prostego, ciekawego jak REST i JAX-RS? :) Wiele trudności w przypadku SOAP czy WSDL wynika, z faktu, że kolejne komitety standaryzacyjne próbują dzielnie walczyć z problemami, które sami stworzyli :). Czego świetnym przykładem jest WS-Resource :)

    Artur

    OdpowiedzUsuń
  11. @Artur, chcesz mi powiedzieć, że dzisiaj wydrukowane 180 stron specyfikacji JAX-WS powinno iść do kosza i swoje ciągoty powinienem skierować ku JAX-RS? No to mnie wkurzyłeś :) A może właśnie powinienem Ci podziękować, że nie wtopiłem kolejnych godzin nad niczym? Pozostanę przy JAX-WS z nadzieją, że moje męki pójdą w zapomnienie przy przejściu do JAX-RS. Niektórzy nawet twierdzą, że najpierw trzeba zrozumieć problem (JAX-WS), aby docenić rozwiązanie (JAX-RS).

    Dzięki Artur za cerpliwość w wyjaśnianiu zawiłości JAX-WS. Jesli dodać, że posiadam certyfikat SCDJWS i pisałem^C^C^Casystowałem przy Redbookiem o WS-Security (z Kerberosem w roli głównej), to i tak znalazłem w Twoich wyjaśnieniach wiele cennych wskazówek. Wydajesz się być dobrze poinformowany i dziwię się, dlaczego jeszcze nie mieliśmy przyjemności powitać Cię jako prelegenta na WJUGu albo konferencji typu Javarsovia, Warsjava, czy wręcz GeeCON? Możesz mi to wyjaśnić?! Nalegam...

    OdpowiedzUsuń
  12. @Artur, może na początku sprecyzuję, że bardziej eksperymentuję i uczę się nowych rzeczy w zakresie WS, niż tworzę tego typu systemy czy integruje je. No i jestem jeden... w swoim poście często używałeś liczby mnogiej ;)

    WS postrzegałem (być może błędnie) jako zunifikowany opis interfejsów systemu, przy czym dla korzystającego z tego opisu nie są potrzebne żadne informację dotyczące jego implementacji. Jeżeli posiadam specyfikacje interfejsów systemu na tyle dokładną, że mogę na jej podstawie utworzyć kontrakt jako wsdl to równie dobrze można go utworzyć jako SEI oraz klasą go implementującą (ale oczywiście bez jakiejkolwiek implementacji - klasa ta ma reprezentować konkretne elementy konktraktu (o ile dobrze cię zrozumiałem w poprzednich postach)).

    W ten sposób także możemy definiować kontrakt, w dodatku szybciej i łatwiej. Mamy tutaj co prawda założenie, że adnotacjami możemy wyrazić to samo co w wsdlu. Sądzę, że masz zdecydowanie większą wiedzę, aby stwierdzić czy jest ono prawdziwe. Jeśli jednak jest, to kod java udekorowany adnotacjami powinien być tak samo dobrym kontraktem jak wsdl.

    I dzięki za namiar na JAX-RS - z pewnością zapoznam się również z nim. :)

    OdpowiedzUsuń
  13. @Jacek

    Ale ja jak najbardziej prezentowałem na javarsovii, tyle, że w 2007 ('do we need esb anymore?' :)

    No ale może już faktycznie czas zebrać się i zrobić jakąś następną prezentację. W sumie nawet miałem chęć uczestniczyć w pojedynku Mule ESB vs. ServiceMix, przedstawiając jak można obyć się bez ESB, i jakie ma to zalety, poza tym, że (włożę tu kij w mrowisko) bez ESB większość rzeczy robi się szybciej, wygodniej i ogólnie lepiej :). Kto wie może jeszcze zdążę się przygotować :).

    A jeśli chodzi o javarsovię? ;) To może tym razem coś jak: 'ESB considered harmful'? :-D

    OdpowiedzUsuń
  14. Witam serdecznie

    Chciałbym przedstawić moje doświadczenia związane z testowaniem web serwisów. Nie jest specjalnie bogate, ale wydaje mi się, że wypracowałem przynajmniej zarys metodologii.

    Web serwisy testuję na dwóch poziomach: od wewnątrz przy pomocy klasycznych unit testów, oraz od zewnątrz przy pomocy testów, które nie wiem jak nazwać (akceptacyjne? integracyjne?), w każdym razie polegających na wołaniu web serwisu poprzez wygenerowane klienckie proxy.

    Uzasadniać użycia unit testów prawdopodobnie w tym miejscu nie muszę. Natomiast jeśli chodzi o użycie testów z wykorzystaniem proxy, najlepszym uzasadnieniem będzie chyba podanie listy błędów, które udało mi się przy pomocy tych testów wyłapać.

    1. Typ xsd:date nie przechowuje godziny i po deserializacji do obiektu javowego Date traciłem informację o godzinie. Przy pomocy unit testów faktu tego nie zarejestrowałem.

    2. Typy xsd:time oraz xsd:dateTime nie przechowują milisekund i przy deserializacji pojawiają się podobne problemy jak wyżej.

    3. Użycie dwóch metod bezargumentowych jest niezgodne z WS-I BP i web serwis jest non-interoperable. Rzecz nie jest łatwa do wykrycia testując wyłącznie kod w Javie.

    4. Wartość xsd:int nullable jest reprezentowana przez niektóre biblioteki (zdaje się, że był to Axis 2) jako javowy int. Null jest reprezentowany przez Integer.MAX_VALUE, lub podobnie. Nieświadome użycie autoboxingu powodowało wiele zgryzot (null w obiekcie klasy Integer, który był automatycznie tworzony ze zmiennej typu int, nie pojawiał się nigdy, zamiast tego pojawiały się MAX_VALUE czy MIN_VALUE).

    Testy ,,od zewnątrz'' realizuję na dwa sposoby. Pierwszy to zestaw automatycznych testów uruchamianych przy pomocy biblioteki typu JUnit, czy NUnit. Drugi to specjalnie przygotowana konsola pozwalająca wysyłać dowolne dane (z zakresu dopuszczanego przez kontrakt) i analizowanie rezultatów.

    Z zasady dla web serwisów w Javie tworzę testy ,,od zewnątrz'' w .NET i odwrotnie. Pozwala to wyłapać wiele (niestety nie wszystkie) problemów z ,,interoperability'' (nie znam polskiego odpowiednika) oraz zaobserwować w jaki sposób web serwis pochodzący z jednego środowiska widziany jest w drugim i czy jego użycie jest wygodne.

    Przykładowo xsd:int nullable był reprezentowany przez javowy int i ustawiając wartość pola trzeba było również ustawić wartość dodatkowego pola ''isSet'' aby nullowatość obsłużyć. Na jednej platformie było normalnie, a na drugiej dziwacznie.

    OdpowiedzUsuń
  15. A propos wyboru pomiędzy top-down a bottom-up, w mojej firmie wykorzystywane są oba podejścia. Część web serwisów tworzona jest zaczynając od kodu, część od kontraktu i specjalnych problemów nigdy z tym nie było.

    Ja preferuję podejście top-down, ponieważ pozwala zdefiniować kontrakt zanim zabiorę się do implementacji. Co więcej, pozwala ono wygenerować klienckie proxy, które może zostać ocenione przez potencjalnych klientów web serwisu zanim zabiorę się do pracy.

    Fakt, wymaga to znajomości XSD i WSDL, ale nie wyobrażam sobie budowania web serwisów, poprawnego utrzymywania ich i diagnozowania problemów bez takiej wiedzy.

    Oprócz tego top-down pozwala na łatwą zamianę biblioteki implementującej web serwisy. W swojej pracy przechodziłem z Axis 1 na Axis 2, a potem na JAX-WS. Wydaje mi się, że gdybym zaczynał od kodu, trudności byłyby zdecydowanie większe.

    Jednak, jak już wspomniałem, część web serwisów w firmie tworzona jest metodą bottom-up i mają się dobrze. Nie jest prawdą, że takie podejście uniemożliwia refaktoryzację. Po pierwsze istnieje możliwość konfigurowania mapowania pomiędzy kodem a WSDLem, a po drugie logika biznesowa musi (chyba nawet nie powinna) znajdować się na samym wierzchu w kodzie serwisu, tylko powinna być odseparowana od spraw web serwisowych.

    OdpowiedzUsuń
  16. @Sebastian Celejewski, ale mnie natchnąłeś z tym testowaniem zgodności/poprawności usług między środowiskami Java i .Net. Niestety nie mam środowiska do programowania w .Net, ale przecież mam F#, który działa nawet na MacOS. W ten sposób możnaby pokusić się o naukę języka funkcyjnego z obszaru kontrolowanego przez Microsoft do...testowania usług sieciowych (!) Należą Ci się brawa za sprowokowanie mnie do tego pomysłu :)

    p.s. interoperability przetłumaczyłbym na polskie współpraca.

    p.s.2. Gdybyś tak zechciał przedstawić to podejście testowania między platformami w postaci prezentacji na WJUGu byłoby cacy. Razem z Arturem moglibyście otworzyć całą serię spotkań na ten temat. Widać gołym okiem, że temat zatacza coraz większe kręgi, a możliwość przyjrzenia się tematowi podczas bezpośredniego spotkania jest bezcenna. Dałbyś się skusić?

    OdpowiedzUsuń
  17. @Jacek Laskowski: ,,Dałbyś się skusić?'' Dałbym chętnie, gdybym dysponował czasem - na razie niestety nie dysponuję.

    OdpowiedzUsuń
  18. Dodam jeszcze, że pracuje w jak najbardziej prawdziwym i jak najbardziej dużym projekcie (wdrożenie SOA w dużej firmie) i nie wyobrażam sobie, żeby mogło by być inaczej niż contract-first. No chyba że wdrożenie jest raczej małe i wszystko pisane jest w Javie.

    A że programiści nie wiedzą jak pisać WSDLe to nie ma najmniejszego znaczenia, bo też nie oni od tego są. Programiści mają za zadanie raczej implementować WSDLe niż je tworzyć.

    OdpowiedzUsuń
  19. No to przyznam się jeszcze że część serwisów rzeczywiście robimy częściowo na zasadach code-first. Ale to tylko na platformie "specjalnej troski", tj. WCF. No i są z tym problemy.

    OdpowiedzUsuń
  20. Święte słowa, Mariusz! Że też o tym zapomniałem - to nie programista od WSDLa, bo nie w jego arsenale odpowiednie narzędzia, ale architekt podczas modelowania swojego rozwiązania deklaruje usługi/kontrakty. Podczas eksportu jego pracy do środowiska programistycznego dostajemy WSDLe, których import daje przyczynek do programowania w Javie.

    Dzięki Mariusz za zaoferowanie światu swojej wiedzy "produkcyjnej"! :P

    OdpowiedzUsuń
  21. @Mariusz Lipiński

    ,,Dodam jeszcze, że pracuje w jak najbardziej prawdziwym i jak najbardziej dużym projekcie (wdrożenie SOA w dużej firmie) i nie wyobrażam sobie, żeby mogło by być inaczej niż contract-first. No chyba że wdrożenie jest raczej małe i wszystko pisane jest w Javie.''

    W aplikacji, którą współtworzę, wykorzystywanych jest osiem web serwisów. Nie są może specjalnie duże, ale na pewno są prawdziwe. Tworzone są w .NET, Javie (tylko jeden) i w Cold Fusion, część została zbudowana zgodnie z contract-first, część zgodnie z code-first.

    OdpowiedzUsuń