Ostatnio dużo było pytań i dyskusji nt. JPA i EJB 3 i już myślałem, że JSF całkowicie poszedł w odstawkę, kiedy rozgorzała dyskusja na temat możliwości wywoływania metod przed uruchomieniem strony JSF w wątku JSF dynamiczne inicjalizowanie zmiennych autorstwa melona. Początkowo miałem kilka pomysłów na rozwiązanie tematu, ale jak się okazało w JSF 1.2 pojawiła się ciekawsza opcja - wprowadzono nowy atrybut beforePhase do elementu f:view.
Czując słabnącą pozycję JSF w uproszczeniu życia twórcy aplikacji internetowych podczas dyskusji i ostatecznie sprowokowany stwierdzeniem Piotra Nowaka:
W asp.net jest to banalnie rozwiazane, mianowicie callback Page_load jest wywolywany przy kazdym wejsciu na strone, ciekawe dlaczego w jsf nie ma czegos takiego, tylko trzeba stosowac jakies hacki..
, postanowiłem przeszukać specyfikację JSF 1.2 w celu odnalezienia odpowiedzi. Nie chciało mi się wierzyć, że skoro istnieje coś w ASP.Net to nie ma tego w JSF 1.2. Nie długo trwało, kiedy podczas lektury zmian między JSF 1.1 a 1.2 (rozdział What’s Changed Since the Last Release) przeczytałem o nowych metodach klasy javax.faces.component.UIViewRoot. W szczególności jedna z nich wzbudziła moją ciekawość - UIViewRoot.getBeforePhaseListener. Krótki rekonesans po dokumentacji javadoc metody, gdzie mogłem przeczytać:
getBeforePhaseListener
public MethodExpression getBeforePhaseListener()
Returns:
the MethodExpression that will be invoked before this view is rendered.
i już wiedziałem, że jest to dokładnie to, czego szukałem. Klasy nasłuchujące zdarzeń związanych z etapami przetwarzania zlecenia JSF - javax.faces.event.PhaseListener - rejestrowane są m.in. w pliku faces-config.xml. Skoro tak, pomyślałem, to może i znajdę coś związanego ze wspomnianą metodą. Postanowiłem przeszukać specyfikację pod kątem klasy UIViewRoot i po chwili natrafiłem na rozdział 4.1.17 UIViewRoot, a tam na sekcję 4.1.17.2 Properties, gdzie moim oczom ukazała się właściwość beforePhaseListener, która może być również ustawiana per strona w znaczniku <f:view> za pomocą atrybutu beforePhase. Jeśli dodać do tego, że wartością właściwości jest wyrażenie wskazujące na metodę (typu javax.el.MethodExpression) to nie miałem już więcej złudzeń, że znalazłem rozwiązanie. Pozostało jedynie sprawdzić znalezisko w działaniu.
Szybko stworzyłem prostą aplikację JSF w NetBeans IDE 6, którą rozszerzyłem o następujące elementy.
1. Stworzyłem klasę pl.jaceklaskowski.jsf.PageLoadCallback
2. Zarejestrowałem powyższą klasę jako komponent zarządzany w faces-config.xml
package pl.jaceklaskowski.jsf;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
public class PageLoadCallback {
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
facesContext.addMessage(null, new FacesMessage("Komunikat z PageLoadCallback.beforePhase"));
UIOutput outputText = (UIOutput) facesContext.getViewRoot().findComponent("komunikat");
outputText.setValue(outputText.getValue() + " (zmodyfikowano w PageLoadCallback.beforePhase)");
}
}
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
<managed-bean>
<managed-bean-name>pageLoadCallback</managed-bean-name>
<managed-bean-class>pl.jaceklaskowski.jsf.PageLoadCallback</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
3. Użyłem atrybutu beforePhase w f:view ze wskazaniem na komponent zarządzany pageLoadCallback
<%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<f:view beforePhase="#{pageLoadCallback.beforePhase}">
<h1>
<h:outputText id="komunikat" value="JavaServer Faces" />
</h1>
<hr>
<h:messages layout="list" />
</f:view>
</body>
</html>
4. Uruchomiłem przykład na Apache Geronimo 2.0M6
A to jest właśnie efekt, którego poszukiwał melon. Dzięki za zwrócenie uwagi na tę kwestię!
To raczej ja powininem podziękować:)
OdpowiedzUsuńmelon.
Można jeszcze tak:
OdpowiedzUsuń<lifecycle>
<phase-listener>
sample.MyPhaseListener
</phase-listener>
</lifecycle>
public class MyPhaseListener implements PhaseListener {
public void afterPhase( PhaseEvent event ) {
}
public void beforePhase( PhaseEvent event ) {
FacesContext fc = event.getFacesContext();
........
}
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}