13 września 2006

Zgłębianie tajników działania środowiska JavaServer Faces (JSF)

0 komentarzy
UWAGA: Przez środowisko JSF rozumie się implementację JavaServer Faces, której środowiskiem uruchomieniowym jest kontener servletów, ewentualnie kontener portletów.

JSF działa w ramach kontenera servletów. Innymi słowy JSF jest aplikacją internetową ze wszystkimi jej konsekwencjami i niewątliwie znajomość technologii Java Servlets znacznie ułatwia zrozumienie działania mechanizmów JSF. Podstawową konsekwencją wykorzystania kontenera servletów jako fundamentu jest sposób uruchamiania aplikacji opartej o JSF. Punktem początkowym obsługującym zlecenia aplikacji JSF jest odpowiednie mapowanie javax.faces.webapp.FacesServlet w deskryptorze rozmieszczenia aplikacji internetowej (ang. deployment descriptor - /WEB-INF/web.xml). Najczęściej wykorzystywanym mapowaniem jest mapowanie per ścieżka (/faces/*) albo per rozszeżenie (*.faces i/lub *.jsf). Wyzwolenie servletu FacesServlet to tak na prawdę uruchomienie środowiska JSF. FacesServlet jest punktem wejścia do JSF - spoiwem łączącym dwie technologie Java Servlets i JavaServer Faces. Jak każdy servlet tak i FacesServlet wczytuje swoją konfigurację z deskryptora instalacji. Jest kilka elementów fundamentalnych dla działania FacesServlet (a tym samym i JSF). Są nimi:

  • fabryka obiektów FacesContext, które dostarczają informacji o aktualnie przetwarzanym zleceniu i odpowiedzi.
  • fabryka obiektów Lifecycle, które odpowiedzialne są za przetwarzanie zleceń według ustalonych etapów.

Parametrów konfiguracyjnych może być znacznie więcej i zależą od implementacji specyfikacji JSF. Konfiguracja obu fabryk obsługiwana jest przez implementację, z której korzystamy i zazwyczaj nie potrzeba się o nią martwić. Najczęściej wymieniane implementacje (środowiska uruchomieniowe) specyfikacji JSF to implementacja referencyjna JSF (ang. JSF Reference Implementation) oraz Apache MyFaces.

Każde zlecenie obsługiwane przez FacesServlet może uczestniczyć w 6 etapach, podczas których wykonywane są odpowiednie czynności związane z przetwarzanym zleceniem. Ważne jest, aby pamiętać, że przejście wszystkich etapów nie jest obowiązkowe i co istotne może być kontrolowane przez samego autora aplikacji JSF. Dodatkowo środowisko JSF może dostarczać własne etapy poza tymi wymienionymi w specyfikacji. Nie można zmienić kolejności wykonywania etapów. Dodać należy również, że lista etapów jest uporządkowana i ominięcie pojedyńczego implikuje zaniechanie wykonania następnych poza ostatnim.

Etapy w życiu zlecenia obsługiwanego przez JSF to:

  1. Restore View
  2. Apply Request Values
  3. Process Validations
  4. Update Model Values
  5. Invoke Application
  6. Render Response

Praca FacesServlet zaczyna się (i jednocześnie kończy) po odebraniu zlecenia i przekazaniu do obiektu implementującego interfejs javax.faces.lifecycle.Lifecycle. Referencyjna implementacja JSF udostępnia go jako obiekt typu com.sun.faces.lifecycle.LifecycleImpl. Po utworzeniu obiektu FacesContext (przy pomocy fabryki FacesContext) przekazuje się go kolejno do metod Lifecycle.execute(FacesContext) i Lifecycle.render(FacesContext). Zakończenie pracy serwletu kończy przetwarzanie zlecenia przez JSF.

Z każdym etapem można związać słuchacza (ang. listener), który jest uruchamiany przy wejściu do i wyjściu z wybranego etapu. Słuchacz implementuje interfejs javax.faces.event.PhaseListener. Domyślnie, konfiguracja słuchacza odbywa się w pliku konfiguracyjnym JSF - faces-config.xml przy pomocy znacznika faces-config/lifecycle/phase-listener (wartością znacznika jest nazwa klasy).

Wywołanie etapu poprzedza sprawdzenie, czy wywołano metodę FacesContext.renderResponse() na obiekcie typu FacesContext związanym z przetwarzanym zleceniem. Wywołanie metody FacesContext.renderResponse() jest sygnałem dla JSF, że zakończenie aktualnie przetwarzanego etapu powinno przekazać kontrolę zlecenia do ostatniego etapu - Render Response - omijając jakiekolwiek etapy pośrednie.

Poza sprawdzeniem stanu związanego z wywołaniem FacesContext.renderResponse(), następuje sprawdzenie, czy wywołano metodę FacesContext.responseComplete() na bieżącym obiekcie typu FacesContext. Informuje ona JSF, że należy zakończyć przetwarzanie zlecenia omijając jakiekolwiek etapy pośrednie. Jest to moment, w którym odpowiedź HTTP została już wysłana do klienta i zakończenie etapu to całkowite zakończenie przetwarzania zlecenia. Możliwym przykładem mogłoby być przekierowanie żądania (ang. HTTP redirect), albo wysłanie strumienia binarnego, np. obrazka.

Każdorazowo przed uruchomieniem etapu następuje wzbudzenie słuchaczy (jeśli takowe zostały zarejestrowane w faces-config.xml). Jeśli słuchacz wykona dowolną z w/w metod (FacesContext.responseComplete() albo FacesContext.renderResponse() w innym etapie niż Render Response) następuje zaniechanie wykonania etapu i zakończenie obsługi żądania, albo przejście do etapu Render Response. Wywołanie słuchaczy odbywa się poprzez wywołanie metod javax.faces.event.PhaseListener.beforePhase(PhaseEvent) bądź javax.faces.event.PhaseListener.afterPhase(PhaseEvent), odpowiednio przed i po etapie. Słuchacz rejestruje swoje zainteresowanie obsługą wejścia do i wyjścia z etapu implementując odpowiednią metodę, a same etapy poprzez metodę javax.faces.event.PhaseListener.getPhaseId().

Jak opisano wyżej, obie metody FacesContext.renderResponse() oraz FacesContext.responseComplete() mogą być wywołane przez elementy stworzone przez programistę w ramach aplikacji JSF (np. słuchacze) i w ten sposób wpływać na przebieg obsługi żądania.