El descriptor de implementación: web.xml

ID de región

REGION_ID es un código abreviado que Google asigna en función de la región que eliges cuando creas la app. El código no corresponde a un país ni a una provincia, aunque algunos ID de región puedan parecer similares a los códigos de país y provincia que se suelen usar. En el caso de las apps creadas después de febrero de 2020, REGION_ID.r se incluye en las URL de App Engine. En el caso de las apps existentes creadas antes de esta fecha, el ID de región es opcional en la URL.

Obtén más información acerca de los ID de región.

Las aplicaciones web de Java usan un archivo descriptor de implementación para determinar cómo se asignan las URL en los servlets, qué URL requieren autenticación y más información. Este archivo se llama web.xml y se encuentra en el WAR de la app en el directorio WEB-INF/. web.xml es parte del servlet estándar para aplicaciones web.

Para obtener más información sobre el estándar web.xml, consulta la especificación del Servlet.

Descriptor de implementación de web.xml

Un descriptor de implementación de una aplicación web describe las clases, recursos y configuración de la aplicación y la forma en que los usa el servidor web para entregar solicitudes web. Cuando el servidor web recibe una solicitud para la aplicación, usa el descriptor de implementación para asignar la URL de la solicitud al código que debe manejarla.

El descriptor de implementación es un archivo llamado web.xml. Se encuentra en el WAR de la app en el directorio WEB-INF/. El archivo es un archivo XML cuyo elemento raíz es <web-app>.

A continuación, se muestra un ejemplo simple de web.xml que asigna todas las rutas de URL (/*) a la clase de servlet mysite.server.ComingSoonServlet:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <servlet>
        <servlet-name>comingsoon</servlet-name>
        <servlet-class>mysite.server.ComingSoonServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>comingsoon</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Servlets y rutas de URL

web.xml define las asignaciones entre las rutas de URL y los servlets que controlan las solicitudes con esas rutas. El servidor web usa esta configuración para identificar el servlet que maneja una solicitud determinada y llamar al método de clase que corresponde al método de la solicitud. Por ejemplo: el método doGet() para solicitudes HTTP GET.

Para asignar una URL a un servlet, declara el servlet con el elemento <servlet> y, luego, define una asignación de una ruta de URL a una declaración de servlet con el elemento <servlet-mapping>.

El elemento <servlet> declara el servlet, lo que incluye un nombre que otros elementos del archivo usan en el archivo, la clase que se usará para el servlet y los parámetros de inicialización. Puedes declarar varios servlets con la misma clase, pero con diferentes parámetros de inicialización. El nombre de cada servlet debe ser único en todo el descriptor de implementación.

    <servlet>
        <servlet-name>redteam</servlet-name>
        <servlet-class>mysite.server.TeamServlet</servlet-class>
        <init-param>
            <param-name>teamColor</param-name>
            <param-value>red</param-value>
        </init-param>
        <init-param>
            <param-name>bgColor</param-name>
            <param-value>#CC0000</param-value>
        </init-param>
    </servlet>

    <servlet>
        <servlet-name>blueteam</servlet-name>
        <servlet-class>mysite.server.TeamServlet</servlet-class>
        <init-param>
            <param-name>teamColor</param-name>
            <param-value>blue</param-value>
        </init-param>
        <init-param>
            <param-name>bgColor</param-name>
            <param-value>#0000CC</param-value>
        </init-param>
    </servlet>

El elemento <servlet-mapping> especifica un patrón de URL y el nombre de un servlet declarado para usar en solicitudes cuyas URL coincidan con el patrón. El patrón de URL puede usar un asterisco (**) al principio o al final del patrón para indicar cero o más de cualquier carácter. El estándar no admite comodines en medio de una string y no permite varios comodines en un solo patrón. El patrón coincide con la ruta completa de la URL, incluida la barra diagonal (/) que sigue al nombre de dominio. La ruta de URL no puede empezar con un punto (.).

    <servlet-mapping>
        <servlet-name>redteam</servlet-name>
        <url-pattern>/red/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>blueteam</servlet-name>
        <url-pattern>/blue/*</url-pattern>
    </servlet-mapping>

Con este ejemplo, una solicitud para la URL http://www.example.com/blue/teamProfile se controla mediante la clase TeamServlet, en la que el parámetro teamColor es igual a blue y el parámetro bgColor es igual a #0000CC. El servlet puede obtener la parte de la ruta de URL que coincide con el comodín mediante el método getPathInfo() del objeto ServletRequest.

El servlet puede acceder a sus parámetros de inicialización si obtiene su configuración de servlet con su propio método getServletConfig() y, luego, llama al método getInitParameter() en el objeto de configuración con el nombre del parámetro como argumento.

String teamColor = getServletConfig().getInitParameter("teamColor");

JSP

Una app puede usar JavaServer Pages (JSP) para implementar páginas web. Los JSP son servlets definidos mediante contenido estático como HTML mezclado con código Java.

App Engine admite la compilación automática y la asignación de URL para JSP. Un archivo JSP en el WAR de la aplicación (fuera de WEB-INF/) cuyo nombre de archivo termina en .jsp se compila en una clase de servlet de manera automática y se le asigna la ruta de URL equivalente a la ruta al archivo JSP desde la raíz del WAR. Por ejemplo, si una app tiene un archivo JSP llamado start.jsp en un subdirectorio llamado register/ en su WAR, App Engine lo compila y lo asigna a la ruta de URL /register/start.jsp.

Para tener más control sobre cómo se asigna el JSP a una URL, puedes especificar la asignación de forma explícita si la declaras con un elemento <servlet> en el descriptor de implementación. En lugar de un elemento <servlet-class>, debes especificar un elemento <jsp-file> con la ruta del archivo JSP desde la raíz del WAR. El elemento <servlet> para el JSP puede contener parámetros de inicialización.

    <servlet>
        <servlet-name>register</servlet-name>
        <jsp-file>/register/start.jsp</jsp-file>
    </servlet>

    <servlet-mapping>
        <servlet-name>register</servlet-name>
        <url-pattern>/register/*</url-pattern>
    </servlet-mapping>

Puedes instalar bibliotecas de etiquetas JSP con el elemento <taglib>. Una biblioteca de etiquetas tiene una ruta al archivo Descriptor de bibliotecas de etiquetas (TLD) JSP (<taglib-location>) y un URI que usan los JSP para seleccionar la biblioteca que se cargará (<taglib-uri>). Ten en cuenta que App Engine proporciona la Biblioteca de etiquetas estándar de JavaServer Pages (JSTL) y no necesitas instalarla.

    <taglib>
        <taglib-uri>/escape</taglib-uri>
        <taglib-location>/WEB-INF/escape-tags.tld</taglib-location>
    </taglib>

Seguridad y autenticación

Una aplicación de App Engine puede usar Cuentas de Google para la autenticación de usuarios. La app puede usar la API de Cuentas de Google para detectar si un usuario accedió a su cuenta, obtener la dirección de correo electrónico del usuario que tiene acceso en ese momento y generar las URL de inicio y cierre de sesión. Una aplicación también puede especificar restricciones de acceso para rutas de URL basadas en Cuentas de Google mediante el descriptor de implementación.

El elemento <security-constraint> define una restricción de seguridad para las URL que coinciden con un patrón. Si un usuario que no ingresó aún accede a una URL cuya ruta tiene una restricción de seguridad, App Engine redirecciona al usuario a la página de acceso de Cuentas de Google. Cuentas de Google vuelve a redireccionar al usuario a la URL de la aplicación después de que este acceda a la cuenta de manera exitosa o cree una cuenta nueva. La app no necesita ninguna acción adicional para asegurarse de que solo los usuarios registrados tienen acceso a la URL.

Una restricción de seguridad incluye una restricción de autorización que especifique qué usuarios de Cuentas de Google pueden acceder a la ruta. Si la restricción de autorización especifica un rol del usuario *, cualquier usuario que haya accedido con una Cuenta de Google puede acceder a la URL. Si la restricción especifica un rol de usuario de admin, solo los desarrolladores registrados de la aplicación pueden acceder a la URL. El rol admin facilita la compilación de secciones exclusivas para administradores de tu sitio.

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>profile</web-resource-name>
            <url-pattern>/profile/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

App Engine no admite funciones de seguridad personalizadas (<security-role>) o mecanismos de autenticación alternativos (<login-config>) en el descriptor de implementación.

Las restricciones de seguridad aplican a los archivos estáticos y a los servlets.

URL seguras

Google App Engine admite conexiones seguras a través de HTTPS para URL mediante el dominio REGION_ID.r.appspot.com. Cuando una solicitud accede a una URL mediante HTTPS y la URL está configurada para usar HTTPS en el archivo web.xml, el remitente encripta los datos de solicitud y los datos de respuesta antes de que se transmitan, y el receptor los desencripta después de recibirlos. Las conexiones seguras son útiles para proteger datos del cliente, como la información de contacto, las contraseñas y los mensajes privados.

Para declarar que HTTPS se debe usar para una URL, configura una restricción de seguridad en el descriptor de implementación (como se describe en Seguridad y autenticación) con una <user-data-constraint> cuya <transport-guarantee> sea CONFIDENTIAL. Por ejemplo:

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>profile</web-resource-name>
            <url-pattern>/profile/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

Las solicitudes que usen HTTP (no seguro) para URL cuya garantía de transporte es CONFIDENTIAL, se redireccionan de manera automática a la misma URL mediante HTTPS.

Cualquier URL puede usar la garantía de transporte CONFIDENTIAL, incluso los JSP y los archivos estáticos.

El servidor web de desarrollado no admite conexiones HTTPS. Este ignora la garantía de transporte para que las rutas destinadas a usarse con HTTPS puedan probarse mediante conexiones HTTP regulares al servidor web de desarrollador.

Cuando pruebes los controladores HTTPS de tu app con la URL con la versión appspot.com, como https://1.latest.your_app_id.REGION_ID.r.appspot.com/, tu navegador te advertirá que el certificado HTTPS no se firmó para esa ruta de acceso específica. Si aceptas el certificado para ese dominio, las páginas se cargarán correctamente. Los usuarios no verán la advertencia sobre el certificado cuando accedan a https://your_app_id.REGION_ID.r.appspot.com/.

También puedes usar una forma alternativa de la URL con la versión appspot.com diseñada para evitar este problema mediante el reemplazo de los puntos que separan los componentes del subdominio por la cadena "-dot-". Por ejemplo, se puede acceder al ejemplo anterior sin una advertencia de certificado en https://VERSION_ID-dot-default-dot-PROJECT_ID.REGION_ID.r.appspot.com.

El acceso y cierre de sesión de las Cuentas de Google siempre se realiza con una conexión segura y es independiente de cómo estén configuradas las URL de la aplicación.

Como se mencionó antes, las restricciones de seguridad aplican a los archivos estáticos y a los servlets. Esto incluye la garantía de transporte.

Nota: Google recomienda usar el protocolo HTTPS para enviar solicitudes a tu app. Google no emite certificados SSL de dominios de comodín doble alojados en appspot.com. Por lo tanto, con HTTPS debes usar la string “-dot-” en vez de “.” para separar los subdominios, como se muestra en los ejemplos que aparecen a continuación. Puedes usar un “.” simple con tu propio dominio personalizado o con las direcciones HTTP. Para obtener más información, consulta HTTPS como indicador de clasificación.

La lista de archivos de bienvenida

Cuando las URL de tu sitio representan rutas a archivos estáticos o JSP en tu WAR, suele ser una buena idea que las rutas a los directorios también hagan algo útil. Un usuario que visita la ruta de URL /help/accounts/password.jsp para obtener información sobre las contraseñas de la cuenta puede intentar visitar /help/accounts/ con el fin de encontrar una página que presente la documentación del sistema de la cuenta. El descriptor de implementación puede especificar una lista de nombres de archivo que el servidor debe probar cuando el usuario accede a una ruta que representa un subdirectorio del WAR que no esté asignado de manera explícita a un servlet. El estándar de servlet llama a esto la “lista de archivos de bienvenida”.

Por ejemplo, si el usuario accede a la ruta de URL /help/accounts/, el siguiente elemento <welcome-file-list> en el descriptor de implementación le dice al servidor que busque help/accounts/index.jsp y help/accounts/index.html antes de informar que la URL no existe:

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

Filtros

Un filtro es una clase que actúa ante una solicitud, como un servlet, pero puede permitir que el procesamiento de la solicitud continúe con otros filtros o servlets. Un filtro puede llevar a cabo una tarea auxiliar, como registrar, realizar verificaciones de autenticación especializadas o anotar los objetos de solicitud o respuesta antes de llamar al servlet. Los filtros te permiten redactar tareas de procesamiento de solicitudes desde el descriptor de implementación.

Una clase de filtro implementa la interfaz javax.servlet.Filter, incluido el método doFilter(). Aquí hay una implementación de filtro simple que registra un mensaje y pasa el control a lo largo de la cadena, que puede incluir otros filtros o un servlet, como lo indica el descriptor de implementación:

package mysite.server;

import java.io.IOException;
import java.util.logging.Logger;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class LogFilterImpl implements Filter {

    private FilterConfig filterConfig;
    private static final Logger log = Logger.getLogger(LogFilterImpl.class.getName());

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {
        log.warning("Log filter processed a " + getFilterConfig().getInitParameter("logType")
            + " request");

        filterChain.doFilter(request, response);
    }

    public FilterConfig getFilterConfig() {
        return filterConfig;
    }

    public void init(FilterConfig filterConfig) {
        this.filterConfig = filterConfig;
    }

    public void destroy() {}

}

De manera similar a los servlets, puedes configurar un filtro en el descriptor de implementación si lo declaras con el elemento <filter> y, luego, lo asignas a un patrón de URL con el elemento <filter-mapping>. También puedes asignar filtros directamente a otros servlets.

El elemento <filter> contiene los elementos <filter-name>, <filter-class> y, de forma opcional, <init-param>.

    <filter>
        <filter-name>logSpecial</filter-name>
        <filter-class>mysite.server.LogFilterImpl</filter-class>
        <init-param>
            <param-name>logType</param-name>
            <param-value>special</param-value>
        </init-param>
    </filter>

El elemento <filter-mapping> contiene un <filter-name> que coincide con el nombre de un filtro declarado, y un elemento <url-pattern> para aplicar el filtro a las URL o un elemento <servlet-name> que coincide con el nombre de un servlet declarado para aplicar el filtro cada vez que se llama al servlet.

    <!-- Log for all URLs ending in ".special" -->
    <filter-mapping>
        <filter-name>logSpecial</filter-name>
        <url-pattern>*.special</url-pattern>
    </filter-mapping>

    <!-- Log for all URLs that use the "comingsoon" servlet -->
    <filter-mapping>
        <filter-name>logSpecial</filter-name>
        <servlet-name>comingsoon</servlet-name>
    </filter-mapping>

Controladores de errores

Puedes personalizar lo que el servidor envía al usuario cuando se produce un error mediante el descriptor de implementación. El servidor puede mostrar una ubicación de página alternativa cuando está por enviar un código de estado HTTP particular o cuando un servlet genera una excepción de Java específica.

El elemento <error-page> contiene un elemento <error-code> con un valor de código de error HTTP (por ejemplo, 500) o un elemento <exception-type> con el nombre de la clase de la excepción esperada (como java.io.IOException). También contiene un elemento <location> que contiene la ruta de URL del recurso para mostrar cuando se produce el error.

    <error-page>
        <error-code>500</error-code>
        <location>/errors/servererror.jsp</location>
    </error-page>

Características de web.xml no compatibles

Las siguientes características de web.xml no son compatibles con App Engine:

  • App Engine es compatible con el elemento <load-on-startup> para las declaraciones de servlet. Sin embargo, la carga se produce en realidad durante la primera solicitud controlada por la instancia del servidor web, no antes.
  • Algunos elementos del descriptor de implementación pueden tener un nombre visible, una descripción y un ícono legibles para su uso en los IDE. App Engine no los usa y los ignora.
  • App Engine no admite variables de entorno JNDI (<env-entry>).
  • App Engine no admite recursos EJB (<resource-ref>).
  • No se admite la notificación de destrucción de servlets, contexto de servlet ni filtros.
  • Se ignora el elemento <distributable>.
  • No se admite la programación de servlets con <run-at>.