jeudi 21 avril 2011

Manipulation de la Remote Api GAE avec Guice/Objectify

Avec la version 1.4.3 de app engine, arrive une api qui jusque là était disponible seulement en python : la remote api. Celle çi permet de se connecter de façon sécurisée au datastore afin d’y insérer des données par exemple.
La documentation, bien que courte, donne de bonnes explications sur la façon de mettre en place. Mais, c’est le cas simple... Si au sein de mon application app engine, j’ai mis en place Guice et Objectify, je me retrouve confronté à 2 problèmes :
1/ Pour fonctionner ma servlet doit être déclarée dans la configuration Guice.
2/ Je manipule des objets métier et non pas des Entity.

Imaginons je possède l'objet "métier" suivant :

public class Hello {
 
 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;
 
 private String message;
 private String name;
 
 public Hello(){
 }
 
 public Hello(String message, String name) {
  super();
  this.message = message;
  this.name = name;
 }
 
 // Getter & Setter/hascode/equals methods ...

}

Je vais devoir modifier mon GuiceServletConfig pour la mapper avec la servlet. Aussi une servlet déclarée doit Guice doit être un singleton.

public class GuiceServletConfig extends GuiceServletContextListener {

 @Override
 protected Injector getInjector() {
  return Guice.createInjector(new ServletModule() {
   @Override
   protected void configureServlets() {
    bind(RemoteApiServlet.class).in(Singleton.class);
    serve("/remote_api").with(RemoteApiServlet.class);
    // D'autres bindings ou serve ...
   }

  });

 }

}

Le 1er problème est ainsi résolu.
Il ne me reste plus qu'à transformer mes Hello en Entity. Une petite ballade dans le code source permet de trouver la solution.


public static Entity helloToEntity(Hello hello) {
 Objectify ofy = ObjectifyService.begin();
 EntityMetadata<Hello> metadata = factory.getMetadataForEntity(hello);
 return metadata.toEntity(hello, ofy);
}


Il ne reste plus qu'à écrire un petit batch d'alimentation :


public static void main(String[] args) throws IOException {
 

 RemoteApiOptions options = new RemoteApiOptions()
         .server("maSuperApplication.appspot.com", 443)
         .credentials("monEmailAMoi@gmail.com", "monMotDePasse");
 RemoteApiInstaller installer = new RemoteApiInstaller();
 installer.install(options);

List<Entity> hellos = Lists.newArrayList(helloToEntity(new Hello("Hello", "Nicolas")),
          helloToEntity(new Hello("Bonjour", "Vincent")),
          helloToEntity(new Hello("Salut", "Guillaume")),
          helloToEntity(new Hello("Enchanté", "M. Gendal")));

 try {
  DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
  for (Entity hello : hellos) {
   Key key = ds.put(hello);
   System.out.println("Hello key=" + key);
  }
 } finally {
  installer.uninstall();
 }
}


En console, nous obtiendrons :
Hello key=Hello(3001)
Hello key=Hello(4001)
Hello key=Hello(1002)
Hello key=Hello(5001)

Et dans l'interface web du datastore, nous voyons bien nos 4 Hellos :

mercredi 20 avril 2011

Des rubis plein les nuages

Comme vous le savez certainement, WMware au travers de Cloud Foundry propose son offre de cloud, mais pour le moment en bêta. Après une longue semaine d'attente, j'ai enfin reçu mes crédentials.
C'est tourjours par un HelloWorld que commence bon nombre de tutoriaux, celui que j'ai essayé n'échappe pas à cette règle.
Je vais vous faire part de ce petit essai que je trouve intéressant.

En fait, il s'agit de la version Ruby que j'ai essayé. Je n'y connais rien en Ruby, mais qu'à celà ne tienne, Hello c'est pas très compliqué.
Il faut tout d'abord s'assurer de ses version de Ruby et de gem :
bash$ ruby -v
bash$ gem -v
Ils doivent être respectivement en version 1.8.7 et 1.7.2. Il est possible que gem ne soit pas à jour, dans ce cas, la commande suivante est à passer :
bash$ sudo gem update --system
Vous pourrez ainsi installer la gem vmc :
bash$ sudo gem install vmc
Ce qui permettra dans un premier temps de se connecter à son compte :
bash$ vmc target api.cloudfoundry.com
Succesfully targeted to [http://api.cloudfoundry.com]
bash$ vmc login
Email: moiMail@mo.i
Password: ***********
Successfully logged into [http://api.cloudfoundry.com]
On a passé le coté configuration et prêt à déchainer la fureur du code.
bash$ mkdir hello
bash$ cd hello/
bash$ nano hello.rb

Chose promise, chose dûe, un hello tout simple :

require "sinatra"
get '/' do
   "Coucou les gens"
end

Et voilà, on a fini la v1, il ne reste plus qu'à déployer, en répondant à quelques questions :
bash$ vmc push
Would you like to deploy from the current directory? [Yn]: Y
Application Name: nfrancois
Application Deployed URL: 'nfrancois.cloudfoundry.com'? y
Detected a Sinatra Application, is this correct? [Yn]: y
Memory Reservation [Default:128M] (64M, 128M, 256M, 512M, 1G or 2G) 
Creating Application: OK
Would you like to bind any services to 'nfrancois'? [yN]: 
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (0K): OK   
Push Status: OK
Staging Application: OK                                                         
Starting Application: OK   

Aussitôt, dégainage du navigateur à l'url, et c'est déjà accessible :




Mince j'ai oublié des gens importants :

require "sinatra"
get '/' do
   "Coucou les gens, surtout toi lecteur"
end

Prêt à déployer ce correctif :
bash$ vmc update nfrancois
Uploading Application:
  Checking for available resources: OK
  Packing application: OK
  Uploading (0K): OK   
Push Status: OK
Stopping Application: OK
Staging Application: OK                                                         
Starting Application: OK   

Un F5 suffit à vérifier :


C'est ainsi que s'est déroulé mon premier essai déploiement Cloud Foundry et j'ai apprécié sa simplicité. C'est donc un bon point qui donne envie de continuer à s'y intéresser car bien sûr pour s'en faire une bonne idée, il reste de nombreuses choses à tester.
Dans le monde du Ruby dans le cloud, il existe aussi Heroku, assez apprécié me semble t-il dans le monde ruby. Mais mes connaissances rubyste s'arrête là, je ne saurais faire un comparatif des 2 offres.

samedi 16 avril 2011

Ma journée à Mix It

Ce 5 avril 2011, s'est tenu le mix-it, conférence autour de java et de ses pratiques, organisée par le Lyon JUG et Kara. Celle çi était articulée de 5 thèmes : Techy, Agility, Tendy, Mixy, Gamy.
Soit 25 sessions au total. J'étais parmis les 250 personnes qui s'y sont rendu. Je vais vous faire une petite rétrospective de quelques sessions auxquelles j'ai assisté au cours de cette belle journée.

Nous étions quelques Parisiens, habitués du Paris JG, à s'être déplacé, dont certains en temps que Speaker.
Et c'est justement par Nicolas Martignole que commence la traditionnelle keynote, après une courte présentation de Objet Direct, principal sponsor de d'évènement.

Keynote

Nicolas commence par une question, « Qui est fier ce qu'il a réalisé ? ». Peu de monde, dans l'assistance lève la main. Cette question a pour but d'amener au constant suivant : le plus important, ce n'est pas le résultat mais le processus.
Il y a quelques années, on ne parlait pas de TDD, agilité, ni aucun terme tendance auquel nous sommes aujourd'hui habitués. A la place, on parlait d'architecture, d'UML, …
Nous n'avons plus la même façon de construire une application qu'autrefois (si j'ose dire) : nous livrons régulièrement par exemple. Nicolas aime assez la vision du craftmanship et nous expose 12 points du métier de développeur aujourd'hui.
1/ Pattern de l'iceberg : le client ne voit que ¼ de ce qui se passe, il est donc normal qu'il ne comprenne pas qu'une tache ait pris plus de temps que ce qu'il n'imaiginait.
2/ Comprendre ce qui va changer à la fin : Imaginons que vous naviger dans un bateau, vous préférez un point de rendez vous que vous recalculer chaque jour en fonction des contraintes ou un point de rendez vous cap 180° et on se revoit dans 10 jours ?
3/ Simplicité. Nous autre développeurs java, sommes de passionnés de technique et inventons régulièrement de nouveaux frameworks. Mais tant de complexité et elle nécessaire ?
4/ Itérer et avancer. Tout comme un musicien qui s'entraine, nous devons savoir jeter du code, le mouvement est important.
5/ Vision du jardinier, savoir pensez long terme.
6/ Mouvement permanent. Il ne faut pas attendre mais savoir bouger, en faisant de la veille par exemple.
7/ On ne dit pas de « Je fais de l'architecture » mais du « je fais du code propre ». C'est implicite dans la réalisation logicielle.
8/ Avoir le temps de se planter. C'est un avantage de l'agilité. Si au bout de 15 jours vous vous rendez compte ça passe. Mais si un cycle en V fait que vous vous en rendez compte, les conséquence auprès du client de sont pas les mêmes.
9/ Soyez sans pitié. Le code pourri ou en commentaire, il file tout droit à la poubelle.
10/ Le client est roi (mais on est pas non plus sa mère). Avec lui, on ne parle de qu'il connait : le métier, mais pas de techno. A l'inverse, ce n'est pas à lui, d'imaginer la solution technique. Lorsque vous prenez l'avion, donnez vous de conseils au pilote ?
11/ Gérer son manager. C'est en quelque sorte le ministre des affaires étrangère, c'est lui gère les relations entre l'équipe et le monde extérieur. S'il passe trop de temps à gérer l'équipe en elle même, c'est qu'il y a un soucis.
12/ Nous sommes des développeurs. Nous ne voulons pas être chef, amusez vous.

Spock, les tests du futur

Mathilde Lemée, habituée du Paris JUG mais aussi une des fondatrices des Duchess France, vient nous parler de Spock. Ce dernier, n'est pas qu'un personnage de la série Star Trek, c'est aussi framework de test venu de la galaxie Groovy.
Lorsque l'on écrit des tests, on a tendance à oublier leur partie critique : la maintenance.
Spock se veut de rendre les tests lisibles, ce qui facilite grandement de travail lorsque l'on refactore.
L'approche setup/expect/where ou given/when/then, facilitée par les labels, permet de tester une seule chose à la fois. La première syntaxe est adaptée à des cas de test simple type action/vérification du résultat. La seconde, elle est plus adaptée à des cas complexe où par exemple, je vais devoir faire appel à des éléments externes.
Il est important de différencier les mocks des stubs, ils n'ont pas les même rôles. Le stub est un bouchon stupide. Il sert simplement à tester l'état, mais n'a pas vocation à faire échouer le test. Au contraire, le mock peut faire échouer le test, car on teste son comportement.
En terme de code, la différence est assez simple : sois je vérifie si mon appel est bien effectué, soit je ne le fais pas.
Le problème posé par la sur utilisation des mocks, c'est que les tests casse au moindre refactor et consomme du temps pour les réparer.
Martin Fowler apporte plus de précisions dans son article sur le sujet.
Ce qu'apporte principal Spock aux tests, c'est une syntaxe plus souple.

def "account activation mail sent to user"(){
setup:
UserService userService = new UserService()

def emailService = Mock(EmailService) 
emailService.sendMail(_,_,_,_) >> true  
userService.emailService = emailService

User user = new User(email : "testUser@gmail")

when:
boolean success = userService.sendActivationMail(user)

then:
1*.emailService.sendMail("testUser@gmail","admin@admin.com","Your account is activated", "Congratulation now you can login")
success == true
}

Ce que l'on note :
  • Nom de méthode expressif et lisible
  • Configuration simple du résultat de la méthode du mock grace à >>
  • Configuration simple de la vérification de l'appel du mock 1* …
  • Pas d'assert sur le résultat, il est implicite

De façon globale, la syntaxe est assouplie grace aux bloc given/when/then qui permettent d'éviter l'utilisation de mots clés, ce qui au final donne une lecture plus naturelle des instructions.

Cette lisibilité est accru dans le cadre de tests avec jeu de données :

def "String param should correspond to numeric spockInfoDay"() {
    setup:
    def spockResource = new SpockResource(new CalendarDaoStatic())
    expect:
    spockResource.findCalendarByDay(day).day == dayNumeric

    where:
    day     | dayNumeric
    "1"  | 1
    "2"  | 2
    "3"  | 3
  }

N'est ce pas plus plaisant à lire ?

Intelligence collective avec Apache Mahout

Je suis allé sur ce sujet que je ne connais absolu pas, par curiosité. C'est Michael Figuière qui nous a présenté à ce framework.
L'intelligence collective est supérieure à l'intelligence du plus intelligent. Wikipedia est un exemple, il contient beaucoup plus de connaissance que quiconque.
Aujourd'hui, internet permet d'agréger tout un tas de données. Page Rank agrège par exemple l'intelligence collective des sites web. Ainsi, par de nombreux liens, le site officiel de mix-it est la première réponse à la recherche « mit it ».
Le machine learning est un concept clé de Apache Mahout. Il s'agit d'un sous ensemble de l'intelligence artificielle. Les applications en sont par exemple :
  • Recommandation à d'un livre à un client en fonction de ce qu'il a déjà acheté ou de ce que les autres ont achetés en même temps.
  • Classification automatique de mails en fonction de qui a déjà été classé.
  • Conseil de fonctionnalité : si un utilisateur ne s'en sert pas, peut être qu'il ne la connait pas.
  • Adapter filtrage en fonctionnalité du profil : quand je cherche un livre sur java, faut il favoriser l'informatique ou les livres sur l'ile ?
  • Filtrage du spam
  • Agreger un flux actualité en fonction des tendances.

Pour nous, dans le domaine de l'informatique de gestion, ces concepts ne nous sont pas familliés. C'est justement là qu'intervient Apache Mahout en fournissant un implémentation java des algorithmes dernières ces concepts, facilitant ainsi leur intégration dans nos applications. Un bonne partie de cette implémentation est faite en Map/Reduce. Le framework est encore jeune mais connait une croissance rapide.


Pimp my app

Pour finir en épanadiplose, je finis ma journée par la présentation de Nicolas Martignole sur Play.
Quelques éléments pour moderniser une dans un navigateur web tout en s'amusant :
  • Html5/css3/jquery
  • Play
  • Huile de coude

Play! est développé à la base par Guillaume Bord, une personne du monde du web et de Ruby, et donc qui pas connu les joies des EJB 2. Devant la complexité du développement web en java, il a voulu réconcilier les 2 mondes en récréant ce qu'il connaissait avec Rails.
Tout d'abord, Play! n'utilise pas l'api servlet, est sans état sur le serveur. Choses banale dans d'autres technos utilisés pour faire du web. C'est justement sur Rails, Django et Grails que vient sont inspiration.
Avec Play! devient facile de développer rapidement une idée, ainsi moins de scrupules à jeter du code si l'on est pas satisfait. Un des aspect qui permette sa simplicité, c'est son absence de session coté serveur et qui va en adéquation avec les principes de REST.
C'est un framework fullstack, je fais tous avec lui : écriture de code, compilation, test et déploiement. Tous ça au sein de son environnement. Pour éviter d'être prisonnier de son serveur, on peut même packager en war et déployer sur un serveur d'application, bien que l'esprit de soit un peu perdu dans ce cas.
Nicolas nous explique que la question de se poser l'utilisation chez le client et la même que celle qui se posait il a 5 ans, à savoir si on peut utiliser Play.
Et bientôt, avec la version 1.2 on pourra piocher des dépendances avec modules Ivy.
Pour le moment, un ses points faibles c'est l'industrialisation des développements.

Un projet Play à la même structure qu'un projet Ruby :
  • src : les sources aussi bien java que html
  • test
  • conf : la configuration telle que le routage ou les propriétés

Nous avons le droit à une live démo :
  • La classe de base qui est un point d'entrée du controller.
  • La page htlml, est du script groovy
  • Oh mais on pas recompilé et ça marche.
  • En mode dev, Play surveille les fichiers et recompile quand il faut, on laisse ainsi tourner le serveur tout en codant
  • La trace d'erreur est lisible, ça fait gagner beaucoup de temps.
  • Utilisation d'un cookie pour gérer l'état conversationnel avec des cookies.
  • Pas de session sur le serveur

Les 2 derniers points nous pousse à avoir une architecture différente.

Niveau graphique, un inconvéniant, c'est qu'il n'y a rien, il faut retourner aux bases : grande utilisation du css et js.
Cela dit, sur ces 2 domaines en cas de problèmes ont peut toujours trouver de l'aide, venant même d'autre communautés comme php et rails.
Pas un peu de code css3, Nicolas nous montre comment embellir sa partie web en ayant par exemple un bouton full-css, de l'ombrage, ….



Cette conférence a été une belle réussite. Aussi, j'ai ouïe dire qu'elle avait été organisées en très peu de temps. Je profite donc de ce billet pour féliciter donc les responsables de celle ci et espère une prochaine édition l'année prochaine. Il est plaisant de voir que les communautés dans toutes la France se bougent pour nous monter de beaux événements à bas coût. Le prochain rendez vous sera le Breizh camp.

1er anniversaire des Duchess France

C'est ce 14 avril que s'est tenu le 1er anniversaire des Duchess France.
Pour ceux qui n'ont pas suivi, non la noblesse n'a pas été rétablie en France. Il s'agit d'un groupe principalement composé de filles et qui oeuvre pour donner plus de visibilité aux femmes dans le milieu informatique; et bien sûr pas en gestion de projet, mais dans la technique.

A l'arrivée, nous recevons notre badge ainsi qu'un classique petit questionnaire : comment avez vous connu les Duchesses ? Quelle thèmes souhaiteriez voir traité .... A remettre dans une urne, car il pourra faire gagner l'un des lots offerts par les Sponsors. Tout les badges sont marqués d'une petite forme colorée. Quelle est sa signification ? C'est en rentrant dans l'amphithéâtre que la question trouve sa réponse : Nous sommes placés d'après la couleur de gommette. Et c'est ainsi que sont faites les équipes du Trivial Java, ainsi pas de jaloux dans la formation de ces dernières.
La soirée commence par une traditionnelle présentation, ce sont elles/qui elles ne sont pas, quel est le but ?, .... On connaissait déjà les avant-JUG, les groupes de préparation à la certification et bientôt s'annonce un nouvel événement : la marmite !
Mais qu'est ce donc ? Il s'agira d'une rencontre type coding dojo "main dans le code" (ou plutôt dans la marmite).
Après quelques remaniement de certaines couleurs en sous effectif, le jeu peu vraiment commencer. Les règles sont assez simples, chaque équipe envoi un représentant répondre à une série de question sur un thème comme le code java, la veille techno, les frameworks et même l'histoire geeko-javaïste. Des questions bien tordue, notamment sur le code java.
C'est après quelques séries de questions qu'a lieu la pause buffet bien méritée après ces efforts. Celle-çi est l'occasion de discuter avec les diverses connaissances présentes.
Au retour ce celui ci a lieu le tant attendu tirage au sort permettant de gagner les cadeaux offerts par les sponsors (dont Sfeir fait bien sûr parti). Parmi ces lots : un ipad2 (d'ailleurs remporté par une Sferienne : Yasmine Aite), des formations, des pass parleys, .... ainsi qu'un "cadeau surprise" qui se révélât être la fameuse Barbie informaticienne qui avait fait le buzz l'année dernière, remportée par Brice Argenson



La seconde parti reprend sur la lancée de la 1ère partie. Pour ce terminer, sur une égalité entre les verts foncés et les ciels. Pour les départager, c'est un duel entre Julien Dubois et Cédric Beurtheret sur une question de rapidité qui tranchera. C'est Spock (le framework de test) qui emmènera les verts foncés à la victoire par la réponse de Cédric. Leur équipe s'est vue remettre un mug aux couleurs des Duchess.




Pour les plus chevronnés, la soirée se termine par une traditionnelle 3ème mi-temps autour d'une pizza.

Félicitations aux duchesses pour cette belle soirée avec une très bonne animation.
En attendant le prochain anniversaire, nous espérons de bonnes marmites pleines de bonnes technos auxquelles nous voulons tous gouter.

mardi 12 avril 2011

Du Jersey, du Guice et de l'App Engine 3/3

Et pour finir cette série d'article, nous allons nous intéresser à la génération de JSon avec Jersey et bien sûr à sa testabilité.

Le xml, c'est bien gentil, mais dans des échanges REST ça peut être un peu lourd, surtout si le consommateur est un appareil mobile.
La génération JSon avec Jersey peut s'appuyer sur JAX-B. Et oui, c'est justement pour cela que l'on s'en est occupé dans le précédant article. Le mapping, lui, ne change pas.

La resource ne nécessite qu'un petit changement :

@GET
 @Path("{name}")
 @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 
 public Hello reply(@PathParam("name") String name){
  return helloService.saysHelloToSomeone(name);
 }

Comme on est sympa, on permet de renvoyer soit du Json, soit du Xml, c'est le consommateur qui décide. Par défaut, c'est le 1er format qui est choisit, soit le JSon.

La génération JSon va nécessiter un peu de configuration, principalement à cause du type de JSon généré. C'est le ContextResolver qui va s'occuper de ça :

@Provider
@Singleton
public class JAXBContextResolver implements ContextResolver<JAXBContext> {

 /** Package that contains object that can be mapped */
 private static final String JAXB_OBJECT_PACKAGE = Hello.class.getPackage().getName();

 private final JAXBContext context;

 public JAXBContextResolver() throws Exception {
  this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), JAXB_OBJECT_PACKAGE);
 }

 @Override
 public JAXBContext getContext(Class objectType) {
  if(objectType.getPackage().getName().equals(JAXB_OBJECT_PACKAGE)){
   return context;
         }
  return null;
 }
}

Ici le type de JSon souhaité est le natural.
Cet objet doit être passés dans le même package que les resources, il profitera ainsi lui aussi de la découverte automatique au démarrage de Guice.
Ce resolver n'est pas obligatoire, sans lui, le JSon généré est par défaut en mode mapped.

La configuration des tests, va devoir évoluer un peu pour prendre en compte notre génération en ode natural.
La méthode configure() devient :

@Override
 protected AppDescriptor configure() {
  ClientConfig clientConfig = new DefaultClientConfig();
  clientConfig.getClasses().add(JAXBContextResolver.class);
  injector = Guice.createInjector(new ServletModule() {
   @Override
   protected void configureServlets() {
    bind(getTestingResourceClass());
    bind(JAXBContextResolver.class);
    serve("/*").with(GuiceContainer.class);
   }
  }); 
  return new WebAppDescriptor.Builder()
           .contextListenerClass(GuiceTestConfig.class)
           .filterClass(GuiceFilter.class)
           .clientConfig(clientConfig)
           .servletPath("/")
           .build();
 }

Ainsi du coté serveur comme du coté client, les échanges seront dans le même format. Nos tests deviendront :

@Test
 public void shoulReplyHelloInXml(){
  doShoulReplyHello(MediaType.APPLICATION_XML_TYPE);
 }
 
 @Test
 public void shoulReplyHelloInJson(){
  doShoulReplyHello(MediaType.APPLICATION_JSON_TYPE);
 } 
 
 private void doShoulReplyHello(MediaType type){
  String message = "Hello";
  String name ="Nicolas";
  Hello hello = new Hello(message, name);
  when(helloServiceMock.saysHelloToSomeone("Nicolas")).thenReturn(hello);
  
  ClientResponse response = resource().path("hello").path(name).accept(type).get(ClientResponse.class);
  
  verify(helloServiceMock).saysHelloToSomeone(name);
  assertThat(response.getClientResponseStatus()).isEqualTo(Status.OK);
  assertThat(response.getType()).isEqualTo(type);
  Hello entity = response.getEntity(Hello.class);
  assertThat(entity).isNotNull().isEqualTo(hello);  
  
 } 

Une des différenciation entre les types de JSon générés se fait sur la façon dont sont écrites les listes. En mode natural, nous avons par exemple : [objet1, objet2, ...] avec des objet {"attributA":"valeurA", ....}

Imaginons que nous avons une autre resource qui par grande politesse retourne 2 Hellos :

@Path("doublehello")
@Singleton
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class DoubleHelloResource {
 
 @Inject
 private HelloService helloService;
 
 @GET
 @Path("/{name}")
 public List<Hello> reply(@PathParam("name") String name){
  List<Hello> hellos = new ArrayList<Hello>();
  hellos.add(helloService.saysHelloToSomeone(name));
  hellos.add(helloService.saysHelloToSomeone(name));
  return hellos;
 }
 
}

Pour vérifier sa bonne génération, nous aurions le test suivant :

@Test
 public void shoudHaveTwoHello(){
  String message = "Hello";
  String name ="Nicolas";
  when(helloServiceMock.saysHelloToSomeone("Nicolas")).thenReturn(new Hello(message, name)); 
  ClientResponse response = resource().path("doublehello").path(name).get(ClientResponse.class);
  assertThat(response.getStatus()).isEqualTo(Status.OK.getStatusCode());
  assertThat(response.getType()).isEqualTo(MediaType.APPLICATION_JSON_TYPE);
  List hellos = response.getEntity(new GenericType<List<Hello>>(){});
  assertThat(hellos).isNotNull().hasSize(2);
 }
 
 @Test
 public void shoudBeInNaturalJson(){
  String message = "Hello";
  String name ="Nicolas";
  when(helloServiceMock.saysHelloToSomeone("Nicolas")).thenReturn(new Hello(message, name)); 
  ClientResponse response = resource().path("doublehello").path(name).get(ClientResponse.class);
  assertThat(response.getStatus()).isEqualTo(Status.OK.getStatusCode());
  assertThat(response.getType()).isEqualTo(MediaType.APPLICATION_JSON_TYPE);
  String hellos = response.getEntity(String.class);
  assertThat(hellos).isEqualTo(naturalHelloJSon(message, name));
 }
 
 public String naturalHelloJSon(String message, String name){
  StringBuilder sb = new StringBuilder();
  sb.append("[{\"message\":\"").append(message).append("\",\"name\":\"").append(name).append("\"},");
  sb.append("{\"message\":\"").append(message).append("\",\"name\":\"").append(name).append("\"}]");
  return sb.toString();
 }

Même s'il est un format intéressant, le JSon souffre d'un problème lié au javascript : celui du cross-domain qui fait que l'on ne peut pas interroger un autre domain que celui de la page web.
JSonP permet d'évincer cette contrainte.

Jersey permet aussi de générer ce type de réponse mais un peu moins facilement.
Nous allons créer une nouvelle méthode pour ce type de réponse :

@GET
 @Path("{name}.jsonp")
        @Produces("application/x-javascript")
 public JSONWithPadding replyWithJsonP(@PathParam("name") String name, @QueryParam("callback") @DefaultValue(CALLBACK_DEFAULT_NAME) String callback){
  Hello hello = helloService.saysHelloToSomeone(name);
  return new JSONWithPadding(hello, callback);
 } 

Son test reste dans l'optique des précédents :

@Test
 public void shoudBeJsonpWithCallbackNameParam(){
  String message = "Hello";
  String name ="Nicolas";
  when(helloServiceMock.saysHelloToSomeone("Nicolas")).thenReturn(new Hello(message, name));
  String callbackName = "monCallback";
  
  ClientResponse response = resource().path("hello").path(name+".jsonp").queryParam("callback", callbackName).get(ClientResponse.class);
  
  assertThat(response.getStatus()).isEqualTo(Status.OK.getStatusCode());
  assertThat(response.getType().toString()).isEqualTo("application/x-javascript");
  assertThat(response.getEntity(String.class)).isNotNull().startsWith(callbackName);
 }

Je n'ai pas malheureusement pas trouvé comment unmarshmaller ce message.


Et voilà, ce tour d'horizon est fini, amusez vous bien avec ces quelques technos.
Comme à chaque fois, le code source est disponible.

mardi 5 avril 2011

Intégration du SDK Facebook dans une application Android

Cet article est une version écrite de la présentation que j'ai pu faire au BOF dernier de Sfeir.

Pourquoi ?

Faire connaitre son application est une problématique courante auquel est confronté le développeur Android. Donner un aspect "social" à son application peut être une solution.
Facebook est aujourd'hui une référence absolue en matière de réseau social et peut donc contribuer à répondre à cette problématique grace aux services qu'ils expose pour les développeurs. Voici quelles fonctionnalités qui pourrait donner la dimension sociale voulue :
  • Publier sur son mur : exprimer son avis.
  • Organisation d'évènements et inviter des amis à y participer
  • Checkins : marquer sa position

Néanmoins l'intégration de Facebook dans une application Android peut susciter une certaine crainte de la part des utilisateurs :
  • Si je dois m'authentifier au travers de l'application, n'y a t-il pas un risque qu'on me vole mes identifiants ? De plus, le fait de devoir me rentrer mes identifiants va certainement freiner l'envie de l'utilisateur de s'exprimer
  • Cette application ne risque t-elle pas de d'acceder à mes informations pour les renvendre et ma boite mail va être innondées de spam ?

Quoi ?

Le SDK Facebook constitue une solution pour le développeur Android.
En s'appuyant sur l'application officielle, la connexion est ainsi quasiment invisible. Celle çi fait seulement valider à l'application les droits dont dispose l'application sur le compte de l'utilisateur.

Ce SDK n'est qu'un simple adaptateur entre du code en java et la Graph API. Né dans l'esprit de Mark Zuckerberg, elle permet d'accéder et d'interagir avec les données Facebook. En terme plus courant, c'est simplement un ensemble de services REST produisant du JSon.

Imaginons que je souhaite publier sur mon mur, la documentation nous donne :




La requête à adresser sera : me/feed
Et nécessitera l'autorisation de publication de flux.

Comment ?

Tout d'abord, il faut télécharger le SDK. Celui fonctionnant sur le principe d'une library android, il suffit d'ajouter la dépendance nécessaire. Le SDk est fourni avec quelques exemples de manipulation de l'api.
Il faudra aussi enregistrer son application sur Facebook. Déclarez vous en tant que développeur en donnant votre numéro de téléphone si cela n'est pas déjà le cas. La déclaration de l'application permettra permettra d'obtenir un identifiant d'application. Celui ci sera utilisé dans le code Android. Et pour finir la dernière étape : la création de la clé d'après le certificat qui signe l'apk :

keytool -exportcert -alias monApplication -keystore ~/.android/monApplication.keystore | openssl sha1 -binary | openssl base64

Pour les utilisateurs de Windows, il est recommande de passer par un outil comme cygwin pour éviter des problèmes de génération avec openssl.

Sans cette clé configuré, il sera impossible à l'application d'accéder aux services Facebook. L'avantage est que même si le code Android est décompilé, il sera impossible d'utiliser le compte de l'application. L'inconvénient est que pour tester le bon fonctionnement, il faudra générer un apk signé à chaque fois que ce soit pour un téléphone, ou sur l'émulateur.

Il ne reste plus qu'à coder

Si votre application ne possède pas le droit d'accès à internet, n'oublier pas de le rajouter dans le fichier AndroidManifest



La majeure partie du SDK est rassemblée dans l'objet Facebook
Pour instancier cet objet, il suffit de lui passer en paramètre l'identifiant de l'application.

private static final Facebook mFacebook = new Facebook(FACEBOOK_APP_ID);

Un aspect primordial dans une application Android est la gestion asynchrone des tâches dès lors que l'on execute.

private static final AsyncFacebookRunner mAsyncFacebookRunner = new AsyncFacebookRunner(mFacebook);

Lors sa tentative de connexion, le SDK envoie un intent à l'application officielle Facebook. Il est donc important de spécifier le code retour que devra avoir de cette activité, surtout si la votre utilise aussi ce mécanisme.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     if(requestCode == FACEBOOK_REQUEST_CODE){// Retour de login facebook
      FacebookFunctions.handleLoginResult(resultCode, data);      
     }
    }

C'est lors de la demande de connexion que sera passé ce code retour. Cette demande doit aussi spécifier quelles sont les permissions requises par l'application.

private static final String PUBLISH_PERMISSION = "publish_stream";
private static final String[] PERMISSIONS = new String[] { PUBLISH_PERMISSION };
mFacebook.authorize(activity, PERMISSIONS, facebookRequestCode, new LoginDialogListener());

Quant à l'action de publication sur le mur, c'est une simple wrapping de requête web :

public static void publishCommentOnWall(String comment, PocRequestListener requestListener){
  final Bundle parameters = new Bundle();
  parameters.putString(GP_LINK_PARAM_FEED, ANDROID_URL);
  parameters.putString(GP_NAME_PARAM_FEED, "Android");
  parameters.putString(GP_PICTURE_PARAM_FEED, ANDROID_IMAGE_URL); 
  parameters.putString(GP_DESCRIPTION_PARAM_FEED, comment);  
  mAsyncFacebookRunner.request(GP_ME_FEED_URI, parameters, GP_POST_REQUEST, requestListener, null); 
 }

Les paramètres de la méthode request sont :
- La requête à effectuée, soit notre me/feed
- Les paramètre de cette requête.
- Le type de requête, soit du POST
- Un callback de réponse.
- Un objet quelconque de synchronisation qui sert à identifier les appels lorsqu'on en fait plusieurs en même temps. Il est facultatif.


J'ai moi même réalisé l'intégration de Facebook au sein de Keoli TV, une application Android permettant de donner le programme TV en temps réel, projet personnel sur lequel je travail avec quelques amis.

dimanche 3 avril 2011

Du Jersey, du Guice et de l'App Engine 2/3

Dans le dernier article, nous disposions d'un service Hello World qui renvoyait du texte brut.
Cette fois, nous allons lui ajouter la capacité à répondre du xml en sérialisant le message avec JAX-B.

Tout d'abord, notre nouvelle réponse sera faite par l'objet suivant :

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Hello {
 
 private String message;
 private String name;
 
 public Hello(){
 }
 
 public Hello(String message, String name) {
  super();
  this.message = message;
  this.name = name;
 }
 
 public String getMessage() {
  return message;
 }
 
 public void setMessage(String message) {
  this.message = message;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 @Override
 public int hashCode() {
  return Objects.hashCode(message, name);
 }
 
 @Override
 public boolean equals(Object obj) {
     if(obj instanceof Hello){
         final Hello other = (Hello) obj;
         return Objects.equal(message, other.message)
             && Objects.equal(name, other.name);
     } else{
         return false;
     }
 }
}

La ressource exposée évolue peu :

@Path("hello")
@Singleton
@Produces(MediaType.APPLICATION_XML)
public class HelloResource {
 
 @Context 
 UriInfo uriInfo; 
 
 @Inject
 private HelloService helloService;
 
 @GET
 @Path("/{name}")
 public Hello reply(@PathParam("name") String name){
  return helloService.saysHelloToSomeone(name);
 }

 @POST
 public Response send(String name){
  Hello hello = helloService.sendHello(name);
  URI uri = uriInfo.getAbsolutePathBuilder().build();
  return Response.created(uri).entity(hello).build();
 }  
 
 
 public void setHelloService(HelloService helloService) {
  this.helloService = helloService;
 }
 
}

Les opérations de marshall/unmarshall sont opérées directement par Jersey lui même.


De même, les tests vont peu évoluer, seul le type de données attendu va changer. Nous aurons par exemple :

@Test
 public void shoulReplyHello(){
  String name ="Nicolas";
  String hello = "Hello "+name;
  when(helloServiceMock.saysHelloToSomeone(name)).thenReturn(hello);
  
  ClientResponse response = resource().path("hello").path(name).get(ClientResponse.class);
  
  verify(helloServiceMock).saysHelloToSomeone(name);
  assertThat(response.getClientResponseStatus()).isEqualTo(Status.OK);
  assertThat(response.getType()).isEqualTo(MediaType.TEXT_PLAIN_TYPE);
  assertThat(response.getEntity(String.class)).isEqualTo("Hello Nicolas");
  
 }

Et c'est tout, pour aujourd'hui ...

Bon ok, je reconnais, c'est un peu l'arnaque cet article, il y a peu de choses à faire. Mais n'est ce pas justement ça qui est intéressant, non ?

La prochaine fois, nous terminerons cette ballade autour de Jersey en générant du JSon.

Le code est disponible ici.

samedi 2 avril 2011

Git & Geeks

Tout commence par quelques tweets qui fusent, pour un besoin projet, quelques Sferiens demandent à 2 autres Sferiens Git lovers, une petite présentation pour les aider à appréhender l'outil.
C'est par quelques autres tweets que le cercle des personnes intéressées s'élargit et finit par donner lieu à 2 ateliers Git d'une heure et demi organisés en fin de journées.
Bien que ces 2 soirées de présentation débutant n'ont pas eu les même présentateurs, elles étaient complementaires.
Lors de la 1ère, @fsznajderman nous a exposé les commandes de base et nous nous sommes amusés à cloner un simple répertoire contenant un fichier qu'il avait mis à notre disposition sous Assembla. Cela, nous à permis de nous familiariser avec quelques commande de base, mais aussi à voir comment gérer des conflits. Et oui, si on s'amuse à modifier la même ligne, il est tout de même perdu...
Pour la 2ème, @ptit_fred nous a plongé dans la dimension du graphes des commits de Git et nous a appris à mettre un projet sous git, de manier les branches et de les merger.
Personnellement, ces 2 séances m'ont donné l'envie d'étendre mes connaissances et d'après l'avis des participant(e)s, je ne suis pas le seul.
Devant cet enthousiasme, une suite devrait avoir lieu.
Sfeir, c'est ça aussi, une bande de geeks qui aime échanger sur divers sujets.