15 października 2007

Konfiguracja aplikacji JSF ze stronami z i bez Facelets

Przez długi czas pytanie Rafała JSF || facelets na grupie pl.comp.lang.java nie dawało mi spokoju. Pytanie dotyczyło migracji aplikacji JSF do Facelets, w taki sposób, że aktualna funkcjonalność pozostałaby bez zmian (bez Facelets), podczas gdy nowe strony już korzystałyby z facelets. Pomysł polega na równoległym utrzymywaniu stron bez facelets i tworzeniu nowych z facelets. Jako, że gro czasu zajmowało mi rozpoznawanie EJB3 zostawiłem rozwiązanie problemu na później. W końcu, kiedy skończyłem prezentację konwerterów w JSF pomyślałem, że w tym tonie zabiorę się za facelets i to właśnie od tego pytania.

Kiedy już miałem odpowiedzieć, że takie rozdzielenie się nie da, aż mnie natchnęło i zacząłem kombinować z różnymi konfiguracjami mapowania servletu FacesServlet. Niestety trochę to trwało, aż natrafiłem na parametr facelets.VIEW_MAPPINGS, który wskazał mi właściwe rozwiązanie (w zasadzie to znalazłem odpowiedź w How do I use Facelets and JSP in the same application?).

Uruchomienie facelets wymaga definicji komponentu zarządzającego widokiem w faces-config.xml.

<?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">
<application>
<view-handler>
com.sun.facelets.FaceletViewHandler
</view-handler>
</application>
</faces-config>
Tutaj wszystko "po staremu". Klucz do rozwiązania leży w konfiguracji aplikacji internetowej - web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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-app_2_5.xsd">
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.jsp</param-value>
</context-param>
<context-param>
<param-name>facelets.VIEW_MAPPINGS</param-name>
<param-value>*.xhtml</param-value>
</context-param>
<context-param>
<param-name>facelets.DEVELOPMENT</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.verifyObjects</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.validateXml</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
</web-app>
Przy takiej konfiguracji wywołanie strony pod adresem /faces/welcomeJSF.jsp spowoduje wykonanie jej bez udziału facelets, który w prawdzie będzie wyzwalany, ale będzie akceptował wyłącznie strony zakończone rozszerzeniem .xhtml (parametr facelets.VIEW_MAPPINGS), np. /faces/welcomeJSF.xhtml. Inne rozszerzenia są poza zainteresowaniem facelets. Istotne jest również związanie FacesServlet poprzez mapowanie ścieżki (url-pattern ustawione na /faces/*) zamiast mapowania po rozszerzeniu.

Przykładowy projekt NetBeans IDE aplikacji internetowej korzystającej z "czystego" JSF oraz facelets dostępny jest jako jsf-zibezfacelets-webapp.zip.