Veamos el código completo de un web.xml de ejemplo:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Ejemplos de JSF</display-name>
<description>
Ejemplos de JSF
</description>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.validateXml</param-name>
<param-value>true</param-value>
<description>
Set this flag to true if you want the JavaServer Faces
Reference Implementation to validate the XML in your
faces-config.xml resources against the DTD. Default
value is false.
</description>
</context-param>
<context-param>
<param-name>com.sun.faces.verifyObjects</param-name>
<param-value>true</param-value>
<description>
Set this flag to true if you want the JavaServer Faces
Reference Implementation to verify that all of the application
objects you have configured (components, converters,
renderers, and validators) can be successfully created.
Default value is false.
</description>
</context-param>
<!-- Faces Servlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
</web-app>
Lo que nos interesa aquí es el servlet controlador de JSF que intercepta las peticiones a las páginas JSF. Además de interceptar estas llamadas el servlet prepara el contexto JSF antes de enrutar a las páginas correspondientes. La invocación a la página JSF debe pasar necesariamente por este servlet, debe ser interceptada por el servlet controlador; veamos como hace esto. La etiqueta 'servlet' se atiene al estandar. La etiqueta 'servlet-mapping', concretamente 'url-pattern', merece un comentario: se refiera al patrón para que sea invocado el servlet, en nuestro caso ponemos una expresión bastante habitual: /faces/*. Es una ruta lógica o virtual, como el 'url-pattern' de todos los servlets que habitualmente hacemos, no hace referencia a un directorio físico de nuestra aplicación (ver la organización del proyecto de ejemplo). En este caso el patrón indica que el servlet será invocado ante cualquier petición a un recurso cuya ruta o dirección comience por 'faces' (el asterisco indica cualquier archivo jsp).
Hasta aquí todo normal. Demos un paso atrás y veamos como en uno de nuestros ejemplos index.jsp (no JSF) invoca a nuestra primera página JSF (Login.jsp). Veremos cómo es posible que el servlet controlador sea invocado:
<a href="faces/jsfs/jsf01Login/Login.jsp" target=_blank>Login</a>
La ruta física de la página JSF es /jsfs/jsf01Login/Login.jsp. Pero lo que nos interesa es la ruta lógica (el patrón de invocación). Para ello anteponemos el patrón (/faces) que hemos indicado en web.xml a la ruta física de Login.jsp. De esta forma la petición a faces/jsfs/jsf01Login/Login.jsp es interceptada por el servlet, ya que encaja con el patrón /faces/* definido en web.xml.
Si no hubiera encaje (supongamos que invocamos a la ruta física http://localhost:8060/jsf/jsfs/jsf01Login/Login.jsp) el error que obtendríamos sería: Cannot find FacesContext; que resulta una respuesta entendible una vez que se sabe algo de JSF: se ha encontrado el recurso jsp, pero el servlet no ha interceptado la petición y, puesto que el servlet prepara el contexto JSF, el servidor de aplicaciones encuentra que no tiene el contexto que precisa para resolver la petición.