Nieczęsto spotykam kod, który ratuje życie. Trochę częściej spotykany jest kod, który ratuje tyłki. Oto jeden z jego przykładów.
Jaki problem?
|
Rys.1: Debugowanie tworzonej aplikacji |
Sytuacja wygląda następująco: otóż na zdalnym serwerze zainstalowana jest aplikacja przygotowywana przez firmę współpracującą z naszą przy wielkim projekcie dla pewnego klienta – aplikację nazwę
HES, a nazwę firmy i klienta pomijam. Firma współpracująca ma siedzibę bardzo daleko od nas i kontakt z wielu względów jest utrudniony. HES ma ustawiony na sztywno adres na który będzie wysyłać wszystkie dane, obojętnie jakie by one nie były.Pracując na lokalnej maszynie deweloperskiej mogłem więc wysyłać do niego żądania (
1), ale odpowiedzi
(2) wychodziły na inny adres i nic nie mogłem z tym zrobić. Otrzymywanie danych z serwera wydatnie ułatwiłoby pracę, więc postanowiłem rozwiązać ten problem w najprostszy, według mojej wiedzy, sposób – uruchomić na maszynie produkcyjnej, do której serwer zdalny wysyła wiadomości, program, który będzie podsłuchiwał nadchodzące komunikaty i przesyłał je na zdefiniowane adresy maszyn deweloperskich. Funkcjonalność tę postanowiłem połączyć ze zrzutami na dysk przychodzących przez WCF wiadomości – funkcją – jak się później okazało – niezmiernie przydatną w procesie testowania.
Czyli przygotowałem dwa kawałki kodu. Pierwszy, przekierowujący wiadomości do mojego komputera, był bardzo pomocny przy „odpluskwianiu”. Drugi – zrzucający wiadomości na dysk – okazał się niebywale pomocny na każdym etapie produkcji i dziś tylko na nim się skupię – w końcu nieczęsto można zobaczyć takie cudo :).
Zrzucanie wiadomości XML na dysk
Zacznę od opisu zapisywania przychodzących/wychodzących do/z serwisu wiadomości na dysk. Nie były mi potrzebne żadne specjalne formatowania, więc po prostu zapisywałem przychodzącą wiadomość do pliku o nazwie składającej się z daty oraz nazwy metody serwisowej. Do tego samego pliku doklejałem synchroniczną odpowiedź wysyłaną przez nas.
Pod adresem https://github.com/PawelSzczygielski/RawXmlMessagesExample znajduje się repo z kodem. Są tam trzy projekty – Serwis, Klient i część wspólna. W poszczególnych commitach można znaleźć stopniowo dokładaną funkcjonalność, ostatni przedstawia działającą całość – klient wysyła wiadomość do serwisu, ten odbiera wiadomość i odpowiada. Obie aplikacje zapisują wiadomości do plików w folderze C:TempAdditionalLogs.
ComplexService zawiera tylko jedną metodę przyjmującą obiekt ComplexRequest i zwracającą ComplexResponse. Klient po prostu wywołuje tę metodę z predefiniowaną wartością. Warto zwrócić uwagę na plik RemoteClassesExtensions zawierający rozszerzenia do klas, których definicje uzyskano przy użyciu opcji „AddServiceReference”. Generowane na podstawie plików WSDL klasy są zawsze zapisywane do pliku, którego nie powinniśmy edytować – będzie on nadpisywany za każdym razem, gdy odświeżymy zdalną referencję – wskazuje na to wszystkomówiący nagłówek:
Zamiast tego, lepiej stworzyć oddzielny plik i skorzystać z tego, że generowane klasy są oznaczone słowem kluczowym partial, umożliwiającym dopisanie drugiej części klasy w innym pliku w danej bibliotece.
Najważniejsza część rozwiązania znajduje się w części wspólnej – projekcie Common. Są to AddXmlToFileDispatcherServiceBehavior oraz XmlToFileDispatchMessageInspector, przy czym z punktu widzenia kodu klienckiego, widoczna jest tylko ta pierwsza klasa. Dziedziczy ona z klasy Attribute, zatem można nią dekorować inne klasy, implementuje także interfejs IServiceBehavior, dzięki czemu można dodać ją jako zachowanie do serwisu, a także interfejs IEndpointBehavior, który umożliwia dodanie go do zachowania klienta. Niepotrzebne z jej punktu widzenia metody nie są zaimplementowane.
Podobnie jest z XmlToFileDispatchMessageInspector. Jej implementacja jest jedną z najprostszych wersji – po prostu tworzy ona pliki i zrzuca do nich wiadomości SOAP. W razie potrzeby można rozdzielić obie te klasy na podklasy odpowiadające klientowi i serwerowi – wtedy będą one mogły zachowywać się inaczej w obu przypadkach.
Najbardziej skomplikowaną rzeczą w parsowaniu wiadomości WCF była obsługa obiektów klasy Message w metodach BeforeXXX i AfterXXX. Oto ona:
Jak widać, są tam dwie linia oznaczone jako
DirtyHack. Niestety nie rozwiązałem inaczej tego problemu, a być może jest on bardzo prosty do obejścia. Chodziło mi bowiem o zrzucanie na dysk wiadomości opakowanych w kopertę SOAP tak, żeby można je było od razu wziąć i wysłać znów przez WCF dalej, do maszyny developerskiej. Stąd tworzę
StringBuilder, dodaję do niego nagłówek SOAP, potem biorę wiadomość
Message i dodaję ją do SB, następnie zamykam kopertę SOAP i gotowe. Zauważyć należy, że
Message nie może być bezpośrednio wrzucona do
SB, ponieważ bufor zostanie opróżniony i serwis nic nie dostanie. Zamiast tego, na początku metody tworzymy jej kopię i to na niej operujemy. Dzięki temu oryginalna wiadomość będzie nietknięta.
Dlaczego to takie ważne?
Jak napisałem powyżej, kluczowy dla tego projektu był fakt współpracy dwóch zupełnie różnych kulturowo, geograficznie itp. firm przy dostarczeniu działającego oprogramowania dla klienta. Klient patrzył na produkt jako całość i średnio go interesowało, kto nawalił, jeżeli coś nie działało. Z kolei my mieliśmy pewność, że nasza część jest zgodna ze specyfikacją i działa jak powinna. Podczas pisania kodu nasz Project Manager poprosił mnie, żebym napisał kawałek kodu zrzucający żądania/odpowiedzi WCF na dysk, żeby ułatwić życie testerom i nam samym. Już sama ta decyzja pokazuje, jak wiele znaczy dobre zarządzanie programistami. Jak ważny jest człowiek ogarniający całość, świadomie wyszukujący potencjalnych przeszkód i aktywnie szukający sposobów na ich uniknięcie bądź pokonanie, jeżeli nie da się inaczej. Twierdzę, że bez takiej osoby właśnie dowolny projekt upadnie i w naszym przypadku to stwierdzenie sprawdziło się w 100%. Gdybyśmy nie napisali tego dumpera, jakiekolwiek błędy pochodziłyby od „nas”, a tak można było łatwo stwierdzić kto „zawinił”, a przede wszystkim – kto musi poprawić swoją część funkcjonalności. Innymi słowy – każdy mógł napisać ten kawałek kodu, ale mało osób byłoby tak dalekowzrocznych, aby zadać sobie ten trud.Zachęcam gorąco do postępowania w ten sposób przy projektach tworzonych we współpracy z inną firmą – nie chodzi o dowalanie sobie, ale o jak najszybsze stwierdzenie gdzie leży przyczyna nieprawidłowości i wyeliminowanie jej. Czyli najważniejsze punkty za udekorowaniem serwisów/klientów opisywanym Behaviorem:
- Programista/Tester może sprawdzić zgodność wysyłanych żądań i przychodzących odpowiedzi ze specyfikacją
- Wynika to z powyższego – można w prosty sposób pokazać zwierzchnikowi (czy to szef czy klient) gdzie leży problem i kto go powinien naprawić
- Zrzucone na dysk wiadomości mogą być w prosty sposób przekazywane dalej
Praktycznie rzecz biorąc już sam pierwszy punkt wystarczy, tym bardziej, że jak widać w repozytorium, całość to tylko dwie klasy nie wymagające dużych umiejętności aby je napisać. Mam nadzieję, że przydadzą się i w Waszych projektach. A może używacie czegoś lepszego do zapisywania wiadomości? Z chęcią się dowiem czegoś nowego 🙂
[…] Podziałał troszkę i znów koniec. Nic w logach nie było (a logów mamy mnóstwo – pisałem kiedyś, jak są one potrzebne na produkcji) i moglibyśmy się długo zastanawiać nad przyczynami, gdyby […]