APIs multiclasse

Se uma única API for particularmente complexa, implemente-a a partir de múltiplas classes Java. Para que diferentes classes façam parte da mesma API, siga estas etapas:

Por exemplo, as duas classes a seguir fazem parte da API tictactoe:

@Api(name = "tictactoe", version = "v1")
class TicTacToeA { … }

@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }

A configuração da API é especificada por meio das propriedades da anotação @Api. No entanto, para várias classes na mesma API, os requisitos de @Api vão além de ter as mesmas strings name e version na anotação @Api de cada classe. Na verdade, a API de back-end não funcionará se houver qualquer diferença nas configurações de API especificadas nas propriedades @Api das classes. Se houver diferenças nas propriedades @Api em uma API multiclasse, será gerada uma configuração "ambígua" que não funciona no Cloud Endpoints Frameworks para App Engine.

Há várias maneiras de criar uma API multiclasse não ambígua:

  • Verifique manualmente se todas as classes de uma única API têm as mesmas propriedades de anotação @Api.
  • Use a herança de anotação por meio da Herança de Java. Nessa herança, todas as classes de uma única API herdam a mesma configuração de API de uma classe base com anotação @Api comum.
  • Use a herança de anotação por meio da anotação @ApiReference em todas as classes de uma única API para que façam referência à mesma configuração de API de uma classe com anotação @Api comum.

Como usar @ApiClass em propriedades que diferem entre as classes

Para usar esse recurso, é necessária a seguinte importação:

import com.google.api.server.spi.config.ApiClass;

Todas as propriedades na anotação @Api precisam corresponder a todas as classes em uma API. No entanto, também é possível usar a anotação @ApiClass para fornecer propriedades que não precisam ser exatamente as mesmas entre classes. Exemplo:

// API methods implemented in this class allow only "clientIdA".
@Api(name = "tictactoe", version = "v1")
@ApiClass(clientIds = { "clientIdA" })
class TicTacToeA { … }

// API methods implemented in this class provide unauthenticated access.
@Api(name = "tictactoe", version = "v1")
class TicTacToeB { … }

em que TicTacToeA limita o acesso usando uma lista de permissões de IDs de clientes contendo o ID do cliente permitido e TicTacToeB não limita o acesso.

Todas as propriedades fornecidas pela anotação @ApiClass têm uma propriedade equivalente na anotação @Api. Observe que a propriedade equivalente de @Api atua como o padrão da API. Se houver um padrão para toda a API para essa mesma propriedade, especificada em @Api, a propriedade @ApiClass específica para classes substituirá o padrão da API.

Nos exemplos a seguir, veja a substituição de propriedades @Api pelos equivalentes @ApiClass específicos da classe:

// For this class "boards" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "boards")
class TicTacToeBoards { … }

// For this class "scores" overrides "games".
@Api(name = "tictactoe", version = "v1", resource = "games")
@ApiClass(resource = "scores")
class TicTacToeScores { … }

// For this class, the API-wide default "games" is used as the resource.
@Api(name = "tictactoe", version = "v1", resource = "games")
class TicTacToeGames { … }

Herança de anotação

As propriedades de anotação @Api e @ApiClass podem ser herdadas de outras classes, e propriedades individuais podem ser modificadas por meio da herança de Java ou da herança de @ApiReference.

Como usar a herança de Java

Uma classe que estende outra classe com anotações @Api ou @ApiClass comporta-se como se fosse anotada com as mesmas propriedades. Exemplo:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }

// TicTacToeA and TicTacToeB both behave as if they have the same @Api annotation as
// TicTacToeBase
class TicTacToeA extends TicTacToeBase { … }
class TicTacToeB extends TicTacToeBase { … }

As anotações são herdadas somente por meio das subclasses Java, não por meio de implementação de interface. Exemplo:

@Api(name = "tictactoe", version = "v1")
interface TicTacToeBase { … }
// Does *not* behave as if annotated.
class TicTacToeA implements TicTacToeBase { … }

Como resultado, não há suporte para qualquer tipo de herança múltipla de anotações de frameworks.

A herança funciona para @ApiClass também:

@ApiClass(resource = "boards")
class BoardsBase { … }

// TicTacToeBoards behaves as if annotated with the @ApiClass from BoardsBase.
// Thus, the "resource" property will be "boards".
@Api(name = "tictactoe", version = "v1", resource = "scores")
class TicTacToeBoards extends BoardsBase { … }

em que TicTacToeBoards herda boards do valor da propriedaderesource de BoardsBase, modificando a configuração de propriedade resource (scores) na anotação @Api. Lembre-se de que será preciso especificar a propriedade de recurso na anotação @Api em todas as classes se alguma classe tiver essa definição na anotação @Api. Com essa técnica de herança, é possível modificar essa propriedade de @Api.

Como usar a herança @ApiReference

Para usar esse recurso, é necessária a seguinte importação:

import com.google.api.server.spi.config.ApiReference;

A anotação @ApiReference fornece um meio alternativo de especificar a herança de anotação. Uma classe que usa @ApiReference para especificar outra classe com anotações @Api ou @ApiClass comporta-se como se estivesse anotada com as mesmas propriedades. Exemplo:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase { … }

// TicTacToeA behaves as if it has the same @Api annotation as TicTacToeBase
@ApiReference(TicTacToeBase.class)
class TicTacToeA { … }

Se a herança de Java e a @ApiReference forem usadas simultaneamente, as anotações serão herdadas somente pela anotação @ApiReference. As anotações @Api e @ApiClass na classe herdada por meio da herança de Java serão ignoradas. Por exemplo:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBaseA { … }
@Api(name = "tictactoe", version = "v2")
class TicTacToeBaseB { … }

// TicTacToe will behave as if annotated the same as TicTacToeBaseA, not TicTacToeBaseB.
// The value of the "version" property will be "v1".
@ApiReference(TicTacToeBaseA.class)
class TicTacToe extends TicTacToeBaseB { … }

Como modificar a configuração herdada

Se sua configuração foi herdada por meio da herança de Java ou @ApiReference, é possível mudá-la usando uma nova anotação @Api ou @ApiClass. Somente as propriedades da configuração especificadas na nova anotação serão modificadas. As propriedades não especificadas ainda serão herdadas. Exemplo:

@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }

// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@Api(name = "checkers")
class Checkers extends TicTacToe { … }

A modificação da herança funciona também para @ApiClass:

@Api(name = "tictactoe", version = "v1")
@ApiClass(resource = "boards", clientIds = { "c1" })
class Boards { … }

// Scores will behave as if annotated with resource = "scores" and clientIds = { "c1" }
@ApiClass(resource = "scores")
class Scores { … }

A modificação funciona também na herança por meio de @ApiReference:

@Api(name = "tictactoe", version = "v2")
class TicTacToe { … }

// Checkers will behave as if annotated with name = "checkers" and version = "v2"
@ApiReference(TicTacToe.class)
@Api(name = "checkers")
class Checkers { … }

Como herdar anotações @ApiMethod

A anotação @ApiMethod pode ser herdada de métodos modificados. Por exemplo:

class TicTacToeBase {
  @ApiMethod(httpMethod = "POST")
  public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
  // setGame behaves as if annotated with the @ApiMethod from TicTacToeBase.setGame.
  // Thus the "httpMethod" property will be "POST".
  @Override
  public Game setGame(Game game) { … }
}

Assim como acontece com a herança das anotações @Api e @ApiClass, as propriedades individuais poderão ser modificadas se vários métodos que modificam um ao outro tiverem anotações @ApiMethod. Exemplo:

class TicTacToeBase {
  @ApiMethod(httpMethod = "POST", clientIds = { "c1" })
  public Game setGame(Game game) { … }
}
@Api(name = "tictactoe", version = "v1")
class TicTacToe extends TicTacToeBase {
  // setGame behaves as if annotated with httpMethod = "GET" and clientIds = { "c1"}.
  @ApiMethod(httpMethod = "GET")
  @Override
  public Game setGame(Game game) { … }
}

Não há qualquer anotação @ApiReference ou equivalente para os métodos, por isso, @ApiMethod é sempre herdado da herança de Java e não de @ApiReference.

Regras de herança e precedência

Para resumir a discussão sobre precedência, a tabela a seguir apresenta as regras de herança e a ordem de precedência:

Anotação/herança Regra
@Api Precisa ser idêntica para todas as classes.
@ApiClass Especificada para uma classe para modificar propriedades de @Api.
Herança de Java A classe herda @Api e @ApiClass da classe base.
@ApiReference A classe herda @Api e @ApiClass da classe especificada.
Usar @ApiReference em uma classe (Java) que herda de uma classe base. A classe herda a @Api e @ApiClass da classe especificada, não da classe base.

Casos de uso comuns da herança de anotação

Exemplos de casos de uso típicos de herança:

Para controle de versão de API:

@Api(name = "tictactoe", version = "v1")
class TicTacToeV1 { … }
@Api(version = "v2")
class TicTacToeV2 extends TicTacToeV1 { … }

Para APIs multiclasse:

@Api(name = "tictactoe", version = "v1")
class TicTacToeBase {}
@ApiClass(resource = "boards")
class TicTacToeBoards extends TicTacToeBase { … }
@ApiClass(resource = "scores")
class TicTacToeScores extends TicTacToeBase { … }

Para testar diferentes versões da mesma API:

@Api(name = "tictactoe", version = "v1")
class TicTacToe {
  protected Foo someMethod() {
    // Do something real;
  }

  public Foo getFoo() { … }
}

@Api(version="v1test")
class TicTacToeTest extends TicTacToe {
  protected Foo someMethod() {
    // Stub out real action;
  }
}

em que someMethod retorna respostas predeterminadas, evita chamadas com efeitos colaterais, ignora uma solicitação de rede ou de armazenamento de dados e assim por diante.

Como adicionar as classes a web.xml

Depois de anotar suas classes, adicione-as ao seu arquivo web.xml. O exemplo a seguir mostra uma única classe:

<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">
    <!-- Wrap the backend with Endpoints Frameworks v2. -->
    <servlet>
        <servlet-name>EndpointsServlet</servlet-name>
        <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
        <init-param>
            <param-name>services</param-name>
            <param-value>com.example.skeleton.MyApi</param-value>
        </init-param>
    </servlet>
    <!-- Route API method requests to the backend. -->
    <servlet-mapping>
        <servlet-name>EndpointsServlet</servlet-name>
        <url-pattern>/_ah/api/*</url-pattern>
    </servlet-mapping>
</web-app>

Para adicionar várias classes:

  1. Substitua <param-value>com.example.skeleton.MyApi</param-value> pelo nome de classe da API.

  2. Adicione cada classe no mesmo campo <param-value> separado por uma vírgula, por exemplo:

    <param-value>com.example-company.example-api.Hello,com.example-company.example-api.Goodbye</param-value>