Descritor de implantação: web.xml

Os aplicativos da Web em Java usam um arquivo descritor de implantação para determinar como os URLs são mapeados para servlets, quais URLs exigem autenticação e outras informações. Esse arquivo é chamado web.xml e reside no WAR do app, no diretório WEB-INF/. O web.xml faz parte do padrão de servlet para aplicativos da Web.

Para mais informações sobre o padrão web.xml, consulte o wiki de referência sobre web.xml no Metawerx e a especificação Servlet.

Descritores de implantação

O descritor de implantação de um aplicativo da Web indica as classes, os recursos e a configuração do aplicativo e como o servidor da Web os utiliza para veicular solicitações da Web. Ao receber uma solicitação do aplicativo, o servidor da Web usa o descritor de implantação a fim de mapear o URL da solicitação para o código que precisa processá-la.

O descritor de implantação é um arquivo chamado web.xml. Ele reside no WAR do app, no diretório WEB-INF/. Trata-se de um arquivo XML de elemento raiz <web-app>.

Veja a seguir um exemplo simples de web.xml que mapeia todos os caminhos de URL (/*) para a classe 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 e caminhos de URL

O web.xml define mapeamentos entre caminhos de URL e servlets que processam solicitações com esses caminhos. O servidor da Web usa essa configuração a fim de identificar o servlet para processar uma determinada solicitação e chamar o método da classe correspondente ao da solicitação. Por exemplo: o método doGet() para solicitações GET HTTP.

De modo a mapear um URL para um servlet, você declara o servlet com o elemento <servlet> e define um mapeamento de um caminho do URL para uma declaração de servlet com o elemento <servlet-mapping>.

O elemento <servlet> declara o servlet, inclusive um nome usado para se referir ao servlet por outros elementos no arquivo, a classe a ser usada no servlet e os parâmetros de inicialização. Você pode declarar vários servlets usando a mesma classe com parâmetros diferentes de inicialização. O nome de cada servlet precisa ser exclusivo no descritor de implantação.

    <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>

O elemento <servlet-mapping> especifica um padrão de URL e o nome de um servlet declarado a ser usado em solicitações com um URL correspondente ao padrão. O padrão de URL pode usar um asterisco (*) no início ou no fim do padrão para indicar zero ou mais de qualquer caractere. O padrão não é compatível com caracteres curinga no meio de uma string e não permite vários desses caracteres em um padrão. O padrão corresponde ao caminho completo do URL, começando com e incluindo a barra (/) seguida do nome de domínio. O caminho do URL não pode começar com um ponto final (.).

    <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>

Com esse exemplo, uma solicitação para o URL http://www.example.com/blue/teamProfile é processada pela classe TeamServlet, com o parâmetro teamColor igual a blue e o parâmetro bgColor igual a #0000CC. O servlet pode conseguir a parte do caminho do URL correspondente ao caractere curinga usando o método getPathInfo() do objeto ServletRequest.

O servlet pode acessar os parâmetros de inicialização por meio da configuração de servlet usando o próprio método getServletConfig() e chamando o método getInitParameter() no objeto de configuração com o nome do parâmetro como um argumento.

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

JSPs

Um app pode usar páginas do JavaServer (JSPs, na sigla em inglês) para implementar páginas da Web. JSPs são servlets definidos por meio do uso de conteúdo estático, como HTML, combinado ao código Java.

O App Engine é compatível com a compilação automática e o mapeamento de URLs para JSPs. Um arquivo JSP no WAR do aplicativo (fora de WEB-INF/) com nome de arquivo que termina em .jsp é compilado automaticamente em uma classe de servlet e mapeado para o caminho do URL equivalente ao caminho para o arquivo JSP da raiz do WAR. Por exemplo, caso um app tenha um arquivo JSP chamado start.jsp em um subdiretório chamado register/ no WAR, o App Engine o compila e mapeia para o caminho do URL /register/start.jsp.

Se quiser mais controle sobre como o JSP é mapeado para um URL, você poderá especificar explicitamente o mapeamento, declarando-o com um elemento <servlet> no descritor de implantação. Em vez de um elemento <servlet-class>, você especifica um elemento <jsp-file> com o caminho para o arquivo JSP da raiz do WAR. O elemento <servlet> do JSP pode conter parâmetros de inicialização.

    <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>

É possível instalar bibliotecas de tags JSP com o elemento <taglib>. Uma biblioteca de tags tem um caminho para o arquivo de descritor da biblioteca de tags (TLD, na sigla em inglês) JSP (<taglib-location>) e um URI que os JSPs usam para selecionar a biblioteca para carregamento (<taglib-uri>). O App Engine fornece a biblioteca de tags padrão de páginas do JavaServer (JSTL, na sigla em inglês), e você não precisa instalá-la.

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

Segurança e autenticação

Um aplicativo do App Engine pode usar as Contas do Google na autenticação do usuário. O app pode usar a Google Accounts API para detectar se o usuário fez login, conseguir o endereço de e-mail do usuário conectado no momento e gerar URLs de login e saída. Um app também pode especificar restrições de acesso a caminhos de URL com base nas Contas do Google, usando o descritor de implantação.

O elemento <security-constraint> define uma restrição de segurança para URLs correspondentes a um padrão. Se um usuário acessar um URL com um caminho que tenha uma restrição de segurança e não tiver feito login, o App Engine o redirecionará para a página de login das Contas do Google. As Contas do Google redirecionam o usuário para o URL do aplicativo depois que ele faz login ou após o registro de uma nova conta. O app não precisa fazer mais nada para garantir que somente usuários que fizeram login possam acessar o URL.

Uma restrição de segurança inclui uma restrição de autorização que especifica quais usuários das Contas do Google podem acessar o caminho. Se a restrição de autorização especificar um papel do usuário *, qualquer usuário que tenha feito login com uma Conta do Google poderá acessar o URL. Se a restrição especificar um papel do usuário admin, somente desenvolvedores registrados do aplicativo poderão acessar o URL. O papel admin facilita a criação de seções somente administrador do site.

    <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>

O App Engine não aceita papéis personalizados de segurança (<security-role>) ou mecanismos alternativos de autenticação (<login-config>) no descritor de implantação.

As restrições de segurança se aplicam a arquivos estáticos e a servlets.

URLs seguros

O Google App Engine aceita conexões seguras via HTTPS para URLs que usam o domínio *.appspot.com. Quando uma solicitação acessa um URL usando HTTPS e esse URL está configurado para usar HTTPS no arquivo web.xml, os dados tanto da solicitação quanto da resposta são criptografados pelo remetente antes de serem transmitidos, e descriptografados pelo destinatário depois de serem recebidos. As conexões seguras são úteis para proteger dados de clientes, como informações de contato, senhas e mensagens particulares.

Para declarar que o HTTPS precisa ser usado em um URL, configure uma restrição de segurança no descritor de implantação, conforme mostrado em Segurança e autenticação, com <user-data-constraint>, em que <transport-guarantee> seja definido como CONFIDENTIAL. Por exemplo:

    <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>

As solicitações que usam HTTP (não seguro) em URLs com garantia de transporte como CONFIDENTIAL são redirecionadas automaticamente para o mesmo URL que usa HTTPS.

Qualquer URL pode usar a garantia de transporte CONFIDENTIAL, inclusive JSPs e arquivos estáticos.

O servidor da Web de desenvolvimento não é compatível com conexões HTTPS. Ele ignora a garantia de transporte. Dessa maneira, os caminhos a serem usados com HTTPS podem ser testados usando conexões HTTP regulares com o servidor da Web de desenvolvimento.

Quando você testar os gerenciadores de HTTPS do aplicativo usando o URL do appspot.com com versão, como https://1.latest.your_app_id.appspot.com/, seu navegador avisa que o certificado HTTPS não foi assinado para esse caminho de domínio específico. Se aceitar o certificado desse domínio, as páginas serão carregadas com êxito. Os usuários não verão o aviso de certificado ao acessar https://your_app_id.appspot.com/.

Também é possível usar um formato alternativo do URL appspot.com com controle de versão criado para evitar esse problema, substituindo os pontos finais que separam os componentes do subdomínio pela string "-dot-". Por exemplo, o exemplo anterior pode ser acessado sem um aviso de certificado em https://1-dot-latest-dot-your_app_id.appspot.com/.

O login e a saída das Contas do Google são sempre realizados usando uma conexão segura, e isso não está relacionado à maneira como os URLs do aplicativo estão configurados.

Conforme mencionado acima, as restrições de segurança se aplicam a arquivos estáticos e a servlets. Isso inclui a garantia de transporte.

Observação: o Google recomenda o uso do protocolo HTTPS para enviar solicitações ao aplicativo. O Google não emite certificados SSL para domínios com caracteres curinga duplos hospedados em appspot.com. Por isso, com HTTPS, você precisa usar a string "-dot-", em vez de ".", para separar subdomínios, conforme demonstrado nos exemplos abaixo. É possível usar um "." simples com seu próprio domínio personalizado ou com endereços HTTP.

Lista de arquivos de boas-vindas

Quando os URLs do site representam caminhos para arquivos estáticos ou JSPs no WAR, costuma ser bom também ter caminhos para diretórios fazendo algo útil. Um usuário que visita o caminho do URL /help/accounts/password.jsp para informações sobre senhas da conta pode tentar visitar /help/accounts/ a fim de encontrar uma página que apresente a documentação do sistema da conta. O descritor de implantação pode especificar uma lista de nomes de arquivo que o servidor precisa tentar quando o usuário acessa um caminho que representa um subdiretório do WAR ainda não mapeado explicitamente para um servlet. O padrão do servlet chama isso de "lista de arquivos de boas-vindas".

Por exemplo, se o usuário acessar o caminho do URL /help/accounts/, o seguinte elemento <welcome-file-list> no descritor de implantação pedirá para o servidor verificar help/accounts/index.jsp e help/accounts/index.html antes de relatar que o URL não existe:

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

Filtros

Um filtro é uma classe que atua em uma solicitação, como um servlet, mas que pode permitir que o processamento da solicitação continue com outros filtros ou servlets. Um filtro pode realizar uma tarefa auxiliar, como geração de registros, realização de verificações especializadas de autenticação ou anotação dos objetos de solicitação ou de resposta, antes de chamar o servlet. Os filtros permitem escrever tarefas de processamento de solicitação no descritor de implantação.

Uma classe de filtros implementa a interface javax.servlet.Filter, inclusive o método doFilter(). Veja a seguir uma implementação de filtro simples que registra uma mensagem e passa o controle pela cadeia, que pode incluir outros filtros ou um servlet, conforme indicado pelo descritor de implantação:

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 maneira semelhante aos servlets, você configura um filtro no descritor de implantação declarando o filtro com o elemento <filter> e mapeando-o para um padrão de URL com o elemento <filter-mapping>. Também é possível mapear filtros diretamente para outros servlets.

O elemento <filter> contém os elementos <filter-name>, <filter-class> e <init-param> (opcional).

    <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>

O elemento <filter-mapping> contém um <filter-name> correspondente ao nome de um filtro declarado e um elemento <url-pattern> para aplicar o filtro a URLs, ou um elemento <servlet-name> que corresponde ao nome de um servlet declarado para aplicar o filtro sempre que o servlet é chamado.

    <!-- 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>

Gerenciadores de erros

Com o descritor de implantação, você pode personalizar o que o servidor envia ao usuário quando ocorre um erro. O servidor poderá exibir um local alternativo de página quando estiver prestes a enviar um determinado código de status HTTP ou quando um servlet gerar uma determinada exceção Java.

O elemento <error-page> contém um <error-code> com um valor de código de erro HTTP (como 500) ou um <exception-type> com o nome de classe da exceção esperada (como java.io.IOException). Ele também contém um elemento <location> com o caminho do URL do recurso a ser mostrado quando o erro ocorrer.

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

Recursos incompatíveis do web.xml

Os recursos do web.xml a seguir não são compatíveis com o App Engine:

  • O App Engine aceita o elemento <load-on-startup> para declarações de servlet. No entanto, o carregamento ocorre efetivamente durante a primeira solicitação processada pela instância do servidor da Web, e não antes dela.
  • Alguns elementos do descritor de implantação aceitam um nome de exibição legível, uma descrição e um ícone a serem usados em ambientes de desenvolvimento integrado. O App Engine não os utiliza e os ignora.
  • O App Engine não aceita variáveis de ambiente JNDI (<env-entry>).
  • O App Engine não aceita recursos EJB (<resource-ref>).
  • Não são aceitos filtros, notificações de destruição de servlets e contexto do servlets.
  • O elemento <distributable> é ignorado.
  • A programação de servlets com <run-at> não é aceita.