Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00617 008413 10491692 na godz. na dobę w sumie
Java Servlet. Programowanie. Wydanie II - książka
Java Servlet. Programowanie. Wydanie II - książka
Autor: , Liczba stron: 668
Wydawca: Helion Język publikacji: polski
ISBN: 83-7197-527-9 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> jsp i javaservlet - programowanie
Porównaj ceny (książka, ebook, audiobook).
W ciągu kilku ostatnich lat serwlety Javy zdobyły uznanie społeczności twórców oprogramowania działającego po stronie serwera. Obecnie, po wprowadzeniu wersji 2.3 Servlet API, serwlety wynoszą Javę na nowy poziom tworzenia oprogramowania dla sieci WWW.

Serwlety zapewniają szybkie, potężne i przenośne środowiska do tworzenia dynamicznej zawartości stron WWW. Są one wykonywane na serwerze, co pozwala im działać efektywniej w porównaniu z innymi rozwiązaniami. Serwlety posiadają pełny dostęp do różnych API Javy, a także klas niezależnych komponentów, są również integralną częścią Java 2 Enterprise Edition (J2EE). Ich najważniejszą zaletą jest możliwość przenoszenia pomiędzy systemami operacyjnymi i serwerami -- serwlety można 'utworzyć raz, używać wszędzie'.

Użytkownicy rozpoczynający dopiero pracę z serwletami znajdą w książce opis wykorzystania serwletów do tworzenia potężnych, interaktywnych aplikacji WWW. Tematy tej książki to między innymi dynamiczne strony HTML, dokumenty XML, WAP, multimedialna zawartość stron, zintegrowane śledzenie sesji oraz wydajna łączność z bazami danych za pomocą JDBC. Osobom znającym już serwlety książka ta oferuje uaktualnione informacje na takie tematy jak archiwa aplikacji WWW (WAR), integracja J2EE, zarządzane przez serwer systemy bezpieczeństwa, zoptymalizowana współpraca serwerów, a także JavaServer Pages (JSP) oraz wiele innych.

Drugie wydanie bestsellerowej książki 'Java Servlet programming' jest doskonałym wprowadzeniem do świata servletów. Książka opisuje metody wykorzystania serweltów do stworzenia profesjonalnych, interaktywnych aplikacji sieciowych.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TRE(cid:140)CI SPIS TRE(cid:140)CI Java Servlet (cid:150) programowanie. Wydanie 2 KATALOG KSI¥flEK KATALOG KSI¥flEK KATALOG ONLINE KATALOG ONLINE ZAM(cid:211)W DRUKOWANY KATALOG ZAM(cid:211)W DRUKOWANY KATALOG TW(cid:211)J KOSZYK TW(cid:211)J KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAM(cid:211)W INFORMACJE ZAM(cid:211)W INFORMACJE O NOWO(cid:140)CIACH O NOWO(cid:140)CIACH ZAM(cid:211)W CENNIK ZAM(cid:211)W CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥flEK ONLINE FRAGMENTY KSI¥flEK ONLINE Autorzy: Jason Hunter, William Crawford T‡umaczenie: Jacek Smycz, Adam Grochowina, Tomasz Miszkiel ISBN: 83-7197-527-9 Tytu‡ orygina‡u: Format: B5, stron: 666 Przyk‡ady na ftp: 193 kB Java Servlet Programming 2nd Edition W ci„gu kilku ostatnich lat serwlety Javy zdoby‡y uznanie spo‡eczno(cid:156)ci tw(cid:243)rc(cid:243)w oprogramowania dzia‡aj„cego po stronie serwera. Obecnie, po wprowadzeniu wersji 2.3 Servlet API, serwlety wynosz„ JavŒ na nowy poziom tworzenia oprogramowania dla sieci WWW. Serwlety zapewniaj„ szybkie, potŒ¿ne i przeno(cid:156)ne (cid:156)rodowiska do tworzenia dynamicznej zawarto(cid:156)ci stron WWW. S„ one wykonywane na serwerze, co pozwala im dzia‡a(cid:230) efektywniej w por(cid:243)wnaniu z innymi rozwi„zaniami. Serwlety posiadaj„ pe‡ny dostŒp do(cid:160) r(cid:243)¿nych API Javy, a tak¿e klas niezale¿nych komponent(cid:243)w, s„ r(cid:243)wnie¿ integraln„ czŒ(cid:156)ci„ Java 2 Enterprise Edition (J2EE). Ich najwa¿niejsz„ zalet„ jest mo¿liwo(cid:156)(cid:230) przenoszenia pomiŒdzy systemami operacyjnymi i serwerami (cid:151) serwlety mo¿na (cid:132)utworzy(cid:230) raz, u¿ywa(cid:230) wszŒdzie(cid:148). U¿ytkownicy rozpoczynaj„cy dopiero pracŒ z(cid:160) serwletami znajd„ w ksi„¿ce opis wykorzystania serwlet(cid:243)w do tworzenia potŒ¿nych, interaktywnych aplikacji WWW. Tematy tej ksi„¿ki to miŒdzy innymi dynamiczne strony HTML, dokumenty XML, WAP, multimedialna zawarto(cid:156)(cid:230) stron, zintegrowane (cid:156)ledzenie sesji oraz wydajna ‡„czno(cid:156)(cid:230) z bazami danych za pomoc„ JDBC. Osobom znaj„cym ju¿ serwlety ksi„¿ka ta oferuje uaktualnione informacje na takie tematy jak archiwa aplikacji WWW (WAR), integracja J2EE, zarz„dzane przez serwer systemy bezpieczeæstwa, zoptymalizowana wsp(cid:243)‡praca serwer(cid:243)w, a tak¿e JavaServer Pages (JSP) oraz wiele innych. Drugie wydanie bestsellerowej ksi„¿ki (cid:132)Java Servlet programming(cid:148) jest doskona‡ym wprowadzeniem do (cid:156)wiata servlet(cid:243)w. Ksi„¿ka opisuje metody wykorzystania serwelt(cid:243)w do stworzenia profesjonalnych, interaktywnych aplikacji sieciowych. Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl 78(cid:4)4    3.+o  463;+./2/   Historia aplikacji WWW ...................................................l...................................................l...20 Obsługa serwletów...................................................l...................................................l............. 24 Potęga serwletów ...................................................l...................................................l............... 28 3.+o  3.78+;7/6;/8(cid:25);$$   Podstawy HTTP...................................................l...................................................l................. 32 Interfejs API (Servlet API) ...................................................l................................................... 34 Tworzenie strony ...................................................l...................................................l............... 36 Aplikacje WWW...................................................l...................................................l................ 42 3.+o +7782/2+(cid:30)- -+ 7/6;/89  Alternatywa serwletu ...................................................l...................................................l......... 49 Odnawianie (powtórne ładowanie) serwletu ...................................................l........................ 55 Metody „Init” i „Destroy” ...................................................l...................................................l. 56 Model jednowątkowy (Single Thread Model)...................................................l...................... 63 Przetwarzanie w tle...................................................l...................................................l............ 65 Ładowanie i uruchamianie...................................................l...................................................l.67 Buforowanie podręczne po stronie klienta ...................................................l........................... 68 Buforowanie podręczne po stronie serwera...................................................l.......................... 70 3.+o  3,/6+2/2036+-   Serwlet ...................................................l...................................................l............................... 82 Serwer ...................................................l...................................................l................................ 85 Klient ...................................................l...................................................l................................. 93  #4786/- 3.+o  7o+2/2036+-$   Struktura odpowiedzi...................................................l...................................................l....... 132 Przesyłanie standardowej odpowiedzi...................................................l................................ 132 Używanie trwałych połączeń ...................................................l.............................................. 134 Buforowanie odpowiedzi...................................................l...................................................l. 135 Kody statusu ...................................................l...................................................l.................... 138 Nagłówki HTTP...................................................l...................................................l............... 140 Rozwiązywanie problemów...................................................l................................................ 147 Sześć sposobów uzyskiwania korzyści z serwletów ...................................................l.......... 158 3.+o  7o+2/+;+683-98/.+2/  WAP i WML...................................................l...................................................l.................... 163 Obrazki...................................................l...................................................l............................. 171 Zawartość skompresowana ...................................................l................................................. 187 Serwer cykliczny.....................................l...................................................l............................ 190 3.+o ~/./2/7/7   Uwierzytelnianie użytkownika ...................................................l........................................... 196 Ukryte pola danych formularza ...................................................l.......................................... 197 Przepisywanie URL-u...................................................l...................................................l......200 Trwałe cookies...................................................l...................................................l................. 202 API — śledzenie sesji ...................................................l...................................................l...... 206 3.+o /4/-/q78;3   Uwierzytelnienie poprzez HTTP ...................................................l........................................ 224 Uwierzytelnienie na podstawie formularza ...................................................l........................ 230 Uwierzytelnienie niestandardowe...................................................l....................................... 233 Certyfikaty cyfrowe ...................................................l...................................................l......... 239 Protokół bezpiecznej transmisji danych (SSL)...................................................l................... 241 3.+o n-23,++.+2-   Relacyjne bazy danych ...................................................l...................................................l....251 JDBC API ...................................................l...................................................l........................ 253 Ponowne użycie obiektów bazy danych ...................................................l............................. 265 Transakcje ...................................................l...................................................l........................ 267 Serwlet księgi gości ...................................................l...................................................l......... 275 Zaawansowane techniki JDBC ...................................................l........................................... 280 Co dalej? ...................................................l...................................................l.......................... 283 +:+#/6:/846316+3;+2/  .+2/  3.+o 392+-++4/897/6;/8   Opcje komunikacji...................................................l...................................................l........... 285 Serwer daytime ...................................................l...................................................l................ 291 Serwer chat ...................................................l...................................................l...................... 321 3.+o  74(cid:25)o46+-+7/6;/8(cid:25);   Dzielenie informacji ...................................................l...................................................l........ 339 Dzielenie kontroli ...................................................l...................................................l............ 343 3.+o  #/6;/836436+-2/   Ładowanie rozproszone ...................................................l...................................................l... 352 Integracja z J2EE ...................................................l...................................................l............. 355 3.+o  28/62+-32++-+   Języki zachodnioeuropejskie ...................................................l.............................................. 362 Hołdowanie lokalnym zwyczajom ...................................................l..................................... 365 Języki spoza Europy Zachodniej ...................................................l........................................ 367 Więcej języków...................................................l...................................................l................ 371 Dynamiczna negocjacja języka...................................................l........................................... 373 Formularze HTML...................................................l...................................................l........... 382 3.+o  #//8$/+   Język Tea ...................................................l...................................................l......................... 390 Początki...................................................l...................................................l............................ 391 Informacja o żądaniu ...................................................l...................................................l....... 393 Administracja Tea...................................................l...................................................l............ 396 Zastosowania Tea ...................................................l...................................................l............ 400 Aplikacja „Narzędzia” ...................................................l...................................................l..... 405 Ostatnie słowo...................................................l...................................................l.................. 415 3.+o   /,+-63    Szkielet WebMacro....................................l...................................................l......................... 418 Instalacja WebMacro ...................................................l...................................................l....... 421 Dyrektywy WebMacro ...................................................l...................................................l.... 426 Szablony WebMacro ...................................................l...................................................l....... 429 Aplikacja „Narzędzia” ...................................................l...................................................l..... 434 Filtry...................................................l...................................................l................................. 439  #4786/- 3.+o  //28327869-832#/8   Elementy strony jako obiekty ...................................................l............................................. 441 Wyświetlanie zbioru wyników ...................................................l........................................... 443 3.+o     Prosta kompilacja języka XML ...................................................l.......................................... 454 Klasa manipulacyjna...................................................l...................................................l........ 459 Aplikacja „Narzędzia” ...................................................l...................................................l..... 463 3.+o  +:+#/6:/6 +1/7   Wykorzystywanie JavaServer Pages ...................................................l.................................. 472 Zasady działania ...................................................l...................................................l.............. 473 Wyrażenia i deklaracje ...................................................l...................................................l.... 476 Dyrektywy ...................................................l...................................................l....................... 477 JSP i JavaBeans ...................................................l...................................................l............... 482 Dołączenia i przekazania ...................................................l...................................................l.487 Aplikacja „Narzędzia” ...................................................l...................................................l..... 489 Biblioteki własnych znaczników ...................................................l........................................ 493 3.+o  2036+-/.3.+83;/   Analiza parametrów...................................................l...................................................l......... 499 Wysyłanie poczty elektronicznej...................................................l........................................ 504 Stosowanie wyrażeń regularnych ...................................................l....................................... 507 Uruchamianie programów ...................................................l.................................................. 511 Stosowanie metod rodzimych...................................................l............................................. 514 Występowanie jako klient RMI ...................................................l.......................................... 515 Usuwanie błędów...................................................l...................................................l............. 517 Poprawa wydajności ...................................................l...................................................l........ 524 3.+o +2;#/6:/8     Zmiany w Servlet API 2.3 ...................................................l.................................................. 527 Konkluzja...................................................l...................................................l......................... 541 3.+8/6(cid:25)8347#/6:/8    3.+8/6(cid:25)8347$$ #/6:/8    +:+#/6:/846316+3;+2/  .+2/  3.+8/6(cid:25)8347$./764836++4+-  3.+8/3.78+8979$$   3.+8/2-/2+3;/   3.+8/3.3;+2+   #363;.    W tym rozdziale: Alternatywa serwletu,  Odnawianie (powtórne ładowanie) serwletu,  Inicjalizacja i usuwanie,  Model jednowątkowy (Single Thread Model),  Przetwarzanie drugoplanowe,  Ładowanie i uruchamianie,  Buforowanie podręczne po stronie klienta,  Buforowanie podręczne po stronie serwera.  Czas istnienia (cykl życia) serwletu jest jednym z bardziej interesujących aspektów dotyczących serwletów. Czas istnienia jest hybrydą czasów używanych w środkach programowania CGI oraz środkach programowania niskiego poziomu WAI/NSAPI i ISAPI, tak jak zostało to omówione w rozdziale 1. („Wprowadzenie”). Czas istnienia (cykl życia) serwletów pozwala ich kontenerom na odniesienie się do wydajności, do problemów związanych z CGI oraz do problemów dotyczących bezpieczeństwa niskopozio- mowych środków programowania API dla serwerów. Kontenery serwletów uruchamiają zwykle wszystkie serwlety razem, w jednej maszynie wirtualnej Javy (JVM). Dzięki umiejscowieniu ser- wletów w tej samej JVM mogą one skutecznie wymieniać dane między sobą, jednak język Java nie daje możliwości wglądu jednemu serwletowi do „prywatnych” danych znajdujących się w drugim. Serwlety mogą istnieć w JVM pomiędzy zleceniami — jako egzemplarze obiektów.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 Dzięki temu zajęte jest mniej pamięci niż w przypadku pełnych procesów, a serwlety są nadal w stanie utrzymać odniesienia do zewnętrznych zasobów. Cykl życia serwletów jest wysoce ela- styczny. Jedyną rzeczą niezmienną i konieczną w tym cyklu jest to, iż kontener serwletu musi przestrzegać następnych zasad: 1. Stworzyć oraz zainicjalizować serwlet. 2. Obsłużyć wywołania usługi od klientów. 3. Usunąć serwlet i zwolnić przydzieloną mu pamięć. W przypadku serwletów jest rzeczą całkowicie naturalną, iż są one ładowane, tworzone i prze- chowywane w swojej własnej maszynie wirtualnej Javy — tylko po to, aby być usuniętymi, nie obsłużywszy żadnych zleceń od klientów lub po obsłużeniu tylko jednego takiego zlecenia. Jednak kontenery serwletów zachowujące się w taki sposób, nie utrzymają się długo na rynku. W tym rozdziale zostaną omówione najpopularniejsze oraz najczulsze realizacje czasów istnienia serwle- tów HTTP. 3/.2-++72+;689+2++: Większość kontenerów wykonuje wszystkie serwletyw jednej JVM w celu maksymalizacji zdol- ności serwletów do wymiany informacji (wyjątkiem są tutaj kontenery wyższej klasy, które reali- zują rozproszone wykonywanie serwletu na wielu serwerach, tak jak zostało to omówione w roz- dziale 12., „Serwlety korporacyjne i J2EE”). Wykonania wyżej wspomnianej pojedynczej maszyny wirtualnej Javy mogą być odmienne na różnych serwerach: Na serwerze napisanym w Javie, np. „Apache Tomcat”, sam sierwer może być wywoływany  w JVM wraz ze swoimi serwletami. Na pojedynczo przetwarzającym, wielowątkowym serwerzie WWW, zapisanym w innym  języku, wirtualna maszyna Javy może zostać zawarta w priocesie serwera. JVM, jako część procesu serwera, zwiększa wydajność, ponieważ serwlet istaje się w pewnym sensie kolejnym rozszerzeniem serwera API niskiego poziomu. Serwer takii może wywołać serwlet z „lekkim” połączeniem kontekstu, może również dostarczyć informacije o zleceniach poprzez bezpośrednie wywołania metod. Wieloprocesowy serwer WWW (który uruchamia kilka procesów, aby obsłużyć zlecenia)  właściwie nie może zawrzeć JVM bezpośrednio w swoim prociesie, ponieważ takiego nie posiada. Ten typ serwerów zwykle uruchamia zewnętrizną JVM, której procesy może współdzielić. Taki sposób oznacza, iż każde wejście do seirwletu będzie się wiązać ze skomplikowanym połączeniem kontekstu przypominającym FastCGI. Jednakże wszystkie serwlety będą nadal dzieliły ten sam zewnętrzny procies. Na szczęście, z perspektywy serwletów (a tym samym z naszej — jako ich twórców), implementa- cja serwerów nie ma większego znaczenia, ponieważ zacihowują się one zawsze w te sam sposób. 8/62+8;+7/6;/89  $6;+o3/1/4+6+ Tak jak to zostało już opisane, serwlety istnieją pomiędzy zleceniami jako egzemplarze obiektów (instancje). Inaczej mówiąc, w czasie ładowania kodu serwletu, serwer tworzy pojedynczy egzem- plarz, który obsługuje wszystkie zlecenia przeznaczone dla niego.. Poprawia to wydajność z trzech powodów: Zajmowana powierzchnia pamięci jest mała.  Eliminowane są obciążenia tworzenia obiektu (w przeciwinym wypadku konieczne byłoby  utworzenie nowego obiektu serwletu). Serwlet może być jużi ładowany w maszynie wirtualnej, kiedy zlecenie dopiero wchodzi, pozwalając mu na rozipoczęcie wywoływania natychmiast.  Umożliwione jest trwanie — serwlet może mieć wszystko, iczego potrzebuje podczas obsługi zlecenia, np. już połączenie z bazą danych może zostać iustanowione raz i używane wielokrotnie, a z takiego połączenia może korzystać wiele serwletów. iKolejnym przykładem jest serwlet koszyka zakupów, który ładuje do pamięci listę cen wrazi z informacją o ostatnio połączonych klientach. Inne serwlety w sytuacji, kiedy otrzymują tio samo zlecenie, pobierają całe strony przetrzymywane w pamięci podręcznej, celem zaoszczędizenia czasu. Serwlety nie tylko trwają pomiędzy zleceniami, lecz także wykonują wszystkie wątki stworzone przez siebie. Taka sytuacja nie jest może zbyt korzystna w przypadku serwletu „run-of-the-mill”, jednak daje interesujące możliwości. Rozważmy sytuację, w której podrzędny wątek przeprowa- dza pewne kalkulacje, podczas gdy inne wyświetlają ostatnie rezultaty. Podobnie jest w przypadku apletu animacyjnego, w którym jeden wątek zamienia obriaz, a inny nanosi kolory. -2 W celu przedstawienia cyklu życia (czasu istnienia serwletu) posłużymy się prostym przykładem. Przykład 3.1 pokazuje serwlet, który zlicza i wyświetla liczbę połączeń z nim realizowanych. Dla uproszczenia wynik przedstawiany jest jako zwykły tekst (kod do wszystkich przykładów dostęp- ny jest w Internecie; zobacz „Wstęp”). Przykład 3.1. Przykładowy prosty licznik import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SimpleCounter extends HttpServlet { int count = 0; public void doGet(HttpServletRequest req, HttpServlevtResponse res) throws ServletExceptivon, IOException { res.setContentType( text/plain; charset=ISO-8859-2 ); PrintWriter out = res.getWriter(); count++; out.println( Od pierwszego uruchomienia z serwletem vpołączono się  + count + razy. ); } }  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 Kod jest prosty: zwiększa oraz wyświetla wartość zmiennej nazwanej count, ale dobrze ukazuje „potęgę” trwałości. Kiedy serwer ładuje ten serwlet, tworzy pojedynczy egzemplarz celem obsłu- żenia wszystkich zleceń nałożonych na ten serwlet, dlatego właśnie kod bywa taki prosty. Kolejne wywołania tego serwletu będą odwoływać się do tego sameigo obiektu. -272-6323;+2/ Z punktu widzenia projektantów serwletów każdy klient to kolejny wątek, który wywołuje serwlet poprzez metody typu: service(), doGet(), doPost(), jak to pokazuje rysunek 3.11. Serwlet Rysunek 3.1. Wiele wątków — jeden egzemplarz serwle3tu Jeżeli nasze serwlety odczytują tylko zlecenia, piszą w odpowiedziach i zapisują informacje w lo- kalnych zmiennych, czyli w zmiennych określonych w metodzie, nie musimy obawiać się interak- cji pomiędzy wątkami. Jeżeli informacje zostają zapisane w zmiennych nielokalnych, czyli w zmiennych określonych w klasie, lecz poza szczególną metodą, musimy być świadomi, iż każdy z wątków klientów może operować tymi zmiennymi serwletu. Bez odpowiednich środków ostroż- ności sytuacja taka może spowodować zniszczenie danych oraz sprzeczności. I tak np. jeżeli ser- wlet SimpleCounter założy fałszywie, że zwiększanie licznika oraz wyprowadzenie wartości są przeprowadzane niepodzielnie (bezpośrednio jeden po drugim, nieprzerwanie), to gdy dwa zle- cenia zostaną złożone do SimpleCounter prawie w tym samym czasie, każdy z nich może wskazać tę samą wartość dla count. Jak? Na przykład jeden wątek zwiększa wartość dla count i zaraz po tym, zanim jeszcze pierwszy watek wypisze wynik count, drugi wątek również zwięk- sza wartość. W takim przypadku każdy z wątków wskaże tę samą wartość, po efektywnym zwięk- szeniu jej o 22. 1 To, iż jeden egzemplarz serwletu może obsłużyć wielne zleceń w tym samym czasie, może wydawać się dziwne. Dzieje się tak prawdopodobnie dlatego, że kniedy obrazujemy program uruchamiający, zwykle obserwujemy, jak egzemplarze obiektów wykonują zadannie, wywołując nawzajem swoje metody. Mimo że przedstawiony model działa w prostych przypadkach, nie przedstawia rzeczywistości dokładnie. Prawdziwa sytuacja wygląda tak, że wszystkie zadanian wykonują wątki. Egzemplarze obiektów nie są niczym więcej jak tylko strukturami danych, którymi noperują wątki. Dlatego możliwa jest sytuacja, w której dwa działające wątki używają w tym samym czasnie tego samego obiektu. 2 Ciekawostka: jeżeli wartość count byłaby zamiast 32-bitowego int, 64-bitowym long, teoretycznie możliwa byłaby sytuacja, że inkrementacja będzie donkonana tylko w połowie, tzn. do czasu gdy przerwie mu inny wątek. Dzieje się tak dlatego, że w Javie używana jest 32-bitowy stos. 8/62+8;+7/6;/89  Porządek wykonania wygląda mniej więcej w ten sposób: count++ // Wątek 1 count++ // Wątek 2 out.println // Wątek 1 out.println // Wątek 2 W tym przypadku ryzyko sprzeczności nie stanowi poważnego zagrożenia, ale wiele innych ser- wletów zagrożonych jest poważniejszymi błędami. W celu zapobiegania tego typu błędom oraz sprzecznościom, które im towarzyszą, można dodać jeden lub więcej synchronizowanych bloków do kodu. To gwarancja, że wszystko, co znajduje się w bloku synchronizowanym lub w metodzie synchronizowanej, nie będzie wywoływane przez inny wątek. Zanim jakikolwiek z wątków roz- pocznie wywoływanie kodu synchronizowanego, musi otrzymać monitor (zamek) na określony egzemplarz obiektu. Jeżeli inny wątek ma już monitor, np. dlatego że wywołuje ten sam blok syn- chronizowany lub inny z tym samym monitorem, wtedy pierwszy wątek musi zaczekać. Działa to na zasadzie „łazienki na stacji benzynowej” zamykanej na klucz (zawieszany zwykle na dużej, drewnianej desce). W naszym przypadku kluczem będzie monitor. Wszystko to dzieje się dzięki mechanizmom wbudowanym w język, tak więc obsługa jest łatwa. Synchronizacja powinna być jednak używana tylko w ostateczności. W przypadku niektórych platform sprzętowych otrzymanie monitora za każdym razem, kiedy wchodzimy do kodu synchronizowanego wymaga wiele opera- cji, a co ważniejsze — w czasie, kiedy jeden wątek wywołuje kod synchronizowany, pozostałe mogą być blokowane aż do zwolnienia monitora. Dla SimpleCounter istnieją cztery sposoby rozwiązywania potencjalnych problemów. Po pierwsze można dodać slowo kluczowe synchronized do doGet(): public synchronized void doGet(HttpServletRequest req, HttpServletResponse vres) Taka sytuacja gwarantuje zgodność przez synchronizację całej metody. Nie jest to jednak najlepszy sposób, ponieważ oznacza, iż serwlet może w tym samym iczasie obsłużyć tylko jedno zlecenie GET. Drugim sposobem jest zsynchronizowanie tylko dwóch wierszy, które chcemy wywołać niepo- dzielnie: PrintWriter out = res.getWriter(); synchronized(this) { count++; out.println ( Z serwletem połączono się + count + razyv. ); } Powyższa technika działa lepiej, ponieważ ogranicza czas, który serwlet spędza w swoim zsyn- chronizowanym bloku, osiągając ten sam cel zgodności w wyniku liczenia. Prawdą jest, iż techni- ka ta nie różni się znacząco od pierwszej. Trzecim sposobem ominięcia potencjalnych problemów jest utworzenie synchronizowanego bloku (który będzie robił wszystko, co musi być wykonane szeregowo), reszta będzie znajdować się po- za blokiem synchronizowanym. W przypadku naszego serwletu liczącego możemy zwiększyć wartość zmiennej (count) w bloku synchronizowanym, zapisać zwiększoną wartość do lokalnej zmiennej (zmiennej określonej wewnątrz metody), a następnie wyświetlić wartość lokalnej zmien- nej poza blokiem synchronizowanym:  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 PrintWriter out = res.getWriter(); int local_count; synchronized(this) { local_count = ++count; } out.println( Z serwletem połączono się + local_counvt + razy. ); Powyższa zmienna zawęża blok synchronizowany do najmniejszych możliwych rozmiarów, za- chowując przy tym zgodność liczenia. Celem zastosowania czwartej, ostatniej z metod musimy zadecydować, czy chcemy ponieść kon- sekwencje zignorowania wyników synchronizacji? Czasem bywa i tak, że konsekwencje te są do przyjęcia, np. zignorowanie synchronizacji może oznaczać, że klienci otrzymają wynik trochę nie- dokładny. Trzeba przyznać, iż to rzeczywiście nie jest wiielki problem. Jeżeli jednak oczekiwanoby od serwletu liczb dokładnych, wtedy sprawa wyglądałabyi trochę gorzej. Mimo iż nie jest to opcja możliwa do zastosowania na omawianym przykładzie, to na innych ser- wletach można dokonać zamiany dotychczasowych zmiennych na zmienne lokalne. Zmienne lo- kalne są niedostępne dla innych wątków i tym samym nie muszą być dokładnie strzeżone przed zniszczeniem. Jednocześnie zmienne lokalne nie istnieją pomiędzy zleceniami, tak więc nie mo- żemy ich użyć do utrzymywania stałego stanu naszego liciznika. -2-+o3-3;/ Model „jeden egzemplarz na jeden serwlet” wymaga jedynie ogólnego omówienia. Prawda jest taka, że każda nazwa zarejestrowana dla serwletu (lecz nie każde URL-owe dopasowanie do wzorca) jest związana z jednym egzemplarzem serwletu. Nazwa używana przy wchodzeniu do serwletu określa, który egzemplarz obsłuży zlecenie. Taka sytuacja wydaje się być właściwa, po- nieważ klient powinien kojarzyć odmienne nazywanie serwletów z ich niezależnym działaniem. Osobne egzemplarze są ponadto wymogiem dla serwletów zgodnych z parametrami inicjalizacji, co zostało omówione poniżej. Nasz przykładowy SimpleCounter posługuje się zmienną przy zliczaniu liczby połączeń z nim wykonanych. Jeżeli zaistniałaby potrzeba liczenia dla wszystkich egzemplarzy (a tym samym wszystkich zarejestrowanych nazw), możliwe jest użycie klasy lub zmiennej statycznej. Zmienne takie są wspólne dla wszystkich egzemplarzy klasy. Przykład 3.2 ukazuje liczbę uruchomień ser- wletu, liczbę egzemplarzy utworzonych przez serwer (na jedną nazwę) oraz całkowitą liczbę połą- czeń z tymi egzemplarzami. Przykład 3.2. Licznik całościowy import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class HolisticCounter extends HttpServlet { static int classCount = 0; // współdzielony przez wsvzystkie egzemplarze int count = 0; // osobny dla każdego vserwletu static Hashtable instances = new Hashtable(); // rvównież współdzielony public void doGet(HttpServletRequest req, HttpServlevtResponse res) throws ServletExceptivon, IOException { .2+;+2/(cid:18)43;8 62/o+.3;+2/(cid:24)7/6;/89  res.setContentType ( text/plain; charset=ISO-8859-2 ); PrintWriter out = res.getWriter(); count++; out.println ( Z serwletem połączono się + count + ravzy. ); // pamięta count poprzez wstawienie odwołania do niego w vtablicy // asocjacyjnej instances. Powtarzające się wpisy są ignvorowane. // Metoda size() zwraca liczbę egzemplarzy instances.put(this, this); out.println( Aktualnie jest + instances.size() + evgzemplarz(y) ); classCount++; out.println ( Licząc wszystkie egzemplarze, z serwletem tyvm +  łączono się + classCount + razy ); } } Przedstawiony licznik całościowy (HolisticCounter), śledzi liczbę połączeń własnych za pomocą zmiennej count, liczbę połączeń wspólnych — za pomocą zmiennej classCount oraz liczbę egzemplarzy — za pomocą tablicy asocjacyjnej instances (kolejny wspólny element, który musi być zmienną klasy). Przykład ten został pokazainy na rysunku 3.2. Rysunek 3.2. Widok licznika całościowego Jeśli ktoś próbował użyć omówionych liczników we własnym zakresie, być może zauważył, iż z każdą kolejną rekompilacją liczenie zaczyna się automatycznie od 1. Nie jest to defektem, tylko właściwością. Większość serwerów odnawia (powtórnie ładuje) serwlety, po tym jak zmieniają się ich pliki klasy (pod domyślnym katalogiem serwletów WEB-INF/classes). Jest to procedura wy- konywana na bieżąco, która znacznie przyśpiesza cykl testu i rozbudowy oraz pozwala na przedłu- żenie czasu sprawnego działania serwera. Odnawianie serwletu może wydawać się proste, jednak wymaga dużego nakładu pracy. Obiekty klasy ClassLoader zaprojektowane są do jednokrotnego załadowania klasy.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 Aby ominąć to ograniczenie i wielokrotnie ładować serwlety, serwery używają własnych progra- mów ładujących, które ładują serwlety ze specjalnych ikatalogów, takich jak WEB-INF/classes. Kiedy serwer wysyła zlecenie do serwletu, najpierw sprawdza, czy plik klasy serwletu zmienił się na dysku. Jeżeli okaże się, że tak, wówczas serwer nie będzie już używał programu ładującego sta- rej wersji pliku, tylko utworzy nowy egzemplarz własnego programu ładującego klasy — celem załadowania nowej wersji. Niektóre serwery poprawiają wydajność poprzez sprawdzanie znaczni- ków modyfikacji czasu tylko co jakiś czas lub na wyraźnei żądanie administratora. W starszych wersjach Interfejsów API (sprzed 2.2) inne serwlety ładowane były przez odmienne programy ładujące — co powodowało czasem zgłoszenie ClassCastException jako wy- jątku, kiedy serwlety wymieniały informacje (ponieważ klasa załadowana przez jeden program ładujący nie jest tym samym, co klasa ładowana przez inny, nawet jeżeli dane dotyczące klasy są identyczne). Interfejs API 2.2 jest gwarancja, że problemy z ClassCastException nie pojawią się dla serwletów w tym samym kontekście. Tak więc obecnie większość implementacji serwerów ładuje każdy kontekst aplikacji WWW w jednym programie ładującym klasy oraz używa nowego pro- gramu ładującego do załadowania całego kontekstu, jeżeli jakikolwiek serwlet w kontekście ule- gnie zmianie. Skoro więc wszystkim serwletom oraz klasom obsługującym w kontekście zawsze odpowiada ten sam program ładujący, nie należy się obawiać żadnych, nieoczekiwanych wyjątków ClassCast- Exception podczas uruchamiania. Powtórne ładowanie całego kontekstu powoduje mały spa- dek wydajności, który jednak występuje tylko podczas twiorzenia. Powtórne ładowanie (odnawianie) klasy nie jest przeprowadzane tylko wtedy, kiedy zmianie ulega klasaosbługująca. Celem większej efektywności określenia, czy jest konieczne odnawianie kon- tekstu, serwery sprawdzają tylko znaczniki czasu serwletów klasy. Klasy obsługujące w WEB- -INF/classes mogą być także powtórnie załadowane, gdy kontekst jest odnowiony, lecz jeżeli kla- sa obsługująca jest jedyną klasą do zmiany, serwer priawdopodobnie nie zauważy tego. Odnowienie serwletu nie jest także wykonywane dla wszystkich klas (serwletu lub innych) do- stępnych w ścieżce klasy serwera. Klasy takie ładowane są przez rdzenny (pierwotny) program ładujący, a nie własny, konieczny do powtórnego załadowania. Są również ładowane jednorazowo i przechowywane w pamięci nawet wtedy, gdy ich pliki ulegają zmianie. Jeżeli chodzi o klasy globalne (takie jak klasy narzędziowe com.oreilly.servlet) to najlepiej jest umieścić je gdzieś na ścieżce klasy, gdzie unikną odnowienia. Przyśpiesza to proces powtórnego ładowania oraz pozwala serwletom w innych kontekstach wspólnie używać tych obiektów bez ClassCast- Exception. Serwlety, podobnie jak aplety mogą określać metody init() i destroy(). Serwer wywołuje metodę init() zaraz po utworzeniu obiektu, jednak zanim jeszcze serwlet obsłuży jakiekolwiek /83.™28˜™/7863˜  zlecenie. Serwer wywołuje metodę destroy() po wyłączeniu serwletu i po zakończeniu wszyst- kich zleceń lub po przekroczeniu ich limitu czasowego3. W zależności od rodzaju serwera oraz konfiguracji aplikacji WWW metoda init() może zostać wywołana w poniższych momentach: podczas uruchamiania serwletu,  podczas pierwszego zlecenia obsługi, przed wywołaniemi metody service(),  na żądania administratora serwera.  W każdym przypadku metoda init() zostanie wywołana i zakończona zanim serwlet obsłuży swoje pierwsze zlecenie. Metoda init() jest zwykle wykorzystywana do inicjalizacji serwletu — tworzenia lub ładowa- nia obiektów używanych przez serwlet w procesie obsługi zleceń. W czasie wykorzystywania metody init() serwlet może chcieć odczytać swoje parametry inicjalizacji (init), które są do- starczane samemu serwletowi i nie są w jakikolwiek sposób związane z jednym zleceniem. Mogą one określać takie wartości początkowe jak wartość początkowa licznika lub wartości domyślne, takie jak np. szablon, który powinien zostać użyty w przypadku nie określenia tego w zleceniu. Parametry początkowe serwletu można znaleźć w deskryptorze wdrożenia (web.xml). Niektóre serwery mają graficzne interfejsy, mogące zmodyfikować iten plik (przykład 3.3). Przykład 3.3. Ustalanie wartości parametrów w deskry3ptorze rozmieszczenia ?xml version= 1.0 encoding= ISO-8859-2 ? !DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//DTD Web Application 2.2//EN http://java.sun.com/j2ee/dtds/web-app_2_2.dtd web-app servlet servlet-name counter /servlet-name servlet-class InitCounter /servlet-class init-param param-name initial /param-name param-value 1000 /param-value description To jest wartość początkowa dla counter !--v opcjonalnie -- /description 3 Specyfikacje projektów, mającego ukazać się na rynku nInterfejsu API 2.3 (Servlet API 2.3), zakładają, że dodane zostaną metody cyklu życia (czasu istnienina), które umożliwią serwletom oczekiwanie na sygnały, kiedy kontekst lub sesja są tworzone lub znakańczane oraz podczas wiązania i rozwiązywania atrybutu z kontekstem lub sesją.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 /init-param /servlet /web-app Wielokrotne elementy init-param mogą zostać umieszczone w znaczniku servlet . Znacznik description jest opcjonalny, pierwotnie miał być przeznaczony do graficznych programów narzędziowych. Pełną definicję typu dokumentu do pliku web.xml można znaleźć w Dodatku F „Kodowania”. Podczas stosowania metody destroy(), serwlet powinien zwolnić wszystkie zasoby, które wcze- śniej pozyskał, takie które nie będą automatycznie usnięte. Metoda destroy()daje również serwletowi możliwość zapisania składowanych informacji nie zapisanych dotychczas lub innych trwałych informacji, które powinny zostać odczytane podczas kolejnego wywołania metody init(). -2/83.28 Parametry początkowe mają wiele zastosowań. Jednak przede wszystkim określają początkowe lub domyślne wartości zmiennych serwletu lub przekazują serwletowi informację, jak w określony sposób dostosować jego zachowanie. W przykładzie 3.4 nasz SimpleCounter został rozsze- rzony celem odczytania parametru początkowego (zwanego initial), który przechowuje war- tość początkową dla naszego licznika. Poprzez ustawianie początkowego stanu licznika na wyso- kie wartości można sprawić, że nasza strona będzie populiarniejsza niż w rzeczywistości. Przykład 3.4. Licznik odczytujący parametry początko3we import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class InitCounter extends HttpServlet { int count; public void init() throws ServletException { String initial = getInitParameter( initial ); try { count = Integer.parseInt(initial); } catch (NumberFormatException e) { count = 0; } } public void doGet (HttpServletRequest req, HttpServlvetResponse res) throws ServletExceptvion, IOException { res.setContentType ( text/plain; charset=ISO-8859-2 ); PrintWriter out = res.getWriter(); count++; out.println ( Od załadowania serwletu + (być może z wartością początkową pobraną z parametru) ); out.println ( z serwletem tym łączono się ); out.println (count + razy. ); } } /83.™28˜™/7863˜  Co się stało z super.init(config)? W Interfejsie API 2.0 serwlet, implementujący metodę init(), musiał implementować również jej formularz, który przejmował parametr ServletConfig oraz wywołać super.init (config): public void init(ServletConfig config) throws ServletExcveption { super.init(config); // poniżej właściwy kodu metody} Parametr ServletConfig dostarczał informacji serwletowi o konfiguracji, a wywołanie su- per.init(config) przekazywało obiekt konfiguracyjny do nadklasy GenericServlet, gdzie był zapisywany do użytku serlwetu. Klasa GenericServlet specjalnie używała przeka- zanego parametru config celem implementacji samego interfejsu ServletConfig (przeka- zując wszystkie wywołania do delegowanej konfiguracji i pozwalając serwletowi na wywołanie metod z ServletConfig — dla wygody). Powyższa operacja była bardzo zawiła, lecz w Interfejsie API 2.1, została jednak uproszczona do tego stopnia, że obecnie wystarczy, aby serwlet implementował wersję bezargumentową init(), a obsługa ServletConfig i GenericServlet będzie zrealizowana na dalszym planie. Klasa GenericServlet współpracuje z bezargumentową metodą init, z kodem przypominającym poniższy: public class GenericServlet implements Servlet, ServvletConfig { ServletConfig _config = null; public void init(ServletConfig config) throws ServletvException { _config = config; log( init wywołano ); init(); } public void init() throws ServletException { } public String getInitParameter(String name) { return _config.getInitParameter(name); } // itd. ... } Zwróćmy uwagę, iż serwer wywołuje w czasie inicjalizacji metodę serwletu init(Serv- letConfig config). Zmiana w wersji 2.1 dotyczyła tego, iż obecnie GenericServlet przekazuje to wywołanie do bezargumentowej metody init(), którą można zignorować, nie martwiąc się o config. Jeżeli chodzi o zgodność z poprzednimi wersjami należy nadal nadpisać init(ServletConfig config) i wywoływać super.init(config). W przeciw- nym wypadku być może nie będziemy mogli wywoływać metodsy bezargumentowej init(). Niektórzy z programistów uważają, iż dobrze jest wywołać najpierw super.destroy() pod- czas implementacji destroy(). Powoduje to, że implementacja metody destroy() z Ge- nericServlet informuje rejestr zdarzeń, że serwlet jest niszczony.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 Metoda init() wykorzystuje metodę getInitParameter() w celu uzyskania wartości pa- rametru zwanego initial. Metoda ta pobiera nazwę parametru jako String i zwraca wartość również jako String. Nie ma możliwości uzyskania wartości innego typu. Dlatego serwlet ten przekształca wartość String w wartość int lub w razie problemów domyślnie ustawia wartość na 0. Należy pamiętać, że jeżeli chcemy wypróbować ten przykład, może się okazać konieczne powtórne uruchomienie serwera celem wprowadzenia zmian w web.xml oraz odniesienie się do serwletu poprzez użycie zarejestrowanej nazwy. -2/83.+28/7863 Dotychczas przykłady liczników demonstrowały, jak stan serwletu utrzymuje się pomiędzy połą- czeniami. To jednak rozwiązuje problem tylko częściowo. Za każdym razem, kiedy serwer jest wyłączany lub serwlet odnawiany, liczenie zaczyna się od nowa. Rzeczą naprawdę potrzebną jest trwanie licznika niezależnie od ładowań — licznik, któriy nie zaczyna ciągle od początku. To zadanie mogą wykonać metody init() i destroy(). Przykład 3.5 poszerza wcześniej omówiony InitCounter poprzez dodanie serwletowi możliwość zachowania swojego stanu podczas wykonywania destroy() oraz ponownego odczytania w init(). Dla uproszczenia przyjmijmy, że serwlet ten nie jest zarejestrowany i dostępny tylko pod adresem http://server:port/ servlet/InitDestroyCounter. Gdyby ten serwlet był zarejestrowany pod różnymi nazwami, musiał- by zachowywać oddzielny stan dla każdej z nazw. Przykład 3.5. Trwały licznik import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class InitDestroyCounter extends HttpServlet {v int count; public void init() throws ServletException { //Spróbuj odczytać wartość początkową dla count z zapisanevgo //i trwałego stanu FileReader fileReader = null; BufferedReader bufferedReader = null; try { fileReader = new FileReader( InitDestroyCounter.inivtial ); bufferedReader = new BufferedReader(fileReader); String initial = bufferedReader.readLine(); count = Integer.parseInt(initial); return; } catch (FileNotFoundException ignored) { } // brak stanvu zapisanego catch (IOException ignored) { } // problem vpodczas czytania catch (NumberFormatException ignored) { } // niepopravwnie zapisany stan finally { // Nie zapomnij zamknąć pliku try { if (bufferedReader != null) { bufferedReader.close(); } } catch (IOException ignored) {} } /83.™28˜™/7863˜  // W razie niepowodzenia, sprawdź parametr inicjalizujvący String initial = getInitParameter( initial ); try { count = Integer.parseInt(initial); return; } catch (NumberFormatException ignored) {} // null lubv liczba nie całkowita // Domyślne dla początkowego stanu licznika 0 count = 0; } public void doGet (HttpServletRequest req, HttpServlvetResponse res) throws ServletExceptvion, IOException { res.setContentType ( text/plain; charset=ISO-8859-2 ); PrintWriter out = res.getWriter(); count++; out.println ( Z serwletem połączono się już + count v+ razy. ); } public void destroy() { super.destroy(); //całkowicie opcjonalne saveState(); } public void saveState() { // Spróbuj zapisać bieżącą wartość FileWriter fileWriter = null; PrintWriter printWriter = null; try { fileWriter = new FileWriter( InitDestroyCounter.inivtial ); printWriter = new PrintWriter(fileWriter); printWriter.println(count); return; } catch (IOException e) { // problem podczas pisania // Zgłoś wyjątek. Patrz rozdział 5. } finally { // Nie zapomnij zamknąć plik if (printWriter != null) { printWriter.close(); } } } } Za każdym razem, gdy serwlet jest usuwany, jego stan jest zachowywany w pliku o nazwie Init- DestroyCounter.initial. Jeżeli nie ma dostarczonej ścieżki dostępu, plik jest zapisywany w bieżą- cym katalogu procesu serwera, zwykle jest to katalog startowy. Sposoby alternatywnej lokalizacji opisano w rozdziale 4., „Pobieranie informacji”. Plik ten zawiera liczbę całkowitą, zapisaną jako ciąg znaków reprezentujący ostatnie liczenie. Przy każdym ładowaniu serwera pojawia się próba odczytu z pliku zachowanego stanu licznika. Jeżeli z jakiegoś powodu próba odczytu nie powiedzie się (jak ma to miejsce podczas pierwszego uruchomienia serwletu, ponieważ plik jeszcze wtedy nie istnieje), serwlet sprawdza, czy parametr inicjalizujący jest określony.W razie niepowodzenia zaczyna od zera. Podczas stosowania metody init() zalecana jest najwyższa ostrożność.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 Serwlety mogą zachowywać swój stan na wiele różnych sposobów. Niektóre z nich mogą posłu- żyć się formatem użytkowym pliku, tak jak zostało to pokazane w niniejszym rozdziale. Inne ser- wery zapisują swój stan jak zserializowane obiekty Javy lub umieszczają go w bazie danych. Nie- które wykorzystują nawet technikę journaling, powszechnie stosowaną przy bazach danych oraz przy kopiach zapasowych taśm, gdzie pełny stan serwletu jest zapisywany rzadko, podczas gdy plik dziennika wprowadza do pamięci przyrostowe aktualizacje w trakcie zmian. To, której meto- dy użyje serwlet, zależy od sytuacji. Powinniśmy być zawsze świadomi tego, iż zapisywany stan nie podlega żadnym zmianom w tle. Teraz może nasuwać się pytanie, co się stanie, jeżeli serwer ulegnie awarii? Metoda destroy() nie zostanie wywołana4. Nie jest to jednak problem dla metod destroy(), które muszą tylko zwolniać zasoby; przeładowany serwer równie dobrze się do tego nadaje (czasem nawet lepiej). Sytuacja taka jest problemem dla serwletu, który musi zapisywać swój stan we własnej metodzie destroy(). Ratunkiem dla tych serwletów jest częstsze zapisywanie swojego stanu. Serwlet może „wybrać” zapisanie swojego stanu po obsłudze każdego ze zleceń, jak powinien to zrobić serwer szachowy (chess server), że nawet gdy serwer jest ponownie uruchamiany, gra może zostać wznowiona z ostatnim układem na szachownicy. Inne serwlety mogą potrzebować zapisać stan tylko po zmianie jakiejś ważnej wartości — lista zakupów (shopping cart) serwlet musi zapisać swój stan tylko wtedy, gdy klient doda lub usunie pozycję z listy. I w końcu niektóre serwlety mo- gą tracić część swoich ostatnich zmian stanu. Takie serwlety mogą zapisywać stan po pewnej określonej liczbie zleceń, np. w naszym InitDestoyCounter wystarczającym powinno być zapisywanie stanu co dziesięć połączeń. Celem zaimplementowania tego można dodać prosty wiersz na końcu doGet(): if (count 10 == 0) saveState(); Można zapytać, czy jest to istotna zmiana? Wydaje się, że tak, biorąc pod uwagę zagadnienia związane z synchronizacją. Stworzyliśmy możliwość utratyi danych (jeśli saveState() zostanie uruchomiony przez dwa wątki w tym samym czasie) oraz ryzyko, że saveState() nie będzie w ogóle wywołane, jeżeli liczenie zostanie zwiększone przez kilka wątków z rzędu przed spraw- dzeniem. Załóżmy, że taka możliwość nie istniała, kiedy saveState() było wywoływane tylko z metody destroy(), bo metoda ta jest wywoływana tylko raz na jeden egzemplarz serwletu. Jednak teraz, kiedy saveState() jest wywoływana w metodzie doGet() musimy ponownie się nad tym zastanowić. Jeżeli zdarzyłoby się kiedyś, że serwlet ten byłby odwiedzany tak często, iż powstałoby więcej niż 10 jednocześnie wykonujących się wątków, jest prawdopodobne, że dwa serwlety (10 osobnych zleceń) będą w saveState() w tym samym czasie. Może to spowodo- wać zniszczenie pliku z danymi lub doprowadzić do jednoczesnego zwiększenia count przez dwa watki, zanim któryś „zorientuje się”, iż minął czas wywoływania saveState(). Rozwiązanie jest proste. Przemieśćmy kontrolę liczenia do bloku zsynchronizowanego, tam gdzie count jest zwiększane: int local_count; synchronized(this) { local_count = ++count; 4 Jeżeli mamy szczęście i nasz serwer nie będzie miał nawarii podczas stosowania metody destroy(). W przeciwnym razie możemy zostać z częściowo zapisannym plikiem stanu — pozostałościami zapisanymi na górze naszego poprzedniego stanu. Celem osiągnięcina całkowitego bezpieczeństwa, serwlet powinien zapisać swój stan w pliku tymczasowym, kopiując go nnastępnie na góre oficjalnego pliku stanu w jednym poleceniu. 3.//.23;83;(cid:18)#21/$6/+.3./(cid:24)  if (count 10 == 0) saveState(); } out.println( Z serwletem połączono się + count + razy. );v Wniosek z powyższych rozważań jest jeden: bądźmy przezorni i chrońmy kod serwletu przed pro- blemami związanymi z wielowątkowym dostępem. Mimo iż typową sytuacją jest jeden egzemplarz serwletu na jedną zarejestrowaną nazwę serwletu, to możliwa jest również pula egzemplarzy utworzonych dla każdej z nazw serwletu, której każdy egzemplarz obsługuje zlecenia. Serwlety sygnalizują taka chęć poprzez zaimplementowanie inter- fejsu javax.servlet.SingleThreadModel. Jest to prosty interfejs tag, który nie określa żadnych metod ani zmiennych, służy tylko do oznaczenia serwletu jako „wyrażającego chęć” zmiany cyklu życia. Serwer, który ładuje serwlet typu SingleThreadModel musi gwarantować, zgodnie z doku- mentacją Interfejsu API, że żadne dwa wątki nie będą wywoływały jednocześnie jego metody „se- rvice”. W celu spełnienia powyższego warunku każdy wątek używa wolnego egzemplarza serwletu z puli, tak jak na rysunku 3.3, dzięki temu każdy serwlet, implementując SingleThreadModel, może zostać uznany za bezpieczny co do wątku oraz nie wymagający synchronizacji dostępu do jego zmiennych. Niektóre serwery dopuszczają konfigurację wielu egzemplarzy na pulę, inne — nie. Z kolei niektóre używają puli tylko z jednym egzemplarzem, powodując zachowanie iden- tyczne z metodą zsynchronizowaną service(). Pula serwletu Egzemplarz serwletu Egzemplarz serwletu Egzemplarz serwletu Egzemplarz serwletu Rysunek 3.3. Model jednowątkowy Czas istnienia modelu jednowątkowego (SingleThreadModel) nie jest używany do liczników lub innych aplikacji, które wymagają obsługi centralnego stanu. Czas istnienia może mieć pewne zastosowanie, jednak tylko w unikaniu synchronizacji, iciągle obsługując zlecenie sprawnie. Dla przykładu, serwlety, które łączą się z bazami danych muszą czasem wykonać kilka poleceń wewnętrznych bazy, niepodzielnie jako część pojedynczej transakcji. Każda transakcja wykony- wana na bazie danych wymaga wydzielonego obiektu połączenia z bazą, dlatego serwlet musi  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 zagwarantować, że żadne dwa wątki nie będą próbowały „wchodzić” na to samo połączenie w tym samym czasie. Można tego dokonać poprzez użycie synchronizacji i „pozwolenie” serwletowi na obsługę tylko jednego zlecenia w jednym czasie. Dzięki implementowaniu SingleThreadMo- del oraz posiadaniu tylko jedno obiektu połączenia na serwlet, może on w prosty sposób obsłu- giwać konkurencyjne (jednoczesne) zlecenia, ponieważ każdy egzemplarz będzie miał swoje połą- czenie. Zarys kodu pokazano w przykładzie 3.6. Przykład 3.6. Obsługa połączeń z baza danych z użyciem 3modelu jednowątkowego import java.io.*; import java.sql.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class SingleThreadConnection extends HttpServvlet implements SingvleThreadModel { Connection con = null; // połączenie z bazą danych, // jedno na każdy egzemplarz z pvuli public void init() throws ServletException { // Rozpocznij połączenie dla tego egzemplarza try { con = estabilishConnection(); con.setAutoCommit(false); } catch (SQLException e) { throw new ServletException(e.getMessage()); } } public void doGet (HttpServletRequest req, HttpServlvetResponse res) throws ServletExceptvion, IOException { res.setContentType ( text/plain ); PrintWriter out = res.getWriter(); try { // Użyj połączenia utworzonego specjalnie dla tego egzevmplarza Statement stmt = con.createStatement(); // Aktualizuj bazę danych jakimikolwiek sposobami // Zatwierdź transakcję con.commit(); } catch (SQLException e) { try { con.rollback(); } catch (SQLException ignorevd) { } } } public void destroy() { if (con != null) { try { con.close(); } catch (SQLException ignored) v{ } } } private Connection estabilishConnection() throws SvQLException { // Nie zaimplementowane. Patrz rozdział 9. } } 6/8;+6+2/;8/  W rzeczywistości SingleThreadModel nie jest najlepszym rozwiązaniem dla tego typu apli- kacji. O wiele lepszym byłoby dla serwletu użycie wydzielonego obiektu ConnectionPool przechowywanego jako zmienna klasy, za pomocą którego mógłby zarządzać połączeniami. Połą- czenie usunięte z ewidencji może być przechowywane jako lokalna zmienna, zapewniająca wy- dzielony dostęp. Zewnętrzna pula zapewnia serwletowi więcej kontroli nad zarządzaniem połącze- niami. Pula może również zweryfikować poprawność każdego połączenia i zostać skonfigurowana w taki sposób, że będzie zawsze tworzyła pewną minimalną liczbę połączeń, lecz nigdy większą niż określona liczba maksymalna. Podczas używania modelu jednowątkowego (SingleThread- Model), serwer mógłby utworzyć znacznie więcej egzemplarzy (a tym samym połączeń) niż baza danych może obsłużyć. Zatem należy unikać stosowania metody SingleThreadModel. Większość innych serwletów mogłaby być lepiej implementowana z użyciem synchronizacji oraz puli zasobów zewnętrznych. Prawdą jest, iż interfejs daje pewien stopień kontroli programistom nie znającym programowania wielowątkowego; choć, gdy SingleThreadModel czyni sam serwlet bezpiecznym co do wątku, to interfejs nie czyni tego z systemem. Interfejs nie zapobiega problemom związanym z synchro- nizacją, które wynikają z jednoczesnego dostępu serwletów do wspólnych zasobów, takich jak np. zmienne statyczne czy obiekty poza zasięgiem serwletu. Problemy związane z wątkami będą się pojawiały zawsze podczas pracy w systemie wielowątkowym — z lub bez SingleThreadModel. Serwlety potrafią więcej niż tylko utrzymywać się pomiędzy kolejnymi odwywołaniami do nich. Każdy wątek uruchomiony przez serwlet może kontynuować wykonywanie nawet po wysłaniu odpowiedzi. Możliwość ta najlepiej sprawdza się przy dłuższych zadaniach, których wyniki przy- rostowe są udostępniane wielu klientom. Wątki drugoplanowe, uruchomione w init(), wyko- nują pracę w sposób ciągły, podczas gdy wątki obsługujące zlecenia wyświetlają stan bieżący za pomocą doGet(). Jest to technika podobna do używanej w apletach animacyjnych, gdzie jeden wątek dokonuje zmian na rysunku, a drugi nanosi koloryi. Przykład 3.7 ukazuje serwlet wyszukujący liczb pierwszych, większych od kwadryliona. Tak wielka liczba wybierana jest celowo, żeby uczynić liczenie powolnym i aby zademonstrować efekty buforujące (które będą potrzebne przy omawianiu dalszej części materiału). Algorytm uży- wany przez serwlet jest bardzo prosty. Serwlet selekcjonuje wszystkie liczby nieparzyste i następ- nie próbuje podzielić je przez liczby nieparzyste całkowite z przedziału od 3 do ich pierwiastka kwadratowego. Jeżeli dana liczba nie jest podzielna bez reszty przez żadną tych liczb, wtedy jest uznawana za liczbę pierwszą5. Przykład 3.7. W poszukiwaniu liczb pierwszych import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; 5 Można zapytać, dlaczego sprawdzane są tylko czynnikin mniejsze od pierwiastka kwadratowego? Ponieważ, jeżeli liczba miałaby dzielniki wśród liczbn większych od tego pierwiastka, to musiałaby je także mieć wśród liczb od niego mniejszych.  3.+o +7782/2+(cid:18)- -+(cid:24)7/6;/89 public class PrimeSearcher extends HttpServlet implvements Runnable { long lastprime = 0; // ostatnia znvaleziona liczba pierwsza Date lastprimeModified = new Date(); // kiedy została znavleziona Thread searcher; // drugoplanovwy wątek szukający public void init() throws ServletException { searcher = new Thread(this); searcher.setPrior
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Java Servlet. Programowanie. Wydanie II
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: