Il y a 3 années · 15 minutes · Cloud

Premiers pas avec Google App Engine Cloud Endpoint – Article 1

Google App Engine (GAE) n’est pas tout nouveau dans l’univers du cloud, mais le constat est là Google ne lâche pas GAE, mieux, continue d’investir dessus (et oui il y a GAE pour PHP, GO, et il se dit qu’une version nodejs serait sur le point de sortir). La référence de l’application “cool” développée sur GAE (la version python) est Snapchat. Dans son offre Cloud, Google le considère comme un élément à part entière, alors qu’avec Google Compute Engine (GCE) on aurait pu penser que GAE allait se faire discrètement sortir de l’offre Google. Mais non ! pas du tout ! et GAE est même mis en avant comme complémentaire à l’offre GCE.

Dans ce premier article (d’une série de quatre), après une introduction éclair sur l’offre Cloud de Google, nous allons mettre en oeuvre une api todos à l’aide de Cloud Endpoint. A la fin du tutorial de 30 minutes, environ, vous aurez la joie et le bonheur d’avoir déployé votre api dans le cloud google.

PAAS, IAAS … comment se place GAE ?

GAE est un PAAS, en plus long “Platform As A Service”. En résumé ça veut dire que vous ne faites pas l’admin sys, vous développez votre application puis vous déployez et hop voilà c’est accessible à tous ! Votre application devient très populaire ? pas de problème ! vous configurez GAE pour utiliser plus d’instances et il va absorber la charge de vos nouveaux utilisateurs.

GCE de l’offre Google Cloud quand à lui est un IAAS (dire “yass” et pas chaque lettre une par une sinon vous n’êtes pas crédible) et là … il faut tout configurer et ça ne scale pas tout seul, il va falloir être très polyvalent ou avoir dans son équipe le panel de compétences nécessaires.

Finalement on comprend mieux pourquoi GAE a toujours sa place. Il va y avoir ceux qui savent tout faire ou qui ont l’équipe et/ou l’argent pour tout faire, ils pourront allez sur Compute Engine et mettre en place “from scratch” l’architecture nécessaire et spécifique à leurs besoins. A l’opposé si vous ne voulez pas passer trop de temps dans l’admin sys, mettre en place vos outils de load balancing, monitoring, backup, … alors GAE vous apportera ce qu’il faut et pourra scaler à la demande.

Selon Google il y a également une place entre ces deux visions : GAE s’occupe de la partie front de votre application et vous profitez de son savoir faire en terme de scaling, protection DDOS, .. et GCE s’occupe du back pour les traitements longs.

Google Cloud Endpoint : un service GAE

L’offre Google App Engine propose un ensemble de services intégrés, je vous propose plus particulièrement de découvrir le service Cloud Endpoint dont le but est de simplifier le développement de votre API et de l’exécuter dans GAE.

Afin de découvrir sa mise en oeuvre nous allons développer une API nous permettant de gérer une liste de todos. Cela sera l’occasion d’une série de quatre articles durant lesquels nous allons:

  • développer une v1 basique de notre API, la déployer dans GAE et l’utiliser grâce à un client Java (cet article) ;
  • faire évoluer notre api vers une v2 qui sera plus réaliste grâce à l’utilisation du Datastore de GAE ;
  • intégrer notre API dans le projet TodoMVC Angularjs ;
  • ajouter la sécurité Oauth Google à notre API ;

Développer une API Todo List avec Cloud Endpoint

Créer le projet Cloud Endpoint avec maven

Allez on démarre notre projet !

Pour cela nous allons passer en ligne de commande et taper la commande maven suivante (attention pour les projets GAE vous aurez besoin de maven au moins en version 3.1 et Java en version 7) :

mvn archetype:generate -DarchetypeGroupId=com.google.appengine.archetypes -DarchetypeArtifactId=skeleton-archetype -DarchetypeVersion=1.7.5

Pour le groupId je vous propose fr.xebia.gae.todo et pour l’artifactId: todo-api. Pour le reste les valeurs par défaut feront très bien l’affaire.

Pour l’instant dans ce qui a été généré nous avons des dépendances purement App Engine, nous allons donc mettre à jour le fichier pom.xml afin d’avoir des dépendances spécifiques à Cloud Endpoint.

Nous allons utiliser la dernière version du SDK GAE à la date de l’article : 1.9.4

Pour cela remplacer la ligne

<appengine.target.version>1.7.5</appengine.target.version>

Par:

<appengine.target.version>1.9.4</appengine.target.version>

Remplacer le bloc <dependencies> par le suivant:

<dependencies>
    <!-- Compile/runtime dependencies -->
    <dependency>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-api-1.0-sdk</artifactId>
        <version>${appengine.target.version}</version>
    </dependency>
    <dependency>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-endpoints</artifactId>
        <version>${appengine.target.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>
    <!-- Test Dependencies -->
    <dependency>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-testing</artifactId>
        <version>${appengine.target.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.google.appengine</groupId>
        <artifactId>appengine-api-stubs</artifactId>
        <version>${appengine.target.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Remplacer le bloc <build> par le suivant:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <version>2.5.1</version>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <webXml>${project.build.directory}/generated-sources/appengine-endpoints/WEB-INF/web.xml</webXml>
                <webResources>
                    <resource>
                        <!-- this is relative to the pom.xml directory -->
                        <directory>${project.build.directory}/generated-sources/appengine-endpoints</directory>
                        <!-- the list has a default value of ** -->
                        <includes>
                            <include>WEB-INF/*.discovery</include>
                            <include>WEB-INF/*.api</include>
                        </includes>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
        <plugin>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-maven-plugin</artifactId>
            <version>${appengine.target.version}</version>
            <configuration>
                <enableJarClasses>false</enableJarClasses>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>endpoints_get_discovery_doc</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Bon ! on retourne en ligne de commande à la racine du projet, on tape

mvn clean install

On peut alors commencer à bosser sur notre API !

La v1 de notre api Todo

La v1 de notre api sera très simple et nous servira principalement à découvrir comment :

  • déclarer une api avec les annotations
  • démarrer un serveur en local et accéder à l’explorer de l’api
  • débugger notre api
  • déployer sur les serveurs de GAE

Le code de l’api v1

Le code de notre api v1 sera volontairement très simple, elle retournera une liste d’élément Todo sans que l’on puisse en ajouter, supprimer, ou modifier.

Tout le code sera placé dans le package fr.xebia.gae.todo.api.v1

Tout d’abord la classe de notre modèle de données :

package fr.xebia.gae.todo.api.v1.model;

public class Todo {
 private Long id;
 private String title;
 private boolean completed;
 public Todo(Long id, String title, boolean completed) {
  this.id = id;
  this.title = title;
  this.completed = completed;
 }
 … ici les getters setters associés ...
}

Une classe TodoRepository qui contiendra le code afin de récupérer les Todo dans le stockage des Todo :

package fr.xebia.gae.todo.api.v1.repository;
import fr.xebia.gae.todo.api.v1.model.Todo;
import java.util.ArrayList;
import java.util.Collection;


public class TodoRepository {

 private static TodoRepository todoRepository = null;

 private TodoRepository() {
 }

 public static synchronized TodoRepository getInstance() {
  if (null == todoRepository) {
   todoRepository = new TodoRepository();
  }
  return todoRepository;
 }

 public Collection<Todo> findTodos() {
  Collection todos = new ArrayList();
  todos.add(new Todo(new Long(1), "Premier Todo", true));
  todos.add(new Todo(new Long(2), "Second Todo", true));
  todos.add(new Todo(new Long(3), "Troisieme Todo", false));
  return todos;
 }
}

Oui le code est très statique, on retourne toujours les mêmes Todo, mais rappelez vous, notre but dans ce premier article est de déployer une version basique qui nous servira de support à de nouvelles fonctionnalités dans la série d’articles à venir.

Et finalement, TodoEndpoint, la classe la plus importante pour cette V1 puisque c’est elle qui va définir notre api à l’aide des annotations Endpoint :

package fr.xebia.gae.todo.api.v1;


import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import fr.xebia.gae.todo.api.v1.model.Todo;
import fr.xebia.gae.todo.api.v1.repository.TodoRepository;

import java.util.Collection;

@Api(
 name = "todo",
 version = "v1"
)
public class TodoEndPoint {

 @ApiMethod(name = "list", httpMethod = ApiMethod.HttpMethod.GET, path="list")
 public Collection<Todo> getTodos() {
  return TodoRepository.getInstance().findTodos();
 }
}

@Api permet de déclarer une classe contenant le code de notre api, nous utilisons ici deux attributs pour configurer notre api :

  • name : pour définir un nom qui sera utilisé dans l’url de notre api (si rien n’est définit ce sera myapi qui sera utilisé).
  • version : le nom de la version de notre api (la valeur par défaut est v1).

Dans notre exemple si le host est todo-endpoint.appspot.com alors l’accès à notre api passera par l’url suivante: https://todo-endpoint.appspot.com/_ah/api/todo/v1

@ApiMethod s’applique sur chaque méthode afin de préciser son mode d’accès dans l’api :

  • name : précise le nom qui sera utilisé lors de la génération du client de l’api, si rien n’est fourni ce sera le nom de la méthode Java qui sera utilisé.
  • httpMethod : la méthode http utilisée, par défaut la méthode GET est utilisée.
  • path : précise le path d’accès à la méthode, dans l’exemple path = “list” ce qui donnera l’url d’accès suivante : https://todo-endpoint.appspot.com/_ah/api/todo/v1/list

Testons tout ça en local.

Pour tester en local nous utilisons le plugin maven de appengine pour démarrer le serveur Jetty de test :

mvn appengine:devserver

(C’est là que le sdk d’appengine est téléchargé, plus de 150Mo !)

Une fois le serveur démarré, allez sur http://localhost:8080/_ah/api/explorer

Notez au passage que ce qui se passe est assez déconcertant la première fois : l’url va vous rediriger vers une application web google permettant d’explorer votre api local, ce qui fait que l’url dans votre navigateur est transformée en : http://apis-explorer.appspot.com/apis-explorer/?base=http://localhost:8080/_ah/api#p/

Cette application web sert uniquement d’IHM à l’exploration de votre api et les appels sont bien réalisés en local !

Débuger en local ?

Selon le choix de votre IDE le débugage de votre application pourra se faire selon différente manière. Une méthode générique que j’ai utilisé est d’ajouter la configuration suivante au plugin maven appengine afin de pouvoir lancer le débugage à distance depuis votre IDE :

<plugin>
 <groupId>com.google.appengine</groupId>
 <artifactId>appengine-maven-plugin</artifactId>
 <version>${appengine.target.version}</version>
 <configuration>
  <enableJarClasses>false</enableJarClasses>
  <jvmFlags>
   <jvmFlag>-Xdebug</jvmFlag>
   <jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
  </jvmFlags>
 </configuration>
 <executions>
  <execution>
   <goals>
    <goal>endpoints_get_discovery_doc</goal>
   </goals>
  </execution>
 </executions>
</plugin>

Créer un compte GAE et une application

Pour déployer votre application sur GAE il vous faudra déclarer une nouvelle application en allant sur https://appengine.google.com/

On déploie !

Reprenez le nom que vous avez mis dans le champ de saisi “Application identifier”, pour moi c’était “todo-endpoint” pour vous ce sera forcément différent dans la mesure ou ce nom est unique.

Ouvrez le fichier appengine-web.xml et copier votre “Application Identifier” dans l’élément <application>:

<application>todo-api</application>

Egalement, toujours dans le fichier appengine-web.xml modifier la valeur de l’élément <version>:

<version>v1</version>

Dans le terminal taper la commande maven suivante afin de déployer vers GAE

mvn appengine:update

Puisque c’est la première fois que nous déployons notre api vers les serveur de GAE, une fenêtre de votre navigateur web par défaut s’ouvre afin de valider l’accès au plugin maven à votre application GAE:

Vous acceptez bien sûr, puis vous copiez le code qui vous sera donné dans l’input du terminal “Please enter code:”

Enfin une fois terminé vous pouvez aller sur http://votre-application-identifier-ici.appspot.com/_ah/api/explorer

(dans mon exemple puisque mon application identifier est “todo-endpoint” l’url est http://todo-endpoint.appspot.com/_ah/api/explorer).

Et vous retrouvez exactement la même IHM qu’en local mais cette fois l’api s’exécute sur les serveurs de GAE.

Appeler notre api à partir d’un client java généré

Première étape générer notre client java :

mvn appengine:endpoints_get_client_lib

La génération produit un projet maven dans le répertoire target/endpoints-client-libs puis lance un mvn install dans le projet généré permettant d’avoir dans votre repository maven local l’artifact du client de l’api.

Je vous invite à ouvrir readme.html qui donne des informations sur ce qu’il vous faut ajouter dans le pom.xml de projet qui sera client de l’api Todo.

Ensuite nous créons un nouveau projet dans lequel nous appellerons l’api Todo:

mvn archetype:generate \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DgroupId=fr.xebia.gae.todo.api -DartifactId=todo-api-client \
-Dversion=1.0-SNAPSHOT

Dans le pom.xml nous ajoutons les dépendances suivantes (pour mieux comprendre comment connaître les groupID, artifactId, version à utiliser n’hésitez pas à ouvrir le fichier ./todo-api/target/endpoints-client-libs/todo/readme.html, d’autant plus que les numéros de version pourraient avoir changer lorsque vous ferez ce tutorial) :

<dependency>
 <groupId>com.appspot.{application-identifier}</groupId>
 <artifactId>todo</artifactId>
 <version>v1-1.18.0-rc-SNAPSHOT</version>
</dependency>

<dependency>
 <groupId>com.google.http-client</groupId>
 <artifactId>google-http-client-jackson2</artifactId>
 <version>1.15.0-rc</version>
</dependency>

<dependency>
 <groupId>com.google.api-client</groupId>
 <artifactId>google-api-client</artifactId>
 <version>1.18.0-rc</version>
</dependency>

Puis dans la classe App.java modifier le code de la façon suivante:

package fr.xebia.gae.todo.api;


import com.appspot.{application-identifier}.todo.Todo;
import com.appspot.{application-identifier}.todo.model.TodoCollection;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;

import java.io.IOException;


public class App {
 public static void main( String[] args ) throws IOException {
  HttpTransport httpTransport = new NetHttpTransport();
  JsonFactory jsonFactory = new JacksonFactory();
  
  
  Todo.Builder builder = new Todo.Builder(httpTransport, jsonFactory, null);
  builder.setRootUrl("http://localhost:8080/_ah/api/");
  Todo todoService = builder.build();
  TodoCollection todoCollection = todoService.list().execute();
  System.out.println(todoCollection.getItems());
 }
}

Dans ce code on appel notre api en local, il nous faut donc démarrer le serveur en local :

mvn appengine:devserver

Puis lancer l’exécution de notre App. (avec maven en ligne de commande : mvn exec:java -Dexec.mainClass= »fr.xebia.gae.todo.api.App »)

Le résultat devrait être le json suivant:

[{"completed":true,"id":"1","title":"Premier Todo"}, {"completed":true,"id":"2","title":"Second Todo"}, {"completed":false,"id":"3","title":"Troisieme Todo"}]

Conclusion

Cet article a posé les bases pour avancer vers une api un peu plus complexe. Nous savons comment tester en local et déployer vers les serveurs GAE. Dans le prochain article nous allons développer une v2 de notre api todo plus complète et qui persistera les todos dans le datastore GAE.

Xebia France
Xebia est un cabinet de conseil international spécialisé dans les technologies Big Data, Web, les architectures Java et la mobilité dans des environnements agiles. Depuis plus de 11 ans nous avons la volonté de partager notre expertise et nos actualités à travers notre blog technique.

3 réflexions au sujet de « Premiers pas avec Google App Engine Cloud Endpoint – Article 1 »

  1. Publié par Crexy, Il y a 3 années

    Super article, merci.

  2. Publié par Steve, Il y a 3 années

    La suite pour quand?

  3. Publié par jb, Il y a 3 années

    la suite est planifiée pour lundi 23 juin : l’article couvrira la création d’une v2 de l’api en utilisant le datastore avec Objectify pour stocker les todos et l’intégration de l’api dans angularjs en hackant TodoMVC Angularjs

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *