O descritor de implementação: web.xml

ID da região

O REGION_ID é um código abreviado que a Google atribui com base na região que seleciona quando cria a sua app. O código não corresponde a um país ou uma província, embora alguns IDs de regiões possam parecer semelhantes aos códigos de países e províncias usados frequentemente. Para apps criadas após fevereiro de 2020, REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criadas antes desta data, o ID da região é opcional no URL.

Saiba mais acerca dos IDs de regiões.

As aplicações Web Java usam um ficheiro de descritor de implementação para determinar como os URLs são mapeados para servlets, que URLs requerem autenticação e outras informações. Este ficheiro tem o nome web.xml e faz parte da especificação de servlet para aplicações Web. Para mais informações sobre o descritor de implementação web.xml, consulte a especificação de servlet.

Se estiver a migrar do Java 8 e precisar de usar os serviços incluídos antigos com a versão Java suportada mais recente, tem de adicionar o elemento <app-engine-apis> e defini-lo como true no ficheiro web.xml:

<app-engine-apis>true</app-engine-apis>

Descritores de implementação

O descritor de implementação de uma aplicação Web descreve as classes, os recursos e a configuração da aplicação, bem como a forma como o servidor Web os usa para publicar pedidos Web. Quando o servidor Web recebe um pedido para a aplicação, usa o descritor de implementação para mapear o URL do pedido para o código que deve processar o pedido.

O descritor de implementação é um ficheiro denominado web.xml. Está localizado no WAR da app no diretório WEB-INF/. O ficheiro é um ficheiro XML cujo elemento raíz é <web-app>.

O exemplo seguinte web.xml mapeia todos os caminhos de URL (/*) para a classe servlet mysite.server.ComingSoonServlet para a versão 21 e posterior no EE10 (predefinição), a versão 21 no EE8 e a versão 17 e anterior. Para usar a versão suportada mais recente na configuração predefinida, tem de atualizar os servlets da aplicação e as dependências para incluir o espaço de nomes Jakarta. Para saber mais acerca das opções de configuração, consulte o artigo Atualize uma aplicação existente.

v21 e posteriores (EE10)

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
     version="6.0">
    <runtime>java21</runtime> <!-- or another supported version -->
    <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>

v21 (EE8)

<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">
   <runtime>java21</runtime>

  <system-properties> <!-- run your apps on EE8  -->
      <property name="appengine.use.EE8" value="true"/>
  </system-properties>

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

v17 e anterior

  <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">
      <runtime>java17</runtime>
      <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>

Se tiver em conta a sua aplicação nos serviços, cada serviço tem os seus próprios parâmetros de configuração.

Servlets e caminhos de URLs

web.xml define mapeamentos entre caminhos de URL e os servlets que processam pedidos com esses caminhos. O servidor Web usa esta configuração para identificar o servlet para processar um determinado pedido e chamar o método da classe que corresponde ao método de pedido. Por exemplo: o método doGet() para pedidos HTTP GET.

Para mapear um URL para um servlet, declara o servlet com o elemento <servlet> e, em seguida, 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, incluindo um nome usado para se referir ao servlet por outros elementos no ficheiro, a classe a usar para o servlet e parâmetros de inicialização. Pode declarar vários servlets usando a mesma classe com diferentes parâmetros de inicialização. O nome de cada servlet tem de ser exclusivo no descritor de implementaçã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 usar para pedidos cujo URL corresponda 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 caráter. O padrão de URL não suporta carateres universais no meio de uma string e não permite vários carateres universais num padrão. O padrão corresponde ao caminho completo do URL, começando e incluindo a barra (/) a seguir ao nome do 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 este exemplo, um pedido para o URL http://www.example.com/blue/teamProfile é processado pela classe TeamServlet, com o parâmetro teamColor igual a blue e o parâmetro bgColor igual a #0000CC. O servlet pode obter a parte do caminho do URL correspondente ao caráter universal através do método getPathInfo() do objeto ServletRequest.

O servlet pode aceder aos respetivos parâmetros de inicialização obtendo a respetiva configuração do servlet através do respetivo método getServletConfig() e, em seguida, chamando o método getInitParameter() no objeto de configuração com o nome do parâmetro como argumento.

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

JSPs

Uma app pode usar páginas JavaServer (JSPs) para implementar páginas Web. Os JSPs são servlets definidos através de conteúdo estático, como HTML, misturado com código Java.

O App Engine suporta a compilação automática e o mapeamento de URLs para JSPs. Um ficheiro JSP no WAR da aplicação (fora de WEB-INF/) cujo nome de ficheiro termina em .jsp é compilado automaticamente numa classe de servlet e mapeado para o caminho do URL equivalente ao caminho para o ficheiro JSP a partir da raiz do WAR. Por exemplo, se uma app tiver um ficheiro JSP denominado start.jsp num subdiretório denominado register/ no respetivo WAR, o App Engine compila-o e mapeia-o para o caminho do URL /register/start.jsp.

Se quiser ter mais controlo sobre como a JSP é mapeada para um URL, pode especificar o mapeamento explicitamente declarando-o com um elemento <servlet> no descritor de implementação. Em vez de um elemento <servlet-class>, especifica um elemento <jsp-file> com o caminho para o ficheiro JSP a partir da raiz do WAR. O elemento <servlet> para o 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>

Pode instalar bibliotecas de etiquetas JSP com o elemento <taglib>. Uma biblioteca de etiquetas tem um caminho para o ficheiro JSP Tag Library Descriptor (TLD) (<taglib-location>) e um URI que os JSPs usam para selecionar a biblioteca para carregamento (<taglib-uri>). Tenha em atenção que o App Engine fornece a JavaServer Pages Standard Tag Library (JSTL) e não precisa de a instalar.

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

Segurança e autenticação

Uma aplicação do App Engine pode usar Contas Google para a autenticação de utilizadores. A app pode usar a API Google Accounts para detetar se o utilizador tem sessão iniciada, obter o endereço de email do utilizador com sessão iniciada atualmente e gerar URLs de início e fim de sessão. Uma app também pode especificar restrições de acesso para caminhos de URL com base em Contas Google, usando o descritor de implementação.

O elemento <security-constraint> define uma restrição de segurança para URLs que correspondem a um padrão. Se um utilizador aceder a um URL cujo caminho tenha uma restrição de segurança e o utilizador não tiver sessão iniciada, o App Engine redireciona o utilizador para a página de início de sessão das Contas Google. As Contas Google redirecionam o utilizador de volta para o URL da aplicação após iniciar sessão ou registar uma nova conta com êxito. A app não tem de fazer mais nada para garantir que apenas os utilizadores com sessão iniciada podem aceder ao URL.

Uma restrição de segurança inclui uma restrição de autorização que especifica a que contas Google os utilizadores podem aceder ao caminho. Se a restrição de autorização especificar uma função de utilizador de *, todos os utilizadores com sessão iniciada numa Conta Google podem aceder ao URL. Se a restrição especificar uma função de utilizador de admin, apenas os programadores registados da aplicação podem aceder ao URL. A função admin facilita a criação de secções do site apenas para administradores.

    <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 suporta funções de segurança personalizadas (<security-role>) nem mecanismos de autenticação alternativos (<login-config>) no descritor de implementação.

As restrições de segurança aplicam-se a ficheiros estáticos, bem como a servlets.

URLs seguros

O App Engine suporta ligações seguras com HTTPS para URLs que usam o domínio REGION_ID.r.appspot.com. Quando um pedido acede a um URL através de HTTPS e esse URL está configurado para usar HTTPS no ficheiro web.xml, os dados do pedido e os dados da resposta são encriptados pelo remetente antes de serem transmitidos e desencriptados pelo destinatário após a receção. As ligações seguras são úteis para proteger os dados dos clientes, como informações de contacto, palavras-passe e mensagens privadas.

Para declarar que o HTTPS deve ser usado para um URL, configura uma restrição de segurança no descritor de implementação (conforme descrito em Segurança e autenticação) com um <user-data-constraint> cujo <transport-guarantee> é 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>

Os pedidos que usam HTTP (não seguro) para URLs cuja garantia de transporte é CONFIDENTIAL são automaticamente redirecionados para o mesmo URL através de HTTPS.

Qualquer URL pode usar a garantia de transporte CONFIDENTIAL, incluindo ficheiros JSP e estáticos.

O servidor Web de desenvolvimento não suporta ligações HTTPS. Ignora a garantia de transporte, pelo que os caminhos destinados a utilização com HTTPS podem ser testados através de ligações HTTP normais ao servidor Web de desenvolvimento.

Quando testa os controladores HTTPS da sua app através do URL appspot.com com versão, como https://1.latest.your_app_id.REGION_ID.r.appspot.com/, o navegador avisa que o certificado HTTPS não foi assinado para esse caminho de domínio específico. Se aceitar o certificado para esse domínio, as páginas são carregadas com êxito. Os utilizadores não veem o aviso de certificado quando acedem a https://your_app_id.REGION_ID.r.appspot.com/.

Também pode usar uma forma alternativa do URL versionado appspot.com concebida para evitar este problema, substituindo os pontos que separam os componentes do subdomínio pela string "-dot-". Por exemplo, o exemplo anterior podia ser acedido sem um aviso de certificado em https://VERSION_ID-dot-default-dot-PROJECT_ID.REGION_ID.r.appspot.com.

O início e o fim de sessão nas Contas Google são sempre realizados através de uma ligação segura e não estão relacionados com a forma como os URLs da aplicação estão configurados.

Conforme mencionado acima, as restrições de segurança aplicam-se a ficheiros estáticos, bem como a servlets. Isto inclui a garantia de transporte.

Nota: a Google recomenda a utilização do protocolo HTTPS para enviar pedidos para a sua app. A Google não emite certificados SSL para domínios com dois carateres universais alojados em appspot.com. Por conseguinte, com HTTPS, tem de usar a string "-dot-" em vez de "." para separar os subdomínios, conforme mostrado nos exemplos abaixo. Pode usar um simples "." com o seu próprio domínio personalizado ou com endereços HTTP.

A lista de ficheiros de boas-vindas

Quando os URLs do seu site representam caminhos para ficheiros estáticos ou JSPs no seu WAR, é frequentemente uma boa ideia que os caminhos para diretórios também façam algo útil. Um utilizador que visite o caminho de URL /help/accounts/password.jsp para obter informações sobre as palavras-passe das contas pode tentar visitar /help/accounts/ para encontrar uma página que apresente a documentação do sistema de contas. O descritor de implementação pode especificar uma lista de nomes de ficheiros que o servidor deve tentar quando o utilizador acede a um caminho que representa um subdiretório WAR que ainda não está mapeado explicitamente para um servlet. A especificação de servlet chama a isto welcome file list.

Por exemplo, se o utilizador aceder ao caminho do URL /help/accounts/, o seguinte elemento <welcome-file-list> no descritor de implementação indica ao servidor que verifique help/accounts/index.jsp e help/accounts/index.html antes de comunicar 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 num pedido como um servlet, mas pode permitir que o processamento do pedido continue com outros filtros ou servlets. Um filtro pode executar uma tarefa auxiliar, como registar, executar verificações de autenticação especializadas ou anotar os objetos de pedido ou resposta antes de chamar o servlet. Os filtros permitem-lhe compor tarefas de processamento de pedidos a partir do descritor de implementação.

O exemplo de implementação de filtro seguinte regista uma mensagem e passa o controlo pela cadeia, que pode incluir outros filtros ou um servlet, conforme descrito pelo descritor de implementação para a versão 21 e posterior no EE10 (predefinição), na versão 21 no EE8 e na versão 17 e anterior. Para usar a versão suportada mais recente na configuração predefinida, tem de atualizar os servlets da aplicação e as dependências para incluir o espaço de nomes Jakarta. Para saber mais acerca das opções de configuração, consulte o artigo Atualize uma aplicação existente.

v21 e posteriores (EE10)

Esta classe de filtro implementa a interface jakarta.servlet.Filter com o método doFilter().

package mysite.server;

import java.io.IOException;
import java.util.logging.Logger;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.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() {}

}

v21 (EE8)

Esta classe de filtro implementa a interface javax.servlet.Filter com o método doFilter().

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() {}

}

v17 e anterior

Esta classe de filtro implementa a interface javax.servlet.Filter com o método doFilter().

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() {}

}

Semelhante aos servlets, configura um filtro no descritor de implementação declarando o filtro com o elemento <filter> e, em seguida, mapeando-o para um padrão de URL com o elemento <filter-mapping>. Também pode mapear filtros diretamente para outros servlets.

O elemento <filter> contém elementos <filter-name>, <filter-class> e <init-param> opcionais.

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

Controladores de erros

Pode personalizar o que o servidor envia ao utilizador quando ocorre um erro através do descritor de implementação. O servidor pode apresentar uma localização de página alternativa quando está prestes a enviar um código de estado HTTP específico ou quando um servlet gera uma exceção Java específica.

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

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

Não pode configurar controladores de erros personalizados para as seguintes condições de erro:

  • 404 página de resposta quando não é definido um mapeamento de servlet para um URL.
  • Página de erro de quota do 403
  • 500 página de erro do servidor que aparece após um erro interno do App Engine.

Funcionalidades web.xml não suportadas

As seguintes funcionalidades do web.xml não são suportadas pelo App Engine:

  • O App Engine suporta o elemento <load-on-startup> para declarações de servlets. No entanto, o carregamento ocorre realmente durante o primeiro pedido processado pela instância do servidor Web e não antes.
  • Alguns elementos do descritor de implementação podem ter um nome a apresentar legível, uma descrição e um ícone para utilização em IDEs. O App Engine não os usa e ignora-os.
  • O App Engine não suporta variáveis de ambiente JNDI (<env-entry>).
  • O App Engine não suporta recursos EJB (<resource-ref>).
  • A notificação da destruição de servlets, contexto de servlet ou filtros não é suportada.
  • O elemento <distributable> é ignorado.
  • O agendamento de servlets com <run-at> não é suportado.