NavigationHandler jest to część szkieletu aplikacyjnego JSF uruchamiania do obsługi łańcucha znakowego będącego logicznym identyfikatorem zwróconym przez wykonaną akcję w aplikacji i wybraniem nowego widoku do wyświetlenia. Jeśli wynik akcji jest null ten sam widok, który spowodował wykonanie akcji zostanie ponownie wyświetlony. W zasadzie najlepiej możnaby to uzmysłowić prezentując sygnaturę jedynej metody abstrakcyjnej klasy NavigationHandler:
public void handleNavigation(FacesContext context, String fromAction, String outcome)Na podstawie przekazanego kontekstu FacesContext oraz wyniku outcome działania akcji fromAction handleNavigation podejmuje decyzję o kolejnej stronie do wyświetlenia.
Implementacja JSF musi dostarczyć domyślną realizację NavigationHandler, który pozwala na zdefiniowanie przepływów (przejść między stronami w aplikacji) w plikach konfiguracyjnych (domyślnie faces-config.xml). Do konfiguracji przepływu służy znacznik <navigation-rule>. Znacznik może zawierać opcjonalny <from-view-id>, który akceptuje wartości wyrażenia regularnego dla aktualnego identyfikatora widoku postaci dosłownej (pełne dopasowanie)
<navigation-rule>, początek widoku (!) zakończonego gwiazdką
<from-view-id>/stworzKategorie.jsp</from-view-id>
<navigation-case>
<from-action>#{kategoriaAgent.stworz}</from-action>
<to-view-id>/wyswietl.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>, gwiazdkę, która określa wszystkie możliwe widoki (strony)
<from-view-id>/stworz*</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/wyswietl.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>lub po prostu bez określenia <from-view-id>, co przekłada się również na dowolny widok:
<from-view-id>*</from-view-id>
<navigation-case>
<from-action>#{kategoriaAgent.stworz}</from-action>
<to-view-id>/wyswietl.jsp</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>W ramach <navigation-rule> znajduje się dowolna liczba <navigation-case>, które precyzują dopasowanie.
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/wyswietl.jsp</to-view-id>
</navigation-case>
</navigation-rule>
Możliwe jest określenie wielu <navigation-rule> dotyczących tego samego <from-view-id>, ale niepoprawnym jest określenie tej samej kombinacji <from-*> dla danego <from-view-id>.
Zwrócenie null z akcji informuje NavigationHandler, aby nie przeszukiwał reguł i ponownie wyświetlił aktualny widok.
Elementy <from-outcome> oraz <from-action> odpowiadają parametrom wejściowym metody handleNavigation.
Możliwe jest skorzystanie z elementu <redirect/> do określenia przekierowania aktualnego żądania do zadanej strony, po którym następuje zatrzymanie przetwarzania żądania JSF (wywołując javax.faces.context.FacesContext.responseComplete()). Wyjątkiem jest środowisko portletowe, gdzie przekierowania nie są dozwolone.
<navigation-rule>Brak <navigation-rule>, która pasuje do parametrów metody handleNavigation nie zmienia aktualnego widoku (podobnie jak wartość null).
<from-view-id>/stworz*</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/wyswietl.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/failure.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
Znalezienie pasującego widoku powoduje zbudowanie nowego, tracąc stan poprzedniej (uwaga: może być czasochłonne).
Podmiana domyślnego NavigationHandler następuje programowo poprzez wykonanie metody javax.faces.application.Application.setNavigationHandler(NavigationHandler handler) lub deklaratywnie w faces-config.xml w sekcji <application>.
<application>Można wyobrazić sobie implementację, której konfigurację przepływów definiujemy w faces-config.xml oraz bazie danych. Dobrym źródłem wiedzy nt. temat będzie z pewnością wątek How the way for create a new NavigationHandler w stosunkowo młodym forum GlassFish WebTier. Możnaby również oprzeć NavigationHandler na regułach pisanych w Groovy czy umożliwić kreowanie przepływów z poziomu interfejsu użytkownika. Chętnie zapoznałbym się z wdrożonymi pomysłami.
<navigation-handler>klasa.realizujaca.NavigationHandler</navigation-handler>
</application>
Pozostaje życzyć sobie, żeby konstruowanie nowych kontrolek JSF było równie proste. Jest to bodajże najbardziej pracochłonne zadanie w JSF. Na razie jest nie na moje siły i wydaje się, że jedynie facelets mógłby być panaceum (acz wprowadza własny ViewHandler i znaczniki JSP należy migrować do znaczników facelets, co ponownie może być nie lada wyzwaniem).
Na końcu zdania w pierwszym akapicie pojawił się jakiś N"latający zarządca" - (N)aviationHandler ;-)
OdpowiedzUsuńTak czytam twój wpis i porównuję go do konfiguracji Struts 2 i jak dla mnie to JSF musi szybko nadrobić zaległości i zredukować ilość tagów z faces-config.xml, ciekawe jak pod tym względem wygląda JSF 2.0? Chyba zaraz sprawdzę...
Poprawione. Dzięki!
OdpowiedzUsuńZ tym faces-config.xml to na pewno znacząco musi się odchudzić, bo nie przypomina rozwiązań znanych obecnie z tak obszernym plikiem XML. Sprawę może znacząco uprościć zastosowanie Springa, acz przepływy nie mam pojęcia jak możnaby inaczej deklarować (w końcu na tym polega deklarowanie zamiast programowania, chociaż i to możnaby zaliczyć do meta-programowania). Potencjalnie możnaby oprzeć się o adnotacje, ale to nie ten kierunek, w którym chciałbym zmierzać z konfiguracją - grzebanie w kodzie to wymaganie jego posiadania przy każdorazowej zmianie - odpada. Sądzę, że napisanie własnego NavigationHandler może być "rescue". Pewnie coś w JSF 2.0 się w tej materii znalazło. Koniecznie raportuj o jakichkolwiek znaleziskach.
Jacek
Niestety, szybko przejrzałem JSF 2.0 Early Draft i ta część się nie zmieniła. Ale twoja uwaga o Springu zainspirowała mnie, żeby bardziej poeksperymentować ze zmianą nie tyle co zarządcy (NavigationHandler) tylko tego co stoi nad nim, tylko co to będzie??? Chyba znów wrócę do specyfikacji...
OdpowiedzUsuń