sobota, 20 października 2007

Parsowanie XML przekazanego przez sockety

Zacznę od przedstawienia problemu, który oczywiście nie istnieje dopóki się go nie napotka :) Problem tkwi w tym, iż część parserów XML zamyka wejściowy strumień (InputStream) kiedy napotka EOF (-1). Dotyczy to choćby Xercesa, który jest domyślnym parserem dla biblioteki JDOM. Oczywiście nie byłoby w tym nic złego gdyby wejściowym strumieniem nie był SocketInputStream, ponieważ jego zamknięcie powoduje jeden niekoniecznie pożądany skutek, a mianowicie zamknięcie socketu, który w efekcie jest już bezużyteczny co oznacza nic innego jak to, iż nie można przez niego wysłać odpowiedzi.

W przypadku dużej części aplikacji sockety są wykorzystywane do realizacji klasycznego modelu żądanie-odpowiedź np. klient wysyła do serwera dokument XML, serwer go przetwarza, a następnie odpowiada klientowi innym dokumentem XML. Jednak w przypadku gdy po wczytaniu dokumentu XML przez serwer socket zostanie zamknięty, klientowi nie zostanie udzielona odpowiedź.

Co można w takim razie zrobić aby osiągnąć pożądany efekt? Rozwiązania są dwa przy czym drugie z nich można zrealizować na kilka sposobów. Pierwsze rozwiązanie to opakowanie InputStream (z socketu) w taki sposób aby nie zamykał on połączenia, czyli przesłonięcie metody close(). Drugie rozwiązanie to przeczytanie wszystkiego do bufora, a następnie przekazanie jego zawartości do parsera XML. Przy czym co zrobić jeżeli wejściowy strumień danych jest duży i nie chcemy niepotrzebnie czekać aż zostanie cały wczytany i dopiero wówczas przekażemy go do parsera. Przecież przychodzące bajty mogą być przetwarzane przez parser XML zanim przyjdą kolejne.

Jak to zrobić w takim razie efektywnie: Zestawiamy ze sobą dwa strumienie typu piped: Tworzymy wątek łączący strumień wyjściowy piped ze strumieniem wejściowym socketu. Jego zadaniem jest czytanie danych z clientSocketInputStream do pipedOutputStream, który jest połączony z pipedInputStream, który możemy bezpośrednio przekazać do parsera XML: Implementacji klasy Output2InputThread nie będę zamieszczał w całości, natomiast zamieszczę fragmenty, które pozwolą zorientować się co powinno się w niej znaleźć: Nie zamieściłem kodu, który sprawdzi kiedy należy przestać czytać, ale takie coś już każdy może zrealizować samodzielnie. Wcześniej oczywiście trzeba ustalić sposób informowania o zakończeniu przesyłania dokumentu XML, można to zrobić np. poprzez przesłanie przed dokumentem nagłówka z ilością bajtów, które należy wczytać lub na końcu dokumentu jakiegoś umownego znacznika. Zwracam też uwagę, że ustalenie rozmiaru bufora na określoną wartość wcale nie oznacza, że podaczas jednej iteracji zostanie on cały zapełniony, w szczególnym przypadku może zostać wczytany tylko jeden bajt.

Zachęcam Was do podzielenia się swoimi sposobami na rozwiązanie przedstawionego przeze mnie problemu.

Brak komentarzy: